Merge pull request #8681 from brendandahl/glyph-ids
Fix several issues with glyph id mappings (issue 8668, bug 1383504)
This commit is contained in:
commit
e20d4a9c21
@ -481,7 +481,7 @@ var ProblematicCharRanges = new Int32Array([
|
||||
*/
|
||||
var Font = (function FontClosure() {
|
||||
function Font(name, file, properties) {
|
||||
var charCode, glyphName, unicode;
|
||||
var charCode;
|
||||
|
||||
this.name = name;
|
||||
this.loadedName = properties.loadedName;
|
||||
@ -498,6 +498,7 @@ var Font = (function FontClosure() {
|
||||
var type = properties.type;
|
||||
var subtype = properties.subtype;
|
||||
this.type = type;
|
||||
this.subtype = subtype;
|
||||
|
||||
this.fallbackName = (this.isMonospace ? 'monospace' :
|
||||
(this.isSerifFont ? 'serif' : 'sans-serif'));
|
||||
@ -512,6 +513,7 @@ var Font = (function FontClosure() {
|
||||
this.descent = properties.descent / PDF_GLYPH_SPACE_UNITS;
|
||||
this.fontMatrix = properties.fontMatrix;
|
||||
this.bbox = properties.bbox;
|
||||
this.defaultEncoding = properties.defaultEncoding;
|
||||
|
||||
this.toUnicode = properties.toUnicode;
|
||||
|
||||
@ -532,88 +534,14 @@ var Font = (function FontClosure() {
|
||||
this.vmetrics = properties.vmetrics;
|
||||
this.defaultVMetrics = properties.defaultVMetrics;
|
||||
}
|
||||
var glyphsUnicodeMap;
|
||||
|
||||
if (!file || file.isEmpty) {
|
||||
if (file) {
|
||||
// Some bad PDF generators will include empty font files,
|
||||
// attempting to recover by assuming that no file exists.
|
||||
warn('Font file is empty in "' + name + '" (' + this.loadedName + ')');
|
||||
}
|
||||
|
||||
this.missingFile = true;
|
||||
// The file data is not specified. Trying to fix the font name
|
||||
// to be used with the canvas.font.
|
||||
var fontName = name.replace(/[,_]/g, '-');
|
||||
var stdFontMap = getStdFontMap(), nonStdFontMap = getNonStdFontMap();
|
||||
var isStandardFont = !!stdFontMap[fontName] ||
|
||||
!!(nonStdFontMap[fontName] && stdFontMap[nonStdFontMap[fontName]]);
|
||||
fontName = stdFontMap[fontName] || nonStdFontMap[fontName] || fontName;
|
||||
|
||||
this.bold = (fontName.search(/bold/gi) !== -1);
|
||||
this.italic = ((fontName.search(/oblique/gi) !== -1) ||
|
||||
(fontName.search(/italic/gi) !== -1));
|
||||
|
||||
// Use 'name' instead of 'fontName' here because the original
|
||||
// name ArialBlack for example will be replaced by Helvetica.
|
||||
this.black = (name.search(/Black/g) !== -1);
|
||||
|
||||
// if at least one width is present, remeasure all chars when exists
|
||||
this.remeasure = Object.keys(this.widths).length > 0;
|
||||
if (isStandardFont && type === 'CIDFontType2' &&
|
||||
properties.cidEncoding.indexOf('Identity-') === 0) {
|
||||
var GlyphMapForStandardFonts = getGlyphMapForStandardFonts();
|
||||
// Standard fonts might be embedded as CID font without glyph mapping.
|
||||
// Building one based on GlyphMapForStandardFonts.
|
||||
var map = [];
|
||||
for (charCode in GlyphMapForStandardFonts) {
|
||||
map[+charCode] = GlyphMapForStandardFonts[charCode];
|
||||
}
|
||||
if (/Arial-?Black/i.test(name)) {
|
||||
var SupplementalGlyphMapForArialBlack =
|
||||
getSupplementalGlyphMapForArialBlack();
|
||||
for (charCode in SupplementalGlyphMapForArialBlack) {
|
||||
map[+charCode] = SupplementalGlyphMapForArialBlack[charCode];
|
||||
}
|
||||
}
|
||||
var isIdentityUnicode = this.toUnicode instanceof IdentityToUnicodeMap;
|
||||
if (!isIdentityUnicode) {
|
||||
this.toUnicode.forEach(function(charCode, unicodeCharCode) {
|
||||
map[+charCode] = unicodeCharCode;
|
||||
});
|
||||
}
|
||||
this.toFontChar = map;
|
||||
this.toUnicode = new ToUnicodeMap(map);
|
||||
} else if (/Symbol/i.test(fontName)) {
|
||||
this.toFontChar = buildToFontChar(SymbolSetEncoding, getGlyphsUnicode(),
|
||||
properties.differences);
|
||||
} else if (/Dingbats/i.test(fontName)) {
|
||||
if (/Wingdings/i.test(name)) {
|
||||
warn('Non-embedded Wingdings font, falling back to ZapfDingbats.');
|
||||
}
|
||||
this.toFontChar = buildToFontChar(ZapfDingbatsEncoding,
|
||||
getDingbatsGlyphsUnicode(),
|
||||
properties.differences);
|
||||
} else if (isStandardFont) {
|
||||
this.toFontChar = buildToFontChar(properties.defaultEncoding,
|
||||
getGlyphsUnicode(),
|
||||
properties.differences);
|
||||
} else {
|
||||
glyphsUnicodeMap = getGlyphsUnicode();
|
||||
this.toUnicode.forEach((charCode, unicodeCharCode) => {
|
||||
if (!this.composite) {
|
||||
glyphName = (properties.differences[charCode] ||
|
||||
properties.defaultEncoding[charCode]);
|
||||
unicode = getUnicodeForGlyph(glyphName, glyphsUnicodeMap);
|
||||
if (unicode !== -1) {
|
||||
unicodeCharCode = unicode;
|
||||
}
|
||||
}
|
||||
this.toFontChar[charCode] = unicodeCharCode;
|
||||
});
|
||||
}
|
||||
this.loadedName = fontName.split('-')[0];
|
||||
this.loading = false;
|
||||
this.fontType = getFontType(type, subtype);
|
||||
this.fallbackToSystemFont();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -649,41 +577,51 @@ var Font = (function FontClosure() {
|
||||
type = 'OpenType';
|
||||
}
|
||||
|
||||
var data;
|
||||
switch (type) {
|
||||
case 'MMType1':
|
||||
info('MMType1 font (' + name + '), falling back to Type1.');
|
||||
/* falls through */
|
||||
case 'Type1':
|
||||
case 'CIDFontType0':
|
||||
this.mimetype = 'font/opentype';
|
||||
try {
|
||||
var data;
|
||||
switch (type) {
|
||||
case 'MMType1':
|
||||
info('MMType1 font (' + name + '), falling back to Type1.');
|
||||
/* falls through */
|
||||
case 'Type1':
|
||||
case 'CIDFontType0':
|
||||
this.mimetype = 'font/opentype';
|
||||
|
||||
var cff = (subtype === 'Type1C' || subtype === 'CIDFontType0C') ?
|
||||
new CFFFont(file, properties) : new Type1Font(name, file, properties);
|
||||
var cff = (subtype === 'Type1C' || subtype === 'CIDFontType0C') ?
|
||||
new CFFFont(file, properties) :
|
||||
new Type1Font(name, file, properties);
|
||||
|
||||
adjustWidths(properties);
|
||||
|
||||
// Wrap the CFF data inside an OTF font file
|
||||
data = this.convert(name, cff, properties);
|
||||
break;
|
||||
|
||||
case 'OpenType':
|
||||
case 'TrueType':
|
||||
case 'CIDFontType2':
|
||||
this.mimetype = 'font/opentype';
|
||||
|
||||
// Repair the TrueType file. It is can be damaged in the point of
|
||||
// view of the sanitizer
|
||||
data = this.checkAndRepair(name, file, properties);
|
||||
if (this.isOpenType) {
|
||||
adjustWidths(properties);
|
||||
|
||||
type = 'OpenType';
|
||||
}
|
||||
break;
|
||||
// Wrap the CFF data inside an OTF font file
|
||||
data = this.convert(name, cff, properties);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new FormatError(`Font ${type} is not supported`);
|
||||
case 'OpenType':
|
||||
case 'TrueType':
|
||||
case 'CIDFontType2':
|
||||
this.mimetype = 'font/opentype';
|
||||
|
||||
// Repair the TrueType file. It is can be damaged in the point of
|
||||
// view of the sanitizer
|
||||
data = this.checkAndRepair(name, file, properties);
|
||||
if (this.isOpenType) {
|
||||
adjustWidths(properties);
|
||||
|
||||
type = 'OpenType';
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new FormatError(`Font ${type} is not supported`);
|
||||
}
|
||||
} catch (e) {
|
||||
if (!(e instanceof FormatError)) {
|
||||
throw e;
|
||||
}
|
||||
warn(e);
|
||||
this.fallbackToSystemFont();
|
||||
return;
|
||||
}
|
||||
|
||||
this.data = data;
|
||||
@ -812,6 +750,11 @@ var Font = (function FontClosure() {
|
||||
for (var originalCharCode in charCodeToGlyphId) {
|
||||
originalCharCode |= 0;
|
||||
var glyphId = charCodeToGlyphId[originalCharCode];
|
||||
// For missing glyphs don't create the mappings so the glyph isn't
|
||||
// drawn.
|
||||
if (missingGlyphs[glyphId]) {
|
||||
continue;
|
||||
}
|
||||
var fontCharCode = originalCharCode;
|
||||
// First try to map the value to a unicode position if a non identity map
|
||||
// was created.
|
||||
@ -830,13 +773,15 @@ var Font = (function FontClosure() {
|
||||
// font was symbolic and there is only an identity unicode map since the
|
||||
// characters probably aren't in the correct position (fixes an issue
|
||||
// with firefox and thuluthfont).
|
||||
if (!missingGlyphs[glyphId] &&
|
||||
(usedFontCharCodes[fontCharCode] !== undefined ||
|
||||
if ((usedFontCharCodes[fontCharCode] !== undefined ||
|
||||
isProblematicUnicodeLocation(fontCharCode) ||
|
||||
(isSymbolic && !hasUnicodeValue)) &&
|
||||
nextAvailableFontCharCode <= PRIVATE_USE_OFFSET_END) { // Room left.
|
||||
(isSymbolic && !hasUnicodeValue))) {
|
||||
// Loop to try and find a free spot in the private use area.
|
||||
do {
|
||||
if (nextAvailableFontCharCode > PRIVATE_USE_OFFSET_END) {
|
||||
warn('Ran out of space in font private use area.');
|
||||
break;
|
||||
}
|
||||
fontCharCode = nextAvailableFontCharCode++;
|
||||
|
||||
if (SKIP_PRIVATE_USE_RANGE_F000_TO_F01F && fontCharCode === 0xF000) {
|
||||
@ -844,8 +789,7 @@ var Font = (function FontClosure() {
|
||||
nextAvailableFontCharCode = fontCharCode + 1;
|
||||
}
|
||||
|
||||
} while (usedFontCharCodes[fontCharCode] !== undefined &&
|
||||
nextAvailableFontCharCode <= PRIVATE_USE_OFFSET_END);
|
||||
} while (usedFontCharCodes[fontCharCode] !== undefined);
|
||||
}
|
||||
|
||||
newMap[fontCharCode] = glyphId;
|
||||
@ -870,6 +814,11 @@ var Font = (function FontClosure() {
|
||||
}
|
||||
codes.push({ fontCharCode: charCode | 0, glyphId: glyphs[charCode], });
|
||||
}
|
||||
// Some fonts have zero glyphs and are used only for text selection, but
|
||||
// there needs to be at least one to build a valid cmap table.
|
||||
if (codes.length === 0) {
|
||||
codes.push({ fontCharCode: 0, glyphId: 0, });
|
||||
}
|
||||
codes.sort(function fontGetRangesSort(a, b) {
|
||||
return a.fontCharCode - b.fontCharCode;
|
||||
});
|
||||
@ -1248,6 +1197,87 @@ var Font = (function FontClosure() {
|
||||
return data;
|
||||
},
|
||||
|
||||
fallbackToSystemFont: function Font_fallbackToSystemFont() {
|
||||
this.missingFile = true;
|
||||
var charCode, unicode;
|
||||
// The file data is not specified. Trying to fix the font name
|
||||
// to be used with the canvas.font.
|
||||
var name = this.name;
|
||||
var type = this.type;
|
||||
var subtype = this.subtype;
|
||||
var fontName = name.replace(/[,_]/g, '-');
|
||||
var stdFontMap = getStdFontMap(), nonStdFontMap = getNonStdFontMap();
|
||||
var isStandardFont = !!stdFontMap[fontName] ||
|
||||
!!(nonStdFontMap[fontName] && stdFontMap[nonStdFontMap[fontName]]);
|
||||
fontName = stdFontMap[fontName] || nonStdFontMap[fontName] || fontName;
|
||||
|
||||
this.bold = (fontName.search(/bold/gi) !== -1);
|
||||
this.italic = ((fontName.search(/oblique/gi) !== -1) ||
|
||||
(fontName.search(/italic/gi) !== -1));
|
||||
|
||||
// Use 'name' instead of 'fontName' here because the original
|
||||
// name ArialBlack for example will be replaced by Helvetica.
|
||||
this.black = (name.search(/Black/g) !== -1);
|
||||
|
||||
// if at least one width is present, remeasure all chars when exists
|
||||
this.remeasure = Object.keys(this.widths).length > 0;
|
||||
if (isStandardFont && type === 'CIDFontType2' &&
|
||||
this.cidEncoding.indexOf('Identity-') === 0) {
|
||||
var GlyphMapForStandardFonts = getGlyphMapForStandardFonts();
|
||||
// Standard fonts might be embedded as CID font without glyph mapping.
|
||||
// Building one based on GlyphMapForStandardFonts.
|
||||
var map = [];
|
||||
for (charCode in GlyphMapForStandardFonts) {
|
||||
map[+charCode] = GlyphMapForStandardFonts[charCode];
|
||||
}
|
||||
if (/Arial-?Black/i.test(name)) {
|
||||
var SupplementalGlyphMapForArialBlack =
|
||||
getSupplementalGlyphMapForArialBlack();
|
||||
for (charCode in SupplementalGlyphMapForArialBlack) {
|
||||
map[+charCode] = SupplementalGlyphMapForArialBlack[charCode];
|
||||
}
|
||||
}
|
||||
var isIdentityUnicode = this.toUnicode instanceof IdentityToUnicodeMap;
|
||||
if (!isIdentityUnicode) {
|
||||
this.toUnicode.forEach(function(charCode, unicodeCharCode) {
|
||||
map[+charCode] = unicodeCharCode;
|
||||
});
|
||||
}
|
||||
this.toFontChar = map;
|
||||
this.toUnicode = new ToUnicodeMap(map);
|
||||
} else if (/Symbol/i.test(fontName)) {
|
||||
this.toFontChar = buildToFontChar(SymbolSetEncoding, getGlyphsUnicode(),
|
||||
this.differences);
|
||||
} else if (/Dingbats/i.test(fontName)) {
|
||||
if (/Wingdings/i.test(name)) {
|
||||
warn('Non-embedded Wingdings font, falling back to ZapfDingbats.');
|
||||
}
|
||||
this.toFontChar = buildToFontChar(ZapfDingbatsEncoding,
|
||||
getDingbatsGlyphsUnicode(),
|
||||
this.differences);
|
||||
} else if (isStandardFont) {
|
||||
this.toFontChar = buildToFontChar(this.defaultEncoding,
|
||||
getGlyphsUnicode(),
|
||||
this.differences);
|
||||
} else {
|
||||
var glyphsUnicodeMap = getGlyphsUnicode();
|
||||
this.toUnicode.forEach((charCode, unicodeCharCode) => {
|
||||
if (!this.composite) {
|
||||
var glyphName = (this.differences[charCode] ||
|
||||
this.defaultEncoding[charCode]);
|
||||
unicode = getUnicodeForGlyph(glyphName, glyphsUnicodeMap);
|
||||
if (unicode !== -1) {
|
||||
unicodeCharCode = unicode;
|
||||
}
|
||||
}
|
||||
this.toFontChar[charCode] = unicodeCharCode;
|
||||
});
|
||||
}
|
||||
this.loadedName = fontName.split('-')[0];
|
||||
this.loading = false;
|
||||
this.fontType = getFontType(type, subtype);
|
||||
},
|
||||
|
||||
checkAndRepair: function Font_checkAndRepair(name, font, properties) {
|
||||
function readTableEntry(file) {
|
||||
var tag = bytesToString(file.getBytes(4));
|
||||
@ -1641,7 +1671,8 @@ var Font = (function FontClosure() {
|
||||
data[50] = 0;
|
||||
data[51] = 1;
|
||||
} else {
|
||||
warn('Could not fix indexToLocFormat: ' + indexToLocFormat);
|
||||
throw new FormatError('Could not fix indexToLocFormat: ' +
|
||||
indexToLocFormat);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1687,9 +1718,6 @@ var Font = (function FontClosure() {
|
||||
var startOffset = itemDecode(locaData, 0);
|
||||
var writeOffset = 0;
|
||||
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);
|
||||
var i, j;
|
||||
// When called with dupFirstEntry the number of glyphs has already been
|
||||
@ -2384,9 +2412,6 @@ var Font = (function FontClosure() {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
charCodeToGlyphId[charCode] = 0; // notdef
|
||||
}
|
||||
}
|
||||
} else if (cmapPlatformId === 0 && cmapEncodingId === 0) {
|
||||
// Default Unicode semantics, use the charcodes as is.
|
||||
|
Loading…
x
Reference in New Issue
Block a user