Convert src/core/fonts.js
to use standard classes
Obviously the `Font`-class is still *very* large, given particularly how TrueType fonts are handled, however this patch-series at least improves things by moving a number of functions/classes into their own files. As a follow-up it might make sense to try and re-factor/extract the TrueType parsing into its own file, since all of this code is quite old, however that's probably best left for another time. For e.g. `gulp mozcentral`, the *built* `pdf.worker.js` files decreases from `1 620 332` to `1 617 466` bytes with this patch-series.
This commit is contained in:
parent
cadc20d8b9
commit
b487edd05d
@ -157,9 +157,8 @@ function adjustToUnicode(properties, builtInEncoding) {
|
|||||||
properties.toUnicode.amend(toUnicode);
|
properties.toUnicode.amend(toUnicode);
|
||||||
}
|
}
|
||||||
|
|
||||||
const Glyph = (function GlyphClosure() {
|
class Glyph {
|
||||||
// eslint-disable-next-line no-shadow
|
constructor(
|
||||||
function Glyph(
|
|
||||||
fontChar,
|
fontChar,
|
||||||
unicode,
|
unicode,
|
||||||
accent,
|
accent,
|
||||||
@ -179,7 +178,7 @@ const Glyph = (function GlyphClosure() {
|
|||||||
this.isInFont = isInFont;
|
this.isInFont = isInFont;
|
||||||
}
|
}
|
||||||
|
|
||||||
Glyph.prototype.matchesForCache = function (
|
matchesForCache(
|
||||||
fontChar,
|
fontChar,
|
||||||
unicode,
|
unicode,
|
||||||
accent,
|
accent,
|
||||||
@ -199,183 +198,32 @@ const Glyph = (function GlyphClosure() {
|
|||||||
this.isSpace === isSpace &&
|
this.isSpace === isSpace &&
|
||||||
this.isInFont === isInFont
|
this.isInFont === isInFont
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
return Glyph;
|
|
||||||
})();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* '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).
|
|
||||||
*
|
|
||||||
* For example to read a Type1 font and to attach it to the document:
|
|
||||||
* var type1Font = new Font("MyFontName", binaryFile, propertiesObject);
|
|
||||||
* type1Font.bind();
|
|
||||||
*/
|
|
||||||
const Font = (function FontClosure() {
|
|
||||||
// eslint-disable-next-line no-shadow
|
|
||||||
function Font(name, file, properties) {
|
|
||||||
let charCode;
|
|
||||||
|
|
||||||
this.name = name;
|
|
||||||
this.loadedName = properties.loadedName;
|
|
||||||
this.isType3Font = properties.isType3Font;
|
|
||||||
this.missingFile = false;
|
|
||||||
this.cssFontInfo = properties.cssFontInfo;
|
|
||||||
|
|
||||||
this.glyphCache = Object.create(null);
|
|
||||||
|
|
||||||
this.isSerifFont = !!(properties.flags & FontFlags.Serif);
|
|
||||||
this.isSymbolicFont = !!(properties.flags & FontFlags.Symbolic);
|
|
||||||
this.isMonospace = !!(properties.flags & FontFlags.FixedPitch);
|
|
||||||
|
|
||||||
let type = properties.type;
|
|
||||||
let subtype = properties.subtype;
|
|
||||||
this.type = type;
|
|
||||||
this.subtype = subtype;
|
|
||||||
|
|
||||||
let fallbackName = "sans-serif";
|
|
||||||
if (this.isMonospace) {
|
|
||||||
fallbackName = "monospace";
|
|
||||||
} else if (this.isSerifFont) {
|
|
||||||
fallbackName = "serif";
|
|
||||||
}
|
}
|
||||||
this.fallbackName = fallbackName;
|
}
|
||||||
|
|
||||||
this.differences = properties.differences;
|
function int16(b0, b1) {
|
||||||
this.widths = properties.widths;
|
|
||||||
this.defaultWidth = properties.defaultWidth;
|
|
||||||
this.composite = properties.composite;
|
|
||||||
this.cMap = properties.cMap;
|
|
||||||
this.capHeight = properties.capHeight / PDF_GLYPH_SPACE_UNITS;
|
|
||||||
this.ascent = properties.ascent / PDF_GLYPH_SPACE_UNITS;
|
|
||||||
this.descent = properties.descent / PDF_GLYPH_SPACE_UNITS;
|
|
||||||
this.fontMatrix = properties.fontMatrix;
|
|
||||||
this.bbox = properties.bbox;
|
|
||||||
this.defaultEncoding = properties.defaultEncoding;
|
|
||||||
|
|
||||||
this.toUnicode = properties.toUnicode;
|
|
||||||
this.fallbackToUnicode = properties.fallbackToUnicode || new ToUnicodeMap();
|
|
||||||
|
|
||||||
this.toFontChar = [];
|
|
||||||
|
|
||||||
if (properties.type === "Type3") {
|
|
||||||
for (charCode = 0; charCode < 256; charCode++) {
|
|
||||||
this.toFontChar[charCode] =
|
|
||||||
this.differences[charCode] || properties.defaultEncoding[charCode];
|
|
||||||
}
|
|
||||||
this.fontType = FontType.TYPE3;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.cidEncoding = properties.cidEncoding;
|
|
||||||
this.vertical = !!properties.vertical;
|
|
||||||
if (this.vertical) {
|
|
||||||
this.vmetrics = properties.vmetrics;
|
|
||||||
this.defaultVMetrics = properties.defaultVMetrics;
|
|
||||||
}
|
|
||||||
|
|
||||||
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.fallbackToSystemFont(properties);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the font file to determine the correct type/subtype, rather than
|
|
||||||
// relying on the (often incorrect) data in the font dictionary; (see e.g.
|
|
||||||
// issue6782.pdf, issue7598.pdf, and issue9949.pdf).
|
|
||||||
[type, subtype] = getFontFileType(file, properties);
|
|
||||||
|
|
||||||
if (type !== this.type || subtype !== this.subtype) {
|
|
||||||
info(
|
|
||||||
"Inconsistent font file Type/SubType, expected: " +
|
|
||||||
`${this.type}/${this.subtype} but found: ${type}/${subtype}.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let data;
|
|
||||||
try {
|
|
||||||
switch (type) {
|
|
||||||
case "MMType1":
|
|
||||||
info("MMType1 font (" + name + "), falling back to Type1.");
|
|
||||||
/* falls through */
|
|
||||||
case "Type1":
|
|
||||||
case "CIDFontType0":
|
|
||||||
this.mimetype = "font/opentype";
|
|
||||||
|
|
||||||
const 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;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new FormatError(`Font ${type} is not supported`);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
warn(e);
|
|
||||||
this.fallbackToSystemFont(properties);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.data = data;
|
|
||||||
this.fontType = getFontType(type, subtype);
|
|
||||||
|
|
||||||
// Transfer some properties again that could change during font conversion
|
|
||||||
this.fontMatrix = properties.fontMatrix;
|
|
||||||
this.widths = properties.widths;
|
|
||||||
this.defaultWidth = properties.defaultWidth;
|
|
||||||
this.toUnicode = properties.toUnicode;
|
|
||||||
this.seacMap = properties.seacMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
function int16(b0, b1) {
|
|
||||||
return (b0 << 8) + b1;
|
return (b0 << 8) + b1;
|
||||||
}
|
}
|
||||||
|
|
||||||
function writeSignedInt16(bytes, index, value) {
|
function writeSignedInt16(bytes, index, value) {
|
||||||
bytes[index + 1] = value;
|
bytes[index + 1] = value;
|
||||||
bytes[index] = value >>> 8;
|
bytes[index] = value >>> 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
function signedInt16(b0, b1) {
|
function signedInt16(b0, b1) {
|
||||||
const value = (b0 << 8) + b1;
|
const value = (b0 << 8) + b1;
|
||||||
return value & (1 << 15) ? value - 0x10000 : value;
|
return value & (1 << 15) ? value - 0x10000 : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function int32(b0, b1, b2, b3) {
|
function int32(b0, b1, b2, b3) {
|
||||||
return (b0 << 24) + (b1 << 16) + (b2 << 8) + b3;
|
return (b0 << 24) + (b1 << 16) + (b2 << 8) + b3;
|
||||||
}
|
}
|
||||||
|
|
||||||
function string16(value) {
|
function string16(value) {
|
||||||
return String.fromCharCode((value >> 8) & 0xff, value & 0xff);
|
return String.fromCharCode((value >> 8) & 0xff, value & 0xff);
|
||||||
}
|
}
|
||||||
|
|
||||||
function safeString16(value) {
|
function safeString16(value) {
|
||||||
// clamp value to the 16-bit int range
|
// clamp value to the 16-bit int range
|
||||||
if (value > 0x7fff) {
|
if (value > 0x7fff) {
|
||||||
value = 0x7fff;
|
value = 0x7fff;
|
||||||
@ -383,26 +231,26 @@ const Font = (function FontClosure() {
|
|||||||
value = -0x8000;
|
value = -0x8000;
|
||||||
}
|
}
|
||||||
return String.fromCharCode((value >> 8) & 0xff, value & 0xff);
|
return String.fromCharCode((value >> 8) & 0xff, value & 0xff);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isTrueTypeFile(file) {
|
function isTrueTypeFile(file) {
|
||||||
const header = file.peekBytes(4);
|
const header = file.peekBytes(4);
|
||||||
return (
|
return (
|
||||||
readUint32(header, 0) === 0x00010000 || bytesToString(header) === "true"
|
readUint32(header, 0) === 0x00010000 || bytesToString(header) === "true"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isTrueTypeCollectionFile(file) {
|
function isTrueTypeCollectionFile(file) {
|
||||||
const header = file.peekBytes(4);
|
const header = file.peekBytes(4);
|
||||||
return bytesToString(header) === "ttcf";
|
return bytesToString(header) === "ttcf";
|
||||||
}
|
}
|
||||||
|
|
||||||
function isOpenTypeFile(file) {
|
function isOpenTypeFile(file) {
|
||||||
const header = file.peekBytes(4);
|
const header = file.peekBytes(4);
|
||||||
return bytesToString(header) === "OTTO";
|
return bytesToString(header) === "OTTO";
|
||||||
}
|
}
|
||||||
|
|
||||||
function isType1File(file) {
|
function isType1File(file) {
|
||||||
const header = file.peekBytes(2);
|
const header = file.peekBytes(2);
|
||||||
// All Type1 font programs must begin with the comment '%!' (0x25 + 0x21).
|
// All Type1 font programs must begin with the comment '%!' (0x25 + 0x21).
|
||||||
if (header[0] === 0x25 && header[1] === 0x21) {
|
if (header[0] === 0x25 && header[1] === 0x21) {
|
||||||
@ -414,14 +262,14 @@ const Font = (function FontClosure() {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compared to other font formats, the header in CFF files is not constant
|
* Compared to other font formats, the header in CFF files is not constant
|
||||||
* but contains version numbers. To reduce the possibility of misclassifying
|
* but contains version numbers. To reduce the possibility of misclassifying
|
||||||
* font files as CFF, it's recommended to check for other font formats first.
|
* font files as CFF, it's recommended to check for other font formats first.
|
||||||
*/
|
*/
|
||||||
function isCFFFile(file) {
|
function isCFFFile(file) {
|
||||||
const header = file.peekBytes(4);
|
const header = file.peekBytes(4);
|
||||||
if (
|
if (
|
||||||
/* major version, [1, 255] */ header[0] >= 1 &&
|
/* major version, [1, 255] */ header[0] >= 1 &&
|
||||||
@ -433,9 +281,9 @@ const Font = (function FontClosure() {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFontFileType(file, { type, subtype, composite }) {
|
function getFontFileType(file, { type, subtype, composite }) {
|
||||||
let fileType, fileSubtype;
|
let fileType, fileSubtype;
|
||||||
|
|
||||||
if (isTrueTypeFile(file) || isTrueTypeCollectionFile(file)) {
|
if (isTrueTypeFile(file) || isTrueTypeCollectionFile(file)) {
|
||||||
@ -471,9 +319,9 @@ const Font = (function FontClosure() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return [fileType, fileSubtype];
|
return [fileType, fileSubtype];
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildToFontChar(encoding, glyphsUnicodeMap, differences) {
|
function buildToFontChar(encoding, glyphsUnicodeMap, differences) {
|
||||||
const toFontChar = [];
|
const toFontChar = [];
|
||||||
let unicode;
|
let unicode;
|
||||||
for (let i = 0, ii = encoding.length; i < ii; i++) {
|
for (let i = 0, ii = encoding.length; i < ii; i++) {
|
||||||
@ -489,9 +337,9 @@ const Font = (function FontClosure() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return toFontChar;
|
return toFontChar;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rebuilds the char code to glyph ID map by moving all char codes to the
|
* 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
|
* 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
|
* unicode areas where either a glyph won't be drawn or is deformed by a
|
||||||
@ -502,7 +350,7 @@ const Font = (function FontClosure() {
|
|||||||
* font that we build
|
* font that we build
|
||||||
* 'charCodeToGlyphId' - maps the new font char codes to glyph ids
|
* 'charCodeToGlyphId' - maps the new font char codes to glyph ids
|
||||||
*/
|
*/
|
||||||
function adjustMapping(charCodeToGlyphId, hasGlyph, newGlyphZeroId) {
|
function adjustMapping(charCodeToGlyphId, hasGlyph, newGlyphZeroId) {
|
||||||
const newMap = Object.create(null);
|
const newMap = Object.create(null);
|
||||||
const toFontChar = [];
|
const toFontChar = [];
|
||||||
let privateUseAreaIndex = 0;
|
let privateUseAreaIndex = 0;
|
||||||
@ -538,9 +386,9 @@ const Font = (function FontClosure() {
|
|||||||
charCodeToGlyphId: newMap,
|
charCodeToGlyphId: newMap,
|
||||||
nextAvailableFontCharCode,
|
nextAvailableFontCharCode,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRanges(glyphs, numGlyphs) {
|
function getRanges(glyphs, numGlyphs) {
|
||||||
// Array.sort() sorts by characters, not numerically, so convert to an
|
// Array.sort() sorts by characters, not numerically, so convert to an
|
||||||
// array of characters.
|
// array of characters.
|
||||||
const codes = [];
|
const codes = [];
|
||||||
@ -580,9 +428,9 @@ const Font = (function FontClosure() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return ranges;
|
return ranges;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createCmapTable(glyphs, numGlyphs) {
|
function createCmapTable(glyphs, numGlyphs) {
|
||||||
const ranges = getRanges(glyphs, numGlyphs);
|
const ranges = getRanges(glyphs, numGlyphs);
|
||||||
const numTables = ranges[ranges.length - 1][1] > 0xffff ? 2 : 1;
|
const numTables = ranges[ranges.length - 1][1] > 0xffff ? 2 : 1;
|
||||||
let cmap =
|
let cmap =
|
||||||
@ -713,9 +561,9 @@ const Font = (function FontClosure() {
|
|||||||
header31012 +
|
header31012 +
|
||||||
format31012
|
format31012
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateOS2Table(os2, file) {
|
function validateOS2Table(os2, file) {
|
||||||
file.pos = (file.start || 0) + os2.offset;
|
file.pos = (file.start || 0) + os2.offset;
|
||||||
const version = file.getUint16();
|
const version = file.getUint16();
|
||||||
// TODO verify all OS/2 tables fields, but currently we validate only those
|
// TODO verify all OS/2 tables fields, but currently we validate only those
|
||||||
@ -740,9 +588,9 @@ const Font = (function FontClosure() {
|
|||||||
// OS/2 appears to be valid, resetting some fields
|
// OS/2 appears to be valid, resetting some fields
|
||||||
os2.data[8] = os2.data[9] = 0; // IE rejects fonts if fsType != 0
|
os2.data[8] = os2.data[9] = 0; // IE rejects fonts if fsType != 0
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createOS2Table(properties, charstrings, override) {
|
function createOS2Table(properties, charstrings, override) {
|
||||||
override = override || {
|
override = override || {
|
||||||
unitsPerEm: 0,
|
unitsPerEm: 0,
|
||||||
yMax: 0,
|
yMax: 0,
|
||||||
@ -857,9 +705,9 @@ const Font = (function FontClosure() {
|
|||||||
string16(firstCharIndex || properties.firstChar) + // usBreakChar
|
string16(firstCharIndex || properties.firstChar) + // usBreakChar
|
||||||
"\x00\x03"
|
"\x00\x03"
|
||||||
); // usMaxContext
|
); // usMaxContext
|
||||||
}
|
}
|
||||||
|
|
||||||
function createPostTable(properties) {
|
function createPostTable(properties) {
|
||||||
const angle = Math.floor(properties.italicAngle * 2 ** 16);
|
const angle = Math.floor(properties.italicAngle * 2 ** 16);
|
||||||
return (
|
return (
|
||||||
"\x00\x03\x00\x00" + // Version number
|
"\x00\x03\x00\x00" + // Version number
|
||||||
@ -872,9 +720,9 @@ const Font = (function FontClosure() {
|
|||||||
"\x00\x00\x00\x00" + // minMemType1
|
"\x00\x00\x00\x00" + // minMemType1
|
||||||
"\x00\x00\x00\x00"
|
"\x00\x00\x00\x00"
|
||||||
); // maxMemType1
|
); // maxMemType1
|
||||||
}
|
}
|
||||||
|
|
||||||
function createNameTable(name, proto) {
|
function createNameTable(name, proto) {
|
||||||
if (!proto) {
|
if (!proto) {
|
||||||
proto = [[], []]; // no strings and unicode strings
|
proto = [[], []]; // no strings and unicode strings
|
||||||
}
|
}
|
||||||
@ -937,18 +785,157 @@ const Font = (function FontClosure() {
|
|||||||
|
|
||||||
nameTable += strings.join("") + stringsUnicode.join("");
|
nameTable += strings.join("") + stringsUnicode.join("");
|
||||||
return nameTable;
|
return nameTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* '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).
|
||||||
|
*/
|
||||||
|
class Font {
|
||||||
|
constructor(name, file, properties) {
|
||||||
|
this.name = name;
|
||||||
|
this.mimetype = null;
|
||||||
|
this.disableFontFace = false;
|
||||||
|
|
||||||
|
this.loadedName = properties.loadedName;
|
||||||
|
this.isType3Font = properties.isType3Font;
|
||||||
|
this.missingFile = false;
|
||||||
|
this.cssFontInfo = properties.cssFontInfo;
|
||||||
|
|
||||||
|
this.glyphCache = Object.create(null);
|
||||||
|
|
||||||
|
this.isSerifFont = !!(properties.flags & FontFlags.Serif);
|
||||||
|
this.isSymbolicFont = !!(properties.flags & FontFlags.Symbolic);
|
||||||
|
this.isMonospace = !!(properties.flags & FontFlags.FixedPitch);
|
||||||
|
|
||||||
|
let type = properties.type;
|
||||||
|
let subtype = properties.subtype;
|
||||||
|
this.type = type;
|
||||||
|
this.subtype = subtype;
|
||||||
|
|
||||||
|
let fallbackName = "sans-serif";
|
||||||
|
if (this.isMonospace) {
|
||||||
|
fallbackName = "monospace";
|
||||||
|
} else if (this.isSerifFont) {
|
||||||
|
fallbackName = "serif";
|
||||||
|
}
|
||||||
|
this.fallbackName = fallbackName;
|
||||||
|
|
||||||
|
this.differences = properties.differences;
|
||||||
|
this.widths = properties.widths;
|
||||||
|
this.defaultWidth = properties.defaultWidth;
|
||||||
|
this.composite = properties.composite;
|
||||||
|
this.cMap = properties.cMap;
|
||||||
|
this.capHeight = properties.capHeight / PDF_GLYPH_SPACE_UNITS;
|
||||||
|
this.ascent = properties.ascent / PDF_GLYPH_SPACE_UNITS;
|
||||||
|
this.descent = properties.descent / PDF_GLYPH_SPACE_UNITS;
|
||||||
|
this.fontMatrix = properties.fontMatrix;
|
||||||
|
this.bbox = properties.bbox;
|
||||||
|
this.defaultEncoding = properties.defaultEncoding;
|
||||||
|
|
||||||
|
this.toUnicode = properties.toUnicode;
|
||||||
|
this.fallbackToUnicode = properties.fallbackToUnicode || new ToUnicodeMap();
|
||||||
|
|
||||||
|
this.toFontChar = [];
|
||||||
|
|
||||||
|
if (properties.type === "Type3") {
|
||||||
|
for (let charCode = 0; charCode < 256; charCode++) {
|
||||||
|
this.toFontChar[charCode] =
|
||||||
|
this.differences[charCode] || properties.defaultEncoding[charCode];
|
||||||
|
}
|
||||||
|
this.fontType = FontType.TYPE3;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Font.prototype = {
|
this.cidEncoding = properties.cidEncoding;
|
||||||
name: null,
|
this.vertical = !!properties.vertical;
|
||||||
font: null,
|
if (this.vertical) {
|
||||||
mimetype: null,
|
this.vmetrics = properties.vmetrics;
|
||||||
disableFontFace: false,
|
this.defaultVMetrics = properties.defaultVMetrics;
|
||||||
|
}
|
||||||
|
|
||||||
|
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.fallbackToSystemFont(properties);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the font file to determine the correct type/subtype, rather than
|
||||||
|
// relying on the (often incorrect) data in the font dictionary; (see e.g.
|
||||||
|
// issue6782.pdf, issue7598.pdf, and issue9949.pdf).
|
||||||
|
[type, subtype] = getFontFileType(file, properties);
|
||||||
|
|
||||||
|
if (type !== this.type || subtype !== this.subtype) {
|
||||||
|
info(
|
||||||
|
"Inconsistent font file Type/SubType, expected: " +
|
||||||
|
`${this.type}/${this.subtype} but found: ${type}/${subtype}.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let data;
|
||||||
|
try {
|
||||||
|
switch (type) {
|
||||||
|
case "MMType1":
|
||||||
|
info("MMType1 font (" + name + "), falling back to Type1.");
|
||||||
|
/* falls through */
|
||||||
|
case "Type1":
|
||||||
|
case "CIDFontType0":
|
||||||
|
this.mimetype = "font/opentype";
|
||||||
|
|
||||||
|
const 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;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new FormatError(`Font ${type} is not supported`);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
warn(e);
|
||||||
|
this.fallbackToSystemFont(properties);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.data = data;
|
||||||
|
this.fontType = getFontType(type, subtype);
|
||||||
|
|
||||||
|
// Transfer some properties again that could change during font conversion
|
||||||
|
this.fontMatrix = properties.fontMatrix;
|
||||||
|
this.widths = properties.widths;
|
||||||
|
this.defaultWidth = properties.defaultWidth;
|
||||||
|
this.toUnicode = properties.toUnicode;
|
||||||
|
this.seacMap = properties.seacMap;
|
||||||
|
}
|
||||||
|
|
||||||
get renderer() {
|
get renderer() {
|
||||||
const renderer = FontRendererFactory.create(this, SEAC_ANALYSIS_ENABLED);
|
const renderer = FontRendererFactory.create(this, SEAC_ANALYSIS_ENABLED);
|
||||||
return shadow(this, "renderer", renderer);
|
return shadow(this, "renderer", renderer);
|
||||||
},
|
}
|
||||||
|
|
||||||
exportData(extraProperties = false) {
|
exportData(extraProperties = false) {
|
||||||
const exportDataProperties = extraProperties
|
const exportDataProperties = extraProperties
|
||||||
@ -965,7 +952,7 @@ const Font = (function FontClosure() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
},
|
}
|
||||||
|
|
||||||
fallbackToSystemFont(properties) {
|
fallbackToSystemFont(properties) {
|
||||||
this.missingFile = true;
|
this.missingFile = true;
|
||||||
@ -985,8 +972,7 @@ const Font = (function FontClosure() {
|
|||||||
fontName = stdFontMap[fontName] || nonStdFontMap[fontName] || fontName;
|
fontName = stdFontMap[fontName] || nonStdFontMap[fontName] || fontName;
|
||||||
this.bold = fontName.search(/bold/gi) !== -1;
|
this.bold = fontName.search(/bold/gi) !== -1;
|
||||||
this.italic =
|
this.italic =
|
||||||
fontName.search(/oblique/gi) !== -1 ||
|
fontName.search(/oblique/gi) !== -1 || fontName.search(/italic/gi) !== -1;
|
||||||
fontName.search(/italic/gi) !== -1;
|
|
||||||
|
|
||||||
// Use 'name' instead of 'fontName' here because the original
|
// Use 'name' instead of 'fontName' here because the original
|
||||||
// name ArialBlack for example will be replaced by Helvetica.
|
// name ArialBlack for example will be replaced by Helvetica.
|
||||||
@ -1034,8 +1020,7 @@ const Font = (function FontClosure() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const isIdentityUnicode =
|
const isIdentityUnicode = this.toUnicode instanceof IdentityToUnicodeMap;
|
||||||
this.toUnicode instanceof IdentityToUnicodeMap;
|
|
||||||
if (!isIdentityUnicode) {
|
if (!isIdentityUnicode) {
|
||||||
this.toUnicode.forEach(function (charCode, unicodeCharCode) {
|
this.toUnicode.forEach(function (charCode, unicodeCharCode) {
|
||||||
map[+charCode] = unicodeCharCode;
|
map[+charCode] = unicodeCharCode;
|
||||||
@ -1094,9 +1079,9 @@ const Font = (function FontClosure() {
|
|||||||
}
|
}
|
||||||
this.loadedName = fontName.split("-")[0];
|
this.loadedName = fontName.split("-")[0];
|
||||||
this.fontType = getFontType(type, subtype);
|
this.fontType = getFontType(type, subtype);
|
||||||
},
|
}
|
||||||
|
|
||||||
checkAndRepair: function Font_checkAndRepair(name, font, properties) {
|
checkAndRepair(name, font, properties) {
|
||||||
const VALID_TABLES = [
|
const VALID_TABLES = [
|
||||||
"OS/2",
|
"OS/2",
|
||||||
"cmap",
|
"cmap",
|
||||||
@ -1496,13 +1481,7 @@ const Font = (function FontClosure() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function sanitizeMetrics(
|
function sanitizeMetrics(file, header, metrics, numGlyphs, dupFirstEntry) {
|
||||||
file,
|
|
||||||
header,
|
|
||||||
metrics,
|
|
||||||
numGlyphs,
|
|
||||||
dupFirstEntry
|
|
||||||
) {
|
|
||||||
if (!header) {
|
if (!header) {
|
||||||
if (metrics) {
|
if (metrics) {
|
||||||
metrics.data = null;
|
metrics.data = null;
|
||||||
@ -1958,11 +1937,7 @@ const Font = (function FontClosure() {
|
|||||||
const NAME_RECORD_LENGTH = 12;
|
const NAME_RECORD_LENGTH = 12;
|
||||||
let i, ii;
|
let i, ii;
|
||||||
|
|
||||||
for (
|
for (i = 0; i < numRecords && font.pos + NAME_RECORD_LENGTH <= end; i++) {
|
||||||
i = 0;
|
|
||||||
i < numRecords && font.pos + NAME_RECORD_LENGTH <= end;
|
|
||||||
i++
|
|
||||||
) {
|
|
||||||
const r = {
|
const r = {
|
||||||
platform: font.getUint16(),
|
platform: font.getUint16(),
|
||||||
encoding: font.getUint16(),
|
encoding: font.getUint16(),
|
||||||
@ -2141,8 +2116,7 @@ const Font = (function FontClosure() {
|
|||||||
funcId = functionsCalled.pop();
|
funcId = functionsCalled.pop();
|
||||||
data = pc.data;
|
data = pc.data;
|
||||||
i = pc.i;
|
i = pc.i;
|
||||||
ttContext.functionsStackDeltas[funcId] =
|
ttContext.functionsStackDeltas[funcId] = stack.length - pc.stackTop;
|
||||||
stack.length - pc.stackTop;
|
|
||||||
}
|
}
|
||||||
} else if (op === 0x89) {
|
} else if (op === 0x89) {
|
||||||
// IDEF - instruction definition
|
// IDEF - instruction definition
|
||||||
@ -2562,8 +2536,7 @@ const Font = (function FontClosure() {
|
|||||||
} else if (cmapPlatformId === 0) {
|
} else if (cmapPlatformId === 0) {
|
||||||
// Default Unicode semantics, use the charcodes as is.
|
// Default Unicode semantics, use the charcodes as is.
|
||||||
for (let i = 0; i < cmapMappingsLength; ++i) {
|
for (let i = 0; i < cmapMappingsLength; ++i) {
|
||||||
charCodeToGlyphId[cmapMappings[i].charCode] =
|
charCodeToGlyphId[cmapMappings[i].charCode] = cmapMappings[i].glyphId;
|
||||||
cmapMappings[i].glyphId;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// When there is only a (1, 0) cmap table, the char code is a single
|
// When there is only a (1, 0) cmap table, the char code is a single
|
||||||
@ -2687,9 +2660,9 @@ const Font = (function FontClosure() {
|
|||||||
builder.addTable(tableTag, tables[tableTag].data);
|
builder.addTable(tableTag, tables[tableTag].data);
|
||||||
}
|
}
|
||||||
return builder.toArray();
|
return builder.toArray();
|
||||||
},
|
}
|
||||||
|
|
||||||
convert: function Font_convert(fontName, font, properties) {
|
convert(fontName, font, properties) {
|
||||||
// TODO: Check the charstring widths to determine this.
|
// TODO: Check the charstring widths to determine this.
|
||||||
properties.fixedPitch = false;
|
properties.fixedPitch = false;
|
||||||
|
|
||||||
@ -2882,7 +2855,7 @@ const Font = (function FontClosure() {
|
|||||||
builder.addTable("post", createPostTable(properties));
|
builder.addTable("post", createPostTable(properties));
|
||||||
|
|
||||||
return builder.toArray();
|
return builder.toArray();
|
||||||
},
|
}
|
||||||
|
|
||||||
get spaceWidth() {
|
get spaceWidth() {
|
||||||
// trying to estimate space character width
|
// trying to estimate space character width
|
||||||
@ -2918,7 +2891,7 @@ const Font = (function FontClosure() {
|
|||||||
}
|
}
|
||||||
width = width || this.defaultWidth;
|
width = width || this.defaultWidth;
|
||||||
return shadow(this, "spaceWidth", width);
|
return shadow(this, "spaceWidth", width);
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
@ -3012,9 +2985,9 @@ const Font = (function FontClosure() {
|
|||||||
this.glyphCache[charcode] = glyph;
|
this.glyphCache[charcode] = glyph;
|
||||||
}
|
}
|
||||||
return glyph;
|
return glyph;
|
||||||
},
|
}
|
||||||
|
|
||||||
charsToGlyphs: function Font_charsToGlyphs(chars) {
|
charsToGlyphs(chars) {
|
||||||
let charsCache = this.charsCache;
|
let charsCache = this.charsCache;
|
||||||
let glyphs, glyph, charcode;
|
let glyphs, glyph, charcode;
|
||||||
|
|
||||||
@ -3060,7 +3033,7 @@ const Font = (function FontClosure() {
|
|||||||
|
|
||||||
// Enter the translated string into the cache
|
// Enter the translated string into the cache
|
||||||
return (charsCache[charsCacheKey] = glyphs);
|
return (charsCache[charsCacheKey] = glyphs);
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Chars can have different sizes (depends on the encoding).
|
* Chars can have different sizes (depends on the encoding).
|
||||||
@ -3088,11 +3061,11 @@ const Font = (function FontClosure() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return positions;
|
return positions;
|
||||||
},
|
}
|
||||||
|
|
||||||
get glyphCacheValues() {
|
get glyphCacheValues() {
|
||||||
return Object.values(this.glyphCache);
|
return Object.values(this.glyphCache);
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encode a js string using font encoding.
|
* Encode a js string using font encoding.
|
||||||
@ -3129,9 +3102,7 @@ const Font = (function FontClosure() {
|
|||||||
? this.cMap.getCharCodeLength(charCode)
|
? this.cMap.getCharCodeLength(charCode)
|
||||||
: 1;
|
: 1;
|
||||||
for (let j = charCodeLength - 1; j >= 0; j--) {
|
for (let j = charCodeLength - 1; j >= 0; j--) {
|
||||||
currentBuf.push(
|
currentBuf.push(String.fromCharCode((charCode >> (8 * j)) & 0xff));
|
||||||
String.fromCharCode((charCode >> (8 * j)) & 0xff)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -3148,33 +3119,27 @@ const Font = (function FontClosure() {
|
|||||||
buffers.push(currentBuf.join(""));
|
buffers.push(currentBuf.join(""));
|
||||||
|
|
||||||
return buffers;
|
return buffers;
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
return Font;
|
class ErrorFont {
|
||||||
})();
|
constructor(error) {
|
||||||
|
|
||||||
const ErrorFont = (function ErrorFontClosure() {
|
|
||||||
// eslint-disable-next-line no-shadow
|
|
||||||
function ErrorFont(error) {
|
|
||||||
this.error = error;
|
this.error = error;
|
||||||
this.loadedName = "g_font_error";
|
this.loadedName = "g_font_error";
|
||||||
this.missingFile = true;
|
this.missingFile = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorFont.prototype = {
|
charsToGlyphs() {
|
||||||
charsToGlyphs: function ErrorFont_charsToGlyphs() {
|
|
||||||
return [];
|
return [];
|
||||||
},
|
}
|
||||||
encodeString: function ErrorFont_encodeString(chars) {
|
|
||||||
|
encodeString(chars) {
|
||||||
return [chars];
|
return [chars];
|
||||||
},
|
}
|
||||||
|
|
||||||
exportData(extraProperties = false) {
|
exportData(extraProperties = false) {
|
||||||
return { error: this.error };
|
return { error: this.error };
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
return ErrorFont;
|
|
||||||
})();
|
|
||||||
|
|
||||||
export { ErrorFont, Font };
|
export { ErrorFont, Font };
|
||||||
|
Loading…
x
Reference in New Issue
Block a user