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
@ -815,11 +815,10 @@ var CFFParser = (function CFFParserClosure() {
|
|||||||
return new CFFEncoding(predefined, format, encoding, raw);
|
return new CFFEncoding(predefined, format, encoding, raw);
|
||||||
},
|
},
|
||||||
parseFDSelect: function CFFParser_parseFDSelect(pos, length) {
|
parseFDSelect: function CFFParser_parseFDSelect(pos, length) {
|
||||||
var start = pos;
|
|
||||||
var bytes = this.bytes;
|
var bytes = this.bytes;
|
||||||
var format = bytes[pos++];
|
var format = bytes[pos++];
|
||||||
var fdSelect = [], rawBytes;
|
var fdSelect = [];
|
||||||
var i, invalidFirstGID = false;
|
var i;
|
||||||
|
|
||||||
switch (format) {
|
switch (format) {
|
||||||
case 0:
|
case 0:
|
||||||
@ -827,7 +826,6 @@ var CFFParser = (function CFFParserClosure() {
|
|||||||
var id = bytes[pos++];
|
var id = bytes[pos++];
|
||||||
fdSelect.push(id);
|
fdSelect.push(id);
|
||||||
}
|
}
|
||||||
rawBytes = bytes.subarray(start, pos);
|
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
var rangesCount = (bytes[pos++] << 8) | bytes[pos++];
|
var rangesCount = (bytes[pos++] << 8) | bytes[pos++];
|
||||||
@ -836,7 +834,6 @@ var CFFParser = (function CFFParserClosure() {
|
|||||||
if (i === 0 && first !== 0) {
|
if (i === 0 && first !== 0) {
|
||||||
warn('parseFDSelect: The first range must have a first GID of 0' +
|
warn('parseFDSelect: The first range must have a first GID of 0' +
|
||||||
' -- trying to recover.');
|
' -- trying to recover.');
|
||||||
invalidFirstGID = true;
|
|
||||||
first = 0;
|
first = 0;
|
||||||
}
|
}
|
||||||
var fdIndex = bytes[pos++];
|
var fdIndex = bytes[pos++];
|
||||||
@ -847,11 +844,6 @@ var CFFParser = (function CFFParserClosure() {
|
|||||||
}
|
}
|
||||||
// Advance past the sentinel(next).
|
// Advance past the sentinel(next).
|
||||||
pos += 2;
|
pos += 2;
|
||||||
rawBytes = bytes.subarray(start, pos);
|
|
||||||
|
|
||||||
if (invalidFirstGID) {
|
|
||||||
rawBytes[3] = rawBytes[4] = 0; // Adjust the first range, first GID.
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new FormatError(`parseFDSelect: Unknown format "${format}".`);
|
throw new FormatError(`parseFDSelect: Unknown format "${format}".`);
|
||||||
@ -860,7 +852,7 @@ var CFFParser = (function CFFParserClosure() {
|
|||||||
throw new FormatError('parseFDSelect: Invalid font data.');
|
throw new FormatError('parseFDSelect: Invalid font data.');
|
||||||
}
|
}
|
||||||
|
|
||||||
return new CFFFDSelect(fdSelect, rawBytes);
|
return new CFFFDSelect(format, fdSelect);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
return CFFParser;
|
return CFFParser;
|
||||||
@ -885,6 +877,30 @@ var CFF = (function CFFClosure() {
|
|||||||
|
|
||||||
this.isCIDFont = false;
|
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;
|
return CFF;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
@ -1142,9 +1158,9 @@ var CFFEncoding = (function CFFEncodingClosure() {
|
|||||||
})();
|
})();
|
||||||
|
|
||||||
var CFFFDSelect = (function CFFFDSelectClosure() {
|
var CFFFDSelect = (function CFFFDSelectClosure() {
|
||||||
function CFFFDSelect(fdSelect, raw) {
|
function CFFFDSelect(format, fdSelect) {
|
||||||
|
this.format = format;
|
||||||
this.fdSelect = fdSelect;
|
this.fdSelect = fdSelect;
|
||||||
this.raw = raw;
|
|
||||||
}
|
}
|
||||||
CFFFDSelect.prototype = {
|
CFFFDSelect.prototype = {
|
||||||
getFDIndex: function CFFFDSelect_get(glyphIndex) {
|
getFDIndex: function CFFFDSelect_get(glyphIndex) {
|
||||||
@ -1261,6 +1277,7 @@ var CFFCompiler = (function CFFCompilerClosure() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cff.topDict.setByName('charset', 0);
|
||||||
var compiled = this.compileTopDicts([cff.topDict],
|
var compiled = this.compileTopDicts([cff.topDict],
|
||||||
output.length,
|
output.length,
|
||||||
cff.isCIDFont);
|
cff.isCIDFont);
|
||||||
@ -1284,17 +1301,9 @@ var CFFCompiler = (function CFFCompilerClosure() {
|
|||||||
output.add(encoding);
|
output.add(encoding);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var charset = this.compileCharset(cff.charset);
|
||||||
if (cff.charset && cff.topDict.hasName('charset')) {
|
topDictTracker.setEntryLocation('charset', [output.length], output);
|
||||||
if (cff.charset.predefined) {
|
output.add(charset);
|
||||||
topDictTracker.setEntryLocation('charset', [cff.charset.format],
|
|
||||||
output);
|
|
||||||
} else {
|
|
||||||
var charset = this.compileCharset(cff.charset);
|
|
||||||
topDictTracker.setEntryLocation('charset', [output.length], output);
|
|
||||||
output.add(charset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var charStrings = this.compileCharStrings(cff.charStrings);
|
var charStrings = this.compileCharStrings(cff.charStrings);
|
||||||
topDictTracker.setEntryLocation('CharStrings', [output.length], output);
|
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
|
// For some reason FDSelect must be in front of FDArray on windows. OSX
|
||||||
// and linux don't seem to care.
|
// and linux don't seem to care.
|
||||||
topDictTracker.setEntryLocation('FDSelect', [output.length], output);
|
topDictTracker.setEntryLocation('FDSelect', [output.length], output);
|
||||||
var fdSelect = this.compileFDSelect(cff.fdSelect.raw);
|
var fdSelect = this.compileFDSelect(cff.fdSelect);
|
||||||
output.add(fdSelect);
|
output.add(fdSelect);
|
||||||
// It is unclear if the sub font dictionary can have CID related
|
// It is unclear if the sub font dictionary can have CID related
|
||||||
// dictionary keys, but the sanitizer doesn't like them so remove them.
|
// 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));
|
this.out.writeByteArray(this.compileIndex(globalSubrIndex));
|
||||||
},
|
},
|
||||||
compileCharStrings: function CFFCompiler_compileCharStrings(charStrings) {
|
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) {
|
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) {
|
compileEncoding: function CFFCompiler_compileEncoding(encoding) {
|
||||||
return this.compileTypedArray(encoding.raw);
|
return this.compileTypedArray(encoding.raw);
|
||||||
},
|
},
|
||||||
compileFDSelect: function CFFCompiler_compileFDSelect(fdSelect) {
|
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) {
|
compileTypedArray: function CFFCompiler_compileTypedArray(data) {
|
||||||
var out = [];
|
var out = [];
|
||||||
@ -1648,4 +1709,5 @@ export {
|
|||||||
CFFTopDict,
|
CFFTopDict,
|
||||||
CFFPrivateDict,
|
CFFPrivateDict,
|
||||||
CFFCompiler,
|
CFFCompiler,
|
||||||
|
CFFFDSelect,
|
||||||
};
|
};
|
||||||
|
@ -122,7 +122,7 @@ var FontRendererFactory = (function FontRendererFactoryClosure() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function lookupCmap(ranges, unicode) {
|
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;
|
var l = 0, r = ranges.length - 1;
|
||||||
while (l < r) {
|
while (l < r) {
|
||||||
var c = (l + r + 1) >> 1;
|
var c = (l + r + 1) >> 1;
|
||||||
|
@ -39,18 +39,24 @@ import { IdentityCMap } from './cmap';
|
|||||||
import { Stream } from './stream';
|
import { Stream } from './stream';
|
||||||
import { Type1Parser } from './type1_parser';
|
import { Type1Parser } from './type1_parser';
|
||||||
|
|
||||||
// Unicode Private Use Area
|
// Unicode Private Use Areas:
|
||||||
var PRIVATE_USE_OFFSET_START = 0xE000;
|
const PRIVATE_USE_AREAS = [
|
||||||
var PRIVATE_USE_OFFSET_END = 0xF8FF;
|
[0xE000, 0xF8FF], // BMP (0)
|
||||||
var SKIP_PRIVATE_USE_RANGE_F000_TO_F01F = false;
|
[0x100000, 0x10FFFD], // PUP (16)
|
||||||
|
];
|
||||||
|
|
||||||
// PDF Glyph Space Units are one Thousandth of a TextSpace Unit
|
// PDF Glyph Space Units are one Thousandth of a TextSpace Unit
|
||||||
// except for Type 3 fonts
|
// except for Type 3 fonts
|
||||||
var PDF_GLYPH_SPACE_UNITS = 1000;
|
var PDF_GLYPH_SPACE_UNITS = 1000;
|
||||||
|
|
||||||
// Accented characters are not displayed properly on Windows, using this flag
|
// Accented characters have issues on Windows and Linux. When this flag is
|
||||||
// to control analysis of seac charstrings.
|
// enabled glyphs that use seac and seac style endchar operators are truncated
|
||||||
var SEAC_ANALYSIS_ENABLED = false;
|
// 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 = {
|
var FontFlags = {
|
||||||
FixedPitch: 1,
|
FixedPitch: 1,
|
||||||
@ -444,37 +450,6 @@ var OpenTypeFileBuilder = (function OpenTypeFileBuilderClosure() {
|
|||||||
return OpenTypeFileBuilder;
|
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
|
* '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).
|
* decoding logics whatever type it is (assuming the font type is supported).
|
||||||
@ -755,91 +730,46 @@ var Font = (function FontClosure() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function for `adjustMapping`.
|
* Rebuilds the char code to glyph ID map by moving all char codes to the
|
||||||
* @return {boolean}
|
* 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
|
||||||
function isProblematicUnicodeLocation(code) {
|
* shaper.
|
||||||
// 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.
|
|
||||||
* @return {Object} Two properties:
|
* @return {Object} Two properties:
|
||||||
* 'toFontChar' - maps original char codes(the value that will be read
|
* '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
|
* from commands such as show text) to the char codes that will be used in the
|
||||||
* 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, missingGlyphs) {
|
function adjustMapping(charCodeToGlyphId, hasGlyph, newGlyphZeroId) {
|
||||||
var toUnicode = properties.toUnicode;
|
|
||||||
var isSymbolic = !!(properties.flags & FontFlags.Symbolic);
|
|
||||||
var isIdentityUnicode =
|
|
||||||
properties.toUnicode instanceof IdentityToUnicodeMap;
|
|
||||||
var newMap = Object.create(null);
|
var newMap = Object.create(null);
|
||||||
var toFontChar = [];
|
var toFontChar = [];
|
||||||
var usedFontCharCodes = [];
|
var privateUseAreaIndex = 0;
|
||||||
var nextAvailableFontCharCode = PRIVATE_USE_OFFSET_START;
|
var nextAvailableFontCharCode = PRIVATE_USE_AREAS[privateUseAreaIndex][0];
|
||||||
|
var privateUseOffetEnd = PRIVATE_USE_AREAS[privateUseAreaIndex][1];
|
||||||
for (var originalCharCode in charCodeToGlyphId) {
|
for (var originalCharCode in charCodeToGlyphId) {
|
||||||
originalCharCode |= 0;
|
originalCharCode |= 0;
|
||||||
var glyphId = charCodeToGlyphId[originalCharCode];
|
var glyphId = charCodeToGlyphId[originalCharCode];
|
||||||
// For missing glyphs don't create the mappings so the glyph isn't
|
// For missing glyphs don't create the mappings so the glyph isn't
|
||||||
// drawn.
|
// drawn.
|
||||||
if (missingGlyphs[glyphId]) {
|
if (!hasGlyph(glyphId)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var fontCharCode = originalCharCode;
|
if (nextAvailableFontCharCode > privateUseOffetEnd) {
|
||||||
// First try to map the value to a unicode position if a non identity map
|
privateUseAreaIndex++;
|
||||||
// was created.
|
if (privateUseAreaIndex >= PRIVATE_USE_AREAS.length) {
|
||||||
var hasUnicodeValue = false;
|
warn('Ran out of space in font private use area.');
|
||||||
if (!isIdentityUnicode && toUnicode.has(originalCharCode)) {
|
break;
|
||||||
hasUnicodeValue = true;
|
|
||||||
var unicode = toUnicode.get(fontCharCode);
|
|
||||||
// TODO: Try to map ligatures to the correct spot.
|
|
||||||
if (unicode.length === 1) {
|
|
||||||
fontCharCode = unicode.charCodeAt(0);
|
|
||||||
}
|
}
|
||||||
|
nextAvailableFontCharCode = PRIVATE_USE_AREAS[privateUseAreaIndex][0];
|
||||||
|
privateUseOffetEnd = PRIVATE_USE_AREAS[privateUseAreaIndex][1];
|
||||||
}
|
}
|
||||||
// Try to move control characters, special characters and already mapped
|
var fontCharCode = nextAvailableFontCharCode++;
|
||||||
// characters to the private use area since they will not be drawn by
|
if (glyphId === 0) {
|
||||||
// canvas if left in their current position. Also, move characters if the
|
glyphId = newGlyphZeroId;
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
newMap[fontCharCode] = glyphId;
|
newMap[fontCharCode] = glyphId;
|
||||||
toFontChar[originalCharCode] = fontCharCode;
|
toFontChar[originalCharCode] = fontCharCode;
|
||||||
usedFontCharCodes[fontCharCode] = true;
|
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
toFontChar,
|
toFontChar,
|
||||||
@ -1076,6 +1006,11 @@ var Font = (function FontClosure() {
|
|||||||
'Unicode ranges Bits > 123 are reserved for internal usage');
|
'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 {
|
} else {
|
||||||
// TODO
|
// TODO
|
||||||
firstCharIndex = 0;
|
firstCharIndex = 0;
|
||||||
@ -1863,14 +1798,14 @@ var Font = (function FontClosure() {
|
|||||||
data[offset + 1] = (value >> 1) & 0xFF;
|
data[offset + 1] = (value >> 1) & 0xFF;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
// The first glyph is duplicated.
|
||||||
|
var numGlyphsOut = dupFirstEntry ? numGlyphs + 1 : numGlyphs;
|
||||||
var locaData = loca.data;
|
var locaData = loca.data;
|
||||||
var locaDataSize = itemSize * (1 + numGlyphs);
|
var locaDataSize = itemSize * (1 + numGlyphsOut);
|
||||||
// is loca.data too short or long?
|
// Resize loca table to account for duplicated glyph.
|
||||||
if (locaData.length !== locaDataSize) {
|
locaData = new Uint8Array(locaDataSize);
|
||||||
locaData = new Uint8Array(locaDataSize);
|
locaData.set(loca.data.subarray(0, locaDataSize));
|
||||||
locaData.set(loca.data.subarray(0, locaDataSize));
|
loca.data = locaData;
|
||||||
loca.data = locaData;
|
|
||||||
}
|
|
||||||
// removing the invalid glyphs
|
// removing the invalid glyphs
|
||||||
var oldGlyfData = glyf.data;
|
var oldGlyfData = glyf.data;
|
||||||
var oldGlyfDataLength = oldGlyfData.length;
|
var oldGlyfDataLength = oldGlyfData.length;
|
||||||
@ -1880,10 +1815,7 @@ var Font = (function FontClosure() {
|
|||||||
var missingGlyphs = Object.create(null);
|
var missingGlyphs = Object.create(null);
|
||||||
itemEncode(locaData, 0, writeOffset);
|
itemEncode(locaData, 0, writeOffset);
|
||||||
var i, j;
|
var i, j;
|
||||||
// When called with dupFirstEntry the number of glyphs has already been
|
for (i = 0, j = itemSize; i < numGlyphs; i++, j += itemSize) {
|
||||||
// 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);
|
||||||
// The spec says the offsets should be in ascending order, however
|
// The spec says the offsets should be in ascending order, however
|
||||||
// some fonts use the offset of 0 to mark a glyph as missing.
|
// 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
|
// to have single glyph with one point
|
||||||
var simpleGlyph = new Uint8Array(
|
var simpleGlyph = new Uint8Array(
|
||||||
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 0]);
|
[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);
|
itemEncode(locaData, j, simpleGlyph.length);
|
||||||
}
|
}
|
||||||
glyf.data = simpleGlyph;
|
glyf.data = simpleGlyph;
|
||||||
} else if (dupFirstEntry) {
|
} 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);
|
var firstEntryLength = itemDecode(locaData, itemSize);
|
||||||
if (newGlyfData.length > firstEntryLength + writeOffset) {
|
if (newGlyfData.length > firstEntryLength + writeOffset) {
|
||||||
glyf.data = newGlyfData.subarray(0, 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;
|
font.pos = (font.start || 0) + tables['maxp'].offset;
|
||||||
var version = font.getInt32();
|
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 maxFunctionDefs = 0;
|
||||||
var maxSizeOfInstructions = 0;
|
var maxSizeOfInstructions = 0;
|
||||||
if (version >= 0x00010000 && tables['maxp'].length >= 22) {
|
if (version >= 0x00010000 && tables['maxp'].length >= 22) {
|
||||||
@ -2399,15 +2342,8 @@ var Font = (function FontClosure() {
|
|||||||
maxSizeOfInstructions = font.getUint16();
|
maxSizeOfInstructions = font.getUint16();
|
||||||
}
|
}
|
||||||
|
|
||||||
var dupFirstEntry = false;
|
tables['maxp'].data[4] = numGlyphsOut >> 8;
|
||||||
if (properties.type === 'CIDFontType2' && properties.toUnicode &&
|
tables['maxp'].data[5] = numGlyphsOut & 255;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
var hintsValid = sanitizeTTPrograms(tables['fpgm'], tables['prep'],
|
var hintsValid = sanitizeTTPrograms(tables['fpgm'], tables['prep'],
|
||||||
tables['cvt '], maxFunctionDefs);
|
tables['cvt '], maxFunctionDefs);
|
||||||
@ -2419,7 +2355,7 @@ var Font = (function FontClosure() {
|
|||||||
|
|
||||||
// Ensure the hmtx table contains the advance width and
|
// Ensure the hmtx table contains the advance width and
|
||||||
// sidebearings information for numGlyphs in the maxp table
|
// 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']) {
|
if (!tables['head']) {
|
||||||
throw new FormatError('Required "head" table is not found');
|
throw new FormatError('Required "head" table is not found');
|
||||||
@ -2472,12 +2408,15 @@ var Font = (function FontClosure() {
|
|||||||
|
|
||||||
// The 'post' table has glyphs names.
|
// The 'post' table has glyphs names.
|
||||||
if (tables['post']) {
|
if (tables['post']) {
|
||||||
var valid = readPostScriptTable(tables['post'], properties, numGlyphs);
|
readPostScriptTable(tables['post'], properties, numGlyphs);
|
||||||
if (!valid) {
|
|
||||||
tables['post'] = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The original 'post' table is not needed, replace it.
|
||||||
|
tables['post'] = {
|
||||||
|
tag: 'post',
|
||||||
|
data: createPostTable(properties),
|
||||||
|
};
|
||||||
|
|
||||||
var charCodeToGlyphId = [], charCode;
|
var charCodeToGlyphId = [], charCode;
|
||||||
|
|
||||||
// Helper function to try to skip mapping of empty glyphs.
|
// Helper function to try to skip mapping of empty glyphs.
|
||||||
@ -2504,12 +2443,6 @@ var Font = (function FontClosure() {
|
|||||||
charCodeToGlyphId[charCode] = glyphId;
|
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 {
|
} else {
|
||||||
// Most of the following logic in this code branch is based on the
|
// Most of the following logic in this code branch is based on the
|
||||||
// 9.6.6.4 of the PDF spec.
|
// 9.6.6.4 of the PDF spec.
|
||||||
@ -2620,13 +2553,21 @@ var Font = (function FontClosure() {
|
|||||||
charCodeToGlyphId[0] = 0;
|
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
|
// Converting glyphs and ids into font's cmap table
|
||||||
var newMapping = adjustMapping(charCodeToGlyphId, properties,
|
var newMapping = adjustMapping(charCodeToGlyphId, hasGlyph, glyphZeroId);
|
||||||
missingGlyphs);
|
|
||||||
this.toFontChar = newMapping.toFontChar;
|
this.toFontChar = newMapping.toFontChar;
|
||||||
tables['cmap'] = {
|
tables['cmap'] = {
|
||||||
tag: 'cmap',
|
tag: 'cmap',
|
||||||
data: createCmapTable(newMapping.charCodeToGlyphId, numGlyphs),
|
data: createCmapTable(newMapping.charCodeToGlyphId, numGlyphsOut),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!tables['OS/2'] || !validateOS2Table(tables['OS/2'])) {
|
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) {
|
if (!isTrueType) {
|
||||||
try {
|
try {
|
||||||
// Trying to repair CFF file
|
// Trying to repair CFF file
|
||||||
@ -2652,6 +2585,7 @@ var Font = (function FontClosure() {
|
|||||||
var parser = new CFFParser(cffFile, properties,
|
var parser = new CFFParser(cffFile, properties,
|
||||||
SEAC_ANALYSIS_ENABLED);
|
SEAC_ANALYSIS_ENABLED);
|
||||||
cff = parser.parse();
|
cff = parser.parse();
|
||||||
|
cff.duplicateFirstGlyph();
|
||||||
var compiler = new CFFCompiler(cff);
|
var compiler = new CFFCompiler(cff);
|
||||||
tables['CFF '].data = compiler.compile();
|
tables['CFF '].data = compiler.compile();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -2688,8 +2622,16 @@ var Font = (function FontClosure() {
|
|||||||
adjustToUnicode(properties, properties.builtInEncoding);
|
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 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;
|
this.toFontChar = newMapping.toFontChar;
|
||||||
var numGlyphs = font.numGlyphs;
|
var numGlyphs = font.numGlyphs;
|
||||||
|
|
||||||
@ -2927,12 +2869,14 @@ var Font = (function FontClosure() {
|
|||||||
var seac = this.seacMap[charcode];
|
var seac = this.seacMap[charcode];
|
||||||
fontCharCode = seac.baseFontCharCode;
|
fontCharCode = seac.baseFontCharCode;
|
||||||
accent = {
|
accent = {
|
||||||
fontChar: String.fromCharCode(seac.accentFontCharCode),
|
fontChar: String.fromCodePoint(seac.accentFontCharCode),
|
||||||
offset: seac.accentOffset,
|
offset: seac.accentOffset,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
var fontChar = String.fromCharCode(fontCharCode);
|
var fontChar = typeof fontCharCode === 'number' ?
|
||||||
|
String.fromCodePoint(fontCharCode) :
|
||||||
|
'';
|
||||||
|
|
||||||
var glyph = this.glyphCache[charcode];
|
var glyph = this.glyphCache[charcode];
|
||||||
if (!glyph ||
|
if (!glyph ||
|
||||||
@ -3283,6 +3227,18 @@ var Type1Font = (function Type1FontClosure() {
|
|||||||
return type1FontGlyphMapping(properties, builtInEncoding, glyphNames);
|
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) {
|
getSeacs: function Type1Font_getSeacs(charstrings) {
|
||||||
var i, ii;
|
var i, ii;
|
||||||
var seacMap = [];
|
var seacMap = [];
|
||||||
@ -3382,14 +3338,7 @@ var Type1Font = (function Type1FontClosure() {
|
|||||||
var charStringsIndex = new CFFIndex();
|
var charStringsIndex = new CFFIndex();
|
||||||
charStringsIndex.add([0x8B, 0x0E]); // .notdef
|
charStringsIndex.add([0x8B, 0x0E]); // .notdef
|
||||||
for (i = 0; i < count; i++) {
|
for (i = 0; i < count; i++) {
|
||||||
var glyph = glyphs[i];
|
charStringsIndex.add(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);
|
|
||||||
}
|
}
|
||||||
cff.charStrings = charStringsIndex;
|
cff.charStrings = charStringsIndex;
|
||||||
|
|
||||||
@ -3448,6 +3397,7 @@ var CFFFont = (function CFFFontClosure() {
|
|||||||
|
|
||||||
var parser = new CFFParser(file, properties, SEAC_ANALYSIS_ENABLED);
|
var parser = new CFFParser(file, properties, SEAC_ANALYSIS_ENABLED);
|
||||||
this.cff = parser.parse();
|
this.cff = parser.parse();
|
||||||
|
this.cff.duplicateFirstGlyph();
|
||||||
var compiler = new CFFCompiler(this.cff);
|
var compiler = new CFFCompiler(this.cff);
|
||||||
this.seacs = this.cff.seacs;
|
this.seacs = this.cff.seacs;
|
||||||
try {
|
try {
|
||||||
@ -3498,37 +3448,20 @@ var CFFFont = (function CFFFontClosure() {
|
|||||||
charCodeToGlyphId = type1FontGlyphMapping(properties, encoding, charsets);
|
charCodeToGlyphId = type1FontGlyphMapping(properties, encoding, charsets);
|
||||||
return charCodeToGlyphId;
|
return charCodeToGlyphId;
|
||||||
},
|
},
|
||||||
|
hasGlyphId: function CFFFont_hasGlyphID(id) {
|
||||||
|
return this.cff.hasGlyphId(id);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return CFFFont;
|
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 {
|
export {
|
||||||
SEAC_ANALYSIS_ENABLED,
|
SEAC_ANALYSIS_ENABLED,
|
||||||
PRIVATE_USE_OFFSET_START,
|
|
||||||
PRIVATE_USE_OFFSET_END,
|
|
||||||
ErrorFont,
|
ErrorFont,
|
||||||
Font,
|
Font,
|
||||||
FontFlags,
|
FontFlags,
|
||||||
ToUnicodeMap,
|
ToUnicodeMap,
|
||||||
IdentityToUnicodeMap,
|
IdentityToUnicodeMap,
|
||||||
ProblematicCharRanges,
|
|
||||||
getFontType,
|
getFontType,
|
||||||
};
|
};
|
||||||
|
@ -165,6 +165,24 @@ const hasDOM = typeof window === 'object' && typeof document === 'object';
|
|||||||
globalScope.WeakMap = require('core-js/fn/weak-map');
|
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')
|
} // End of !PDFJSDev.test('CHROME')
|
||||||
|
|
||||||
// Provides support for Object.values in legacy browsers.
|
// Provides support for Object.values in legacy browsers.
|
||||||
|
3
test/pdfs/.gitignore
vendored
3
test/pdfs/.gitignore
vendored
@ -114,6 +114,7 @@
|
|||||||
!issue5686.pdf
|
!issue5686.pdf
|
||||||
!issue3928.pdf
|
!issue3928.pdf
|
||||||
!clippath.pdf
|
!clippath.pdf
|
||||||
|
!issue8795_reduced.pdf
|
||||||
!close-path-bug.pdf
|
!close-path-bug.pdf
|
||||||
!issue6019.pdf
|
!issue6019.pdf
|
||||||
!issue6621.pdf
|
!issue6621.pdf
|
||||||
@ -281,6 +282,7 @@
|
|||||||
!issue5475.pdf
|
!issue5475.pdf
|
||||||
!annotation-border-styles.pdf
|
!annotation-border-styles.pdf
|
||||||
!IdentityToUnicodeMap_charCodeOf.pdf
|
!IdentityToUnicodeMap_charCodeOf.pdf
|
||||||
|
!PDFJS-9279-reduced.pdf
|
||||||
!issue5481.pdf
|
!issue5481.pdf
|
||||||
!issue5567.pdf
|
!issue5567.pdf
|
||||||
!issue5701.pdf
|
!issue5701.pdf
|
||||||
@ -303,6 +305,7 @@
|
|||||||
!issue7014.pdf
|
!issue7014.pdf
|
||||||
!issue8187.pdf
|
!issue8187.pdf
|
||||||
!annotation-link-text-popup.pdf
|
!annotation-link-text-popup.pdf
|
||||||
|
!issue9278.pdf
|
||||||
!annotation-text-without-popup.pdf
|
!annotation-text-without-popup.pdf
|
||||||
!annotation-underline.pdf
|
!annotation-underline.pdf
|
||||||
!annotation-strikeout.pdf
|
!annotation-strikeout.pdf
|
||||||
|
BIN
test/pdfs/PDFJS-9279-reduced.pdf
Normal file
BIN
test/pdfs/PDFJS-9279-reduced.pdf
Normal file
Binary file not shown.
1
test/pdfs/bug1425312.pdf.link
Normal file
1
test/pdfs/bug1425312.pdf.link
Normal file
@ -0,0 +1 @@
|
|||||||
|
https://web.archive.org/web/20171116124714/https://www.unicode.org/charts/PDF/U11A50.pdf
|
15887
test/pdfs/issue8795_reduced.pdf
Normal file
15887
test/pdfs/issue8795_reduced.pdf
Normal file
File diff suppressed because one or more lines are too long
BIN
test/pdfs/issue9278.pdf
Normal file
BIN
test/pdfs/issue9278.pdf
Normal file
Binary file not shown.
@ -122,6 +122,12 @@
|
|||||||
"lastPage": 1,
|
"lastPage": 1,
|
||||||
"type": "eq"
|
"type": "eq"
|
||||||
},
|
},
|
||||||
|
{ "id": "issue8795_reduced",
|
||||||
|
"file": "pdfs/issue8795_reduced.pdf",
|
||||||
|
"md5": "3ce58fa4aff351d46c42e0677d582099",
|
||||||
|
"rounds": 1,
|
||||||
|
"type": "eq"
|
||||||
|
},
|
||||||
{ "id": "issue2391-1",
|
{ "id": "issue2391-1",
|
||||||
"file": "pdfs/issue2391-1.pdf",
|
"file": "pdfs/issue2391-1.pdf",
|
||||||
"md5": "25ae9cb959612e7b343b55da63af2716",
|
"md5": "25ae9cb959612e7b343b55da63af2716",
|
||||||
@ -2443,6 +2449,12 @@
|
|||||||
"link": false,
|
"link": false,
|
||||||
"type": "text"
|
"type": "text"
|
||||||
},
|
},
|
||||||
|
{ "id": "issue9278",
|
||||||
|
"file": "pdfs/issue9278.pdf",
|
||||||
|
"md5": "9819c3a5715c1a46ea5a6740f9ead3da",
|
||||||
|
"rounds": 1,
|
||||||
|
"type": "eq"
|
||||||
|
},
|
||||||
{ "id": "bug894572",
|
{ "id": "bug894572",
|
||||||
"file": "pdfs/bug894572.pdf",
|
"file": "pdfs/bug894572.pdf",
|
||||||
"md5": "e54a6b0451939f685ed37e3d46e16158",
|
"md5": "e54a6b0451939f685ed37e3d46e16158",
|
||||||
@ -3467,6 +3479,15 @@
|
|||||||
"rounds": 1,
|
"rounds": 1,
|
||||||
"type": "eq"
|
"type": "eq"
|
||||||
},
|
},
|
||||||
|
{ "id": "bug1425312",
|
||||||
|
"file": "pdfs/bug1425312.pdf",
|
||||||
|
"md5": "5b1e7d3e4ba7792fab2b69d1836df5a9",
|
||||||
|
"link": true,
|
||||||
|
"rounds": 1,
|
||||||
|
"firstPage": 2,
|
||||||
|
"lastPage": 2,
|
||||||
|
"type": "eq"
|
||||||
|
},
|
||||||
{ "id": "issue3438",
|
{ "id": "issue3438",
|
||||||
"file": "pdfs/issue3438.pdf",
|
"file": "pdfs/issue3438.pdf",
|
||||||
"md5": "5aa3340b0920b65a377f697587668f89",
|
"md5": "5aa3340b0920b65a377f697587668f89",
|
||||||
@ -3706,6 +3727,12 @@
|
|||||||
"type": "eq",
|
"type": "eq",
|
||||||
"about": "Image mask in higher resolution than the image itself"
|
"about": "Image mask in higher resolution than the image itself"
|
||||||
},
|
},
|
||||||
|
{ "id": "PDFJS-9279-reduced",
|
||||||
|
"file": "pdfs/PDFJS-9279-reduced.pdf",
|
||||||
|
"md5": "a562a25596e9fe571ac6fb5b9f561974",
|
||||||
|
"rounds": 1,
|
||||||
|
"type": "eq"
|
||||||
|
},
|
||||||
{ "id": "issue4436",
|
{ "id": "issue4436",
|
||||||
"file": "pdfs/issue4436r.pdf",
|
"file": "pdfs/issue4436r.pdf",
|
||||||
"md5": "4e43d692d213f56674fcac92110c7364",
|
"md5": "4e43d692d213f56674fcac92110c7364",
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CFFCompiler, CFFParser, CFFStrings
|
CFFCompiler, CFFFDSelect, CFFParser, CFFStrings
|
||||||
} from '../../src/core/cff_parser';
|
} from '../../src/core/cff_parser';
|
||||||
import { SEAC_ANALYSIS_ENABLED } from '../../src/core/fonts';
|
import { SEAC_ANALYSIS_ENABLED } from '../../src/core/fonts';
|
||||||
import { Stream } from '../../src/core/stream';
|
import { Stream } from '../../src/core/stream';
|
||||||
@ -311,7 +311,7 @@ describe('CFFParser', function() {
|
|||||||
var fdSelect = parser.parseFDSelect(0, 2);
|
var fdSelect = parser.parseFDSelect(0, 2);
|
||||||
|
|
||||||
expect(fdSelect.fdSelect).toEqual([0, 1]);
|
expect(fdSelect.fdSelect).toEqual([0, 1]);
|
||||||
expect(fdSelect.raw).toEqual(bytes);
|
expect(fdSelect.format).toEqual(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('parses fdselect format 3', function() {
|
it('parses fdselect format 3', function() {
|
||||||
@ -327,7 +327,7 @@ describe('CFFParser', function() {
|
|||||||
var fdSelect = parser.parseFDSelect(0, 4);
|
var fdSelect = parser.parseFDSelect(0, 4);
|
||||||
|
|
||||||
expect(fdSelect.fdSelect).toEqual([9, 9, 0xa, 0xa]);
|
expect(fdSelect.fdSelect).toEqual([9, 9, 0xa, 0xa]);
|
||||||
expect(fdSelect.raw).toEqual(bytes);
|
expect(fdSelect.format).toEqual(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('parses invalid fdselect format 3 (bug 1146106)', function() {
|
it('parses invalid fdselect format 3 (bug 1146106)', function() {
|
||||||
@ -343,8 +343,7 @@ describe('CFFParser', function() {
|
|||||||
var fdSelect = parser.parseFDSelect(0, 4);
|
var fdSelect = parser.parseFDSelect(0, 4);
|
||||||
|
|
||||||
expect(fdSelect.fdSelect).toEqual([9, 9, 0xa, 0xa]);
|
expect(fdSelect.fdSelect).toEqual([9, 9, 0xa, 0xa]);
|
||||||
bytes[3] = bytes[4] = 0x00; // The adjusted first range, first gid.
|
expect(fdSelect.format).toEqual(3);
|
||||||
expect(fdSelect.raw).toEqual(bytes);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO fdArray
|
// TODO fdArray
|
||||||
@ -400,5 +399,52 @@ describe('CFFCompiler', function() {
|
|||||||
expect(names[0].length).toEqual(127);
|
expect(names[0].length).toEqual(127);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('compiles fdselect format 0', function() {
|
||||||
|
var fdSelect = new CFFFDSelect(0, [3, 2, 1]);
|
||||||
|
var c = new CFFCompiler();
|
||||||
|
var out = c.compileFDSelect(fdSelect);
|
||||||
|
expect(out).toEqual([
|
||||||
|
0, // format
|
||||||
|
3, // gid: 0 fd 3
|
||||||
|
2, // gid: 1 fd 3
|
||||||
|
1, // gid: 2 fd 3
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('compiles fdselect format 3', function() {
|
||||||
|
var fdSelect = new CFFFDSelect(3, [0, 0, 1, 1]);
|
||||||
|
var c = new CFFCompiler();
|
||||||
|
var out = c.compileFDSelect(fdSelect);
|
||||||
|
expect(out).toEqual([
|
||||||
|
3, // format
|
||||||
|
0, // nRanges (high)
|
||||||
|
2, // nRanges (low)
|
||||||
|
0, // range struct 0 - first (high)
|
||||||
|
0, // range struct 0 - first (low)
|
||||||
|
0, // range struct 0 - fd
|
||||||
|
0, // range struct 0 - first (high)
|
||||||
|
2, // range struct 0 - first (low)
|
||||||
|
1, // range struct 0 - fd
|
||||||
|
0, // sentinel (high)
|
||||||
|
4, // sentinel (low)
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('compiles fdselect format 3, single range', function() {
|
||||||
|
var fdSelect = new CFFFDSelect(3, [0, 0]);
|
||||||
|
var c = new CFFCompiler();
|
||||||
|
var out = c.compileFDSelect(fdSelect);
|
||||||
|
expect(out).toEqual([
|
||||||
|
3, // format
|
||||||
|
0, // nRanges (high)
|
||||||
|
1, // nRanges (low)
|
||||||
|
0, // range struct 0 - first (high)
|
||||||
|
0, // range struct 0 - first (low)
|
||||||
|
0, // range struct 0 - fd
|
||||||
|
0, // sentinel (high)
|
||||||
|
2, // sentinel (low)
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
// TODO a lot more compiler tests
|
// TODO a lot more compiler tests
|
||||||
});
|
});
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
"dom_utils_spec.js",
|
"dom_utils_spec.js",
|
||||||
"encodings_spec.js",
|
"encodings_spec.js",
|
||||||
"evaluator_spec.js",
|
"evaluator_spec.js",
|
||||||
"fonts_spec.js",
|
|
||||||
"function_spec.js",
|
"function_spec.js",
|
||||||
"message_handler_spec.js",
|
"message_handler_spec.js",
|
||||||
"metadata_spec.js",
|
"metadata_spec.js",
|
||||||
|
@ -1,91 +0,0 @@
|
|||||||
/* Copyright 2017 Mozilla Foundation
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {
|
|
||||||
PRIVATE_USE_OFFSET_END, PRIVATE_USE_OFFSET_START, ProblematicCharRanges
|
|
||||||
} from '../../src/core/fonts';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to validate the entries in `ProblematicCharRanges`, and to ensure that
|
|
||||||
* its total number of characters does not exceed the PUA (Private Use Area)
|
|
||||||
* length.
|
|
||||||
* @returns {Object} An object with {number} `numChars`, {number} `puaLength`,
|
|
||||||
* and {number} `percentage` parameters.
|
|
||||||
*/
|
|
||||||
var checkProblematicCharRanges = function checkProblematicCharRanges() {
|
|
||||||
function printRange(limits) {
|
|
||||||
return '[' + limits.lower.toString('16').toUpperCase() + ', ' +
|
|
||||||
limits.upper.toString('16').toUpperCase() + ')';
|
|
||||||
}
|
|
||||||
|
|
||||||
var numRanges = ProblematicCharRanges.length;
|
|
||||||
if (numRanges % 2 !== 0) {
|
|
||||||
throw new Error('Char ranges must contain an even number of elements.');
|
|
||||||
}
|
|
||||||
var prevLimits, numChars = 0;
|
|
||||||
for (var i = 0; i < numRanges; i += 2) {
|
|
||||||
var limits = {
|
|
||||||
lower: ProblematicCharRanges[i],
|
|
||||||
upper: ProblematicCharRanges[i + 1],
|
|
||||||
};
|
|
||||||
if (!Number.isInteger(limits.lower) || !Number.isInteger(limits.upper)) {
|
|
||||||
throw new Error('Range endpoints must be integers: ' +
|
|
||||||
printRange(limits));
|
|
||||||
}
|
|
||||||
if (limits.lower < 0 || limits.upper < 0) {
|
|
||||||
throw new Error('Range endpoints must be non-negative: ' +
|
|
||||||
printRange(limits));
|
|
||||||
}
|
|
||||||
var range = limits.upper - limits.lower;
|
|
||||||
if (range < 1) {
|
|
||||||
throw new Error('Range must contain at least one element: ' +
|
|
||||||
printRange(limits));
|
|
||||||
}
|
|
||||||
if (prevLimits) {
|
|
||||||
if (limits.lower < prevLimits.lower) {
|
|
||||||
throw new Error('Ranges must be sorted in ascending order: ' +
|
|
||||||
printRange(limits) + ', ' + printRange(prevLimits));
|
|
||||||
}
|
|
||||||
if (limits.lower < prevLimits.upper) {
|
|
||||||
throw new Error('Ranges must not overlap: ' +
|
|
||||||
printRange(limits) + ', ' + printRange(prevLimits));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
prevLimits = {
|
|
||||||
lower: limits.lower,
|
|
||||||
upper: limits.upper,
|
|
||||||
};
|
|
||||||
// The current range is OK.
|
|
||||||
numChars += range;
|
|
||||||
}
|
|
||||||
var puaLength = (PRIVATE_USE_OFFSET_END + 1) - PRIVATE_USE_OFFSET_START;
|
|
||||||
if (numChars > puaLength) {
|
|
||||||
throw new Error('Total number of chars must not exceed the PUA length.');
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
numChars,
|
|
||||||
puaLength,
|
|
||||||
percentage: 100 * (numChars / puaLength),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
describe('Fonts', function() {
|
|
||||||
it('checkProblematicCharRanges', function() {
|
|
||||||
var EXPECTED_PERCENTAGE = 100;
|
|
||||||
var result = checkProblematicCharRanges();
|
|
||||||
|
|
||||||
expect(result.percentage).toBeLessThan(EXPECTED_PERCENTAGE);
|
|
||||||
});
|
|
||||||
});
|
|
@ -60,7 +60,6 @@ function initializePDFJS(callback) {
|
|||||||
'pdfjs-test/unit/dom_utils_spec',
|
'pdfjs-test/unit/dom_utils_spec',
|
||||||
'pdfjs-test/unit/encodings_spec',
|
'pdfjs-test/unit/encodings_spec',
|
||||||
'pdfjs-test/unit/evaluator_spec',
|
'pdfjs-test/unit/evaluator_spec',
|
||||||
'pdfjs-test/unit/fonts_spec',
|
|
||||||
'pdfjs-test/unit/function_spec',
|
'pdfjs-test/unit/function_spec',
|
||||||
'pdfjs-test/unit/message_handler_spec',
|
'pdfjs-test/unit/message_handler_spec',
|
||||||
'pdfjs-test/unit/metadata_spec',
|
'pdfjs-test/unit/metadata_spec',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user