UNPKG

101 kBJavaScriptView Raw
1/**
2 * @licstart The following is the entire license notice for the
3 * Javascript code in this page
4 *
5 * Copyright 2018 Mozilla Foundation
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 * @licend The above is the entire license notice for the
20 * Javascript code in this page
21 */
22"use strict";
23
24Object.defineProperty(exports, "__esModule", {
25 value: true
26});
27exports.getFontType = getFontType;
28exports.IdentityToUnicodeMap = exports.ToUnicodeMap = exports.FontFlags = exports.Font = exports.ErrorFont = exports.SEAC_ANALYSIS_ENABLED = void 0;
29
30var _util = require("../shared/util");
31
32var _cff_parser = require("./cff_parser");
33
34var _glyphlist = require("./glyphlist");
35
36var _encodings = require("./encodings");
37
38var _standard_fonts = require("./standard_fonts");
39
40var _unicode = require("./unicode");
41
42var _font_renderer = require("./font_renderer");
43
44var _cmap = require("./cmap");
45
46var _stream = require("./stream");
47
48var _type1_parser = require("./type1_parser");
49
50function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); }
51
52function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); }
53
54function _iterableToArrayLimit(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
55
56function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
57
58var PRIVATE_USE_AREAS = [[0xE000, 0xF8FF], [0x100000, 0x10FFFD]];
59var PDF_GLYPH_SPACE_UNITS = 1000;
60var SEAC_ANALYSIS_ENABLED = true;
61exports.SEAC_ANALYSIS_ENABLED = SEAC_ANALYSIS_ENABLED;
62var FontFlags = {
63 FixedPitch: 1,
64 Serif: 2,
65 Symbolic: 4,
66 Script: 8,
67 Nonsymbolic: 32,
68 Italic: 64,
69 AllCap: 65536,
70 SmallCap: 131072,
71 ForceBold: 262144
72};
73exports.FontFlags = FontFlags;
74var MacStandardGlyphOrdering = ['.notdef', '.null', 'nonmarkingreturn', 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent', 'ampersand', 'quotesingle', 'parenleft', 'parenright', 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon', 'less', 'equal', 'greater', 'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash', 'bracketright', 'asciicircum', 'underscore', 'grave', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright', 'asciitilde', 'Adieresis', 'Aring', 'Ccedilla', 'Eacute', 'Ntilde', 'Odieresis', 'Udieresis', 'aacute', 'agrave', 'acircumflex', 'adieresis', 'atilde', 'aring', 'ccedilla', 'eacute', 'egrave', 'ecircumflex', 'edieresis', 'iacute', 'igrave', 'icircumflex', 'idieresis', 'ntilde', 'oacute', 'ograve', 'ocircumflex', 'odieresis', 'otilde', 'uacute', 'ugrave', 'ucircumflex', 'udieresis', 'dagger', 'degree', 'cent', 'sterling', 'section', 'bullet', 'paragraph', 'germandbls', 'registered', 'copyright', 'trademark', 'acute', 'dieresis', 'notequal', 'AE', 'Oslash', 'infinity', 'plusminus', 'lessequal', 'greaterequal', 'yen', 'mu', 'partialdiff', 'summation', 'product', 'pi', 'integral', 'ordfeminine', 'ordmasculine', 'Omega', 'ae', 'oslash', 'questiondown', 'exclamdown', 'logicalnot', 'radical', 'florin', 'approxequal', 'Delta', 'guillemotleft', 'guillemotright', 'ellipsis', 'nonbreakingspace', 'Agrave', 'Atilde', 'Otilde', 'OE', 'oe', 'endash', 'emdash', 'quotedblleft', 'quotedblright', 'quoteleft', 'quoteright', 'divide', 'lozenge', 'ydieresis', 'Ydieresis', 'fraction', 'currency', 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'daggerdbl', 'periodcentered', 'quotesinglbase', 'quotedblbase', 'perthousand', 'Acircumflex', 'Ecircumflex', 'Aacute', 'Edieresis', 'Egrave', 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Oacute', 'Ocircumflex', 'apple', 'Ograve', 'Uacute', 'Ucircumflex', 'Ugrave', 'dotlessi', 'circumflex', 'tilde', 'macron', 'breve', 'dotaccent', 'ring', 'cedilla', 'hungarumlaut', 'ogonek', 'caron', 'Lslash', 'lslash', 'Scaron', 'scaron', 'Zcaron', 'zcaron', 'brokenbar', 'Eth', 'eth', 'Yacute', 'yacute', 'Thorn', 'thorn', 'minus', 'multiply', 'onesuperior', 'twosuperior', 'threesuperior', 'onehalf', 'onequarter', 'threequarters', 'franc', 'Gbreve', 'gbreve', 'Idotaccent', 'Scedilla', 'scedilla', 'Cacute', 'cacute', 'Ccaron', 'ccaron', 'dcroat'];
75
76function adjustWidths(properties) {
77 if (!properties.fontMatrix) {
78 return;
79 }
80
81 if (properties.fontMatrix[0] === _util.FONT_IDENTITY_MATRIX[0]) {
82 return;
83 }
84
85 var scale = 0.001 / properties.fontMatrix[0];
86 var glyphsWidths = properties.widths;
87
88 for (var glyph in glyphsWidths) {
89 glyphsWidths[glyph] *= scale;
90 }
91
92 properties.defaultWidth *= scale;
93}
94
95function adjustToUnicode(properties, builtInEncoding) {
96 if (properties.hasIncludedToUnicodeMap) {
97 return;
98 }
99
100 if (properties.hasEncoding) {
101 return;
102 }
103
104 if (builtInEncoding === properties.defaultEncoding) {
105 return;
106 }
107
108 if (properties.toUnicode instanceof IdentityToUnicodeMap) {
109 return;
110 }
111
112 var toUnicode = [],
113 glyphsUnicodeMap = (0, _glyphlist.getGlyphsUnicode)();
114
115 for (var charCode in builtInEncoding) {
116 var glyphName = builtInEncoding[charCode];
117 var unicode = (0, _unicode.getUnicodeForGlyph)(glyphName, glyphsUnicodeMap);
118
119 if (unicode !== -1) {
120 toUnicode[charCode] = String.fromCharCode(unicode);
121 }
122 }
123
124 properties.toUnicode.amend(toUnicode);
125}
126
127function getFontType(type, subtype) {
128 switch (type) {
129 case 'Type1':
130 return subtype === 'Type1C' ? _util.FontType.TYPE1C : _util.FontType.TYPE1;
131
132 case 'CIDFontType0':
133 return subtype === 'CIDFontType0C' ? _util.FontType.CIDFONTTYPE0C : _util.FontType.CIDFONTTYPE0;
134
135 case 'OpenType':
136 return _util.FontType.OPENTYPE;
137
138 case 'TrueType':
139 return _util.FontType.TRUETYPE;
140
141 case 'CIDFontType2':
142 return _util.FontType.CIDFONTTYPE2;
143
144 case 'MMType1':
145 return _util.FontType.MMTYPE1;
146
147 case 'Type0':
148 return _util.FontType.TYPE0;
149
150 default:
151 return _util.FontType.UNKNOWN;
152 }
153}
154
155function recoverGlyphName(name, glyphsUnicodeMap) {
156 if (glyphsUnicodeMap[name] !== undefined) {
157 return name;
158 }
159
160 var unicode = (0, _unicode.getUnicodeForGlyph)(name, glyphsUnicodeMap);
161
162 if (unicode !== -1) {
163 for (var key in glyphsUnicodeMap) {
164 if (glyphsUnicodeMap[key] === unicode) {
165 return key;
166 }
167 }
168 }
169
170 (0, _util.info)('Unable to recover a standard glyph name for: ' + name);
171 return name;
172}
173
174var Glyph = function GlyphClosure() {
175 function Glyph(fontChar, unicode, accent, width, vmetric, operatorListId, isSpace, isInFont) {
176 this.fontChar = fontChar;
177 this.unicode = unicode;
178 this.accent = accent;
179 this.width = width;
180 this.vmetric = vmetric;
181 this.operatorListId = operatorListId;
182 this.isSpace = isSpace;
183 this.isInFont = isInFont;
184 }
185
186 Glyph.prototype.matchesForCache = function (fontChar, unicode, accent, width, vmetric, operatorListId, isSpace, isInFont) {
187 return this.fontChar === fontChar && this.unicode === unicode && this.accent === accent && this.width === width && this.vmetric === vmetric && this.operatorListId === operatorListId && this.isSpace === isSpace && this.isInFont === isInFont;
188 };
189
190 return Glyph;
191}();
192
193var ToUnicodeMap = function ToUnicodeMapClosure() {
194 function ToUnicodeMap() {
195 var cmap = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
196 this._map = cmap;
197 }
198
199 ToUnicodeMap.prototype = {
200 get length() {
201 return this._map.length;
202 },
203
204 forEach: function forEach(callback) {
205 for (var charCode in this._map) {
206 callback(charCode, this._map[charCode].charCodeAt(0));
207 }
208 },
209 has: function has(i) {
210 return this._map[i] !== undefined;
211 },
212 get: function get(i) {
213 return this._map[i];
214 },
215 charCodeOf: function charCodeOf(value) {
216 var map = this._map;
217
218 if (map.length <= 0x10000) {
219 return map.indexOf(value);
220 }
221
222 for (var charCode in map) {
223 if (map[charCode] === value) {
224 return charCode | 0;
225 }
226 }
227
228 return -1;
229 },
230 amend: function amend(map) {
231 for (var charCode in map) {
232 this._map[charCode] = map[charCode];
233 }
234 }
235 };
236 return ToUnicodeMap;
237}();
238
239exports.ToUnicodeMap = ToUnicodeMap;
240
241var IdentityToUnicodeMap = function IdentityToUnicodeMapClosure() {
242 function IdentityToUnicodeMap(firstChar, lastChar) {
243 this.firstChar = firstChar;
244 this.lastChar = lastChar;
245 }
246
247 IdentityToUnicodeMap.prototype = {
248 get length() {
249 return this.lastChar + 1 - this.firstChar;
250 },
251
252 forEach: function forEach(callback) {
253 for (var i = this.firstChar, ii = this.lastChar; i <= ii; i++) {
254 callback(i, i);
255 }
256 },
257 has: function has(i) {
258 return this.firstChar <= i && i <= this.lastChar;
259 },
260 get: function get(i) {
261 if (this.firstChar <= i && i <= this.lastChar) {
262 return String.fromCharCode(i);
263 }
264
265 return undefined;
266 },
267 charCodeOf: function charCodeOf(v) {
268 return Number.isInteger(v) && v >= this.firstChar && v <= this.lastChar ? v : -1;
269 },
270 amend: function amend(map) {
271 (0, _util.unreachable)('Should not call amend()');
272 }
273 };
274 return IdentityToUnicodeMap;
275}();
276
277exports.IdentityToUnicodeMap = IdentityToUnicodeMap;
278
279var OpenTypeFileBuilder = function OpenTypeFileBuilderClosure() {
280 function writeInt16(dest, offset, num) {
281 dest[offset] = num >> 8 & 0xFF;
282 dest[offset + 1] = num & 0xFF;
283 }
284
285 function writeInt32(dest, offset, num) {
286 dest[offset] = num >> 24 & 0xFF;
287 dest[offset + 1] = num >> 16 & 0xFF;
288 dest[offset + 2] = num >> 8 & 0xFF;
289 dest[offset + 3] = num & 0xFF;
290 }
291
292 function writeData(dest, offset, data) {
293 var i, ii;
294
295 if (data instanceof Uint8Array) {
296 dest.set(data, offset);
297 } else if (typeof data === 'string') {
298 for (i = 0, ii = data.length; i < ii; i++) {
299 dest[offset++] = data.charCodeAt(i) & 0xFF;
300 }
301 } else {
302 for (i = 0, ii = data.length; i < ii; i++) {
303 dest[offset++] = data[i] & 0xFF;
304 }
305 }
306 }
307
308 function OpenTypeFileBuilder(sfnt) {
309 this.sfnt = sfnt;
310 this.tables = Object.create(null);
311 }
312
313 OpenTypeFileBuilder.getSearchParams = function OpenTypeFileBuilder_getSearchParams(entriesCount, entrySize) {
314 var maxPower2 = 1,
315 log2 = 0;
316
317 while ((maxPower2 ^ entriesCount) > maxPower2) {
318 maxPower2 <<= 1;
319 log2++;
320 }
321
322 var searchRange = maxPower2 * entrySize;
323 return {
324 range: searchRange,
325 entry: log2,
326 rangeShift: entrySize * entriesCount - searchRange
327 };
328 };
329
330 var OTF_HEADER_SIZE = 12;
331 var OTF_TABLE_ENTRY_SIZE = 16;
332 OpenTypeFileBuilder.prototype = {
333 toArray: function OpenTypeFileBuilder_toArray() {
334 var sfnt = this.sfnt;
335 var tables = this.tables;
336 var tablesNames = Object.keys(tables);
337 tablesNames.sort();
338 var numTables = tablesNames.length;
339 var i, j, jj, table, tableName;
340 var offset = OTF_HEADER_SIZE + numTables * OTF_TABLE_ENTRY_SIZE;
341 var tableOffsets = [offset];
342
343 for (i = 0; i < numTables; i++) {
344 table = tables[tablesNames[i]];
345 var paddedLength = (table.length + 3 & ~3) >>> 0;
346 offset += paddedLength;
347 tableOffsets.push(offset);
348 }
349
350 var file = new Uint8Array(offset);
351
352 for (i = 0; i < numTables; i++) {
353 table = tables[tablesNames[i]];
354 writeData(file, tableOffsets[i], table);
355 }
356
357 if (sfnt === 'true') {
358 sfnt = (0, _util.string32)(0x00010000);
359 }
360
361 file[0] = sfnt.charCodeAt(0) & 0xFF;
362 file[1] = sfnt.charCodeAt(1) & 0xFF;
363 file[2] = sfnt.charCodeAt(2) & 0xFF;
364 file[3] = sfnt.charCodeAt(3) & 0xFF;
365 writeInt16(file, 4, numTables);
366 var searchParams = OpenTypeFileBuilder.getSearchParams(numTables, 16);
367 writeInt16(file, 6, searchParams.range);
368 writeInt16(file, 8, searchParams.entry);
369 writeInt16(file, 10, searchParams.rangeShift);
370 offset = OTF_HEADER_SIZE;
371
372 for (i = 0; i < numTables; i++) {
373 tableName = tablesNames[i];
374 file[offset] = tableName.charCodeAt(0) & 0xFF;
375 file[offset + 1] = tableName.charCodeAt(1) & 0xFF;
376 file[offset + 2] = tableName.charCodeAt(2) & 0xFF;
377 file[offset + 3] = tableName.charCodeAt(3) & 0xFF;
378 var checksum = 0;
379
380 for (j = tableOffsets[i], jj = tableOffsets[i + 1]; j < jj; j += 4) {
381 var quad = (0, _util.readUint32)(file, j);
382 checksum = checksum + quad >>> 0;
383 }
384
385 writeInt32(file, offset + 4, checksum);
386 writeInt32(file, offset + 8, tableOffsets[i]);
387 writeInt32(file, offset + 12, tables[tableName].length);
388 offset += OTF_TABLE_ENTRY_SIZE;
389 }
390
391 return file;
392 },
393 addTable: function OpenTypeFileBuilder_addTable(tag, data) {
394 if (tag in this.tables) {
395 throw new Error('Table ' + tag + ' already exists');
396 }
397
398 this.tables[tag] = data;
399 }
400 };
401 return OpenTypeFileBuilder;
402}();
403
404var Font = function FontClosure() {
405 function Font(name, file, properties) {
406 var charCode;
407 this.name = name;
408 this.loadedName = properties.loadedName;
409 this.isType3Font = properties.isType3Font;
410 this.sizes = [];
411 this.missingFile = false;
412 this.glyphCache = Object.create(null);
413 this.isSerifFont = !!(properties.flags & FontFlags.Serif);
414 this.isSymbolicFont = !!(properties.flags & FontFlags.Symbolic);
415 this.isMonospace = !!(properties.flags & FontFlags.FixedPitch);
416 var type = properties.type;
417 var subtype = properties.subtype;
418 this.type = type;
419 this.subtype = subtype;
420 this.fallbackName = this.isMonospace ? 'monospace' : this.isSerifFont ? 'serif' : 'sans-serif';
421 this.differences = properties.differences;
422 this.widths = properties.widths;
423 this.defaultWidth = properties.defaultWidth;
424 this.composite = properties.composite;
425 this.wideChars = properties.wideChars;
426 this.cMap = properties.cMap;
427 this.ascent = properties.ascent / PDF_GLYPH_SPACE_UNITS;
428 this.descent = properties.descent / PDF_GLYPH_SPACE_UNITS;
429 this.fontMatrix = properties.fontMatrix;
430 this.bbox = properties.bbox;
431 this.defaultEncoding = properties.defaultEncoding;
432 this.toUnicode = properties.toUnicode;
433 this.fallbackToUnicode = properties.fallbackToUnicode || new ToUnicodeMap();
434 this.toFontChar = [];
435
436 if (properties.type === 'Type3') {
437 for (charCode = 0; charCode < 256; charCode++) {
438 this.toFontChar[charCode] = this.differences[charCode] || properties.defaultEncoding[charCode];
439 }
440
441 this.fontType = _util.FontType.TYPE3;
442 return;
443 }
444
445 this.cidEncoding = properties.cidEncoding;
446 this.vertical = properties.vertical;
447
448 if (this.vertical) {
449 this.vmetrics = properties.vmetrics;
450 this.defaultVMetrics = properties.defaultVMetrics;
451 }
452
453 if (!file || file.isEmpty) {
454 if (file) {
455 (0, _util.warn)('Font file is empty in "' + name + '" (' + this.loadedName + ')');
456 }
457
458 this.fallbackToSystemFont();
459 return;
460 }
461
462 var _getFontFileType = getFontFileType(file, properties);
463
464 var _getFontFileType2 = _slicedToArray(_getFontFileType, 2);
465
466 type = _getFontFileType2[0];
467 subtype = _getFontFileType2[1];
468
469 if (type !== this.type || subtype !== this.subtype) {
470 (0, _util.info)('Inconsistent font file Type/SubType, expected: ' + "".concat(this.type, "/").concat(this.subtype, " but found: ").concat(type, "/").concat(subtype, "."));
471 }
472
473 try {
474 var data;
475
476 switch (type) {
477 case 'MMType1':
478 (0, _util.info)('MMType1 font (' + name + '), falling back to Type1.');
479
480 case 'Type1':
481 case 'CIDFontType0':
482 this.mimetype = 'font/opentype';
483 var cff = subtype === 'Type1C' || subtype === 'CIDFontType0C' ? new CFFFont(file, properties) : new Type1Font(name, file, properties);
484 adjustWidths(properties);
485 data = this.convert(name, cff, properties);
486 break;
487
488 case 'OpenType':
489 case 'TrueType':
490 case 'CIDFontType2':
491 this.mimetype = 'font/opentype';
492 data = this.checkAndRepair(name, file, properties);
493
494 if (this.isOpenType) {
495 adjustWidths(properties);
496 type = 'OpenType';
497 }
498
499 break;
500
501 default:
502 throw new _util.FormatError("Font ".concat(type, " is not supported"));
503 }
504 } catch (e) {
505 (0, _util.warn)(e);
506 this.fallbackToSystemFont();
507 return;
508 }
509
510 this.data = data;
511 this.fontType = getFontType(type, subtype);
512 this.fontMatrix = properties.fontMatrix;
513 this.widths = properties.widths;
514 this.defaultWidth = properties.defaultWidth;
515 this.toUnicode = properties.toUnicode;
516 this.encoding = properties.baseEncoding;
517 this.seacMap = properties.seacMap;
518 }
519
520 Font.getFontID = function () {
521 var ID = 1;
522 return function Font_getFontID() {
523 return String(ID++);
524 };
525 }();
526
527 function int16(b0, b1) {
528 return (b0 << 8) + b1;
529 }
530
531 function writeSignedInt16(bytes, index, value) {
532 bytes[index + 1] = value;
533 bytes[index] = value >>> 8;
534 }
535
536 function signedInt16(b0, b1) {
537 var value = (b0 << 8) + b1;
538 return value & 1 << 15 ? value - 0x10000 : value;
539 }
540
541 function int32(b0, b1, b2, b3) {
542 return (b0 << 24) + (b1 << 16) + (b2 << 8) + b3;
543 }
544
545 function string16(value) {
546 return String.fromCharCode(value >> 8 & 0xff, value & 0xff);
547 }
548
549 function safeString16(value) {
550 value = value > 0x7FFF ? 0x7FFF : value < -0x8000 ? -0x8000 : value;
551 return String.fromCharCode(value >> 8 & 0xff, value & 0xff);
552 }
553
554 function isTrueTypeFile(file) {
555 var header = file.peekBytes(4);
556 return (0, _util.readUint32)(header, 0) === 0x00010000 || (0, _util.bytesToString)(header) === 'true';
557 }
558
559 function isTrueTypeCollectionFile(file) {
560 var header = file.peekBytes(4);
561 return (0, _util.bytesToString)(header) === 'ttcf';
562 }
563
564 function isOpenTypeFile(file) {
565 var header = file.peekBytes(4);
566 return (0, _util.bytesToString)(header) === 'OTTO';
567 }
568
569 function isType1File(file) {
570 var header = file.peekBytes(2);
571
572 if (header[0] === 0x25 && header[1] === 0x21) {
573 return true;
574 }
575
576 if (header[0] === 0x80 && header[1] === 0x01) {
577 return true;
578 }
579
580 return false;
581 }
582
583 function isCFFFile(file) {
584 var header = file.peekBytes(4);
585
586 if (header[0] >= 1 && header[3] >= 1 && header[3] <= 4) {
587 return true;
588 }
589
590 return false;
591 }
592
593 function getFontFileType(file, _ref) {
594 var type = _ref.type,
595 subtype = _ref.subtype,
596 composite = _ref.composite;
597 var fileType, fileSubtype;
598
599 if (isTrueTypeFile(file) || isTrueTypeCollectionFile(file)) {
600 if (composite) {
601 fileType = 'CIDFontType2';
602 } else {
603 fileType = 'TrueType';
604 }
605 } else if (isOpenTypeFile(file)) {
606 if (composite) {
607 fileType = 'CIDFontType2';
608 } else {
609 fileType = 'OpenType';
610 }
611 } else if (isType1File(file)) {
612 if (composite) {
613 fileType = 'CIDFontType0';
614 } else {
615 fileType = type === 'MMType1' ? 'MMType1' : 'Type1';
616 }
617 } else if (isCFFFile(file)) {
618 if (composite) {
619 fileType = 'CIDFontType0';
620 fileSubtype = 'CIDFontType0C';
621 } else {
622 fileType = type === 'MMType1' ? 'MMType1' : 'Type1';
623 fileSubtype = 'Type1C';
624 }
625 } else {
626 (0, _util.warn)('getFontFileType: Unable to detect correct font file Type/Subtype.');
627 fileType = type;
628 fileSubtype = subtype;
629 }
630
631 return [fileType, fileSubtype];
632 }
633
634 function buildToFontChar(encoding, glyphsUnicodeMap, differences) {
635 var toFontChar = [],
636 unicode;
637
638 for (var i = 0, ii = encoding.length; i < ii; i++) {
639 unicode = (0, _unicode.getUnicodeForGlyph)(encoding[i], glyphsUnicodeMap);
640
641 if (unicode !== -1) {
642 toFontChar[i] = unicode;
643 }
644 }
645
646 for (var charCode in differences) {
647 unicode = (0, _unicode.getUnicodeForGlyph)(differences[charCode], glyphsUnicodeMap);
648
649 if (unicode !== -1) {
650 toFontChar[+charCode] = unicode;
651 }
652 }
653
654 return toFontChar;
655 }
656
657 function adjustMapping(charCodeToGlyphId, hasGlyph, newGlyphZeroId) {
658 var newMap = Object.create(null);
659 var toFontChar = [];
660 var privateUseAreaIndex = 0;
661 var nextAvailableFontCharCode = PRIVATE_USE_AREAS[privateUseAreaIndex][0];
662 var privateUseOffetEnd = PRIVATE_USE_AREAS[privateUseAreaIndex][1];
663
664 for (var originalCharCode in charCodeToGlyphId) {
665 originalCharCode |= 0;
666 var glyphId = charCodeToGlyphId[originalCharCode];
667
668 if (!hasGlyph(glyphId)) {
669 continue;
670 }
671
672 if (nextAvailableFontCharCode > privateUseOffetEnd) {
673 privateUseAreaIndex++;
674
675 if (privateUseAreaIndex >= PRIVATE_USE_AREAS.length) {
676 (0, _util.warn)('Ran out of space in font private use area.');
677 break;
678 }
679
680 nextAvailableFontCharCode = PRIVATE_USE_AREAS[privateUseAreaIndex][0];
681 privateUseOffetEnd = PRIVATE_USE_AREAS[privateUseAreaIndex][1];
682 }
683
684 var fontCharCode = nextAvailableFontCharCode++;
685
686 if (glyphId === 0) {
687 glyphId = newGlyphZeroId;
688 }
689
690 newMap[fontCharCode] = glyphId;
691 toFontChar[originalCharCode] = fontCharCode;
692 }
693
694 return {
695 toFontChar: toFontChar,
696 charCodeToGlyphId: newMap,
697 nextAvailableFontCharCode: nextAvailableFontCharCode
698 };
699 }
700
701 function getRanges(glyphs, numGlyphs) {
702 var codes = [];
703
704 for (var charCode in glyphs) {
705 if (glyphs[charCode] >= numGlyphs) {
706 continue;
707 }
708
709 codes.push({
710 fontCharCode: charCode | 0,
711 glyphId: glyphs[charCode]
712 });
713 }
714
715 if (codes.length === 0) {
716 codes.push({
717 fontCharCode: 0,
718 glyphId: 0
719 });
720 }
721
722 codes.sort(function fontGetRangesSort(a, b) {
723 return a.fontCharCode - b.fontCharCode;
724 });
725 var ranges = [];
726 var length = codes.length;
727
728 for (var n = 0; n < length;) {
729 var start = codes[n].fontCharCode;
730 var codeIndices = [codes[n].glyphId];
731 ++n;
732 var end = start;
733
734 while (n < length && end + 1 === codes[n].fontCharCode) {
735 codeIndices.push(codes[n].glyphId);
736 ++end;
737 ++n;
738
739 if (end === 0xFFFF) {
740 break;
741 }
742 }
743
744 ranges.push([start, end, codeIndices]);
745 }
746
747 return ranges;
748 }
749
750 function createCmapTable(glyphs, numGlyphs) {
751 var ranges = getRanges(glyphs, numGlyphs);
752 var numTables = ranges[ranges.length - 1][1] > 0xFFFF ? 2 : 1;
753 var cmap = '\x00\x00' + string16(numTables) + '\x00\x03' + '\x00\x01' + (0, _util.string32)(4 + numTables * 8);
754 var i, ii, j, jj;
755
756 for (i = ranges.length - 1; i >= 0; --i) {
757 if (ranges[i][0] <= 0xFFFF) {
758 break;
759 }
760 }
761
762 var bmpLength = i + 1;
763
764 if (ranges[i][0] < 0xFFFF && ranges[i][1] === 0xFFFF) {
765 ranges[i][1] = 0xFFFE;
766 }
767
768 var trailingRangesCount = ranges[i][1] < 0xFFFF ? 1 : 0;
769 var segCount = bmpLength + trailingRangesCount;
770 var searchParams = OpenTypeFileBuilder.getSearchParams(segCount, 2);
771 var startCount = '';
772 var endCount = '';
773 var idDeltas = '';
774 var idRangeOffsets = '';
775 var glyphsIds = '';
776 var bias = 0;
777 var range, start, end, codes;
778
779 for (i = 0, ii = bmpLength; i < ii; i++) {
780 range = ranges[i];
781 start = range[0];
782 end = range[1];
783 startCount += string16(start);
784 endCount += string16(end);
785 codes = range[2];
786 var contiguous = true;
787
788 for (j = 1, jj = codes.length; j < jj; ++j) {
789 if (codes[j] !== codes[j - 1] + 1) {
790 contiguous = false;
791 break;
792 }
793 }
794
795 if (!contiguous) {
796 var offset = (segCount - i) * 2 + bias * 2;
797 bias += end - start + 1;
798 idDeltas += string16(0);
799 idRangeOffsets += string16(offset);
800
801 for (j = 0, jj = codes.length; j < jj; ++j) {
802 glyphsIds += string16(codes[j]);
803 }
804 } else {
805 var startCode = codes[0];
806 idDeltas += string16(startCode - start & 0xFFFF);
807 idRangeOffsets += string16(0);
808 }
809 }
810
811 if (trailingRangesCount > 0) {
812 endCount += '\xFF\xFF';
813 startCount += '\xFF\xFF';
814 idDeltas += '\x00\x01';
815 idRangeOffsets += '\x00\x00';
816 }
817
818 var format314 = '\x00\x00' + string16(2 * segCount) + string16(searchParams.range) + string16(searchParams.entry) + string16(searchParams.rangeShift) + endCount + '\x00\x00' + startCount + idDeltas + idRangeOffsets + glyphsIds;
819 var format31012 = '';
820 var header31012 = '';
821
822 if (numTables > 1) {
823 cmap += '\x00\x03' + '\x00\x0A' + (0, _util.string32)(4 + numTables * 8 + 4 + format314.length);
824 format31012 = '';
825
826 for (i = 0, ii = ranges.length; i < ii; i++) {
827 range = ranges[i];
828 start = range[0];
829 codes = range[2];
830 var code = codes[0];
831
832 for (j = 1, jj = codes.length; j < jj; ++j) {
833 if (codes[j] !== codes[j - 1] + 1) {
834 end = range[0] + j - 1;
835 format31012 += (0, _util.string32)(start) + (0, _util.string32)(end) + (0, _util.string32)(code);
836 start = end + 1;
837 code = codes[j];
838 }
839 }
840
841 format31012 += (0, _util.string32)(start) + (0, _util.string32)(range[1]) + (0, _util.string32)(code);
842 }
843
844 header31012 = '\x00\x0C' + '\x00\x00' + (0, _util.string32)(format31012.length + 16) + '\x00\x00\x00\x00' + (0, _util.string32)(format31012.length / 12);
845 }
846
847 return cmap + '\x00\x04' + string16(format314.length + 4) + format314 + header31012 + format31012;
848 }
849
850 function validateOS2Table(os2) {
851 var stream = new _stream.Stream(os2.data);
852 var version = stream.getUint16();
853 stream.getBytes(60);
854 var selection = stream.getUint16();
855
856 if (version < 4 && selection & 0x0300) {
857 return false;
858 }
859
860 var firstChar = stream.getUint16();
861 var lastChar = stream.getUint16();
862
863 if (firstChar > lastChar) {
864 return false;
865 }
866
867 stream.getBytes(6);
868 var usWinAscent = stream.getUint16();
869
870 if (usWinAscent === 0) {
871 return false;
872 }
873
874 os2.data[8] = os2.data[9] = 0;
875 return true;
876 }
877
878 function createOS2Table(properties, charstrings, override) {
879 override = override || {
880 unitsPerEm: 0,
881 yMax: 0,
882 yMin: 0,
883 ascent: 0,
884 descent: 0
885 };
886 var ulUnicodeRange1 = 0;
887 var ulUnicodeRange2 = 0;
888 var ulUnicodeRange3 = 0;
889 var ulUnicodeRange4 = 0;
890 var firstCharIndex = null;
891 var lastCharIndex = 0;
892
893 if (charstrings) {
894 for (var code in charstrings) {
895 code |= 0;
896
897 if (firstCharIndex > code || !firstCharIndex) {
898 firstCharIndex = code;
899 }
900
901 if (lastCharIndex < code) {
902 lastCharIndex = code;
903 }
904
905 var position = (0, _unicode.getUnicodeRangeFor)(code);
906
907 if (position < 32) {
908 ulUnicodeRange1 |= 1 << position;
909 } else if (position < 64) {
910 ulUnicodeRange2 |= 1 << position - 32;
911 } else if (position < 96) {
912 ulUnicodeRange3 |= 1 << position - 64;
913 } else if (position < 123) {
914 ulUnicodeRange4 |= 1 << position - 96;
915 } else {
916 throw new _util.FormatError('Unicode ranges Bits > 123 are reserved for internal usage');
917 }
918 }
919
920 if (lastCharIndex > 0xFFFF) {
921 lastCharIndex = 0xFFFF;
922 }
923 } else {
924 firstCharIndex = 0;
925 lastCharIndex = 255;
926 }
927
928 var bbox = properties.bbox || [0, 0, 0, 0];
929 var unitsPerEm = override.unitsPerEm || 1 / (properties.fontMatrix || _util.FONT_IDENTITY_MATRIX)[0];
930 var scale = properties.ascentScaled ? 1.0 : unitsPerEm / PDF_GLYPH_SPACE_UNITS;
931 var typoAscent = override.ascent || Math.round(scale * (properties.ascent || bbox[3]));
932 var typoDescent = override.descent || Math.round(scale * (properties.descent || bbox[1]));
933
934 if (typoDescent > 0 && properties.descent > 0 && bbox[1] < 0) {
935 typoDescent = -typoDescent;
936 }
937
938 var winAscent = override.yMax || typoAscent;
939 var winDescent = -override.yMin || -typoDescent;
940 return '\x00\x03' + '\x02\x24' + '\x01\xF4' + '\x00\x05' + '\x00\x00' + '\x02\x8A' + '\x02\xBB' + '\x00\x00' + '\x00\x8C' + '\x02\x8A' + '\x02\xBB' + '\x00\x00' + '\x01\xDF' + '\x00\x31' + '\x01\x02' + '\x00\x00' + '\x00\x00\x06' + String.fromCharCode(properties.fixedPitch ? 0x09 : 0x00) + '\x00\x00\x00\x00\x00\x00' + (0, _util.string32)(ulUnicodeRange1) + (0, _util.string32)(ulUnicodeRange2) + (0, _util.string32)(ulUnicodeRange3) + (0, _util.string32)(ulUnicodeRange4) + '\x2A\x32\x31\x2A' + string16(properties.italicAngle ? 1 : 0) + string16(firstCharIndex || properties.firstChar) + string16(lastCharIndex || properties.lastChar) + string16(typoAscent) + string16(typoDescent) + '\x00\x64' + string16(winAscent) + string16(winDescent) + '\x00\x00\x00\x00' + '\x00\x00\x00\x00' + string16(properties.xHeight) + string16(properties.capHeight) + string16(0) + string16(firstCharIndex || properties.firstChar) + '\x00\x03';
941 }
942
943 function createPostTable(properties) {
944 var angle = Math.floor(properties.italicAngle * Math.pow(2, 16));
945 return '\x00\x03\x00\x00' + (0, _util.string32)(angle) + '\x00\x00' + '\x00\x00' + (0, _util.string32)(properties.fixedPitch) + '\x00\x00\x00\x00' + '\x00\x00\x00\x00' + '\x00\x00\x00\x00' + '\x00\x00\x00\x00';
946 }
947
948 function createNameTable(name, proto) {
949 if (!proto) {
950 proto = [[], []];
951 }
952
953 var strings = [proto[0][0] || 'Original licence', proto[0][1] || name, proto[0][2] || 'Unknown', proto[0][3] || 'uniqueID', proto[0][4] || name, proto[0][5] || 'Version 0.11', proto[0][6] || '', proto[0][7] || 'Unknown', proto[0][8] || 'Unknown', proto[0][9] || 'Unknown'];
954 var stringsUnicode = [];
955 var i, ii, j, jj, str;
956
957 for (i = 0, ii = strings.length; i < ii; i++) {
958 str = proto[1][i] || strings[i];
959 var strBufUnicode = [];
960
961 for (j = 0, jj = str.length; j < jj; j++) {
962 strBufUnicode.push(string16(str.charCodeAt(j)));
963 }
964
965 stringsUnicode.push(strBufUnicode.join(''));
966 }
967
968 var names = [strings, stringsUnicode];
969 var platforms = ['\x00\x01', '\x00\x03'];
970 var encodings = ['\x00\x00', '\x00\x01'];
971 var languages = ['\x00\x00', '\x04\x09'];
972 var namesRecordCount = strings.length * platforms.length;
973 var nameTable = '\x00\x00' + string16(namesRecordCount) + string16(namesRecordCount * 12 + 6);
974 var strOffset = 0;
975
976 for (i = 0, ii = platforms.length; i < ii; i++) {
977 var strs = names[i];
978
979 for (j = 0, jj = strs.length; j < jj; j++) {
980 str = strs[j];
981 var nameRecord = platforms[i] + encodings[i] + languages[i] + string16(j) + string16(str.length) + string16(strOffset);
982 nameTable += nameRecord;
983 strOffset += str.length;
984 }
985 }
986
987 nameTable += strings.join('') + stringsUnicode.join('');
988 return nameTable;
989 }
990
991 Font.prototype = {
992 name: null,
993 font: null,
994 mimetype: null,
995 encoding: null,
996 disableFontFace: false,
997
998 get renderer() {
999 var renderer = _font_renderer.FontRendererFactory.create(this, SEAC_ANALYSIS_ENABLED);
1000
1001 return (0, _util.shadow)(this, 'renderer', renderer);
1002 },
1003
1004 exportData: function Font_exportData() {
1005 var data = {};
1006
1007 for (var i in this) {
1008 if (this.hasOwnProperty(i)) {
1009 data[i] = this[i];
1010 }
1011 }
1012
1013 return data;
1014 },
1015 fallbackToSystemFont: function Font_fallbackToSystemFont() {
1016 var _this = this;
1017
1018 this.missingFile = true;
1019 var charCode, unicode;
1020 var name = this.name;
1021 var type = this.type;
1022 var subtype = this.subtype;
1023 var fontName = name.replace(/[,_]/g, '-');
1024 var stdFontMap = (0, _standard_fonts.getStdFontMap)(),
1025 nonStdFontMap = (0, _standard_fonts.getNonStdFontMap)();
1026 var isStandardFont = !!stdFontMap[fontName] || !!(nonStdFontMap[fontName] && stdFontMap[nonStdFontMap[fontName]]);
1027 fontName = stdFontMap[fontName] || nonStdFontMap[fontName] || fontName;
1028 this.bold = fontName.search(/bold/gi) !== -1;
1029 this.italic = fontName.search(/oblique/gi) !== -1 || fontName.search(/italic/gi) !== -1;
1030 this.black = name.search(/Black/g) !== -1;
1031 this.remeasure = Object.keys(this.widths).length > 0;
1032
1033 if (isStandardFont && type === 'CIDFontType2' && this.cidEncoding.startsWith('Identity-')) {
1034 var GlyphMapForStandardFonts = (0, _standard_fonts.getGlyphMapForStandardFonts)();
1035 var map = [];
1036
1037 for (charCode in GlyphMapForStandardFonts) {
1038 map[+charCode] = GlyphMapForStandardFonts[charCode];
1039 }
1040
1041 if (/Arial-?Black/i.test(name)) {
1042 var SupplementalGlyphMapForArialBlack = (0, _standard_fonts.getSupplementalGlyphMapForArialBlack)();
1043
1044 for (charCode in SupplementalGlyphMapForArialBlack) {
1045 map[+charCode] = SupplementalGlyphMapForArialBlack[charCode];
1046 }
1047 } else if (/Calibri/i.test(name)) {
1048 var SupplementalGlyphMapForCalibri = (0, _standard_fonts.getSupplementalGlyphMapForCalibri)();
1049
1050 for (charCode in SupplementalGlyphMapForCalibri) {
1051 map[+charCode] = SupplementalGlyphMapForCalibri[charCode];
1052 }
1053 }
1054
1055 var isIdentityUnicode = this.toUnicode instanceof IdentityToUnicodeMap;
1056
1057 if (!isIdentityUnicode) {
1058 this.toUnicode.forEach(function (charCode, unicodeCharCode) {
1059 map[+charCode] = unicodeCharCode;
1060 });
1061 }
1062
1063 this.toFontChar = map;
1064 this.toUnicode = new ToUnicodeMap(map);
1065 } else if (/Symbol/i.test(fontName)) {
1066 this.toFontChar = buildToFontChar(_encodings.SymbolSetEncoding, (0, _glyphlist.getGlyphsUnicode)(), this.differences);
1067 } else if (/Dingbats/i.test(fontName)) {
1068 if (/Wingdings/i.test(name)) {
1069 (0, _util.warn)('Non-embedded Wingdings font, falling back to ZapfDingbats.');
1070 }
1071
1072 this.toFontChar = buildToFontChar(_encodings.ZapfDingbatsEncoding, (0, _glyphlist.getDingbatsGlyphsUnicode)(), this.differences);
1073 } else if (isStandardFont) {
1074 this.toFontChar = buildToFontChar(this.defaultEncoding, (0, _glyphlist.getGlyphsUnicode)(), this.differences);
1075 } else {
1076 var glyphsUnicodeMap = (0, _glyphlist.getGlyphsUnicode)();
1077 this.toUnicode.forEach(function (charCode, unicodeCharCode) {
1078 if (!_this.composite) {
1079 var glyphName = _this.differences[charCode] || _this.defaultEncoding[charCode];
1080 unicode = (0, _unicode.getUnicodeForGlyph)(glyphName, glyphsUnicodeMap);
1081
1082 if (unicode !== -1) {
1083 unicodeCharCode = unicode;
1084 }
1085 }
1086
1087 _this.toFontChar[charCode] = unicodeCharCode;
1088 });
1089 }
1090
1091 this.loadedName = fontName.split('-')[0];
1092 this.fontType = getFontType(type, subtype);
1093 },
1094 checkAndRepair: function Font_checkAndRepair(name, font, properties) {
1095 var VALID_TABLES = ['OS/2', 'cmap', 'head', 'hhea', 'hmtx', 'maxp', 'name', 'post', 'loca', 'glyf', 'fpgm', 'prep', 'cvt ', 'CFF '];
1096
1097 function readTables(file, numTables) {
1098 var tables = Object.create(null);
1099 tables['OS/2'] = null;
1100 tables['cmap'] = null;
1101 tables['head'] = null;
1102 tables['hhea'] = null;
1103 tables['hmtx'] = null;
1104 tables['maxp'] = null;
1105 tables['name'] = null;
1106 tables['post'] = null;
1107
1108 for (var i = 0; i < numTables; i++) {
1109 var table = readTableEntry(font);
1110
1111 if (!VALID_TABLES.includes(table.tag)) {
1112 continue;
1113 }
1114
1115 if (table.length === 0) {
1116 continue;
1117 }
1118
1119 tables[table.tag] = table;
1120 }
1121
1122 return tables;
1123 }
1124
1125 function readTableEntry(file) {
1126 var tag = (0, _util.bytesToString)(file.getBytes(4));
1127 var checksum = file.getInt32() >>> 0;
1128 var offset = file.getInt32() >>> 0;
1129 var length = file.getInt32() >>> 0;
1130 var previousPosition = file.pos;
1131 file.pos = file.start ? file.start : 0;
1132 file.skip(offset);
1133 var data = file.getBytes(length);
1134 file.pos = previousPosition;
1135
1136 if (tag === 'head') {
1137 data[8] = data[9] = data[10] = data[11] = 0;
1138 data[17] |= 0x20;
1139 }
1140
1141 return {
1142 tag: tag,
1143 checksum: checksum,
1144 length: length,
1145 offset: offset,
1146 data: data
1147 };
1148 }
1149
1150 function readOpenTypeHeader(ttf) {
1151 return {
1152 version: (0, _util.bytesToString)(ttf.getBytes(4)),
1153 numTables: ttf.getUint16(),
1154 searchRange: ttf.getUint16(),
1155 entrySelector: ttf.getUint16(),
1156 rangeShift: ttf.getUint16()
1157 };
1158 }
1159
1160 function readTrueTypeCollectionHeader(ttc) {
1161 var ttcTag = (0, _util.bytesToString)(ttc.getBytes(4));
1162 (0, _util.assert)(ttcTag === 'ttcf', 'Must be a TrueType Collection font.');
1163 var majorVersion = ttc.getUint16();
1164 var minorVersion = ttc.getUint16();
1165 var numFonts = ttc.getInt32() >>> 0;
1166 var offsetTable = [];
1167
1168 for (var i = 0; i < numFonts; i++) {
1169 offsetTable.push(ttc.getInt32() >>> 0);
1170 }
1171
1172 var header = {
1173 ttcTag: ttcTag,
1174 majorVersion: majorVersion,
1175 minorVersion: minorVersion,
1176 numFonts: numFonts,
1177 offsetTable: offsetTable
1178 };
1179
1180 switch (majorVersion) {
1181 case 1:
1182 return header;
1183
1184 case 2:
1185 header.dsigTag = ttc.getInt32() >>> 0;
1186 header.dsigLength = ttc.getInt32() >>> 0;
1187 header.dsigOffset = ttc.getInt32() >>> 0;
1188 return header;
1189 }
1190
1191 throw new _util.FormatError("Invalid TrueType Collection majorVersion: ".concat(majorVersion, "."));
1192 }
1193
1194 function readTrueTypeCollectionData(ttc, fontName) {
1195 var _readTrueTypeCollecti = readTrueTypeCollectionHeader(ttc),
1196 numFonts = _readTrueTypeCollecti.numFonts,
1197 offsetTable = _readTrueTypeCollecti.offsetTable;
1198
1199 for (var i = 0; i < numFonts; i++) {
1200 ttc.pos = (ttc.start || 0) + offsetTable[i];
1201 var potentialHeader = readOpenTypeHeader(ttc);
1202 var potentialTables = readTables(ttc, potentialHeader.numTables);
1203
1204 if (!potentialTables['name']) {
1205 throw new _util.FormatError('TrueType Collection font must contain a "name" table.');
1206 }
1207
1208 var nameTable = readNameTable(potentialTables['name']);
1209
1210 for (var j = 0, jj = nameTable.length; j < jj; j++) {
1211 for (var k = 0, kk = nameTable[j].length; k < kk; k++) {
1212 var nameEntry = nameTable[j][k];
1213
1214 if (nameEntry && nameEntry.replace(/\s/g, '') === fontName) {
1215 return {
1216 header: potentialHeader,
1217 tables: potentialTables
1218 };
1219 }
1220 }
1221 }
1222 }
1223
1224 throw new _util.FormatError("TrueType Collection does not contain \"".concat(fontName, "\" font."));
1225 }
1226
1227 function readCmapTable(cmap, font, isSymbolicFont, hasEncoding) {
1228 if (!cmap) {
1229 (0, _util.warn)('No cmap table available.');
1230 return {
1231 platformId: -1,
1232 encodingId: -1,
1233 mappings: [],
1234 hasShortCmap: false
1235 };
1236 }
1237
1238 var segment;
1239 var start = (font.start ? font.start : 0) + cmap.offset;
1240 font.pos = start;
1241 font.getUint16();
1242 var numTables = font.getUint16();
1243 var potentialTable;
1244 var canBreak = false;
1245
1246 for (var i = 0; i < numTables; i++) {
1247 var platformId = font.getUint16();
1248 var encodingId = font.getUint16();
1249 var offset = font.getInt32() >>> 0;
1250 var useTable = false;
1251
1252 if (potentialTable && potentialTable.platformId === platformId && potentialTable.encodingId === encodingId) {
1253 continue;
1254 }
1255
1256 if (platformId === 0 && encodingId === 0) {
1257 useTable = true;
1258 } else if (platformId === 1 && encodingId === 0) {
1259 useTable = true;
1260 } else if (platformId === 3 && encodingId === 1 && (hasEncoding || !potentialTable)) {
1261 useTable = true;
1262
1263 if (!isSymbolicFont) {
1264 canBreak = true;
1265 }
1266 } else if (isSymbolicFont && platformId === 3 && encodingId === 0) {
1267 useTable = true;
1268 canBreak = true;
1269 }
1270
1271 if (useTable) {
1272 potentialTable = {
1273 platformId: platformId,
1274 encodingId: encodingId,
1275 offset: offset
1276 };
1277 }
1278
1279 if (canBreak) {
1280 break;
1281 }
1282 }
1283
1284 if (potentialTable) {
1285 font.pos = start + potentialTable.offset;
1286 }
1287
1288 if (!potentialTable || font.peekByte() === -1) {
1289 (0, _util.warn)('Could not find a preferred cmap table.');
1290 return {
1291 platformId: -1,
1292 encodingId: -1,
1293 mappings: [],
1294 hasShortCmap: false
1295 };
1296 }
1297
1298 var format = font.getUint16();
1299 font.getUint16();
1300 font.getUint16();
1301 var hasShortCmap = false;
1302 var mappings = [];
1303 var j, glyphId;
1304
1305 if (format === 0) {
1306 for (j = 0; j < 256; j++) {
1307 var index = font.getByte();
1308
1309 if (!index) {
1310 continue;
1311 }
1312
1313 mappings.push({
1314 charCode: j,
1315 glyphId: index
1316 });
1317 }
1318
1319 hasShortCmap = true;
1320 } else if (format === 4) {
1321 var segCount = font.getUint16() >> 1;
1322 font.getBytes(6);
1323 var segIndex,
1324 segments = [];
1325
1326 for (segIndex = 0; segIndex < segCount; segIndex++) {
1327 segments.push({
1328 end: font.getUint16()
1329 });
1330 }
1331
1332 font.getUint16();
1333
1334 for (segIndex = 0; segIndex < segCount; segIndex++) {
1335 segments[segIndex].start = font.getUint16();
1336 }
1337
1338 for (segIndex = 0; segIndex < segCount; segIndex++) {
1339 segments[segIndex].delta = font.getUint16();
1340 }
1341
1342 var offsetsCount = 0;
1343
1344 for (segIndex = 0; segIndex < segCount; segIndex++) {
1345 segment = segments[segIndex];
1346 var rangeOffset = font.getUint16();
1347
1348 if (!rangeOffset) {
1349 segment.offsetIndex = -1;
1350 continue;
1351 }
1352
1353 var offsetIndex = (rangeOffset >> 1) - (segCount - segIndex);
1354 segment.offsetIndex = offsetIndex;
1355 offsetsCount = Math.max(offsetsCount, offsetIndex + segment.end - segment.start + 1);
1356 }
1357
1358 var offsets = [];
1359
1360 for (j = 0; j < offsetsCount; j++) {
1361 offsets.push(font.getUint16());
1362 }
1363
1364 for (segIndex = 0; segIndex < segCount; segIndex++) {
1365 segment = segments[segIndex];
1366 start = segment.start;
1367 var end = segment.end;
1368 var delta = segment.delta;
1369 offsetIndex = segment.offsetIndex;
1370
1371 for (j = start; j <= end; j++) {
1372 if (j === 0xFFFF) {
1373 continue;
1374 }
1375
1376 glyphId = offsetIndex < 0 ? j : offsets[offsetIndex + j - start];
1377 glyphId = glyphId + delta & 0xFFFF;
1378 mappings.push({
1379 charCode: j,
1380 glyphId: glyphId
1381 });
1382 }
1383 }
1384 } else if (format === 6) {
1385 var firstCode = font.getUint16();
1386 var entryCount = font.getUint16();
1387
1388 for (j = 0; j < entryCount; j++) {
1389 glyphId = font.getUint16();
1390 var charCode = firstCode + j;
1391 mappings.push({
1392 charCode: charCode,
1393 glyphId: glyphId
1394 });
1395 }
1396 } else {
1397 (0, _util.warn)('cmap table has unsupported format: ' + format);
1398 return {
1399 platformId: -1,
1400 encodingId: -1,
1401 mappings: [],
1402 hasShortCmap: false
1403 };
1404 }
1405
1406 mappings.sort(function (a, b) {
1407 return a.charCode - b.charCode;
1408 });
1409
1410 for (i = 1; i < mappings.length; i++) {
1411 if (mappings[i - 1].charCode === mappings[i].charCode) {
1412 mappings.splice(i, 1);
1413 i--;
1414 }
1415 }
1416
1417 return {
1418 platformId: potentialTable.platformId,
1419 encodingId: potentialTable.encodingId,
1420 mappings: mappings,
1421 hasShortCmap: hasShortCmap
1422 };
1423 }
1424
1425 function sanitizeMetrics(font, header, metrics, numGlyphs) {
1426 if (!header) {
1427 if (metrics) {
1428 metrics.data = null;
1429 }
1430
1431 return;
1432 }
1433
1434 font.pos = (font.start ? font.start : 0) + header.offset;
1435 font.pos += 4;
1436 font.pos += 2;
1437 font.pos += 2;
1438 font.pos += 2;
1439 font.pos += 2;
1440 font.pos += 2;
1441 font.pos += 2;
1442 font.pos += 2;
1443 font.pos += 2;
1444 font.pos += 2;
1445 font.pos += 2;
1446 font.pos += 8;
1447 font.pos += 2;
1448 var numOfMetrics = font.getUint16();
1449
1450 if (numOfMetrics > numGlyphs) {
1451 (0, _util.info)('The numOfMetrics (' + numOfMetrics + ') should not be ' + 'greater than the numGlyphs (' + numGlyphs + ')');
1452 numOfMetrics = numGlyphs;
1453 header.data[34] = (numOfMetrics & 0xff00) >> 8;
1454 header.data[35] = numOfMetrics & 0x00ff;
1455 }
1456
1457 var numOfSidebearings = numGlyphs - numOfMetrics;
1458 var numMissing = numOfSidebearings - (metrics.length - numOfMetrics * 4 >> 1);
1459
1460 if (numMissing > 0) {
1461 var entries = new Uint8Array(metrics.length + numMissing * 2);
1462 entries.set(metrics.data);
1463 metrics.data = entries;
1464 }
1465 }
1466
1467 function sanitizeGlyph(source, sourceStart, sourceEnd, dest, destStart, hintsValid) {
1468 var glyphProfile = {
1469 length: 0,
1470 sizeOfInstructions: 0
1471 };
1472
1473 if (sourceEnd - sourceStart <= 12) {
1474 return glyphProfile;
1475 }
1476
1477 var glyf = source.subarray(sourceStart, sourceEnd);
1478 var contoursCount = signedInt16(glyf[0], glyf[1]);
1479
1480 if (contoursCount < 0) {
1481 contoursCount = -1;
1482 writeSignedInt16(glyf, 0, contoursCount);
1483 dest.set(glyf, destStart);
1484 glyphProfile.length = glyf.length;
1485 return glyphProfile;
1486 }
1487
1488 var i,
1489 j = 10,
1490 flagsCount = 0;
1491
1492 for (i = 0; i < contoursCount; i++) {
1493 var endPoint = glyf[j] << 8 | glyf[j + 1];
1494 flagsCount = endPoint + 1;
1495 j += 2;
1496 }
1497
1498 var instructionsStart = j;
1499 var instructionsLength = glyf[j] << 8 | glyf[j + 1];
1500 glyphProfile.sizeOfInstructions = instructionsLength;
1501 j += 2 + instructionsLength;
1502 var instructionsEnd = j;
1503 var coordinatesLength = 0;
1504
1505 for (i = 0; i < flagsCount; i++) {
1506 var flag = glyf[j++];
1507
1508 if (flag & 0xC0) {
1509 glyf[j - 1] = flag & 0x3F;
1510 }
1511
1512 var xyLength = (flag & 2 ? 1 : flag & 16 ? 0 : 2) + (flag & 4 ? 1 : flag & 32 ? 0 : 2);
1513 coordinatesLength += xyLength;
1514
1515 if (flag & 8) {
1516 var repeat = glyf[j++];
1517 i += repeat;
1518 coordinatesLength += repeat * xyLength;
1519 }
1520 }
1521
1522 if (coordinatesLength === 0) {
1523 return glyphProfile;
1524 }
1525
1526 var glyphDataLength = j + coordinatesLength;
1527
1528 if (glyphDataLength > glyf.length) {
1529 return glyphProfile;
1530 }
1531
1532 if (!hintsValid && instructionsLength > 0) {
1533 dest.set(glyf.subarray(0, instructionsStart), destStart);
1534 dest.set([0, 0], destStart + instructionsStart);
1535 dest.set(glyf.subarray(instructionsEnd, glyphDataLength), destStart + instructionsStart + 2);
1536 glyphDataLength -= instructionsLength;
1537
1538 if (glyf.length - glyphDataLength > 3) {
1539 glyphDataLength = glyphDataLength + 3 & ~3;
1540 }
1541
1542 glyphProfile.length = glyphDataLength;
1543 return glyphProfile;
1544 }
1545
1546 if (glyf.length - glyphDataLength > 3) {
1547 glyphDataLength = glyphDataLength + 3 & ~3;
1548 dest.set(glyf.subarray(0, glyphDataLength), destStart);
1549 glyphProfile.length = glyphDataLength;
1550 return glyphProfile;
1551 }
1552
1553 dest.set(glyf, destStart);
1554 glyphProfile.length = glyf.length;
1555 return glyphProfile;
1556 }
1557
1558 function sanitizeHead(head, numGlyphs, locaLength) {
1559 var data = head.data;
1560 var version = int32(data[0], data[1], data[2], data[3]);
1561
1562 if (version >> 16 !== 1) {
1563 (0, _util.info)('Attempting to fix invalid version in head table: ' + version);
1564 data[0] = 0;
1565 data[1] = 1;
1566 data[2] = 0;
1567 data[3] = 0;
1568 }
1569
1570 var indexToLocFormat = int16(data[50], data[51]);
1571
1572 if (indexToLocFormat < 0 || indexToLocFormat > 1) {
1573 (0, _util.info)('Attempting to fix invalid indexToLocFormat in head table: ' + indexToLocFormat);
1574 var numGlyphsPlusOne = numGlyphs + 1;
1575
1576 if (locaLength === numGlyphsPlusOne << 1) {
1577 data[50] = 0;
1578 data[51] = 0;
1579 } else if (locaLength === numGlyphsPlusOne << 2) {
1580 data[50] = 0;
1581 data[51] = 1;
1582 } else {
1583 throw new _util.FormatError('Could not fix indexToLocFormat: ' + indexToLocFormat);
1584 }
1585 }
1586 }
1587
1588 function sanitizeGlyphLocations(loca, glyf, numGlyphs, isGlyphLocationsLong, hintsValid, dupFirstEntry, maxSizeOfInstructions) {
1589 var itemSize, itemDecode, itemEncode;
1590
1591 if (isGlyphLocationsLong) {
1592 itemSize = 4;
1593
1594 itemDecode = function fontItemDecodeLong(data, offset) {
1595 return data[offset] << 24 | data[offset + 1] << 16 | data[offset + 2] << 8 | data[offset + 3];
1596 };
1597
1598 itemEncode = function fontItemEncodeLong(data, offset, value) {
1599 data[offset] = value >>> 24 & 0xFF;
1600 data[offset + 1] = value >> 16 & 0xFF;
1601 data[offset + 2] = value >> 8 & 0xFF;
1602 data[offset + 3] = value & 0xFF;
1603 };
1604 } else {
1605 itemSize = 2;
1606
1607 itemDecode = function fontItemDecode(data, offset) {
1608 return data[offset] << 9 | data[offset + 1] << 1;
1609 };
1610
1611 itemEncode = function fontItemEncode(data, offset, value) {
1612 data[offset] = value >> 9 & 0xFF;
1613 data[offset + 1] = value >> 1 & 0xFF;
1614 };
1615 }
1616
1617 var numGlyphsOut = dupFirstEntry ? numGlyphs + 1 : numGlyphs;
1618 var locaData = loca.data;
1619 var locaDataSize = itemSize * (1 + numGlyphsOut);
1620 locaData = new Uint8Array(locaDataSize);
1621 locaData.set(loca.data.subarray(0, locaDataSize));
1622 loca.data = locaData;
1623 var oldGlyfData = glyf.data;
1624 var oldGlyfDataLength = oldGlyfData.length;
1625 var newGlyfData = new Uint8Array(oldGlyfDataLength);
1626 var startOffset = itemDecode(locaData, 0);
1627 var writeOffset = 0;
1628 var missingGlyphs = Object.create(null);
1629 itemEncode(locaData, 0, writeOffset);
1630 var i, j;
1631
1632 for (i = 0, j = itemSize; i < numGlyphs; i++, j += itemSize) {
1633 var endOffset = itemDecode(locaData, j);
1634
1635 if (endOffset === 0) {
1636 endOffset = startOffset;
1637 }
1638
1639 if (endOffset > oldGlyfDataLength && (oldGlyfDataLength + 3 & ~3) === endOffset) {
1640 endOffset = oldGlyfDataLength;
1641 }
1642
1643 if (endOffset > oldGlyfDataLength) {
1644 startOffset = endOffset;
1645 }
1646
1647 var glyphProfile = sanitizeGlyph(oldGlyfData, startOffset, endOffset, newGlyfData, writeOffset, hintsValid);
1648 var newLength = glyphProfile.length;
1649
1650 if (newLength === 0) {
1651 missingGlyphs[i] = true;
1652 }
1653
1654 if (glyphProfile.sizeOfInstructions > maxSizeOfInstructions) {
1655 maxSizeOfInstructions = glyphProfile.sizeOfInstructions;
1656 }
1657
1658 writeOffset += newLength;
1659 itemEncode(locaData, j, writeOffset);
1660 startOffset = endOffset;
1661 }
1662
1663 if (writeOffset === 0) {
1664 var simpleGlyph = new Uint8Array([0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 0]);
1665
1666 for (i = 0, j = itemSize; i < numGlyphsOut; i++, j += itemSize) {
1667 itemEncode(locaData, j, simpleGlyph.length);
1668 }
1669
1670 glyf.data = simpleGlyph;
1671 } else if (dupFirstEntry) {
1672 var firstEntryLength = itemDecode(locaData, itemSize);
1673
1674 if (newGlyfData.length > firstEntryLength + writeOffset) {
1675 glyf.data = newGlyfData.subarray(0, firstEntryLength + writeOffset);
1676 } else {
1677 glyf.data = new Uint8Array(firstEntryLength + writeOffset);
1678 glyf.data.set(newGlyfData.subarray(0, writeOffset));
1679 }
1680
1681 glyf.data.set(newGlyfData.subarray(0, firstEntryLength), writeOffset);
1682 itemEncode(loca.data, locaData.length - itemSize, writeOffset + firstEntryLength);
1683 } else {
1684 glyf.data = newGlyfData.subarray(0, writeOffset);
1685 }
1686
1687 return {
1688 missingGlyphs: missingGlyphs,
1689 maxSizeOfInstructions: maxSizeOfInstructions
1690 };
1691 }
1692
1693 function readPostScriptTable(post, properties, maxpNumGlyphs) {
1694 var start = (font.start ? font.start : 0) + post.offset;
1695 font.pos = start;
1696 var length = post.length,
1697 end = start + length;
1698 var version = font.getInt32();
1699 font.getBytes(28);
1700 var glyphNames;
1701 var valid = true;
1702 var i;
1703
1704 switch (version) {
1705 case 0x00010000:
1706 glyphNames = MacStandardGlyphOrdering;
1707 break;
1708
1709 case 0x00020000:
1710 var numGlyphs = font.getUint16();
1711
1712 if (numGlyphs !== maxpNumGlyphs) {
1713 valid = false;
1714 break;
1715 }
1716
1717 var glyphNameIndexes = [];
1718
1719 for (i = 0; i < numGlyphs; ++i) {
1720 var index = font.getUint16();
1721
1722 if (index >= 32768) {
1723 valid = false;
1724 break;
1725 }
1726
1727 glyphNameIndexes.push(index);
1728 }
1729
1730 if (!valid) {
1731 break;
1732 }
1733
1734 var customNames = [];
1735 var strBuf = [];
1736
1737 while (font.pos < end) {
1738 var stringLength = font.getByte();
1739 strBuf.length = stringLength;
1740
1741 for (i = 0; i < stringLength; ++i) {
1742 strBuf[i] = String.fromCharCode(font.getByte());
1743 }
1744
1745 customNames.push(strBuf.join(''));
1746 }
1747
1748 glyphNames = [];
1749
1750 for (i = 0; i < numGlyphs; ++i) {
1751 var j = glyphNameIndexes[i];
1752
1753 if (j < 258) {
1754 glyphNames.push(MacStandardGlyphOrdering[j]);
1755 continue;
1756 }
1757
1758 glyphNames.push(customNames[j - 258]);
1759 }
1760
1761 break;
1762
1763 case 0x00030000:
1764 break;
1765
1766 default:
1767 (0, _util.warn)('Unknown/unsupported post table version ' + version);
1768 valid = false;
1769
1770 if (properties.defaultEncoding) {
1771 glyphNames = properties.defaultEncoding;
1772 }
1773
1774 break;
1775 }
1776
1777 properties.glyphNames = glyphNames;
1778 return valid;
1779 }
1780
1781 function readNameTable(nameTable) {
1782 var start = (font.start ? font.start : 0) + nameTable.offset;
1783 font.pos = start;
1784 var names = [[], []];
1785 var length = nameTable.length,
1786 end = start + length;
1787 var format = font.getUint16();
1788 var FORMAT_0_HEADER_LENGTH = 6;
1789
1790 if (format !== 0 || length < FORMAT_0_HEADER_LENGTH) {
1791 return names;
1792 }
1793
1794 var numRecords = font.getUint16();
1795 var stringsStart = font.getUint16();
1796 var records = [];
1797 var NAME_RECORD_LENGTH = 12;
1798 var i, ii;
1799
1800 for (i = 0; i < numRecords && font.pos + NAME_RECORD_LENGTH <= end; i++) {
1801 var r = {
1802 platform: font.getUint16(),
1803 encoding: font.getUint16(),
1804 language: font.getUint16(),
1805 name: font.getUint16(),
1806 length: font.getUint16(),
1807 offset: font.getUint16()
1808 };
1809
1810 if (r.platform === 1 && r.encoding === 0 && r.language === 0 || r.platform === 3 && r.encoding === 1 && r.language === 0x409) {
1811 records.push(r);
1812 }
1813 }
1814
1815 for (i = 0, ii = records.length; i < ii; i++) {
1816 var record = records[i];
1817
1818 if (record.length <= 0) {
1819 continue;
1820 }
1821
1822 var pos = start + stringsStart + record.offset;
1823
1824 if (pos + record.length > end) {
1825 continue;
1826 }
1827
1828 font.pos = pos;
1829 var nameIndex = record.name;
1830
1831 if (record.encoding) {
1832 var str = '';
1833
1834 for (var j = 0, jj = record.length; j < jj; j += 2) {
1835 str += String.fromCharCode(font.getUint16());
1836 }
1837
1838 names[1][nameIndex] = str;
1839 } else {
1840 names[0][nameIndex] = (0, _util.bytesToString)(font.getBytes(record.length));
1841 }
1842 }
1843
1844 return names;
1845 }
1846
1847 var TTOpsStackDeltas = [0, 0, 0, 0, 0, 0, 0, 0, -2, -2, -2, -2, 0, 0, -2, -5, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, -1, 0, -1, -1, -1, -1, 1, -1, -999, 0, 1, 0, -1, -2, 0, -1, -2, -1, -1, 0, -1, -1, 0, 0, -999, -999, -1, -1, -1, -1, -2, -999, -2, -2, -999, 0, -2, -2, 0, 0, -2, 0, -2, 0, 0, 0, -2, -1, -1, 1, 1, 0, 0, -1, -1, -1, -1, -1, -1, -1, 0, 0, -1, 0, -1, -1, 0, -999, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -999, -999, -999, -999, -999, -1, -1, -2, -2, 0, 0, 0, 0, -1, -1, -999, -2, -2, 0, 0, -1, -2, -2, 0, 0, 0, -1, -1, -1, -2];
1848
1849 function sanitizeTTProgram(table, ttContext) {
1850 var data = table.data;
1851 var i = 0,
1852 j,
1853 n,
1854 b,
1855 funcId,
1856 pc,
1857 lastEndf = 0,
1858 lastDeff = 0;
1859 var stack = [];
1860 var callstack = [];
1861 var functionsCalled = [];
1862 var tooComplexToFollowFunctions = ttContext.tooComplexToFollowFunctions;
1863 var inFDEF = false,
1864 ifLevel = 0,
1865 inELSE = 0;
1866
1867 for (var ii = data.length; i < ii;) {
1868 var op = data[i++];
1869
1870 if (op === 0x40) {
1871 n = data[i++];
1872
1873 if (inFDEF || inELSE) {
1874 i += n;
1875 } else {
1876 for (j = 0; j < n; j++) {
1877 stack.push(data[i++]);
1878 }
1879 }
1880 } else if (op === 0x41) {
1881 n = data[i++];
1882
1883 if (inFDEF || inELSE) {
1884 i += n * 2;
1885 } else {
1886 for (j = 0; j < n; j++) {
1887 b = data[i++];
1888 stack.push(b << 8 | data[i++]);
1889 }
1890 }
1891 } else if ((op & 0xF8) === 0xB0) {
1892 n = op - 0xB0 + 1;
1893
1894 if (inFDEF || inELSE) {
1895 i += n;
1896 } else {
1897 for (j = 0; j < n; j++) {
1898 stack.push(data[i++]);
1899 }
1900 }
1901 } else if ((op & 0xF8) === 0xB8) {
1902 n = op - 0xB8 + 1;
1903
1904 if (inFDEF || inELSE) {
1905 i += n * 2;
1906 } else {
1907 for (j = 0; j < n; j++) {
1908 b = data[i++];
1909 stack.push(b << 8 | data[i++]);
1910 }
1911 }
1912 } else if (op === 0x2B && !tooComplexToFollowFunctions) {
1913 if (!inFDEF && !inELSE) {
1914 funcId = stack[stack.length - 1];
1915
1916 if (isNaN(funcId)) {
1917 (0, _util.info)('TT: CALL empty stack (or invalid entry).');
1918 } else {
1919 ttContext.functionsUsed[funcId] = true;
1920
1921 if (funcId in ttContext.functionsStackDeltas) {
1922 var newStackLength = stack.length + ttContext.functionsStackDeltas[funcId];
1923
1924 if (newStackLength < 0) {
1925 (0, _util.warn)('TT: CALL invalid functions stack delta.');
1926 ttContext.hintsValid = false;
1927 return;
1928 }
1929
1930 stack.length = newStackLength;
1931 } else if (funcId in ttContext.functionsDefined && !functionsCalled.includes(funcId)) {
1932 callstack.push({
1933 data: data,
1934 i: i,
1935 stackTop: stack.length - 1
1936 });
1937 functionsCalled.push(funcId);
1938 pc = ttContext.functionsDefined[funcId];
1939
1940 if (!pc) {
1941 (0, _util.warn)('TT: CALL non-existent function');
1942 ttContext.hintsValid = false;
1943 return;
1944 }
1945
1946 data = pc.data;
1947 i = pc.i;
1948 }
1949 }
1950 }
1951 } else if (op === 0x2C && !tooComplexToFollowFunctions) {
1952 if (inFDEF || inELSE) {
1953 (0, _util.warn)('TT: nested FDEFs not allowed');
1954 tooComplexToFollowFunctions = true;
1955 }
1956
1957 inFDEF = true;
1958 lastDeff = i;
1959 funcId = stack.pop();
1960 ttContext.functionsDefined[funcId] = {
1961 data: data,
1962 i: i
1963 };
1964 } else if (op === 0x2D) {
1965 if (inFDEF) {
1966 inFDEF = false;
1967 lastEndf = i;
1968 } else {
1969 pc = callstack.pop();
1970
1971 if (!pc) {
1972 (0, _util.warn)('TT: ENDF bad stack');
1973 ttContext.hintsValid = false;
1974 return;
1975 }
1976
1977 funcId = functionsCalled.pop();
1978 data = pc.data;
1979 i = pc.i;
1980 ttContext.functionsStackDeltas[funcId] = stack.length - pc.stackTop;
1981 }
1982 } else if (op === 0x89) {
1983 if (inFDEF || inELSE) {
1984 (0, _util.warn)('TT: nested IDEFs not allowed');
1985 tooComplexToFollowFunctions = true;
1986 }
1987
1988 inFDEF = true;
1989 lastDeff = i;
1990 } else if (op === 0x58) {
1991 ++ifLevel;
1992 } else if (op === 0x1B) {
1993 inELSE = ifLevel;
1994 } else if (op === 0x59) {
1995 if (inELSE === ifLevel) {
1996 inELSE = 0;
1997 }
1998
1999 --ifLevel;
2000 } else if (op === 0x1C) {
2001 if (!inFDEF && !inELSE) {
2002 var offset = stack[stack.length - 1];
2003
2004 if (offset > 0) {
2005 i += offset - 1;
2006 }
2007 }
2008 }
2009
2010 if (!inFDEF && !inELSE) {
2011 var stackDelta = op <= 0x8E ? TTOpsStackDeltas[op] : op >= 0xC0 && op <= 0xDF ? -1 : op >= 0xE0 ? -2 : 0;
2012
2013 if (op >= 0x71 && op <= 0x75) {
2014 n = stack.pop();
2015
2016 if (!isNaN(n)) {
2017 stackDelta = -n * 2;
2018 }
2019 }
2020
2021 while (stackDelta < 0 && stack.length > 0) {
2022 stack.pop();
2023 stackDelta++;
2024 }
2025
2026 while (stackDelta > 0) {
2027 stack.push(NaN);
2028 stackDelta--;
2029 }
2030 }
2031 }
2032
2033 ttContext.tooComplexToFollowFunctions = tooComplexToFollowFunctions;
2034 var content = [data];
2035
2036 if (i > data.length) {
2037 content.push(new Uint8Array(i - data.length));
2038 }
2039
2040 if (lastDeff > lastEndf) {
2041 (0, _util.warn)('TT: complementing a missing function tail');
2042 content.push(new Uint8Array([0x22, 0x2D]));
2043 }
2044
2045 foldTTTable(table, content);
2046 }
2047
2048 function checkInvalidFunctions(ttContext, maxFunctionDefs) {
2049 if (ttContext.tooComplexToFollowFunctions) {
2050 return;
2051 }
2052
2053 if (ttContext.functionsDefined.length > maxFunctionDefs) {
2054 (0, _util.warn)('TT: more functions defined than expected');
2055 ttContext.hintsValid = false;
2056 return;
2057 }
2058
2059 for (var j = 0, jj = ttContext.functionsUsed.length; j < jj; j++) {
2060 if (j > maxFunctionDefs) {
2061 (0, _util.warn)('TT: invalid function id: ' + j);
2062 ttContext.hintsValid = false;
2063 return;
2064 }
2065
2066 if (ttContext.functionsUsed[j] && !ttContext.functionsDefined[j]) {
2067 (0, _util.warn)('TT: undefined function: ' + j);
2068 ttContext.hintsValid = false;
2069 return;
2070 }
2071 }
2072 }
2073
2074 function foldTTTable(table, content) {
2075 if (content.length > 1) {
2076 var newLength = 0;
2077 var j, jj;
2078
2079 for (j = 0, jj = content.length; j < jj; j++) {
2080 newLength += content[j].length;
2081 }
2082
2083 newLength = newLength + 3 & ~3;
2084 var result = new Uint8Array(newLength);
2085 var pos = 0;
2086
2087 for (j = 0, jj = content.length; j < jj; j++) {
2088 result.set(content[j], pos);
2089 pos += content[j].length;
2090 }
2091
2092 table.data = result;
2093 table.length = newLength;
2094 }
2095 }
2096
2097 function sanitizeTTPrograms(fpgm, prep, cvt, maxFunctionDefs) {
2098 var ttContext = {
2099 functionsDefined: [],
2100 functionsUsed: [],
2101 functionsStackDeltas: [],
2102 tooComplexToFollowFunctions: false,
2103 hintsValid: true
2104 };
2105
2106 if (fpgm) {
2107 sanitizeTTProgram(fpgm, ttContext);
2108 }
2109
2110 if (prep) {
2111 sanitizeTTProgram(prep, ttContext);
2112 }
2113
2114 if (fpgm) {
2115 checkInvalidFunctions(ttContext, maxFunctionDefs);
2116 }
2117
2118 if (cvt && cvt.length & 1) {
2119 var cvtData = new Uint8Array(cvt.length + 1);
2120 cvtData.set(cvt.data);
2121 cvt.data = cvtData;
2122 }
2123
2124 return ttContext.hintsValid;
2125 }
2126
2127 font = new _stream.Stream(new Uint8Array(font.getBytes()));
2128 var header, tables;
2129
2130 if (isTrueTypeCollectionFile(font)) {
2131 var ttcData = readTrueTypeCollectionData(font, this.name);
2132 header = ttcData.header;
2133 tables = ttcData.tables;
2134 } else {
2135 header = readOpenTypeHeader(font);
2136 tables = readTables(font, header.numTables);
2137 }
2138
2139 var cff, cffFile;
2140 var isTrueType = !tables['CFF '];
2141
2142 if (!isTrueType) {
2143 var isComposite = properties.composite && ((properties.cidToGidMap || []).length > 0 || !(properties.cMap instanceof _cmap.IdentityCMap));
2144
2145 if (header.version === 'OTTO' && !isComposite || !tables['head'] || !tables['hhea'] || !tables['maxp'] || !tables['post']) {
2146 cffFile = new _stream.Stream(tables['CFF '].data);
2147 cff = new CFFFont(cffFile, properties);
2148 adjustWidths(properties);
2149 return this.convert(name, cff, properties);
2150 }
2151
2152 delete tables['glyf'];
2153 delete tables['loca'];
2154 delete tables['fpgm'];
2155 delete tables['prep'];
2156 delete tables['cvt '];
2157 this.isOpenType = true;
2158 } else {
2159 if (!tables['loca']) {
2160 throw new _util.FormatError('Required "loca" table is not found');
2161 }
2162
2163 if (!tables['glyf']) {
2164 (0, _util.warn)('Required "glyf" table is not found -- trying to recover.');
2165 tables['glyf'] = {
2166 tag: 'glyf',
2167 data: new Uint8Array(0)
2168 };
2169 }
2170
2171 this.isOpenType = false;
2172 }
2173
2174 if (!tables['maxp']) {
2175 throw new _util.FormatError('Required "maxp" table is not found');
2176 }
2177
2178 font.pos = (font.start || 0) + tables['maxp'].offset;
2179 var version = font.getInt32();
2180 var numGlyphs = font.getUint16();
2181 var numGlyphsOut = numGlyphs + 1;
2182 var dupFirstEntry = true;
2183
2184 if (numGlyphsOut > 0xFFFF) {
2185 dupFirstEntry = false;
2186 numGlyphsOut = numGlyphs;
2187 (0, _util.warn)('Not enough space in glyfs to duplicate first glyph.');
2188 }
2189
2190 var maxFunctionDefs = 0;
2191 var maxSizeOfInstructions = 0;
2192
2193 if (version >= 0x00010000 && tables['maxp'].length >= 22) {
2194 font.pos += 8;
2195 var maxZones = font.getUint16();
2196
2197 if (maxZones > 2) {
2198 tables['maxp'].data[14] = 0;
2199 tables['maxp'].data[15] = 2;
2200 }
2201
2202 font.pos += 4;
2203 maxFunctionDefs = font.getUint16();
2204 font.pos += 4;
2205 maxSizeOfInstructions = font.getUint16();
2206 }
2207
2208 tables['maxp'].data[4] = numGlyphsOut >> 8;
2209 tables['maxp'].data[5] = numGlyphsOut & 255;
2210 var hintsValid = sanitizeTTPrograms(tables['fpgm'], tables['prep'], tables['cvt '], maxFunctionDefs);
2211
2212 if (!hintsValid) {
2213 delete tables['fpgm'];
2214 delete tables['prep'];
2215 delete tables['cvt '];
2216 }
2217
2218 sanitizeMetrics(font, tables['hhea'], tables['hmtx'], numGlyphsOut);
2219
2220 if (!tables['head']) {
2221 throw new _util.FormatError('Required "head" table is not found');
2222 }
2223
2224 sanitizeHead(tables['head'], numGlyphs, isTrueType ? tables['loca'].length : 0);
2225 var missingGlyphs = Object.create(null);
2226
2227 if (isTrueType) {
2228 var isGlyphLocationsLong = int16(tables['head'].data[50], tables['head'].data[51]);
2229 var glyphsInfo = sanitizeGlyphLocations(tables['loca'], tables['glyf'], numGlyphs, isGlyphLocationsLong, hintsValid, dupFirstEntry, maxSizeOfInstructions);
2230 missingGlyphs = glyphsInfo.missingGlyphs;
2231
2232 if (version >= 0x00010000 && tables['maxp'].length >= 22) {
2233 tables['maxp'].data[26] = glyphsInfo.maxSizeOfInstructions >> 8;
2234 tables['maxp'].data[27] = glyphsInfo.maxSizeOfInstructions & 255;
2235 }
2236 }
2237
2238 if (!tables['hhea']) {
2239 throw new _util.FormatError('Required "hhea" table is not found');
2240 }
2241
2242 if (tables['hhea'].data[10] === 0 && tables['hhea'].data[11] === 0) {
2243 tables['hhea'].data[10] = 0xFF;
2244 tables['hhea'].data[11] = 0xFF;
2245 }
2246
2247 var metricsOverride = {
2248 unitsPerEm: int16(tables['head'].data[18], tables['head'].data[19]),
2249 yMax: int16(tables['head'].data[42], tables['head'].data[43]),
2250 yMin: signedInt16(tables['head'].data[38], tables['head'].data[39]),
2251 ascent: int16(tables['hhea'].data[4], tables['hhea'].data[5]),
2252 descent: signedInt16(tables['hhea'].data[6], tables['hhea'].data[7])
2253 };
2254 this.ascent = metricsOverride.ascent / metricsOverride.unitsPerEm;
2255 this.descent = metricsOverride.descent / metricsOverride.unitsPerEm;
2256
2257 if (tables['post']) {
2258 readPostScriptTable(tables['post'], properties, numGlyphs);
2259 }
2260
2261 tables['post'] = {
2262 tag: 'post',
2263 data: createPostTable(properties)
2264 };
2265 var charCodeToGlyphId = [],
2266 charCode;
2267
2268 function hasGlyph(glyphId) {
2269 return !missingGlyphs[glyphId];
2270 }
2271
2272 if (properties.composite) {
2273 var cidToGidMap = properties.cidToGidMap || [];
2274 var isCidToGidMapEmpty = cidToGidMap.length === 0;
2275 properties.cMap.forEach(function (charCode, cid) {
2276 if (cid > 0xffff) {
2277 throw new _util.FormatError('Max size of CID is 65,535');
2278 }
2279
2280 var glyphId = -1;
2281
2282 if (isCidToGidMapEmpty) {
2283 glyphId = cid;
2284 } else if (cidToGidMap[cid] !== undefined) {
2285 glyphId = cidToGidMap[cid];
2286 }
2287
2288 if (glyphId >= 0 && glyphId < numGlyphs && hasGlyph(glyphId)) {
2289 charCodeToGlyphId[charCode] = glyphId;
2290 }
2291 });
2292 } else {
2293 var cmapTable = readCmapTable(tables['cmap'], font, this.isSymbolicFont, properties.hasEncoding);
2294 var cmapPlatformId = cmapTable.platformId;
2295 var cmapEncodingId = cmapTable.encodingId;
2296 var cmapMappings = cmapTable.mappings;
2297 var cmapMappingsLength = cmapMappings.length;
2298
2299 if (properties.hasEncoding && (cmapPlatformId === 3 && cmapEncodingId === 1 || cmapPlatformId === 1 && cmapEncodingId === 0) || cmapPlatformId === -1 && cmapEncodingId === -1 && !!(0, _encodings.getEncoding)(properties.baseEncodingName)) {
2300 var baseEncoding = [];
2301
2302 if (properties.baseEncodingName === 'MacRomanEncoding' || properties.baseEncodingName === 'WinAnsiEncoding') {
2303 baseEncoding = (0, _encodings.getEncoding)(properties.baseEncodingName);
2304 }
2305
2306 var glyphsUnicodeMap = (0, _glyphlist.getGlyphsUnicode)();
2307
2308 for (charCode = 0; charCode < 256; charCode++) {
2309 var glyphName, standardGlyphName;
2310
2311 if (this.differences && charCode in this.differences) {
2312 glyphName = this.differences[charCode];
2313 } else if (charCode in baseEncoding && baseEncoding[charCode] !== '') {
2314 glyphName = baseEncoding[charCode];
2315 } else {
2316 glyphName = _encodings.StandardEncoding[charCode];
2317 }
2318
2319 if (!glyphName) {
2320 continue;
2321 }
2322
2323 standardGlyphName = recoverGlyphName(glyphName, glyphsUnicodeMap);
2324 var unicodeOrCharCode;
2325
2326 if (cmapPlatformId === 3 && cmapEncodingId === 1) {
2327 unicodeOrCharCode = glyphsUnicodeMap[standardGlyphName];
2328 } else if (cmapPlatformId === 1 && cmapEncodingId === 0) {
2329 unicodeOrCharCode = _encodings.MacRomanEncoding.indexOf(standardGlyphName);
2330 }
2331
2332 var found = false;
2333
2334 for (var i = 0; i < cmapMappingsLength; ++i) {
2335 if (cmapMappings[i].charCode !== unicodeOrCharCode) {
2336 continue;
2337 }
2338
2339 charCodeToGlyphId[charCode] = cmapMappings[i].glyphId;
2340 found = true;
2341 break;
2342 }
2343
2344 if (!found && properties.glyphNames) {
2345 var glyphId = properties.glyphNames.indexOf(glyphName);
2346
2347 if (glyphId === -1 && standardGlyphName !== glyphName) {
2348 glyphId = properties.glyphNames.indexOf(standardGlyphName);
2349 }
2350
2351 if (glyphId > 0 && hasGlyph(glyphId)) {
2352 charCodeToGlyphId[charCode] = glyphId;
2353 }
2354 }
2355 }
2356 } else if (cmapPlatformId === 0 && cmapEncodingId === 0) {
2357 for (var _i2 = 0; _i2 < cmapMappingsLength; ++_i2) {
2358 charCodeToGlyphId[cmapMappings[_i2].charCode] = cmapMappings[_i2].glyphId;
2359 }
2360 } else {
2361 for (var _i3 = 0; _i3 < cmapMappingsLength; ++_i3) {
2362 charCode = cmapMappings[_i3].charCode;
2363
2364 if (cmapPlatformId === 3 && charCode >= 0xF000 && charCode <= 0xF0FF) {
2365 charCode &= 0xFF;
2366 }
2367
2368 charCodeToGlyphId[charCode] = cmapMappings[_i3].glyphId;
2369 }
2370 }
2371 }
2372
2373 if (charCodeToGlyphId.length === 0) {
2374 charCodeToGlyphId[0] = 0;
2375 }
2376
2377 var glyphZeroId = numGlyphsOut - 1;
2378
2379 if (!dupFirstEntry) {
2380 glyphZeroId = 0;
2381 }
2382
2383 var newMapping = adjustMapping(charCodeToGlyphId, hasGlyph, glyphZeroId);
2384 this.toFontChar = newMapping.toFontChar;
2385 tables['cmap'] = {
2386 tag: 'cmap',
2387 data: createCmapTable(newMapping.charCodeToGlyphId, numGlyphsOut)
2388 };
2389
2390 if (!tables['OS/2'] || !validateOS2Table(tables['OS/2'])) {
2391 tables['OS/2'] = {
2392 tag: 'OS/2',
2393 data: createOS2Table(properties, newMapping.charCodeToGlyphId, metricsOverride)
2394 };
2395 }
2396
2397 if (!isTrueType) {
2398 try {
2399 cffFile = new _stream.Stream(tables['CFF '].data);
2400 var parser = new _cff_parser.CFFParser(cffFile, properties, SEAC_ANALYSIS_ENABLED);
2401 cff = parser.parse();
2402 cff.duplicateFirstGlyph();
2403 var compiler = new _cff_parser.CFFCompiler(cff);
2404 tables['CFF '].data = compiler.compile();
2405 } catch (e) {
2406 (0, _util.warn)('Failed to compile font ' + properties.loadedName);
2407 }
2408 }
2409
2410 if (!tables['name']) {
2411 tables['name'] = {
2412 tag: 'name',
2413 data: createNameTable(this.name)
2414 };
2415 } else {
2416 var namePrototype = readNameTable(tables['name']);
2417 tables['name'].data = createNameTable(name, namePrototype);
2418 }
2419
2420 var builder = new OpenTypeFileBuilder(header.version);
2421
2422 for (var tableTag in tables) {
2423 builder.addTable(tableTag, tables[tableTag].data);
2424 }
2425
2426 return builder.toArray();
2427 },
2428 convert: function Font_convert(fontName, font, properties) {
2429 properties.fixedPitch = false;
2430
2431 if (properties.builtInEncoding) {
2432 adjustToUnicode(properties, properties.builtInEncoding);
2433 }
2434
2435 var glyphZeroId = 1;
2436
2437 if (font instanceof CFFFont) {
2438 glyphZeroId = font.numGlyphs - 1;
2439 }
2440
2441 var mapping = font.getGlyphMapping(properties);
2442 var newMapping = adjustMapping(mapping, font.hasGlyphId.bind(font), glyphZeroId);
2443 this.toFontChar = newMapping.toFontChar;
2444 var numGlyphs = font.numGlyphs;
2445
2446 function getCharCodes(charCodeToGlyphId, glyphId) {
2447 var charCodes = null;
2448
2449 for (var charCode in charCodeToGlyphId) {
2450 if (glyphId === charCodeToGlyphId[charCode]) {
2451 if (!charCodes) {
2452 charCodes = [];
2453 }
2454
2455 charCodes.push(charCode | 0);
2456 }
2457 }
2458
2459 return charCodes;
2460 }
2461
2462 function createCharCode(charCodeToGlyphId, glyphId) {
2463 for (var charCode in charCodeToGlyphId) {
2464 if (glyphId === charCodeToGlyphId[charCode]) {
2465 return charCode | 0;
2466 }
2467 }
2468
2469 newMapping.charCodeToGlyphId[newMapping.nextAvailableFontCharCode] = glyphId;
2470 return newMapping.nextAvailableFontCharCode++;
2471 }
2472
2473 var seacs = font.seacs;
2474
2475 if (SEAC_ANALYSIS_ENABLED && seacs && seacs.length) {
2476 var matrix = properties.fontMatrix || _util.FONT_IDENTITY_MATRIX;
2477 var charset = font.getCharset();
2478 var seacMap = Object.create(null);
2479
2480 for (var glyphId in seacs) {
2481 glyphId |= 0;
2482 var seac = seacs[glyphId];
2483 var baseGlyphName = _encodings.StandardEncoding[seac[2]];
2484 var accentGlyphName = _encodings.StandardEncoding[seac[3]];
2485 var baseGlyphId = charset.indexOf(baseGlyphName);
2486 var accentGlyphId = charset.indexOf(accentGlyphName);
2487
2488 if (baseGlyphId < 0 || accentGlyphId < 0) {
2489 continue;
2490 }
2491
2492 var accentOffset = {
2493 x: seac[0] * matrix[0] + seac[1] * matrix[2] + matrix[4],
2494 y: seac[0] * matrix[1] + seac[1] * matrix[3] + matrix[5]
2495 };
2496 var charCodes = getCharCodes(mapping, glyphId);
2497
2498 if (!charCodes) {
2499 continue;
2500 }
2501
2502 for (var i = 0, ii = charCodes.length; i < ii; i++) {
2503 var charCode = charCodes[i];
2504 var charCodeToGlyphId = newMapping.charCodeToGlyphId;
2505 var baseFontCharCode = createCharCode(charCodeToGlyphId, baseGlyphId);
2506 var accentFontCharCode = createCharCode(charCodeToGlyphId, accentGlyphId);
2507 seacMap[charCode] = {
2508 baseFontCharCode: baseFontCharCode,
2509 accentFontCharCode: accentFontCharCode,
2510 accentOffset: accentOffset
2511 };
2512 }
2513 }
2514
2515 properties.seacMap = seacMap;
2516 }
2517
2518 var unitsPerEm = 1 / (properties.fontMatrix || _util.FONT_IDENTITY_MATRIX)[0];
2519 var builder = new OpenTypeFileBuilder('\x4F\x54\x54\x4F');
2520 builder.addTable('CFF ', font.data);
2521 builder.addTable('OS/2', createOS2Table(properties, newMapping.charCodeToGlyphId));
2522 builder.addTable('cmap', createCmapTable(newMapping.charCodeToGlyphId, numGlyphs));
2523 builder.addTable('head', '\x00\x01\x00\x00' + '\x00\x00\x10\x00' + '\x00\x00\x00\x00' + '\x5F\x0F\x3C\xF5' + '\x00\x00' + safeString16(unitsPerEm) + '\x00\x00\x00\x00\x9e\x0b\x7e\x27' + '\x00\x00\x00\x00\x9e\x0b\x7e\x27' + '\x00\x00' + safeString16(properties.descent) + '\x0F\xFF' + safeString16(properties.ascent) + string16(properties.italicAngle ? 2 : 0) + '\x00\x11' + '\x00\x00' + '\x00\x00' + '\x00\x00');
2524 builder.addTable('hhea', '\x00\x01\x00\x00' + safeString16(properties.ascent) + safeString16(properties.descent) + '\x00\x00' + '\xFF\xFF' + '\x00\x00' + '\x00\x00' + '\x00\x00' + safeString16(properties.capHeight) + safeString16(Math.tan(properties.italicAngle) * properties.xHeight) + '\x00\x00' + '\x00\x00' + '\x00\x00' + '\x00\x00' + '\x00\x00' + '\x00\x00' + string16(numGlyphs));
2525 builder.addTable('hmtx', function fontFieldsHmtx() {
2526 var charstrings = font.charstrings;
2527 var cffWidths = font.cff ? font.cff.widths : null;
2528 var hmtx = '\x00\x00\x00\x00';
2529
2530 for (var i = 1, ii = numGlyphs; i < ii; i++) {
2531 var width = 0;
2532
2533 if (charstrings) {
2534 var charstring = charstrings[i - 1];
2535 width = 'width' in charstring ? charstring.width : 0;
2536 } else if (cffWidths) {
2537 width = Math.ceil(cffWidths[i] || 0);
2538 }
2539
2540 hmtx += string16(width) + string16(0);
2541 }
2542
2543 return hmtx;
2544 }());
2545 builder.addTable('maxp', '\x00\x00\x50\x00' + string16(numGlyphs));
2546 builder.addTable('name', createNameTable(fontName));
2547 builder.addTable('post', createPostTable(properties));
2548 return builder.toArray();
2549 },
2550
2551 get spaceWidth() {
2552 if ('_shadowWidth' in this) {
2553 return this._shadowWidth;
2554 }
2555
2556 var possibleSpaceReplacements = ['space', 'minus', 'one', 'i', 'I'];
2557 var width;
2558
2559 for (var i = 0, ii = possibleSpaceReplacements.length; i < ii; i++) {
2560 var glyphName = possibleSpaceReplacements[i];
2561
2562 if (glyphName in this.widths) {
2563 width = this.widths[glyphName];
2564 break;
2565 }
2566
2567 var glyphsUnicodeMap = (0, _glyphlist.getGlyphsUnicode)();
2568 var glyphUnicode = glyphsUnicodeMap[glyphName];
2569 var charcode = 0;
2570
2571 if (this.composite) {
2572 if (this.cMap.contains(glyphUnicode)) {
2573 charcode = this.cMap.lookup(glyphUnicode);
2574 }
2575 }
2576
2577 if (!charcode && this.toUnicode) {
2578 charcode = this.toUnicode.charCodeOf(glyphUnicode);
2579 }
2580
2581 if (charcode <= 0) {
2582 charcode = glyphUnicode;
2583 }
2584
2585 width = this.widths[charcode];
2586
2587 if (width) {
2588 break;
2589 }
2590 }
2591
2592 width = width || this.defaultWidth;
2593 this._shadowWidth = width;
2594 return width;
2595 },
2596
2597 charToGlyph: function Font_charToGlyph(charcode, isSpace) {
2598 var fontCharCode, width, operatorListId;
2599 var widthCode = charcode;
2600
2601 if (this.cMap && this.cMap.contains(charcode)) {
2602 widthCode = this.cMap.lookup(charcode);
2603 }
2604
2605 width = this.widths[widthCode];
2606 width = (0, _util.isNum)(width) ? width : this.defaultWidth;
2607 var vmetric = this.vmetrics && this.vmetrics[widthCode];
2608 var unicode = this.toUnicode.get(charcode) || this.fallbackToUnicode.get(charcode) || charcode;
2609
2610 if (typeof unicode === 'number') {
2611 unicode = String.fromCharCode(unicode);
2612 }
2613
2614 var isInFont = charcode in this.toFontChar;
2615 fontCharCode = this.toFontChar[charcode] || charcode;
2616
2617 if (this.missingFile) {
2618 fontCharCode = (0, _unicode.mapSpecialUnicodeValues)(fontCharCode);
2619 }
2620
2621 if (this.isType3Font) {
2622 operatorListId = fontCharCode;
2623 }
2624
2625 var accent = null;
2626
2627 if (this.seacMap && this.seacMap[charcode]) {
2628 isInFont = true;
2629 var seac = this.seacMap[charcode];
2630 fontCharCode = seac.baseFontCharCode;
2631 accent = {
2632 fontChar: String.fromCodePoint(seac.accentFontCharCode),
2633 offset: seac.accentOffset
2634 };
2635 }
2636
2637 var fontChar = typeof fontCharCode === 'number' ? String.fromCodePoint(fontCharCode) : '';
2638 var glyph = this.glyphCache[charcode];
2639
2640 if (!glyph || !glyph.matchesForCache(fontChar, unicode, accent, width, vmetric, operatorListId, isSpace, isInFont)) {
2641 glyph = new Glyph(fontChar, unicode, accent, width, vmetric, operatorListId, isSpace, isInFont);
2642 this.glyphCache[charcode] = glyph;
2643 }
2644
2645 return glyph;
2646 },
2647 charsToGlyphs: function Font_charsToGlyphs(chars) {
2648 var charsCache = this.charsCache;
2649 var glyphs, glyph, charcode;
2650
2651 if (charsCache) {
2652 glyphs = charsCache[chars];
2653
2654 if (glyphs) {
2655 return glyphs;
2656 }
2657 }
2658
2659 if (!charsCache) {
2660 charsCache = this.charsCache = Object.create(null);
2661 }
2662
2663 glyphs = [];
2664 var charsCacheKey = chars;
2665 var i = 0,
2666 ii;
2667
2668 if (this.cMap) {
2669 var c = Object.create(null);
2670
2671 while (i < chars.length) {
2672 this.cMap.readCharCode(chars, i, c);
2673 charcode = c.charcode;
2674 var length = c.length;
2675 i += length;
2676 var isSpace = length === 1 && chars.charCodeAt(i - 1) === 0x20;
2677 glyph = this.charToGlyph(charcode, isSpace);
2678 glyphs.push(glyph);
2679 }
2680 } else {
2681 for (i = 0, ii = chars.length; i < ii; ++i) {
2682 charcode = chars.charCodeAt(i);
2683 glyph = this.charToGlyph(charcode, charcode === 0x20);
2684 glyphs.push(glyph);
2685 }
2686 }
2687
2688 return charsCache[charsCacheKey] = glyphs;
2689 },
2690
2691 get glyphCacheValues() {
2692 return Object.values(this.glyphCache);
2693 }
2694
2695 };
2696 return Font;
2697}();
2698
2699exports.Font = Font;
2700
2701var ErrorFont = function ErrorFontClosure() {
2702 function ErrorFont(error) {
2703 this.error = error;
2704 this.loadedName = 'g_font_error';
2705 this.missingFile = true;
2706 }
2707
2708 ErrorFont.prototype = {
2709 charsToGlyphs: function ErrorFont_charsToGlyphs() {
2710 return [];
2711 },
2712 exportData: function ErrorFont_exportData() {
2713 return {
2714 error: this.error
2715 };
2716 }
2717 };
2718 return ErrorFont;
2719}();
2720
2721exports.ErrorFont = ErrorFont;
2722
2723function type1FontGlyphMapping(properties, builtInEncoding, glyphNames) {
2724 var charCodeToGlyphId = Object.create(null);
2725 var glyphId, charCode, baseEncoding;
2726 var isSymbolicFont = !!(properties.flags & FontFlags.Symbolic);
2727
2728 if (properties.baseEncodingName) {
2729 baseEncoding = (0, _encodings.getEncoding)(properties.baseEncodingName);
2730
2731 for (charCode = 0; charCode < baseEncoding.length; charCode++) {
2732 glyphId = glyphNames.indexOf(baseEncoding[charCode]);
2733
2734 if (glyphId >= 0) {
2735 charCodeToGlyphId[charCode] = glyphId;
2736 } else {
2737 charCodeToGlyphId[charCode] = 0;
2738 }
2739 }
2740 } else if (isSymbolicFont) {
2741 for (charCode in builtInEncoding) {
2742 charCodeToGlyphId[charCode] = builtInEncoding[charCode];
2743 }
2744 } else {
2745 baseEncoding = _encodings.StandardEncoding;
2746
2747 for (charCode = 0; charCode < baseEncoding.length; charCode++) {
2748 glyphId = glyphNames.indexOf(baseEncoding[charCode]);
2749
2750 if (glyphId >= 0) {
2751 charCodeToGlyphId[charCode] = glyphId;
2752 } else {
2753 charCodeToGlyphId[charCode] = 0;
2754 }
2755 }
2756 }
2757
2758 var differences = properties.differences,
2759 glyphsUnicodeMap;
2760
2761 if (differences) {
2762 for (charCode in differences) {
2763 var glyphName = differences[charCode];
2764 glyphId = glyphNames.indexOf(glyphName);
2765
2766 if (glyphId === -1) {
2767 if (!glyphsUnicodeMap) {
2768 glyphsUnicodeMap = (0, _glyphlist.getGlyphsUnicode)();
2769 }
2770
2771 var standardGlyphName = recoverGlyphName(glyphName, glyphsUnicodeMap);
2772
2773 if (standardGlyphName !== glyphName) {
2774 glyphId = glyphNames.indexOf(standardGlyphName);
2775 }
2776 }
2777
2778 if (glyphId >= 0) {
2779 charCodeToGlyphId[charCode] = glyphId;
2780 } else {
2781 charCodeToGlyphId[charCode] = 0;
2782 }
2783 }
2784 }
2785
2786 return charCodeToGlyphId;
2787}
2788
2789var Type1Font = function Type1FontClosure() {
2790 function findBlock(streamBytes, signature, startIndex) {
2791 var streamBytesLength = streamBytes.length;
2792 var signatureLength = signature.length;
2793 var scanLength = streamBytesLength - signatureLength;
2794 var i = startIndex,
2795 j,
2796 found = false;
2797
2798 while (i < scanLength) {
2799 j = 0;
2800
2801 while (j < signatureLength && streamBytes[i + j] === signature[j]) {
2802 j++;
2803 }
2804
2805 if (j >= signatureLength) {
2806 i += j;
2807
2808 while (i < streamBytesLength && (0, _util.isSpace)(streamBytes[i])) {
2809 i++;
2810 }
2811
2812 found = true;
2813 break;
2814 }
2815
2816 i++;
2817 }
2818
2819 return {
2820 found: found,
2821 length: i
2822 };
2823 }
2824
2825 function getHeaderBlock(stream, suggestedLength) {
2826 var EEXEC_SIGNATURE = [0x65, 0x65, 0x78, 0x65, 0x63];
2827 var streamStartPos = stream.pos;
2828 var headerBytes, headerBytesLength, block;
2829
2830 try {
2831 headerBytes = stream.getBytes(suggestedLength);
2832 headerBytesLength = headerBytes.length;
2833 } catch (ex) {
2834 if (ex instanceof _util.MissingDataException) {
2835 throw ex;
2836 }
2837 }
2838
2839 if (headerBytesLength === suggestedLength) {
2840 block = findBlock(headerBytes, EEXEC_SIGNATURE, suggestedLength - 2 * EEXEC_SIGNATURE.length);
2841
2842 if (block.found && block.length === suggestedLength) {
2843 return {
2844 stream: new _stream.Stream(headerBytes),
2845 length: suggestedLength
2846 };
2847 }
2848 }
2849
2850 (0, _util.warn)('Invalid "Length1" property in Type1 font -- trying to recover.');
2851 stream.pos = streamStartPos;
2852 var SCAN_BLOCK_LENGTH = 2048;
2853 var actualLength;
2854
2855 while (true) {
2856 var scanBytes = stream.peekBytes(SCAN_BLOCK_LENGTH);
2857 block = findBlock(scanBytes, EEXEC_SIGNATURE, 0);
2858
2859 if (block.length === 0) {
2860 break;
2861 }
2862
2863 stream.pos += block.length;
2864
2865 if (block.found) {
2866 actualLength = stream.pos - streamStartPos;
2867 break;
2868 }
2869 }
2870
2871 stream.pos = streamStartPos;
2872
2873 if (actualLength) {
2874 return {
2875 stream: new _stream.Stream(stream.getBytes(actualLength)),
2876 length: actualLength
2877 };
2878 }
2879
2880 (0, _util.warn)('Unable to recover "Length1" property in Type1 font -- using as is.');
2881 return {
2882 stream: new _stream.Stream(stream.getBytes(suggestedLength)),
2883 length: suggestedLength
2884 };
2885 }
2886
2887 function getEexecBlock(stream, suggestedLength) {
2888 var eexecBytes = stream.getBytes();
2889 return {
2890 stream: new _stream.Stream(eexecBytes),
2891 length: eexecBytes.length
2892 };
2893 }
2894
2895 function Type1Font(name, file, properties) {
2896 var PFB_HEADER_SIZE = 6;
2897 var headerBlockLength = properties.length1;
2898 var eexecBlockLength = properties.length2;
2899 var pfbHeader = file.peekBytes(PFB_HEADER_SIZE);
2900 var pfbHeaderPresent = pfbHeader[0] === 0x80 && pfbHeader[1] === 0x01;
2901
2902 if (pfbHeaderPresent) {
2903 file.skip(PFB_HEADER_SIZE);
2904 headerBlockLength = pfbHeader[5] << 24 | pfbHeader[4] << 16 | pfbHeader[3] << 8 | pfbHeader[2];
2905 }
2906
2907 var headerBlock = getHeaderBlock(file, headerBlockLength);
2908 var headerBlockParser = new _type1_parser.Type1Parser(headerBlock.stream, false, SEAC_ANALYSIS_ENABLED);
2909 headerBlockParser.extractFontHeader(properties);
2910
2911 if (pfbHeaderPresent) {
2912 pfbHeader = file.getBytes(PFB_HEADER_SIZE);
2913 eexecBlockLength = pfbHeader[5] << 24 | pfbHeader[4] << 16 | pfbHeader[3] << 8 | pfbHeader[2];
2914 }
2915
2916 var eexecBlock = getEexecBlock(file, eexecBlockLength);
2917 var eexecBlockParser = new _type1_parser.Type1Parser(eexecBlock.stream, true, SEAC_ANALYSIS_ENABLED);
2918 var data = eexecBlockParser.extractFontProgram();
2919
2920 for (var info in data.properties) {
2921 properties[info] = data.properties[info];
2922 }
2923
2924 var charstrings = data.charstrings;
2925 var type2Charstrings = this.getType2Charstrings(charstrings);
2926 var subrs = this.getType2Subrs(data.subrs);
2927 this.charstrings = charstrings;
2928 this.data = this.wrap(name, type2Charstrings, this.charstrings, subrs, properties);
2929 this.seacs = this.getSeacs(data.charstrings);
2930 }
2931
2932 Type1Font.prototype = {
2933 get numGlyphs() {
2934 return this.charstrings.length + 1;
2935 },
2936
2937 getCharset: function Type1Font_getCharset() {
2938 var charset = ['.notdef'];
2939 var charstrings = this.charstrings;
2940
2941 for (var glyphId = 0; glyphId < charstrings.length; glyphId++) {
2942 charset.push(charstrings[glyphId].glyphName);
2943 }
2944
2945 return charset;
2946 },
2947 getGlyphMapping: function Type1Font_getGlyphMapping(properties) {
2948 var charstrings = this.charstrings;
2949 var glyphNames = ['.notdef'],
2950 glyphId;
2951
2952 for (glyphId = 0; glyphId < charstrings.length; glyphId++) {
2953 glyphNames.push(charstrings[glyphId].glyphName);
2954 }
2955
2956 var encoding = properties.builtInEncoding;
2957
2958 if (encoding) {
2959 var builtInEncoding = Object.create(null);
2960
2961 for (var charCode in encoding) {
2962 glyphId = glyphNames.indexOf(encoding[charCode]);
2963
2964 if (glyphId >= 0) {
2965 builtInEncoding[charCode] = glyphId;
2966 }
2967 }
2968 }
2969
2970 return type1FontGlyphMapping(properties, builtInEncoding, glyphNames);
2971 },
2972 hasGlyphId: function Type1Font_hasGlyphID(id) {
2973 if (id < 0 || id >= this.numGlyphs) {
2974 return false;
2975 }
2976
2977 if (id === 0) {
2978 return true;
2979 }
2980
2981 var glyph = this.charstrings[id - 1];
2982 return glyph.charstring.length > 0;
2983 },
2984 getSeacs: function Type1Font_getSeacs(charstrings) {
2985 var i, ii;
2986 var seacMap = [];
2987
2988 for (i = 0, ii = charstrings.length; i < ii; i++) {
2989 var charstring = charstrings[i];
2990
2991 if (charstring.seac) {
2992 seacMap[i + 1] = charstring.seac;
2993 }
2994 }
2995
2996 return seacMap;
2997 },
2998 getType2Charstrings: function Type1Font_getType2Charstrings(type1Charstrings) {
2999 var type2Charstrings = [];
3000
3001 for (var i = 0, ii = type1Charstrings.length; i < ii; i++) {
3002 type2Charstrings.push(type1Charstrings[i].charstring);
3003 }
3004
3005 return type2Charstrings;
3006 },
3007 getType2Subrs: function Type1Font_getType2Subrs(type1Subrs) {
3008 var bias = 0;
3009 var count = type1Subrs.length;
3010
3011 if (count < 1133) {
3012 bias = 107;
3013 } else if (count < 33769) {
3014 bias = 1131;
3015 } else {
3016 bias = 32768;
3017 }
3018
3019 var type2Subrs = [];
3020 var i;
3021
3022 for (i = 0; i < bias; i++) {
3023 type2Subrs.push([0x0B]);
3024 }
3025
3026 for (i = 0; i < count; i++) {
3027 type2Subrs.push(type1Subrs[i]);
3028 }
3029
3030 return type2Subrs;
3031 },
3032 wrap: function Type1Font_wrap(name, glyphs, charstrings, subrs, properties) {
3033 var cff = new _cff_parser.CFF();
3034 cff.header = new _cff_parser.CFFHeader(1, 0, 4, 4);
3035 cff.names = [name];
3036 var topDict = new _cff_parser.CFFTopDict();
3037 topDict.setByName('version', 391);
3038 topDict.setByName('Notice', 392);
3039 topDict.setByName('FullName', 393);
3040 topDict.setByName('FamilyName', 394);
3041 topDict.setByName('Weight', 395);
3042 topDict.setByName('Encoding', null);
3043 topDict.setByName('FontMatrix', properties.fontMatrix);
3044 topDict.setByName('FontBBox', properties.bbox);
3045 topDict.setByName('charset', null);
3046 topDict.setByName('CharStrings', null);
3047 topDict.setByName('Private', null);
3048 cff.topDict = topDict;
3049 var strings = new _cff_parser.CFFStrings();
3050 strings.add('Version 0.11');
3051 strings.add('See original notice');
3052 strings.add(name);
3053 strings.add(name);
3054 strings.add('Medium');
3055 cff.strings = strings;
3056 cff.globalSubrIndex = new _cff_parser.CFFIndex();
3057 var count = glyphs.length;
3058 var charsetArray = [0];
3059 var i, ii;
3060
3061 for (i = 0; i < count; i++) {
3062 var index = _cff_parser.CFFStandardStrings.indexOf(charstrings[i].glyphName);
3063
3064 if (index === -1) {
3065 index = 0;
3066 }
3067
3068 charsetArray.push(index >> 8 & 0xff, index & 0xff);
3069 }
3070
3071 cff.charset = new _cff_parser.CFFCharset(false, 0, [], charsetArray);
3072 var charStringsIndex = new _cff_parser.CFFIndex();
3073 charStringsIndex.add([0x8B, 0x0E]);
3074
3075 for (i = 0; i < count; i++) {
3076 charStringsIndex.add(glyphs[i]);
3077 }
3078
3079 cff.charStrings = charStringsIndex;
3080 var privateDict = new _cff_parser.CFFPrivateDict();
3081 privateDict.setByName('Subrs', null);
3082 var fields = ['BlueValues', 'OtherBlues', 'FamilyBlues', 'FamilyOtherBlues', 'StemSnapH', 'StemSnapV', 'BlueShift', 'BlueFuzz', 'BlueScale', 'LanguageGroup', 'ExpansionFactor', 'ForceBold', 'StdHW', 'StdVW'];
3083
3084 for (i = 0, ii = fields.length; i < ii; i++) {
3085 var field = fields[i];
3086
3087 if (!(field in properties.privateData)) {
3088 continue;
3089 }
3090
3091 var value = properties.privateData[field];
3092
3093 if (Array.isArray(value)) {
3094 for (var j = value.length - 1; j > 0; j--) {
3095 value[j] -= value[j - 1];
3096 }
3097 }
3098
3099 privateDict.setByName(field, value);
3100 }
3101
3102 cff.topDict.privateDict = privateDict;
3103 var subrIndex = new _cff_parser.CFFIndex();
3104
3105 for (i = 0, ii = subrs.length; i < ii; i++) {
3106 subrIndex.add(subrs[i]);
3107 }
3108
3109 privateDict.subrsIndex = subrIndex;
3110 var compiler = new _cff_parser.CFFCompiler(cff);
3111 return compiler.compile();
3112 }
3113 };
3114 return Type1Font;
3115}();
3116
3117var CFFFont = function CFFFontClosure() {
3118 function CFFFont(file, properties) {
3119 this.properties = properties;
3120 var parser = new _cff_parser.CFFParser(file, properties, SEAC_ANALYSIS_ENABLED);
3121 this.cff = parser.parse();
3122 this.cff.duplicateFirstGlyph();
3123 var compiler = new _cff_parser.CFFCompiler(this.cff);
3124 this.seacs = this.cff.seacs;
3125
3126 try {
3127 this.data = compiler.compile();
3128 } catch (e) {
3129 (0, _util.warn)('Failed to compile font ' + properties.loadedName);
3130 this.data = file;
3131 }
3132 }
3133
3134 CFFFont.prototype = {
3135 get numGlyphs() {
3136 return this.cff.charStrings.count;
3137 },
3138
3139 getCharset: function CFFFont_getCharset() {
3140 return this.cff.charset.charset;
3141 },
3142 getGlyphMapping: function CFFFont_getGlyphMapping() {
3143 var cff = this.cff;
3144 var properties = this.properties;
3145 var charsets = cff.charset.charset;
3146 var charCodeToGlyphId;
3147 var glyphId;
3148
3149 if (properties.composite) {
3150 charCodeToGlyphId = Object.create(null);
3151
3152 if (cff.isCIDFont) {
3153 for (glyphId = 0; glyphId < charsets.length; glyphId++) {
3154 var cid = charsets[glyphId];
3155 var charCode = properties.cMap.charCodeOf(cid);
3156 charCodeToGlyphId[charCode] = glyphId;
3157 }
3158 } else {
3159 for (glyphId = 0; glyphId < cff.charStrings.count; glyphId++) {
3160 charCodeToGlyphId[glyphId] = glyphId;
3161 }
3162 }
3163
3164 return charCodeToGlyphId;
3165 }
3166
3167 var encoding = cff.encoding ? cff.encoding.encoding : null;
3168 charCodeToGlyphId = type1FontGlyphMapping(properties, encoding, charsets);
3169 return charCodeToGlyphId;
3170 },
3171 hasGlyphId: function CFFFont_hasGlyphID(id) {
3172 return this.cff.hasGlyphId(id);
3173 }
3174 };
3175 return CFFFont;
3176}();
\No newline at end of file