Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

### Unreleased

- Fix links to a named destination going nowhere in readers that look the name up in sorted order, such as Chrome (PDFium) and qpdf, because name tree keys were sorted by locale with `localeCompare` instead of by their bytes: with destinations named `appendix` and `Chapter1`, `Chapter1` was written second and could not be found. The keys of the `EmbeddedFiles` and `JavaScript` name trees are sorted the same way
- Escape document information in XMP metadata and omit characters XML 1.0 cannot represent, without changing the PDF Info dictionary. Fixes #1817
- Fix text in a standard font garbling or dropping the characters after a tab, a carriage return or any other character code below `0x10`, which was written into the hex string of the text as a single digit, so the following digits paired up wrongly: a `\r` before a line break showed as `Ð`, and `Tab\there` was read back as `Tab–†W&P`. Fixes #1425, #606
- Reuse the glyph width and kerning tables of a standard font across documents instead of building them again for every document that uses it, which made a short document using two standard fonts more than twice as slow to create
Expand Down
5 changes: 4 additions & 1 deletion lib/name_tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import PDFTree from './tree';

class PDFNameTree extends PDFTree {
_compareKeys(a, b) {
return a.localeCompare(b);
// Readers look a key up expecting the keys in ascending byte order
// (ISO 32000-1, 7.9.6). Character codes compare in that order for ASCII
// keys, while `localeCompare` puts `appendix` before `Chapter1`.
return a < b ? -1 : a > b ? 1 : 0;
}

_keysName() {
Expand Down
18 changes: 18 additions & 0 deletions tests/unit/tree.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,24 @@ describe.each([
});
});

describe('PDFNameTree key order', () => {
test('sorts the keys by their bytes rather than by locale', () => {
const tree = new PDFNameTree();
tree.add('appendix', new String('one'));
tree.add('Chapter1', new String('two'));

// readers look keys up in byte order, in which `Chapter1` (0x43) comes
// before `appendix` (0x61); in locale order they could not find it
expect(PDFObject.convert(tree)).toEqual(`<<
/Limits [(Chapter1) (appendix)]
/Names [
(Chapter1) (two)
(appendix) (one)
]
>>`);
});
});

describe('name trees in a document', () => {
const names = ['(heading)', '(data.txt)', 'app.alert'];

Expand Down
Loading