mirror of
https://github.com/mozilla/pdf.js.git
synced 2025-04-22 16:18:08 +02:00
Merge pull request #9340 from brendandahl/private-use
Map all glyphs to the private use area and duplicate the first glyph.
This commit is contained in:
commit
66422eb83e
14 changed files with 16188 additions and 304 deletions
|
@ -815,11 +815,10 @@ var CFFParser = (function CFFParserClosure() {
|
|||
return new CFFEncoding(predefined, format, encoding, raw);
|
||||
},
|
||||
parseFDSelect: function CFFParser_parseFDSelect(pos, length) {
|
||||
var start = pos;
|
||||
var bytes = this.bytes;
|
||||
var format = bytes[pos++];
|
||||
var fdSelect = [], rawBytes;
|
||||
var i, invalidFirstGID = false;
|
||||
var fdSelect = [];
|
||||
var i;
|
||||
|
||||
switch (format) {
|
||||
case 0:
|
||||
|
@ -827,7 +826,6 @@ var CFFParser = (function CFFParserClosure() {
|
|||
var id = bytes[pos++];
|
||||
fdSelect.push(id);
|
||||
}
|
||||
rawBytes = bytes.subarray(start, pos);
|
||||
break;
|
||||
case 3:
|
||||
var rangesCount = (bytes[pos++] << 8) | bytes[pos++];
|
||||
|
@ -836,7 +834,6 @@ var CFFParser = (function CFFParserClosure() {
|
|||
if (i === 0 && first !== 0) {
|
||||
warn('parseFDSelect: The first range must have a first GID of 0' +
|
||||
' -- trying to recover.');
|
||||
invalidFirstGID = true;
|
||||
first = 0;
|
||||
}
|
||||
var fdIndex = bytes[pos++];
|
||||
|
@ -847,11 +844,6 @@ var CFFParser = (function CFFParserClosure() {
|
|||
}
|
||||
// Advance past the sentinel(next).
|
||||
pos += 2;
|
||||
rawBytes = bytes.subarray(start, pos);
|
||||
|
||||
if (invalidFirstGID) {
|
||||
rawBytes[3] = rawBytes[4] = 0; // Adjust the first range, first GID.
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new FormatError(`parseFDSelect: Unknown format "${format}".`);
|
||||
|
@ -860,7 +852,7 @@ var CFFParser = (function CFFParserClosure() {
|
|||
throw new FormatError('parseFDSelect: Invalid font data.');
|
||||
}
|
||||
|
||||
return new CFFFDSelect(fdSelect, rawBytes);
|
||||
return new CFFFDSelect(format, fdSelect);
|
||||
},
|
||||
};
|
||||
return CFFParser;
|
||||
|
@ -885,6 +877,30 @@ var CFF = (function CFFClosure() {
|
|||
|
||||
this.isCIDFont = false;
|
||||
}
|
||||
CFF.prototype = {
|
||||
duplicateFirstGlyph: function CFF_duplicateFirstGlyph() {
|
||||
// Browsers will not display a glyph at position 0. Typically glyph 0 is
|
||||
// notdef, but a number of fonts put a valid glyph there so it must be
|
||||
// duplicated and appended.
|
||||
if (this.charStrings.count >= 65535) {
|
||||
warn('Not enough space in charstrings to duplicate first glyph.');
|
||||
return;
|
||||
}
|
||||
var glyphZero = this.charStrings.get(0);
|
||||
this.charStrings.add(glyphZero);
|
||||
if (this.isCIDFont) {
|
||||
this.fdSelect.fdSelect.push(this.fdSelect.fdSelect[0]);
|
||||
}
|
||||
},
|
||||
hasGlyphId: function CFF_hasGlyphID(id) {
|
||||
if (id < 0 || id >= this.charStrings.count) {
|
||||
return false;
|
||||
}
|
||||
var glyph = this.charStrings.get(id);
|
||||
return glyph.length > 0;
|
||||
},
|
||||
};
|
||||
|
||||
return CFF;
|
||||
})();
|
||||
|
||||
|
@ -1142,9 +1158,9 @@ var CFFEncoding = (function CFFEncodingClosure() {
|
|||
})();
|
||||
|
||||
var CFFFDSelect = (function CFFFDSelectClosure() {
|
||||
function CFFFDSelect(fdSelect, raw) {
|
||||
function CFFFDSelect(format, fdSelect) {
|
||||
this.format = format;
|
||||
this.fdSelect = fdSelect;
|
||||
this.raw = raw;
|
||||
}
|
||||
CFFFDSelect.prototype = {
|
||||
getFDIndex: function CFFFDSelect_get(glyphIndex) {
|
||||
|
@ -1261,6 +1277,7 @@ var CFFCompiler = (function CFFCompilerClosure() {
|
|||
}
|
||||
}
|
||||
|
||||
cff.topDict.setByName('charset', 0);
|
||||
var compiled = this.compileTopDicts([cff.topDict],
|
||||
output.length,
|
||||
cff.isCIDFont);
|
||||
|
@ -1284,17 +1301,9 @@ var CFFCompiler = (function CFFCompilerClosure() {
|
|||
output.add(encoding);
|
||||
}
|
||||
}
|
||||
|
||||
if (cff.charset && cff.topDict.hasName('charset')) {
|
||||
if (cff.charset.predefined) {
|
||||
topDictTracker.setEntryLocation('charset', [cff.charset.format],
|
||||
output);
|
||||
} else {
|
||||
var charset = this.compileCharset(cff.charset);
|
||||
topDictTracker.setEntryLocation('charset', [output.length], output);
|
||||
output.add(charset);
|
||||
}
|
||||
}
|
||||
var charset = this.compileCharset(cff.charset);
|
||||
topDictTracker.setEntryLocation('charset', [output.length], output);
|
||||
output.add(charset);
|
||||
|
||||
var charStrings = this.compileCharStrings(cff.charStrings);
|
||||
topDictTracker.setEntryLocation('CharStrings', [output.length], output);
|
||||
|
@ -1304,7 +1313,7 @@ var CFFCompiler = (function CFFCompilerClosure() {
|
|||
// For some reason FDSelect must be in front of FDArray on windows. OSX
|
||||
// and linux don't seem to care.
|
||||
topDictTracker.setEntryLocation('FDSelect', [output.length], output);
|
||||
var fdSelect = this.compileFDSelect(cff.fdSelect.raw);
|
||||
var fdSelect = this.compileFDSelect(cff.fdSelect);
|
||||
output.add(fdSelect);
|
||||
// It is unclear if the sub font dictionary can have CID related
|
||||
// dictionary keys, but the sanitizer doesn't like them so remove them.
|
||||
|
@ -1547,16 +1556,68 @@ var CFFCompiler = (function CFFCompilerClosure() {
|
|||
this.out.writeByteArray(this.compileIndex(globalSubrIndex));
|
||||
},
|
||||
compileCharStrings: function CFFCompiler_compileCharStrings(charStrings) {
|
||||
return this.compileIndex(charStrings);
|
||||
var charStringsIndex = new CFFIndex();
|
||||
for (var i = 0; i < charStrings.count; i++) {
|
||||
var glyph = charStrings.get(i);
|
||||
// If the CharString outline is empty, replace it with .notdef to
|
||||
// prevent OTS from rejecting the font (fixes bug1252420.pdf).
|
||||
if (glyph.length === 0) {
|
||||
charStringsIndex.add(new Uint8Array([0x8B, 0x0E]));
|
||||
continue;
|
||||
}
|
||||
charStringsIndex.add(glyph);
|
||||
}
|
||||
return this.compileIndex(charStringsIndex);
|
||||
},
|
||||
compileCharset: function CFFCompiler_compileCharset(charset) {
|
||||
return this.compileTypedArray(charset.raw);
|
||||
let length = 1 + (this.cff.charStrings.count - 1) * 2;
|
||||
// The contents of the charset doesn't matter, it's just there to make
|
||||
// freetype happy.
|
||||
let out = new Uint8Array(length);
|
||||
return this.compileTypedArray(out);
|
||||
},
|
||||
compileEncoding: function CFFCompiler_compileEncoding(encoding) {
|
||||
return this.compileTypedArray(encoding.raw);
|
||||
},
|
||||
compileFDSelect: function CFFCompiler_compileFDSelect(fdSelect) {
|
||||
return this.compileTypedArray(fdSelect);
|
||||
let format = fdSelect.format;
|
||||
let out, i;
|
||||
switch (format) {
|
||||
case 0:
|
||||
out = new Uint8Array(1 + fdSelect.fdSelect.length);
|
||||
out[0] = format;
|
||||
for (i = 0; i < fdSelect.fdSelect.length; i++) {
|
||||
out[i + 1] = fdSelect.fdSelect[i];
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
let start = 0;
|
||||
let lastFD = fdSelect.fdSelect[0];
|
||||
let ranges = [
|
||||
format,
|
||||
0, // nRanges place holder
|
||||
0, // nRanges place holder
|
||||
(start >> 8) & 0xFF,
|
||||
start & 0xFF,
|
||||
lastFD
|
||||
];
|
||||
for (i = 1; i < fdSelect.fdSelect.length; i++) {
|
||||
let currentFD = fdSelect.fdSelect[i];
|
||||
if (currentFD !== lastFD) {
|
||||
ranges.push((i >> 8) & 0xFF, i & 0xFF, currentFD);
|
||||
lastFD = currentFD;
|
||||
}
|
||||
}
|
||||
// 3 bytes are pushed for every range and there are 3 header bytes.
|
||||
let numRanges = (ranges.length - 3) / 3;
|
||||
ranges[1] = (numRanges >> 8) & 0xFF;
|
||||
ranges[2] = numRanges & 0xFF;
|
||||
// sentinel
|
||||
ranges.push((i >> 8) & 0xFF, i & 0xFF);
|
||||
out = new Uint8Array(ranges);
|
||||
break;
|
||||
}
|
||||
return this.compileTypedArray(out);
|
||||
},
|
||||
compileTypedArray: function CFFCompiler_compileTypedArray(data) {
|
||||
var out = [];
|
||||
|
@ -1648,4 +1709,5 @@ export {
|
|||
CFFTopDict,
|
||||
CFFPrivateDict,
|
||||
CFFCompiler,
|
||||
CFFFDSelect,
|
||||
};
|
||||
|
|
|
@ -122,7 +122,7 @@ var FontRendererFactory = (function FontRendererFactoryClosure() {
|
|||
}
|
||||
|
||||
function lookupCmap(ranges, unicode) {
|
||||
var code = unicode.charCodeAt(0), gid = 0;
|
||||
var code = unicode.codePointAt(0), gid = 0;
|
||||
var l = 0, r = ranges.length - 1;
|
||||
while (l < r) {
|
||||
var c = (l + r + 1) >> 1;
|
||||
|
|
|
@ -39,18 +39,24 @@ import { IdentityCMap } from './cmap';
|
|||
import { Stream } from './stream';
|
||||
import { Type1Parser } from './type1_parser';
|
||||
|
||||
// Unicode Private Use Area
|
||||
var PRIVATE_USE_OFFSET_START = 0xE000;
|
||||
var PRIVATE_USE_OFFSET_END = 0xF8FF;
|
||||
var SKIP_PRIVATE_USE_RANGE_F000_TO_F01F = false;
|
||||
// Unicode Private Use Areas:
|
||||
const PRIVATE_USE_AREAS = [
|
||||
[0xE000, 0xF8FF], // BMP (0)
|
||||
[0x100000, 0x10FFFD], // PUP (16)
|
||||
];
|
||||
|
||||
// PDF Glyph Space Units are one Thousandth of a TextSpace Unit
|
||||
// except for Type 3 fonts
|
||||
var PDF_GLYPH_SPACE_UNITS = 1000;
|
||||
|
||||
// Accented characters are not displayed properly on Windows, using this flag
|
||||
// to control analysis of seac charstrings.
|
||||
var SEAC_ANALYSIS_ENABLED = false;
|
||||
// Accented characters have issues on Windows and Linux. When this flag is
|
||||
// enabled glyphs that use seac and seac style endchar operators are truncated
|
||||
// and we instead just store the glyph id's of the base glyph and its accent to
|
||||
// be drawn individually.
|
||||
// Linux (freetype) requires that when a seac style endchar is used
|
||||
// that the charset must be a predefined one, however we build a
|
||||
// custom one. Windows just refuses to draw glyphs with seac operators.
|
||||
var SEAC_ANALYSIS_ENABLED = true;
|
||||
|
||||
var FontFlags = {
|
||||
FixedPitch: 1,
|
||||
|
@ -444,37 +450,6 @@ var OpenTypeFileBuilder = (function OpenTypeFileBuilderClosure() {
|
|||
return OpenTypeFileBuilder;
|
||||
})();
|
||||
|
||||
// Problematic Unicode characters in the fonts that needs to be moved to avoid
|
||||
// issues when they are painted on the canvas, e.g. complex-script shaping or
|
||||
// control/whitespace characters. The ranges are listed in pairs: the first item
|
||||
// is a code of the first problematic code, the second one is the next
|
||||
// non-problematic code. The ranges must be in sorted order.
|
||||
var ProblematicCharRanges = new Int32Array([
|
||||
// Control characters.
|
||||
0x0000, 0x0020,
|
||||
0x007F, 0x00A1,
|
||||
0x00AD, 0x00AE,
|
||||
// Chars that is used in complex-script shaping.
|
||||
0x0600, 0x0780,
|
||||
0x08A0, 0x10A0,
|
||||
0x1780, 0x1800,
|
||||
0x1C00, 0x1C50,
|
||||
// General punctuation chars.
|
||||
0x2000, 0x2010,
|
||||
0x2011, 0x2012,
|
||||
0x2028, 0x2030,
|
||||
0x205F, 0x2070,
|
||||
0x25CC, 0x25CD,
|
||||
0x3000, 0x3001,
|
||||
0x3164, 0x3165,
|
||||
// Chars that is used in complex-script shaping.
|
||||
0xAA60, 0xAA80,
|
||||
// Unicode high surrogates.
|
||||
0xD800, 0xE000,
|
||||
// Specials Unicode block.
|
||||
0xFFF0, 0x10000
|
||||
]);
|
||||
|
||||
/**
|
||||
* 'Font' is the class the outside world should use, it encapsulate all the font
|
||||
* decoding logics whatever type it is (assuming the font type is supported).
|
||||
|
@ -755,91 +730,46 @@ var Font = (function FontClosure() {
|
|||
}
|
||||
|
||||
/**
|
||||
* Helper function for `adjustMapping`.
|
||||
* @return {boolean}
|
||||
*/
|
||||
function isProblematicUnicodeLocation(code) {
|
||||
// Using binary search to find a range start.
|
||||
var i = 0, j = ProblematicCharRanges.length - 1;
|
||||
while (i < j) {
|
||||
var c = (i + j + 1) >> 1;
|
||||
if (code < ProblematicCharRanges[c]) {
|
||||
j = c - 1;
|
||||
} else {
|
||||
i = c;
|
||||
}
|
||||
}
|
||||
// Even index means code in problematic range.
|
||||
return !(i & 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuilds the char code to glyph ID map by trying to replace the char codes
|
||||
* with their unicode value. It also moves char codes that are in known
|
||||
* problematic locations.
|
||||
* Rebuilds the char code to glyph ID map by moving all char codes to the
|
||||
* private use area. This is done to avoid issues with various problematic
|
||||
* unicode areas where either a glyph won't be drawn or is deformed by a
|
||||
* shaper.
|
||||
* @return {Object} Two properties:
|
||||
* 'toFontChar' - maps original char codes(the value that will be read
|
||||
* from commands such as show text) to the char codes that will be used in the
|
||||
* font that we build
|
||||
* 'charCodeToGlyphId' - maps the new font char codes to glyph ids
|
||||
*/
|
||||
function adjustMapping(charCodeToGlyphId, properties, missingGlyphs) {
|
||||
var toUnicode = properties.toUnicode;
|
||||
var isSymbolic = !!(properties.flags & FontFlags.Symbolic);
|
||||
var isIdentityUnicode =
|
||||
properties.toUnicode instanceof IdentityToUnicodeMap;
|
||||
function adjustMapping(charCodeToGlyphId, hasGlyph, newGlyphZeroId) {
|
||||
var newMap = Object.create(null);
|
||||
var toFontChar = [];
|
||||
var usedFontCharCodes = [];
|
||||
var nextAvailableFontCharCode = PRIVATE_USE_OFFSET_START;
|
||||
var privateUseAreaIndex = 0;
|
||||
var nextAvailableFontCharCode = PRIVATE_USE_AREAS[privateUseAreaIndex][0];
|
||||
var privateUseOffetEnd = PRIVATE_USE_AREAS[privateUseAreaIndex][1];
|
||||
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]) {
|
||||
if (!hasGlyph(glyphId)) {
|
||||
continue;
|
||||
}
|
||||
var fontCharCode = originalCharCode;
|
||||
// First try to map the value to a unicode position if a non identity map
|
||||
// was created.
|
||||
var hasUnicodeValue = false;
|
||||
if (!isIdentityUnicode && toUnicode.has(originalCharCode)) {
|
||||
hasUnicodeValue = true;
|
||||
var unicode = toUnicode.get(fontCharCode);
|
||||
// TODO: Try to map ligatures to the correct spot.
|
||||
if (unicode.length === 1) {
|
||||
fontCharCode = unicode.charCodeAt(0);
|
||||
if (nextAvailableFontCharCode > privateUseOffetEnd) {
|
||||
privateUseAreaIndex++;
|
||||
if (privateUseAreaIndex >= PRIVATE_USE_AREAS.length) {
|
||||
warn('Ran out of space in font private use area.');
|
||||
break;
|
||||
}
|
||||
nextAvailableFontCharCode = PRIVATE_USE_AREAS[privateUseAreaIndex][0];
|
||||
privateUseOffetEnd = PRIVATE_USE_AREAS[privateUseAreaIndex][1];
|
||||
}
|
||||
// Try to move control characters, special characters and already mapped
|
||||
// characters to the private use area since they will not be drawn by
|
||||
// canvas if left in their current position. Also, move characters if 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
|
||||
// with firefox and thuluthfont).
|
||||
if ((usedFontCharCodes[fontCharCode] !== undefined ||
|
||||
isProblematicUnicodeLocation(fontCharCode) ||
|
||||
(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) {
|
||||
fontCharCode = 0xF020;
|
||||
nextAvailableFontCharCode = fontCharCode + 1;
|
||||
}
|
||||
|
||||
} while (usedFontCharCodes[fontCharCode] !== undefined);
|
||||
var fontCharCode = nextAvailableFontCharCode++;
|
||||
if (glyphId === 0) {
|
||||
glyphId = newGlyphZeroId;
|
||||
}
|
||||
|
||||
newMap[fontCharCode] = glyphId;
|
||||
toFontChar[originalCharCode] = fontCharCode;
|
||||
usedFontCharCodes[fontCharCode] = true;
|
||||
}
|
||||
return {
|
||||
toFontChar,
|
||||
|
@ -1076,6 +1006,11 @@ var Font = (function FontClosure() {
|
|||
'Unicode ranges Bits > 123 are reserved for internal usage');
|
||||
}
|
||||
}
|
||||
if (lastCharIndex > 0xFFFF) {
|
||||
// OS2 only supports a 16 bit int. The spec says if supplementary
|
||||
// characters are used the field should just be set to 0xFFFF.
|
||||
lastCharIndex = 0xFFFF;
|
||||
}
|
||||
} else {
|
||||
// TODO
|
||||
firstCharIndex = 0;
|
||||
|
@ -1863,14 +1798,14 @@ var Font = (function FontClosure() {
|
|||
data[offset + 1] = (value >> 1) & 0xFF;
|
||||
};
|
||||
}
|
||||
// The first glyph is duplicated.
|
||||
var numGlyphsOut = dupFirstEntry ? numGlyphs + 1 : numGlyphs;
|
||||
var locaData = loca.data;
|
||||
var locaDataSize = itemSize * (1 + numGlyphs);
|
||||
// is loca.data too short or long?
|
||||
if (locaData.length !== locaDataSize) {
|
||||
locaData = new Uint8Array(locaDataSize);
|
||||
locaData.set(loca.data.subarray(0, locaDataSize));
|
||||
loca.data = locaData;
|
||||
}
|
||||
var locaDataSize = itemSize * (1 + numGlyphsOut);
|
||||
// Resize loca table to account for duplicated glyph.
|
||||
locaData = new Uint8Array(locaDataSize);
|
||||
locaData.set(loca.data.subarray(0, locaDataSize));
|
||||
loca.data = locaData;
|
||||
// removing the invalid glyphs
|
||||
var oldGlyfData = glyf.data;
|
||||
var oldGlyfDataLength = oldGlyfData.length;
|
||||
|
@ -1880,10 +1815,7 @@ var Font = (function FontClosure() {
|
|||
var missingGlyphs = Object.create(null);
|
||||
itemEncode(locaData, 0, writeOffset);
|
||||
var i, j;
|
||||
// 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) {
|
||||
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.
|
||||
|
@ -1921,11 +1853,14 @@ var Font = (function FontClosure() {
|
|||
// to have single glyph with one point
|
||||
var simpleGlyph = new Uint8Array(
|
||||
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 0]);
|
||||
for (i = 0, j = itemSize; i < numGlyphs; i++, j += itemSize) {
|
||||
for (i = 0, j = itemSize; i < numGlyphsOut; i++, j += itemSize) {
|
||||
itemEncode(locaData, j, simpleGlyph.length);
|
||||
}
|
||||
glyf.data = simpleGlyph;
|
||||
} else if (dupFirstEntry) {
|
||||
// Browsers will not display a glyph at position 0. Typically glyph 0
|
||||
// is notdef, but a number of fonts put a valid glyph there so it must
|
||||
// be duplicated and appended.
|
||||
var firstEntryLength = itemDecode(locaData, itemSize);
|
||||
if (newGlyfData.length > firstEntryLength + writeOffset) {
|
||||
glyf.data = newGlyfData.subarray(0, firstEntryLength + writeOffset);
|
||||
|
@ -2382,7 +2317,15 @@ var Font = (function FontClosure() {
|
|||
|
||||
font.pos = (font.start || 0) + tables['maxp'].offset;
|
||||
var version = font.getInt32();
|
||||
var numGlyphs = font.getUint16();
|
||||
const numGlyphs = font.getUint16();
|
||||
// Glyph 0 is duplicated and appended.
|
||||
let numGlyphsOut = numGlyphs + 1;
|
||||
let dupFirstEntry = true;
|
||||
if (numGlyphsOut > 0xFFFF) {
|
||||
dupFirstEntry = false;
|
||||
numGlyphsOut = numGlyphs;
|
||||
warn('Not enough space in glyfs to duplicate first glyph.');
|
||||
}
|
||||
var maxFunctionDefs = 0;
|
||||
var maxSizeOfInstructions = 0;
|
||||
if (version >= 0x00010000 && tables['maxp'].length >= 22) {
|
||||
|
@ -2399,15 +2342,8 @@ var Font = (function FontClosure() {
|
|||
maxSizeOfInstructions = font.getUint16();
|
||||
}
|
||||
|
||||
var dupFirstEntry = false;
|
||||
if (properties.type === 'CIDFontType2' && properties.toUnicode &&
|
||||
properties.toUnicode.get(0) > '\u0000') {
|
||||
// oracle's defect (see 3427), duplicating first entry
|
||||
dupFirstEntry = true;
|
||||
numGlyphs++;
|
||||
tables['maxp'].data[4] = numGlyphs >> 8;
|
||||
tables['maxp'].data[5] = numGlyphs & 255;
|
||||
}
|
||||
tables['maxp'].data[4] = numGlyphsOut >> 8;
|
||||
tables['maxp'].data[5] = numGlyphsOut & 255;
|
||||
|
||||
var hintsValid = sanitizeTTPrograms(tables['fpgm'], tables['prep'],
|
||||
tables['cvt '], maxFunctionDefs);
|
||||
|
@ -2419,7 +2355,7 @@ var Font = (function FontClosure() {
|
|||
|
||||
// Ensure the hmtx table contains the advance width and
|
||||
// sidebearings information for numGlyphs in the maxp table
|
||||
sanitizeMetrics(font, tables['hhea'], tables['hmtx'], numGlyphs);
|
||||
sanitizeMetrics(font, tables['hhea'], tables['hmtx'], numGlyphsOut);
|
||||
|
||||
if (!tables['head']) {
|
||||
throw new FormatError('Required "head" table is not found');
|
||||
|
@ -2472,12 +2408,15 @@ var Font = (function FontClosure() {
|
|||
|
||||
// The 'post' table has glyphs names.
|
||||
if (tables['post']) {
|
||||
var valid = readPostScriptTable(tables['post'], properties, numGlyphs);
|
||||
if (!valid) {
|
||||
tables['post'] = null;
|
||||
}
|
||||
readPostScriptTable(tables['post'], properties, numGlyphs);
|
||||
}
|
||||
|
||||
// The original 'post' table is not needed, replace it.
|
||||
tables['post'] = {
|
||||
tag: 'post',
|
||||
data: createPostTable(properties),
|
||||
};
|
||||
|
||||
var charCodeToGlyphId = [], charCode;
|
||||
|
||||
// Helper function to try to skip mapping of empty glyphs.
|
||||
|
@ -2504,12 +2443,6 @@ var Font = (function FontClosure() {
|
|||
charCodeToGlyphId[charCode] = glyphId;
|
||||
}
|
||||
});
|
||||
if (dupFirstEntry && (isCidToGidMapEmpty || !charCodeToGlyphId[0])) {
|
||||
// We don't duplicate the first entry in the `charCodeToGlyphId` map
|
||||
// if the font has a `CIDToGIDMap` which has already mapped the first
|
||||
// entry to a non-zero `glyphId` (fixes issue7544.pdf).
|
||||
charCodeToGlyphId[0] = numGlyphs - 1;
|
||||
}
|
||||
} else {
|
||||
// Most of the following logic in this code branch is based on the
|
||||
// 9.6.6.4 of the PDF spec.
|
||||
|
@ -2620,13 +2553,21 @@ var Font = (function FontClosure() {
|
|||
charCodeToGlyphId[0] = 0;
|
||||
}
|
||||
|
||||
// Typically glyph 0 is duplicated and the mapping must be updated, but if
|
||||
// there isn't enough room to duplicate, the glyph id is left the same. In
|
||||
// this case, glyph 0 may not work correctly, but that is better than
|
||||
// having the whole font fail.
|
||||
let glyphZeroId = numGlyphsOut - 1;
|
||||
if (!dupFirstEntry) {
|
||||
glyphZeroId = 0;
|
||||
}
|
||||
|
||||
// Converting glyphs and ids into font's cmap table
|
||||
var newMapping = adjustMapping(charCodeToGlyphId, properties,
|
||||
missingGlyphs);
|
||||
var newMapping = adjustMapping(charCodeToGlyphId, hasGlyph, glyphZeroId);
|
||||
this.toFontChar = newMapping.toFontChar;
|
||||
tables['cmap'] = {
|
||||
tag: 'cmap',
|
||||
data: createCmapTable(newMapping.charCodeToGlyphId, numGlyphs),
|
||||
data: createCmapTable(newMapping.charCodeToGlyphId, numGlyphsOut),
|
||||
};
|
||||
|
||||
if (!tables['OS/2'] || !validateOS2Table(tables['OS/2'])) {
|
||||
|
@ -2637,14 +2578,6 @@ var Font = (function FontClosure() {
|
|||
};
|
||||
}
|
||||
|
||||
// Rewrite the 'post' table if needed
|
||||
if (!tables['post']) {
|
||||
tables['post'] = {
|
||||
tag: 'post',
|
||||
data: createPostTable(properties),
|
||||
};
|
||||
}
|
||||
|
||||
if (!isTrueType) {
|
||||
try {
|
||||
// Trying to repair CFF file
|
||||
|
@ -2652,6 +2585,7 @@ var Font = (function FontClosure() {
|
|||
var parser = new CFFParser(cffFile, properties,
|
||||
SEAC_ANALYSIS_ENABLED);
|
||||
cff = parser.parse();
|
||||
cff.duplicateFirstGlyph();
|
||||
var compiler = new CFFCompiler(cff);
|
||||
tables['CFF '].data = compiler.compile();
|
||||
} catch (e) {
|
||||
|
@ -2688,8 +2622,16 @@ var Font = (function FontClosure() {
|
|||
adjustToUnicode(properties, properties.builtInEncoding);
|
||||
}
|
||||
|
||||
// Type 1 fonts have a notdef inserted at the beginning, so glyph 0
|
||||
// becomes glyph 1. In a CFF font glyph 0 is appended to the end of the
|
||||
// char strings.
|
||||
let glyphZeroId = 1;
|
||||
if (font instanceof CFFFont) {
|
||||
glyphZeroId = font.numGlyphs - 1;
|
||||
}
|
||||
var mapping = font.getGlyphMapping(properties);
|
||||
var newMapping = adjustMapping(mapping, properties, Object.create(null));
|
||||
var newMapping = adjustMapping(mapping, font.hasGlyphId.bind(font),
|
||||
glyphZeroId);
|
||||
this.toFontChar = newMapping.toFontChar;
|
||||
var numGlyphs = font.numGlyphs;
|
||||
|
||||
|
@ -2927,12 +2869,14 @@ var Font = (function FontClosure() {
|
|||
var seac = this.seacMap[charcode];
|
||||
fontCharCode = seac.baseFontCharCode;
|
||||
accent = {
|
||||
fontChar: String.fromCharCode(seac.accentFontCharCode),
|
||||
fontChar: String.fromCodePoint(seac.accentFontCharCode),
|
||||
offset: seac.accentOffset,
|
||||
};
|
||||
}
|
||||
|
||||
var fontChar = String.fromCharCode(fontCharCode);
|
||||
var fontChar = typeof fontCharCode === 'number' ?
|
||||
String.fromCodePoint(fontCharCode) :
|
||||
'';
|
||||
|
||||
var glyph = this.glyphCache[charcode];
|
||||
if (!glyph ||
|
||||
|
@ -3283,6 +3227,18 @@ var Type1Font = (function Type1FontClosure() {
|
|||
return type1FontGlyphMapping(properties, builtInEncoding, glyphNames);
|
||||
},
|
||||
|
||||
hasGlyphId: function Type1Font_hasGlyphID(id) {
|
||||
if (id < 0 || id >= this.numGlyphs) {
|
||||
return false;
|
||||
}
|
||||
if (id === 0) {
|
||||
// notdef is always defined.
|
||||
return true;
|
||||
}
|
||||
var glyph = this.charstrings[id - 1];
|
||||
return glyph.charstring.length > 0;
|
||||
},
|
||||
|
||||
getSeacs: function Type1Font_getSeacs(charstrings) {
|
||||
var i, ii;
|
||||
var seacMap = [];
|
||||
|
@ -3382,14 +3338,7 @@ var Type1Font = (function Type1FontClosure() {
|
|||
var charStringsIndex = new CFFIndex();
|
||||
charStringsIndex.add([0x8B, 0x0E]); // .notdef
|
||||
for (i = 0; i < count; i++) {
|
||||
var glyph = glyphs[i];
|
||||
// If the CharString outline is empty, replace it with .notdef to
|
||||
// prevent OTS from rejecting the font (fixes bug1252420.pdf).
|
||||
if (glyph.length === 0) {
|
||||
charStringsIndex.add([0x8B, 0x0E]); // .notdef
|
||||
continue;
|
||||
}
|
||||
charStringsIndex.add(glyph);
|
||||
charStringsIndex.add(glyphs[i]);
|
||||
}
|
||||
cff.charStrings = charStringsIndex;
|
||||
|
||||
|
@ -3448,6 +3397,7 @@ var CFFFont = (function CFFFontClosure() {
|
|||
|
||||
var parser = new CFFParser(file, properties, SEAC_ANALYSIS_ENABLED);
|
||||
this.cff = parser.parse();
|
||||
this.cff.duplicateFirstGlyph();
|
||||
var compiler = new CFFCompiler(this.cff);
|
||||
this.seacs = this.cff.seacs;
|
||||
try {
|
||||
|
@ -3498,37 +3448,20 @@ var CFFFont = (function CFFFontClosure() {
|
|||
charCodeToGlyphId = type1FontGlyphMapping(properties, encoding, charsets);
|
||||
return charCodeToGlyphId;
|
||||
},
|
||||
hasGlyphId: function CFFFont_hasGlyphID(id) {
|
||||
return this.cff.hasGlyphId(id);
|
||||
},
|
||||
};
|
||||
|
||||
return CFFFont;
|
||||
})();
|
||||
|
||||
// Workaround for seac on Windows.
|
||||
(function checkSeacSupport() {
|
||||
if (typeof navigator !== 'undefined' && /Windows/.test(navigator.userAgent)) {
|
||||
SEAC_ANALYSIS_ENABLED = true;
|
||||
}
|
||||
})();
|
||||
|
||||
// Workaround for Private Use Area characters in Chrome on Windows
|
||||
// http://code.google.com/p/chromium/issues/detail?id=122465
|
||||
// https://github.com/mozilla/pdf.js/issues/1689
|
||||
(function checkChromeWindows() {
|
||||
if (typeof navigator !== 'undefined' &&
|
||||
/Windows.*Chrome/.test(navigator.userAgent)) {
|
||||
SKIP_PRIVATE_USE_RANGE_F000_TO_F01F = true;
|
||||
}
|
||||
})();
|
||||
|
||||
export {
|
||||
SEAC_ANALYSIS_ENABLED,
|
||||
PRIVATE_USE_OFFSET_START,
|
||||
PRIVATE_USE_OFFSET_END,
|
||||
ErrorFont,
|
||||
Font,
|
||||
FontFlags,
|
||||
ToUnicodeMap,
|
||||
IdentityToUnicodeMap,
|
||||
ProblematicCharRanges,
|
||||
getFontType,
|
||||
};
|
||||
|
|
|
@ -165,6 +165,24 @@ const hasDOM = typeof window === 'object' && typeof document === 'object';
|
|||
globalScope.WeakMap = require('core-js/fn/weak-map');
|
||||
})();
|
||||
|
||||
// Provides support for String.codePointAt in legacy browsers.
|
||||
// Support: IE11.
|
||||
(function checkStringCodePointAt() {
|
||||
if (String.codePointAt) {
|
||||
return;
|
||||
}
|
||||
String.codePointAt = require('core-js/fn/string/code-point-at');
|
||||
})();
|
||||
|
||||
// Provides support for String.fromCodePoint in legacy browsers.
|
||||
// Support: IE11.
|
||||
(function checkStringFromCodePoint() {
|
||||
if (String.fromCodePoint) {
|
||||
return;
|
||||
}
|
||||
String.fromCodePoint = require('core-js/fn/string/from-code-point');
|
||||
})();
|
||||
|
||||
} // End of !PDFJSDev.test('CHROME')
|
||||
|
||||
// Provides support for Object.values in legacy browsers.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue