Fix bad truetype loca tables.

Some fonts have loca tables that aren't sorted or use 0 as an offset to
signal a missing glyph. This fixes the bad loca tables by sorting them
and then rewriting the loca table and potentially re-ordering the glyf
table to match.

Fixes #11131 and bug 1650302.
This commit is contained in:
Brendan Dahl 2020-08-07 13:04:53 -07:00
parent 8c162f57f7
commit f6dff81223
5 changed files with 53 additions and 27 deletions

View File

@ -2050,35 +2050,48 @@ var Font = (function FontClosure() {
var oldGlyfData = glyf.data;
var oldGlyfDataLength = oldGlyfData.length;
var newGlyfData = new Uint8Array(oldGlyfDataLength);
var startOffset = itemDecode(locaData, 0);
var writeOffset = 0;
var missingGlyphs = Object.create(null);
itemEncode(locaData, 0, writeOffset);
var i, j;
for (i = 0, j = itemSize; i < numGlyphs; i++, j += itemSize) {
var endOffset = itemDecode(locaData, j);
// The spec says the offsets should be in ascending order, however
// some fonts use the offset of 0 to mark a glyph as missing.
if (endOffset === 0) {
endOffset = startOffset;
}
if (
endOffset > oldGlyfDataLength &&
((oldGlyfDataLength + 3) & ~3) === endOffset
) {
// Aspose breaks fonts by aligning the glyphs to the qword, but not
// the glyf table size, which makes last glyph out of range.
endOffset = oldGlyfDataLength;
}
if (endOffset > oldGlyfDataLength) {
// glyph end offset points outside glyf data, rejecting the glyph
startOffset = endOffset;
}
// The spec says the offsets should be in ascending order, however
// this is not true for some fonts or they use the offset of 0 to mark a
// glyph as missing. OTS requires the offsets to be in order and not to
// be zero, so we must sort and rebuild the loca table and potentially
// re-arrange the glyf data.
var i, j;
const locaEntries = [];
// There are numGlyphs + 1 loca table entries.
for (i = 0, j = 0; i < numGlyphs + 1; i++, j += itemSize) {
let offset = itemDecode(locaData, j);
if (offset > oldGlyfDataLength) {
offset = oldGlyfDataLength;
}
locaEntries.push({
index: i,
offset,
endOffset: 0,
});
}
locaEntries.sort((a, b) => {
return a.offset - b.offset;
});
// Now the offsets are sorted, calculate the end offset of each glyph.
// The last loca entry's endOffset is not calculated since it's the end
// of the data and will be stored on the previous entry's endOffset.
for (i = 0; i < numGlyphs; i++) {
locaEntries[i].endOffset = locaEntries[i + 1].offset;
}
// Re-sort so glyphs aren't out of order.
locaEntries.sort((a, b) => {
return a.index - b.index;
});
var missingGlyphs = Object.create(null);
var writeOffset = 0;
itemEncode(locaData, 0, writeOffset);
for (i = 0, j = itemSize; i < numGlyphs; i++, j += itemSize) {
var glyphProfile = sanitizeGlyph(
oldGlyfData,
startOffset,
endOffset,
locaEntries[i].offset,
locaEntries[i].endOffset,
newGlyfData,
writeOffset,
hintsValid
@ -2092,7 +2105,6 @@ var Font = (function FontClosure() {
}
writeOffset += newLength;
itemEncode(locaData, j, writeOffset);
startOffset = endOffset;
}
if (writeOffset === 0) {

View File

@ -63,6 +63,7 @@
!issue8372.pdf
!issue8424.pdf
!issue8480.pdf
!bug1650302_reduced.pdf
!issue8570.pdf
!issue8697.pdf
!issue8702.pdf
@ -121,6 +122,7 @@
!bug1132849.pdf
!issue6894.pdf
!issue5804.pdf
!issue11131_reduced.pdf
!Pages-tree-refs.pdf
!ShowText-ShadingPattern.pdf
!complex_ttf_font.pdf

Binary file not shown.

Binary file not shown.

View File

@ -3268,6 +3268,12 @@
"link": true,
"type": "eq"
},
{ "id": "issue11131_reduced",
"file": "pdfs/issue11131_reduced.pdf",
"md5": "004b7e7d2b133a8dc4fe64aaf3dc9533",
"rounds": 1,
"type": "eq"
},
{ "id": "issue818",
"file": "pdfs/issue818.pdf",
"md5": "dd2f8a5bd65164ad74da2b45a6ca90cc",
@ -3365,6 +3371,12 @@
"link": false,
"type": "load"
},
{ "id": "bug1650302_reduced",
"file": "pdfs/bug1650302_reduced.pdf",
"md5": "d918c9ec936486e8b6656e10dd909014",
"rounds": 1,
"type": "eq"
},
{ "id": "blendmode",
"file": "pdfs/blendmode.pdf",
"md5": "5a86e7e9333e93c58abc3f382e1e6ea2",