Skip to content

Fix addNamedDestination writing the wrong number of parameters - #1783

Open
MahathirMohammadShuvo wants to merge 1 commit into
foliojs:masterfrom
MahathirMohammadShuvo:fix-xyz-destination-params
Open

Fix addNamedDestination writing the wrong number of parameters#1783
MahathirMohammadShuvo wants to merge 1 commit into
foliojs:masterfrom
MahathirMohammadShuvo:fix-xyz-destination-params

Conversation

@MahathirMohammadShuvo

Copy link
Copy Markdown
Contributor

What kind of change does this PR introduce?

Bug fix. No linked issue.

addNamedDestination passes its arguments straight through, so a call can write a
destination carrying more or fewer parameters than its type takes (ISO 32000-1,
Table 151). Two things go wrong.

It can throw. An XYZ top is flipped against the page height, and
page.height - undefined is NaN:

doc.addNamedDestination('LINK', 'XYZ', 36);
doc.end();   // Error: unsupported number: NaN

That is raised from PDFObject.number at doc.end() and names neither the method
nor the argument. Only the top position was affected — an undefined left or zoom
already serialised as null — so the method was inconsistent about which missing
parameter it tolerated. Passing the top explicitly as undefined threw the same way
and no longer does, which is the one behaviour change an existing caller could
notice.

Surplus parameters silently produce a dead link. pdfjs-dist rejects these
outright, so getDestination() resolves to null and the link goes nowhere:

call before after
addNamedDestination('L', 'Fit', 99) [page /Fit 99] — dead [page /Fit]
addNamedDestination('L', 'XYZ', 1, 2, 3, 4) [page /XYZ 1 790 3 4] — dead [page /XYZ 1 790 3]

Short parameter lists are the milder half, since a reader does accept
[page /XYZ left top]. But it is still a parameter short of what Table 151 defines,
and it is the natural way to write a contents entry meant to jump to a point without
disturbing the reader's zoom: addNamedDestination('LINK', 'XYZ', x, y).

What the change does. Arguments are trimmed to the type's parameter list, and a
short list is filled out with null for the five types whose parameters Table 151
allows to be null: XYZ, FitH, FitV, FitBH and FitBV. FitR has no such
allowance
, so a short FitR is passed through exactly as it is today, as is a
destination type that is not recognised. This resizes the argument list; it does not
validate the parameter values, so ('L', 'FitR', 1, null, 3, 4) still goes out as it
does now.

Across all eight types at arities 0–5, every exact-arity output is byte-identical to
master — no well-formed call changes. doc.text(..., { destination }) and the image
path both pass every parameter explicitly and are unaffected.

One thing I left alone, and a question.

XYZ's top is flipped against the page height but FitH's is not, so
docs/destinations.md:11 describes ('LINK', 'FitH', 100) as "vertical top is
100"
while that value reaches the reader in bottom-left origin — unlike the XYZ
example below it on line 14. tests/unit/trailer.spec.js pins the current behaviour,
so I have not touched it and it is out of scope here. Is that deliberate? If you
would like it changed I am happy to do it separately.

Checklist:

  • Unit Tests
  • Documentation
  • Update CHANGELOG.md
  • Ready to be merged

addNamedDestination passed its arguments straight through, so a call could
write a destination carrying more or fewer parameters than its type takes
(ISO 32000-1, Table 151).

Surplus parameters are the clearer problem, because a reader does not
tolerate them: pdfjs-dist rejects [page /Fit 99] and
[page /XYZ left top zoom extra] outright, so the link goes nowhere, while
accepting both of them trimmed.

Leaving the top off an XYZ destination, or passing it as undefined, was the
noisier case. The top is flipped against the page height, and page.height
minus undefined is NaN, which threw `unsupported number: NaN` from PDFObject
once the destination was serialised — an error naming neither the method nor
the argument. Only that one position was affected: an undefined left or zoom
already serialised as null, so the method was inconsistent about which
missing parameter it would tolerate.

Arguments are now trimmed to the type's parameter list, and a short list is
filled out with null for the five types whose parameters Table 151 allows to
be null: XYZ, FitH, FitV, FitBH and FitBV. FitR has no such allowance, so a
short FitR is passed through as it was given, as is a destination type that
is not recognised. This resizes the argument list; it does not validate the
parameter values.
@blikblum

Copy link
Copy Markdown
Member

IMHO such defensive approaches should not land. All examples are using the api in a wrong way

pdfkit is a low level library and sanitization of input should be done a layer above

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants