Fix how we detect and handle missing glyph data.

This commit is contained in:
Brendan Dahl 2017-06-28 10:34:36 -07:00
parent f2fcf2a59c
commit 6d4f748fb1
4 changed files with 30 additions and 34 deletions

View File

@ -801,7 +801,7 @@ var Font = (function FontClosure() {
* font that we build * font that we build
* 'charCodeToGlyphId' - maps the new font char codes to glyph ids * 'charCodeToGlyphId' - maps the new font char codes to glyph ids
*/ */
function adjustMapping(charCodeToGlyphId, properties) { function adjustMapping(charCodeToGlyphId, properties, missingGlyphs) {
var toUnicode = properties.toUnicode; var toUnicode = properties.toUnicode;
var isSymbolic = !!(properties.flags & FontFlags.Symbolic); var isSymbolic = !!(properties.flags & FontFlags.Symbolic);
var isIdentityUnicode = var isIdentityUnicode =
@ -831,7 +831,8 @@ var Font = (function FontClosure() {
// font was symbolic and there is only an identity unicode map since the // font was symbolic and there is only an identity unicode map since the
// characters probably aren't in the correct position (fixes an issue // characters probably aren't in the correct position (fixes an issue
// with firefox and thuluthfont). // with firefox and thuluthfont).
if ((usedFontCharCodes[fontCharCode] !== undefined || if (!missingGlyphs[glyphId] &&
(usedFontCharCodes[fontCharCode] !== undefined ||
isProblematicUnicodeLocation(fontCharCode) || isProblematicUnicodeLocation(fontCharCode) ||
(isSymbolic && !hasUnicodeValue)) && (isSymbolic && !hasUnicodeValue)) &&
nextAvailableFontCharCode <= PRIVATE_USE_OFFSET_END) { // Room left. nextAvailableFontCharCode <= PRIVATE_USE_OFFSET_END) { // Room left.
@ -1686,9 +1687,15 @@ var Font = (function FontClosure() {
var startOffset = itemDecode(locaData, 0); var startOffset = itemDecode(locaData, 0);
var writeOffset = 0; var writeOffset = 0;
var missingGlyphData = Object.create(null); var missingGlyphData = Object.create(null);
// Glyph zero should be notdef which isn't drawn. Sometimes this is a
// valid glyph but, then it is duplicated.
missingGlyphData[0] = true;
itemEncode(locaData, 0, writeOffset); itemEncode(locaData, 0, writeOffset);
var i, j; var i, j;
for (i = 0, j = itemSize; i < numGlyphs; i++, j += itemSize) { // When called with dupFirstEntry the number of glyphs has already been
// increased but there isn't data yet for the duplicated glyph.
var locaCount = dupFirstEntry ? numGlyphs - 1 : numGlyphs;
for (i = 0, j = itemSize; i < locaCount; i++, j += itemSize) {
var endOffset = itemDecode(locaData, j); var endOffset = itemDecode(locaData, j);
if (endOffset > oldGlyfDataLength && if (endOffset > oldGlyfDataLength &&
((oldGlyfDataLength + 3) & ~3) === endOffset) { ((oldGlyfDataLength + 3) & ~3) === endOffset) {
@ -1698,17 +1705,14 @@ var Font = (function FontClosure() {
} }
if (endOffset > oldGlyfDataLength) { if (endOffset > oldGlyfDataLength) {
// glyph end offset points outside glyf data, rejecting the glyph // glyph end offset points outside glyf data, rejecting the glyph
itemEncode(locaData, j, writeOffset);
startOffset = endOffset; startOffset = endOffset;
continue;
}
if (startOffset === endOffset) {
missingGlyphData[i] = true;
} }
var newLength = sanitizeGlyph(oldGlyfData, startOffset, endOffset, var newLength = sanitizeGlyph(oldGlyfData, startOffset, endOffset,
newGlyfData, writeOffset, hintsValid); newGlyfData, writeOffset, hintsValid);
if (newLength === 0) {
missingGlyphData[i] = true;
}
writeOffset += newLength; writeOffset += newLength;
itemEncode(locaData, j, writeOffset); itemEncode(locaData, j, writeOffset);
startOffset = endOffset; startOffset = endOffset;
@ -2271,24 +2275,10 @@ var Font = (function FontClosure() {
} }
var charCodeToGlyphId = [], charCode; var charCodeToGlyphId = [], charCode;
var toUnicode = properties.toUnicode, widths = properties.widths;
var skipToUnicode = (toUnicode instanceof IdentityToUnicodeMap ||
toUnicode.length === 0x10000);
// Helper function to try to skip mapping of empty glyphs. // Helper function to try to skip mapping of empty glyphs.
// Note: In some cases, just relying on the glyph data doesn't work, function hasGlyph(glyphId) {
// hence we also use a few heuristics to fix various PDF files. return !missingGlyphs[glyphId];
function hasGlyph(glyphId, charCode, widthCode) {
if (!missingGlyphs[glyphId]) {
return true;
}
if (!skipToUnicode && charCode >= 0 && toUnicode.has(charCode)) {
return true;
}
if (widths && widthCode >= 0 && isNum(widths[widthCode])) {
return true;
}
return false;
} }
if (properties.composite) { if (properties.composite) {
@ -2304,8 +2294,7 @@ var Font = (function FontClosure() {
glyphId = cidToGidMap[cid]; glyphId = cidToGidMap[cid];
} }
if (glyphId >= 0 && glyphId < numGlyphs && if (glyphId >= 0 && glyphId < numGlyphs && hasGlyph(glyphId)) {
hasGlyph(glyphId, charCode, cid)) {
charCodeToGlyphId[charCode] = glyphId; charCodeToGlyphId[charCode] = glyphId;
} }
}); });
@ -2361,10 +2350,9 @@ var Font = (function FontClosure() {
// Ensure that non-standard glyph names are resolved to valid ones. // Ensure that non-standard glyph names are resolved to valid ones.
standardGlyphName = recoverGlyphName(glyphName, glyphsUnicodeMap); standardGlyphName = recoverGlyphName(glyphName, glyphsUnicodeMap);
var unicodeOrCharCode, isUnicode = false; var unicodeOrCharCode;
if (cmapPlatformId === 3 && cmapEncodingId === 1) { if (cmapPlatformId === 3 && cmapEncodingId === 1) {
unicodeOrCharCode = glyphsUnicodeMap[standardGlyphName]; unicodeOrCharCode = glyphsUnicodeMap[standardGlyphName];
isUnicode = true;
} else if (cmapPlatformId === 1 && cmapEncodingId === 0) { } else if (cmapPlatformId === 1 && cmapEncodingId === 0) {
// TODO: the encoding needs to be updated with mac os table. // TODO: the encoding needs to be updated with mac os table.
unicodeOrCharCode = MacRomanEncoding.indexOf(standardGlyphName); unicodeOrCharCode = MacRomanEncoding.indexOf(standardGlyphName);
@ -2375,8 +2363,7 @@ var Font = (function FontClosure() {
if (cmapMappings[i].charCode !== unicodeOrCharCode) { if (cmapMappings[i].charCode !== unicodeOrCharCode) {
continue; continue;
} }
var code = isUnicode ? charCode : unicodeOrCharCode; if (hasGlyph(cmapMappings[i].glyphId)) {
if (hasGlyph(cmapMappings[i].glyphId, code, -1)) {
charCodeToGlyphId[charCode] = cmapMappings[i].glyphId; charCodeToGlyphId[charCode] = cmapMappings[i].glyphId;
found = true; found = true;
break; break;
@ -2390,7 +2377,7 @@ var Font = (function FontClosure() {
if (glyphId === -1 && standardGlyphName !== glyphName) { if (glyphId === -1 && standardGlyphName !== glyphName) {
glyphId = properties.glyphNames.indexOf(standardGlyphName); glyphId = properties.glyphNames.indexOf(standardGlyphName);
} }
if (glyphId > 0 && hasGlyph(glyphId, -1, -1)) { if (glyphId > 0 && hasGlyph(glyphId)) {
charCodeToGlyphId[charCode] = glyphId; charCodeToGlyphId[charCode] = glyphId;
found = true; found = true;
} }
@ -2432,7 +2419,8 @@ var Font = (function FontClosure() {
} }
// Converting glyphs and ids into font's cmap table // Converting glyphs and ids into font's cmap table
var newMapping = adjustMapping(charCodeToGlyphId, properties); var newMapping = adjustMapping(charCodeToGlyphId, properties,
missingGlyphs);
this.toFontChar = newMapping.toFontChar; this.toFontChar = newMapping.toFontChar;
tables['cmap'] = { tables['cmap'] = {
tag: 'cmap', tag: 'cmap',
@ -2499,7 +2487,7 @@ var Font = (function FontClosure() {
} }
var mapping = font.getGlyphMapping(properties); var mapping = font.getGlyphMapping(properties);
var newMapping = adjustMapping(mapping, properties); var newMapping = adjustMapping(mapping, properties, Object.create(null));
this.toFontChar = newMapping.toFontChar; this.toFontChar = newMapping.toFontChar;
var numGlyphs = font.numGlyphs; var numGlyphs = font.numGlyphs;

View File

@ -53,6 +53,7 @@
!issue8372.pdf !issue8372.pdf
!issue8424.pdf !issue8424.pdf
!issue8480.pdf !issue8480.pdf
!issue8570.pdf
!bad-PageLabels.pdf !bad-PageLabels.pdf
!filled-background.pdf !filled-background.pdf
!ArabicCIDTrueType.pdf !ArabicCIDTrueType.pdf

BIN
test/pdfs/issue8570.pdf Normal file

Binary file not shown.

View File

@ -1156,6 +1156,13 @@
"link": false, "link": false,
"type": "eq" "type": "eq"
}, },
{ "id": "issue8570",
"file": "pdfs/issue8570.pdf",
"md5": "0355731adb72df233eaa10464dcc8c51",
"rounds": 1,
"link": false,
"type": "eq"
},
{ "id": "tutorial", { "id": "tutorial",
"file": "pdfs/tutorial.pdf", "file": "pdfs/tutorial.pdf",
"md5": "6e122f618c27f3aa9a689423e3be6b8d", "md5": "6e122f618c27f3aa9a689423e3be6b8d",