Strip TT programs if it's invalid
This commit is contained in:
parent
78ad4d4eaf
commit
cc5cba8bec
100
src/fonts.js
100
src/fonts.js
@ -3341,7 +3341,8 @@ var Font = (function FontClosure() {
|
||||
}
|
||||
}
|
||||
|
||||
function sanitizeGlyph(source, sourceStart, sourceEnd, dest, destStart) {
|
||||
function sanitizeGlyph(source, sourceStart, sourceEnd, dest, destStart,
|
||||
hintsValid) {
|
||||
if (sourceEnd - sourceStart <= 12) {
|
||||
// glyph with data less than 12 is invalid one
|
||||
return 0;
|
||||
@ -3361,8 +3362,10 @@ var Font = (function FontClosure() {
|
||||
j += 2;
|
||||
}
|
||||
// skipping instructions
|
||||
var instructionsStart = j;
|
||||
var instructionsLength = (glyf[j] << 8) | glyf[j + 1];
|
||||
j += 2 + instructionsLength;
|
||||
var instructionsEnd = j;
|
||||
// validating flags
|
||||
var coordinatesLength = 0;
|
||||
for (var i = 0; i < flagsCount; i++) {
|
||||
@ -3385,6 +3388,17 @@ var Font = (function FontClosure() {
|
||||
// not enough data for coordinates
|
||||
return 0;
|
||||
}
|
||||
if (!hintsValid && instructionsLength > 0) {
|
||||
dest.set(glyf.subarray(0, instructionsStart), destStart);
|
||||
dest.set([0, 0], destStart + instructionsStart);
|
||||
dest.set(glyf.subarray(instructionsEnd, glyphDataLength),
|
||||
destStart + instructionsStart + 2);
|
||||
glyphDataLength -= instructionsLength;
|
||||
if (glyf.length - glyphDataLength > 3) {
|
||||
glyphDataLength = (glyphDataLength + 3) & ~3;
|
||||
}
|
||||
return glyphDataLength;
|
||||
}
|
||||
if (glyf.length - glyphDataLength > 3) {
|
||||
// truncating and aligning to 4 bytes the long glyph data
|
||||
glyphDataLength = (glyphDataLength + 3) & ~3;
|
||||
@ -3441,7 +3455,7 @@ var Font = (function FontClosure() {
|
||||
}
|
||||
|
||||
function sanitizeGlyphLocations(loca, glyf, numGlyphs,
|
||||
isGlyphLocationsLong) {
|
||||
isGlyphLocationsLong, hintsValid) {
|
||||
var itemSize, itemDecode, itemEncode;
|
||||
if (isGlyphLocationsLong) {
|
||||
itemSize = 4;
|
||||
@ -3483,7 +3497,7 @@ var Font = (function FontClosure() {
|
||||
}
|
||||
|
||||
var newLength = sanitizeGlyph(oldGlyfData, startOffset, endOffset,
|
||||
newGlyfData, writeOffset);
|
||||
newGlyfData, writeOffset, hintsValid);
|
||||
writeOffset += newLength;
|
||||
itemEncode(locaData, j, writeOffset);
|
||||
startOffset = endOffset;
|
||||
@ -3648,7 +3662,7 @@ var Font = (function FontClosure() {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, -2, -2, -2, -2, 0, 0, -2, -5,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, 0, 0, -1, 0, -1, -1, -1, -1,
|
||||
1, -1, -999, 0, 1, 0, -1, -2, 0, -1, -2, -1, -1, 0, -1, -1,
|
||||
0, 0, -999, -999, -1, -1, -1, -1, -2, -999, -2, -2, -2, 0, -2, -2,
|
||||
0, 0, -999, -999, -1, -1, -1, -1, -2, -999, -2, -2, -999, 0, -2, -2,
|
||||
0, 0, -2, 0, -2, 0, 0, 0, -2, -1, -1, 1, 1, 0, 0, -1,
|
||||
-1, -1, -1, -1, -1, -1, 0, 0, -1, 0, -1, -1, 0, -999, -1, -1,
|
||||
-1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
@ -3801,33 +3815,22 @@ var Font = (function FontClosure() {
|
||||
foldTTTable(table, content);
|
||||
}
|
||||
|
||||
function addTTDummyFunctions(table, ttContext, maxFunctionDefs) {
|
||||
var content = [table.data];
|
||||
if (!ttContext.tooComplexToFollowFunctions) {
|
||||
var undefinedFunctions = [];
|
||||
for (var j = 0, jj = ttContext.functionsUsed.length; j < jj; j++) {
|
||||
if (!ttContext.functionsUsed[j] || ttContext.functionsDefined[j]) {
|
||||
continue;
|
||||
}
|
||||
undefinedFunctions.push(j);
|
||||
if (j >= maxFunctionDefs) {
|
||||
continue;
|
||||
}
|
||||
// function is used, but not defined
|
||||
if (j < 256) {
|
||||
// creating empty one [PUSHB, function-id, FDEF, ENDF]
|
||||
content.push(new Uint8Array([0xB0, j, 0x2C, 0x2D]));
|
||||
} else {
|
||||
// creating empty one [PUSHW, function-id, FDEF, ENDF]
|
||||
content.push(
|
||||
new Uint8Array([0xB8, j >> 8, j & 255, 0x2C, 0x2D]));
|
||||
}
|
||||
function checkInvalidFunctions(ttContext, maxFunctionDefs) {
|
||||
if (ttContext.tooComplexToFollowFunctions) {
|
||||
return;
|
||||
}
|
||||
for (var j = 0, jj = ttContext.functionsUsed.length; j < jj; j++) {
|
||||
if (j > maxFunctionDefs) {
|
||||
warn('TT: invalid function id: ' + j);
|
||||
ttContext.hintsValid = false;
|
||||
return;
|
||||
}
|
||||
if (undefinedFunctions.length > 0) {
|
||||
warn('TT: undefined functions: ' + undefinedFunctions);
|
||||
if (ttContext.functionsUsed[j] && !ttContext.functionsDefined[j]) {
|
||||
warn('TT: undefined function: ' + j);
|
||||
ttContext.hintsValid = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
foldTTTable(table, content);
|
||||
}
|
||||
|
||||
function foldTTTable(table, content) {
|
||||
@ -3854,7 +3857,8 @@ var Font = (function FontClosure() {
|
||||
functionsDefined: [],
|
||||
functionsUsed: [],
|
||||
functionsStackDeltas: [],
|
||||
tooComplexToFollowFunctions: false
|
||||
tooComplexToFollowFunctions: false,
|
||||
hintsValid: true
|
||||
};
|
||||
if (fpgm) {
|
||||
sanitizeTTProgram(fpgm, ttContext);
|
||||
@ -3863,8 +3867,9 @@ var Font = (function FontClosure() {
|
||||
sanitizeTTProgram(prep, ttContext);
|
||||
}
|
||||
if (fpgm) {
|
||||
addTTDummyFunctions(fpgm, ttContext, maxFunctionDefs);
|
||||
checkInvalidFunctions(ttContext, maxFunctionDefs);
|
||||
}
|
||||
return ttContext.hintsValid;
|
||||
}
|
||||
|
||||
// Check that required tables are present
|
||||
@ -3916,6 +3921,25 @@ var Font = (function FontClosure() {
|
||||
tables.push(table);
|
||||
}
|
||||
|
||||
// Ensure the hmtx table contains the advance width and
|
||||
// sidebearings information for numGlyphs in the maxp table
|
||||
font.pos = (font.start || 0) + maxp.offset;
|
||||
var version = int32(font.getBytes(4));
|
||||
var numGlyphs = int16(font.getBytes(2));
|
||||
var maxFunctionDefs = 0;
|
||||
if (version >= 0x00010000 && maxp.length >= 22) {
|
||||
font.pos += 14;
|
||||
var maxFunctionDefs = int16(font.getBytes(2));
|
||||
}
|
||||
|
||||
var hintsValid = sanitizeTTPrograms(fpgm, prep, maxFunctionDefs);
|
||||
if (!hintsValid) {
|
||||
tables.splice(tables.indexOf(fpgm), 1);
|
||||
fpgm = null;
|
||||
tables.splice(tables.indexOf(prep), 1);
|
||||
prep = null;
|
||||
}
|
||||
|
||||
var numTables = tables.length + requiredTables.length;
|
||||
|
||||
// header and new offsets. Table entry information is appended to the
|
||||
@ -3930,28 +3954,16 @@ var Font = (function FontClosure() {
|
||||
// of missing tables
|
||||
createOpenTypeHeader(header.version, ttf, numTables);
|
||||
|
||||
// Ensure the hmtx table contains the advance width and
|
||||
// sidebearings information for numGlyphs in the maxp table
|
||||
font.pos = (font.start || 0) + maxp.offset;
|
||||
var version = int32(font.getBytes(4));
|
||||
var numGlyphs = int16(font.getBytes(2));
|
||||
var maxFunctionDefs = 0;
|
||||
if (version >= 0x00010000 && maxp.length >= 22) {
|
||||
font.pos += 14;
|
||||
var maxFunctionDefs = int16(font.getBytes(2));
|
||||
}
|
||||
|
||||
sanitizeMetrics(font, hhea, hmtx, numGlyphs);
|
||||
|
||||
sanitizeTTPrograms(fpgm, prep, maxFunctionDefs);
|
||||
|
||||
if (head) {
|
||||
sanitizeHead(head, numGlyphs, loca.length);
|
||||
}
|
||||
|
||||
var isGlyphLocationsLong = int16([head.data[50], head.data[51]]);
|
||||
if (head && loca && glyf) {
|
||||
sanitizeGlyphLocations(loca, glyf, numGlyphs, isGlyphLocationsLong);
|
||||
sanitizeGlyphLocations(loca, glyf, numGlyphs, isGlyphLocationsLong,
|
||||
hintsValid);
|
||||
}
|
||||
|
||||
var emptyGlyphIds = [];
|
||||
|
1
test/pdfs/.gitignore
vendored
1
test/pdfs/.gitignore
vendored
@ -48,3 +48,4 @@
|
||||
!noembed-sjis.pdf
|
||||
!vertical.pdf
|
||||
!issue2099-1.pdf
|
||||
!issue2956.pdf
|
||||
|
BIN
test/pdfs/issue2956.pdf
Normal file
BIN
test/pdfs/issue2956.pdf
Normal file
Binary file not shown.
@ -1120,6 +1120,12 @@
|
||||
"rounds": 1,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "issue2956",
|
||||
"file": "pdfs/issue2956.pdf",
|
||||
"md5": "d8f68cbbb4bf54cde9f7f878acb6d7cd",
|
||||
"rounds": 1,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "issue2177-eq",
|
||||
"file": "pdfs/issue2177.pdf",
|
||||
"md5": "48a808278bf31de8414c4e03ecd0900a",
|
||||
|
Loading…
Reference in New Issue
Block a user