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:
parent
8c162f57f7
commit
f6dff81223
@ -2050,35 +2050,48 @@ var Font = (function FontClosure() {
|
|||||||
var oldGlyfData = glyf.data;
|
var oldGlyfData = glyf.data;
|
||||||
var oldGlyfDataLength = oldGlyfData.length;
|
var oldGlyfDataLength = oldGlyfData.length;
|
||||||
var newGlyfData = new Uint8Array(oldGlyfDataLength);
|
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(
|
var glyphProfile = sanitizeGlyph(
|
||||||
oldGlyfData,
|
oldGlyfData,
|
||||||
startOffset,
|
locaEntries[i].offset,
|
||||||
endOffset,
|
locaEntries[i].endOffset,
|
||||||
newGlyfData,
|
newGlyfData,
|
||||||
writeOffset,
|
writeOffset,
|
||||||
hintsValid
|
hintsValid
|
||||||
@ -2092,7 +2105,6 @@ var Font = (function FontClosure() {
|
|||||||
}
|
}
|
||||||
writeOffset += newLength;
|
writeOffset += newLength;
|
||||||
itemEncode(locaData, j, writeOffset);
|
itemEncode(locaData, j, writeOffset);
|
||||||
startOffset = endOffset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (writeOffset === 0) {
|
if (writeOffset === 0) {
|
||||||
|
2
test/pdfs/.gitignore
vendored
2
test/pdfs/.gitignore
vendored
@ -63,6 +63,7 @@
|
|||||||
!issue8372.pdf
|
!issue8372.pdf
|
||||||
!issue8424.pdf
|
!issue8424.pdf
|
||||||
!issue8480.pdf
|
!issue8480.pdf
|
||||||
|
!bug1650302_reduced.pdf
|
||||||
!issue8570.pdf
|
!issue8570.pdf
|
||||||
!issue8697.pdf
|
!issue8697.pdf
|
||||||
!issue8702.pdf
|
!issue8702.pdf
|
||||||
@ -121,6 +122,7 @@
|
|||||||
!bug1132849.pdf
|
!bug1132849.pdf
|
||||||
!issue6894.pdf
|
!issue6894.pdf
|
||||||
!issue5804.pdf
|
!issue5804.pdf
|
||||||
|
!issue11131_reduced.pdf
|
||||||
!Pages-tree-refs.pdf
|
!Pages-tree-refs.pdf
|
||||||
!ShowText-ShadingPattern.pdf
|
!ShowText-ShadingPattern.pdf
|
||||||
!complex_ttf_font.pdf
|
!complex_ttf_font.pdf
|
||||||
|
BIN
test/pdfs/bug1650302_reduced.pdf
Normal file
BIN
test/pdfs/bug1650302_reduced.pdf
Normal file
Binary file not shown.
BIN
test/pdfs/issue11131_reduced.pdf
Normal file
BIN
test/pdfs/issue11131_reduced.pdf
Normal file
Binary file not shown.
@ -3268,6 +3268,12 @@
|
|||||||
"link": true,
|
"link": true,
|
||||||
"type": "eq"
|
"type": "eq"
|
||||||
},
|
},
|
||||||
|
{ "id": "issue11131_reduced",
|
||||||
|
"file": "pdfs/issue11131_reduced.pdf",
|
||||||
|
"md5": "004b7e7d2b133a8dc4fe64aaf3dc9533",
|
||||||
|
"rounds": 1,
|
||||||
|
"type": "eq"
|
||||||
|
},
|
||||||
{ "id": "issue818",
|
{ "id": "issue818",
|
||||||
"file": "pdfs/issue818.pdf",
|
"file": "pdfs/issue818.pdf",
|
||||||
"md5": "dd2f8a5bd65164ad74da2b45a6ca90cc",
|
"md5": "dd2f8a5bd65164ad74da2b45a6ca90cc",
|
||||||
@ -3365,6 +3371,12 @@
|
|||||||
"link": false,
|
"link": false,
|
||||||
"type": "load"
|
"type": "load"
|
||||||
},
|
},
|
||||||
|
{ "id": "bug1650302_reduced",
|
||||||
|
"file": "pdfs/bug1650302_reduced.pdf",
|
||||||
|
"md5": "d918c9ec936486e8b6656e10dd909014",
|
||||||
|
"rounds": 1,
|
||||||
|
"type": "eq"
|
||||||
|
},
|
||||||
{ "id": "blendmode",
|
{ "id": "blendmode",
|
||||||
"file": "pdfs/blendmode.pdf",
|
"file": "pdfs/blendmode.pdf",
|
||||||
"md5": "5a86e7e9333e93c58abc3f382e1e6ea2",
|
"md5": "5a86e7e9333e93c58abc3f382e1e6ea2",
|
||||||
|
Loading…
Reference in New Issue
Block a user