UNPKG

53.4 kBJavaScriptView Raw
1/**
2 * @licstart The following is the entire license notice for the
3 * Javascript code in this page
4 *
5 * Copyright 2017 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.CFFCompiler = exports.CFFPrivateDict = exports.CFFTopDict = exports.CFFCharset = exports.CFFIndex = exports.CFFStrings = exports.CFFHeader = exports.CFF = exports.CFFParser = exports.CFFStandardStrings = undefined;
28
29var _util = require('../shared/util');
30
31var _charsets = require('./charsets');
32
33var _encodings = require('./encodings');
34
35var MAX_SUBR_NESTING = 10;
36var CFFStandardStrings = ['.notdef', 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent', 'ampersand', 'quoteright', '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', 'quoteleft', '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', 'exclamdown', 'cent', 'sterling', 'fraction', 'yen', 'florin', 'section', 'currency', 'quotesingle', 'quotedblleft', 'guillemotleft', 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'endash', 'dagger', 'daggerdbl', 'periodcentered', 'paragraph', 'bullet', 'quotesinglbase', 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand', 'questiondown', 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve', 'dotaccent', 'dieresis', 'ring', 'cedilla', 'hungarumlaut', 'ogonek', 'caron', 'emdash', 'AE', 'ordfeminine', 'Lslash', 'Oslash', 'OE', 'ordmasculine', 'ae', 'dotlessi', 'lslash', 'oslash', 'oe', 'germandbls', 'onesuperior', 'logicalnot', 'mu', 'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn', 'onequarter', 'divide', 'brokenbar', 'degree', 'thorn', 'threequarters', 'twosuperior', 'registered', 'minus', 'eth', 'multiply', 'threesuperior', 'copyright', 'Aacute', 'Acircumflex', 'Adieresis', 'Agrave', 'Aring', 'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex', 'Edieresis', 'Egrave', 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute', 'Ocircumflex', 'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute', 'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute', 'Ydieresis', 'Zcaron', 'aacute', 'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla', 'eacute', 'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex', 'idieresis', 'igrave', 'ntilde', 'oacute', 'ocircumflex', 'odieresis', 'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis', 'ugrave', 'yacute', 'ydieresis', 'zcaron', 'exclamsmall', 'Hungarumlautsmall', 'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', 'Acutesmall', 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader', 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle', 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle', 'nineoldstyle', 'commasuperior', 'threequartersemdash', 'periodsuperior', 'questionsmall', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior', 'esuperior', 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior', 'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'ffi', 'ffl', 'parenleftinferior', 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall', 'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall', 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall', 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', 'Xsmall', 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall', 'exclamdownsmall', 'centoldstyle', 'Lslashsmall', 'Scaronsmall', 'Zcaronsmall', 'Dieresissmall', 'Brevesmall', 'Caronsmall', 'Dotaccentsmall', 'Macronsmall', 'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall', 'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths', 'onethird', 'twothirds', 'zerosuperior', 'foursuperior', 'fivesuperior', 'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior', 'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior', 'sixinferior', 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior', 'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall', 'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall', 'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall', 'Edieresissmall', 'Igravesmall', 'Iacutesmall', 'Icircumflexsmall', 'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall', 'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall', 'Ugravesmall', 'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall', 'Yacutesmall', 'Thornsmall', 'Ydieresissmall', '001.000', '001.001', '001.002', '001.003', 'Black', 'Bold', 'Book', 'Light', 'Medium', 'Regular', 'Roman', 'Semibold'];
37var CFFParser = function CFFParserClosure() {
38 var CharstringValidationData = [null, {
39 id: 'hstem',
40 min: 2,
41 stackClearing: true,
42 stem: true
43 }, null, {
44 id: 'vstem',
45 min: 2,
46 stackClearing: true,
47 stem: true
48 }, {
49 id: 'vmoveto',
50 min: 1,
51 stackClearing: true
52 }, {
53 id: 'rlineto',
54 min: 2,
55 resetStack: true
56 }, {
57 id: 'hlineto',
58 min: 1,
59 resetStack: true
60 }, {
61 id: 'vlineto',
62 min: 1,
63 resetStack: true
64 }, {
65 id: 'rrcurveto',
66 min: 6,
67 resetStack: true
68 }, null, {
69 id: 'callsubr',
70 min: 1,
71 undefStack: true
72 }, {
73 id: 'return',
74 min: 0,
75 undefStack: true
76 }, null, null, {
77 id: 'endchar',
78 min: 0,
79 stackClearing: true
80 }, null, null, null, {
81 id: 'hstemhm',
82 min: 2,
83 stackClearing: true,
84 stem: true
85 }, {
86 id: 'hintmask',
87 min: 0,
88 stackClearing: true
89 }, {
90 id: 'cntrmask',
91 min: 0,
92 stackClearing: true
93 }, {
94 id: 'rmoveto',
95 min: 2,
96 stackClearing: true
97 }, {
98 id: 'hmoveto',
99 min: 1,
100 stackClearing: true
101 }, {
102 id: 'vstemhm',
103 min: 2,
104 stackClearing: true,
105 stem: true
106 }, {
107 id: 'rcurveline',
108 min: 8,
109 resetStack: true
110 }, {
111 id: 'rlinecurve',
112 min: 8,
113 resetStack: true
114 }, {
115 id: 'vvcurveto',
116 min: 4,
117 resetStack: true
118 }, {
119 id: 'hhcurveto',
120 min: 4,
121 resetStack: true
122 }, null, {
123 id: 'callgsubr',
124 min: 1,
125 undefStack: true
126 }, {
127 id: 'vhcurveto',
128 min: 4,
129 resetStack: true
130 }, {
131 id: 'hvcurveto',
132 min: 4,
133 resetStack: true
134 }];
135 var CharstringValidationData12 = [null, null, null, {
136 id: 'and',
137 min: 2,
138 stackDelta: -1
139 }, {
140 id: 'or',
141 min: 2,
142 stackDelta: -1
143 }, {
144 id: 'not',
145 min: 1,
146 stackDelta: 0
147 }, null, null, null, {
148 id: 'abs',
149 min: 1,
150 stackDelta: 0
151 }, {
152 id: 'add',
153 min: 2,
154 stackDelta: -1,
155 stackFn: function stack_div(stack, index) {
156 stack[index - 2] = stack[index - 2] + stack[index - 1];
157 }
158 }, {
159 id: 'sub',
160 min: 2,
161 stackDelta: -1,
162 stackFn: function stack_div(stack, index) {
163 stack[index - 2] = stack[index - 2] - stack[index - 1];
164 }
165 }, {
166 id: 'div',
167 min: 2,
168 stackDelta: -1,
169 stackFn: function stack_div(stack, index) {
170 stack[index - 2] = stack[index - 2] / stack[index - 1];
171 }
172 }, null, {
173 id: 'neg',
174 min: 1,
175 stackDelta: 0,
176 stackFn: function stack_div(stack, index) {
177 stack[index - 1] = -stack[index - 1];
178 }
179 }, {
180 id: 'eq',
181 min: 2,
182 stackDelta: -1
183 }, null, null, {
184 id: 'drop',
185 min: 1,
186 stackDelta: -1
187 }, null, {
188 id: 'put',
189 min: 2,
190 stackDelta: -2
191 }, {
192 id: 'get',
193 min: 1,
194 stackDelta: 0
195 }, {
196 id: 'ifelse',
197 min: 4,
198 stackDelta: -3
199 }, {
200 id: 'random',
201 min: 0,
202 stackDelta: 1
203 }, {
204 id: 'mul',
205 min: 2,
206 stackDelta: -1,
207 stackFn: function stack_div(stack, index) {
208 stack[index - 2] = stack[index - 2] * stack[index - 1];
209 }
210 }, null, {
211 id: 'sqrt',
212 min: 1,
213 stackDelta: 0
214 }, {
215 id: 'dup',
216 min: 1,
217 stackDelta: 1
218 }, {
219 id: 'exch',
220 min: 2,
221 stackDelta: 0
222 }, {
223 id: 'index',
224 min: 2,
225 stackDelta: 0
226 }, {
227 id: 'roll',
228 min: 3,
229 stackDelta: -2
230 }, null, null, null, {
231 id: 'hflex',
232 min: 7,
233 resetStack: true
234 }, {
235 id: 'flex',
236 min: 13,
237 resetStack: true
238 }, {
239 id: 'hflex1',
240 min: 9,
241 resetStack: true
242 }, {
243 id: 'flex1',
244 min: 11,
245 resetStack: true
246 }];
247 function CFFParser(file, properties, seacAnalysisEnabled) {
248 this.bytes = file.getBytes();
249 this.properties = properties;
250 this.seacAnalysisEnabled = !!seacAnalysisEnabled;
251 }
252 CFFParser.prototype = {
253 parse: function CFFParser_parse() {
254 var properties = this.properties;
255 var cff = new CFF();
256 this.cff = cff;
257 var header = this.parseHeader();
258 var nameIndex = this.parseIndex(header.endPos);
259 var topDictIndex = this.parseIndex(nameIndex.endPos);
260 var stringIndex = this.parseIndex(topDictIndex.endPos);
261 var globalSubrIndex = this.parseIndex(stringIndex.endPos);
262 var topDictParsed = this.parseDict(topDictIndex.obj.get(0));
263 var topDict = this.createDict(CFFTopDict, topDictParsed, cff.strings);
264 cff.header = header.obj;
265 cff.names = this.parseNameIndex(nameIndex.obj);
266 cff.strings = this.parseStringIndex(stringIndex.obj);
267 cff.topDict = topDict;
268 cff.globalSubrIndex = globalSubrIndex.obj;
269 this.parsePrivateDict(cff.topDict);
270 cff.isCIDFont = topDict.hasName('ROS');
271 var charStringOffset = topDict.getByName('CharStrings');
272 var charStringIndex = this.parseIndex(charStringOffset).obj;
273 var fontMatrix = topDict.getByName('FontMatrix');
274 if (fontMatrix) {
275 properties.fontMatrix = fontMatrix;
276 }
277 var fontBBox = topDict.getByName('FontBBox');
278 if (fontBBox) {
279 properties.ascent = Math.max(fontBBox[3], fontBBox[1]);
280 properties.descent = Math.min(fontBBox[1], fontBBox[3]);
281 properties.ascentScaled = true;
282 }
283 var charset, encoding;
284 if (cff.isCIDFont) {
285 var fdArrayIndex = this.parseIndex(topDict.getByName('FDArray')).obj;
286 for (var i = 0, ii = fdArrayIndex.count; i < ii; ++i) {
287 var dictRaw = fdArrayIndex.get(i);
288 var fontDict = this.createDict(CFFTopDict, this.parseDict(dictRaw), cff.strings);
289 this.parsePrivateDict(fontDict);
290 cff.fdArray.push(fontDict);
291 }
292 encoding = null;
293 charset = this.parseCharsets(topDict.getByName('charset'), charStringIndex.count, cff.strings, true);
294 cff.fdSelect = this.parseFDSelect(topDict.getByName('FDSelect'), charStringIndex.count);
295 } else {
296 charset = this.parseCharsets(topDict.getByName('charset'), charStringIndex.count, cff.strings, false);
297 encoding = this.parseEncoding(topDict.getByName('Encoding'), properties, cff.strings, charset.charset);
298 }
299 cff.charset = charset;
300 cff.encoding = encoding;
301 var charStringsAndSeacs = this.parseCharStrings({
302 charStrings: charStringIndex,
303 localSubrIndex: topDict.privateDict.subrsIndex,
304 globalSubrIndex: globalSubrIndex.obj,
305 fdSelect: cff.fdSelect,
306 fdArray: cff.fdArray,
307 privateDict: topDict.privateDict
308 });
309 cff.charStrings = charStringsAndSeacs.charStrings;
310 cff.seacs = charStringsAndSeacs.seacs;
311 cff.widths = charStringsAndSeacs.widths;
312 return cff;
313 },
314 parseHeader: function CFFParser_parseHeader() {
315 var bytes = this.bytes;
316 var bytesLength = bytes.length;
317 var offset = 0;
318 while (offset < bytesLength && bytes[offset] !== 1) {
319 ++offset;
320 }
321 if (offset >= bytesLength) {
322 throw new _util.FormatError('Invalid CFF header');
323 }
324 if (offset !== 0) {
325 (0, _util.info)('cff data is shifted');
326 bytes = bytes.subarray(offset);
327 this.bytes = bytes;
328 }
329 var major = bytes[0];
330 var minor = bytes[1];
331 var hdrSize = bytes[2];
332 var offSize = bytes[3];
333 var header = new CFFHeader(major, minor, hdrSize, offSize);
334 return {
335 obj: header,
336 endPos: hdrSize
337 };
338 },
339 parseDict: function CFFParser_parseDict(dict) {
340 var pos = 0;
341 function parseOperand() {
342 var value = dict[pos++];
343 if (value === 30) {
344 return parseFloatOperand();
345 } else if (value === 28) {
346 value = dict[pos++];
347 value = (value << 24 | dict[pos++] << 16) >> 16;
348 return value;
349 } else if (value === 29) {
350 value = dict[pos++];
351 value = value << 8 | dict[pos++];
352 value = value << 8 | dict[pos++];
353 value = value << 8 | dict[pos++];
354 return value;
355 } else if (value >= 32 && value <= 246) {
356 return value - 139;
357 } else if (value >= 247 && value <= 250) {
358 return (value - 247) * 256 + dict[pos++] + 108;
359 } else if (value >= 251 && value <= 254) {
360 return -((value - 251) * 256) - dict[pos++] - 108;
361 }
362 (0, _util.warn)('CFFParser_parseDict: "' + value + '" is a reserved command.');
363 return NaN;
364 }
365 function parseFloatOperand() {
366 var str = '';
367 var eof = 15;
368 var lookup = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', 'E', 'E-', null, '-'];
369 var length = dict.length;
370 while (pos < length) {
371 var b = dict[pos++];
372 var b1 = b >> 4;
373 var b2 = b & 15;
374 if (b1 === eof) {
375 break;
376 }
377 str += lookup[b1];
378 if (b2 === eof) {
379 break;
380 }
381 str += lookup[b2];
382 }
383 return parseFloat(str);
384 }
385 var operands = [];
386 var entries = [];
387 pos = 0;
388 var end = dict.length;
389 while (pos < end) {
390 var b = dict[pos];
391 if (b <= 21) {
392 if (b === 12) {
393 b = b << 8 | dict[++pos];
394 }
395 entries.push([b, operands]);
396 operands = [];
397 ++pos;
398 } else {
399 operands.push(parseOperand());
400 }
401 }
402 return entries;
403 },
404 parseIndex: function CFFParser_parseIndex(pos) {
405 var cffIndex = new CFFIndex();
406 var bytes = this.bytes;
407 var count = bytes[pos++] << 8 | bytes[pos++];
408 var offsets = [];
409 var end = pos;
410 var i, ii;
411 if (count !== 0) {
412 var offsetSize = bytes[pos++];
413 var startPos = pos + (count + 1) * offsetSize - 1;
414 for (i = 0, ii = count + 1; i < ii; ++i) {
415 var offset = 0;
416 for (var j = 0; j < offsetSize; ++j) {
417 offset <<= 8;
418 offset += bytes[pos++];
419 }
420 offsets.push(startPos + offset);
421 }
422 end = offsets[count];
423 }
424 for (i = 0, ii = offsets.length - 1; i < ii; ++i) {
425 var offsetStart = offsets[i];
426 var offsetEnd = offsets[i + 1];
427 cffIndex.add(bytes.subarray(offsetStart, offsetEnd));
428 }
429 return {
430 obj: cffIndex,
431 endPos: end
432 };
433 },
434 parseNameIndex: function CFFParser_parseNameIndex(index) {
435 var names = [];
436 for (var i = 0, ii = index.count; i < ii; ++i) {
437 var name = index.get(i);
438 names.push((0, _util.bytesToString)(name));
439 }
440 return names;
441 },
442 parseStringIndex: function CFFParser_parseStringIndex(index) {
443 var strings = new CFFStrings();
444 for (var i = 0, ii = index.count; i < ii; ++i) {
445 var data = index.get(i);
446 strings.add((0, _util.bytesToString)(data));
447 }
448 return strings;
449 },
450 createDict: function CFFParser_createDict(Type, dict, strings) {
451 var cffDict = new Type(strings);
452 for (var i = 0, ii = dict.length; i < ii; ++i) {
453 var pair = dict[i];
454 var key = pair[0];
455 var value = pair[1];
456 cffDict.setByKey(key, value);
457 }
458 return cffDict;
459 },
460 parseCharString: function CFFParser_parseCharString(state, data, localSubrIndex, globalSubrIndex) {
461 if (!data || state.callDepth > MAX_SUBR_NESTING) {
462 return false;
463 }
464 var stackSize = state.stackSize;
465 var stack = state.stack;
466 var length = data.length;
467 for (var j = 0; j < length;) {
468 var value = data[j++];
469 var validationCommand = null;
470 if (value === 12) {
471 var q = data[j++];
472 if (q === 0) {
473 data[j - 2] = 139;
474 data[j - 1] = 22;
475 stackSize = 0;
476 } else {
477 validationCommand = CharstringValidationData12[q];
478 }
479 } else if (value === 28) {
480 stack[stackSize] = (data[j] << 24 | data[j + 1] << 16) >> 16;
481 j += 2;
482 stackSize++;
483 } else if (value === 14) {
484 if (stackSize >= 4) {
485 stackSize -= 4;
486 if (this.seacAnalysisEnabled) {
487 state.seac = stack.slice(stackSize, stackSize + 4);
488 return false;
489 }
490 }
491 validationCommand = CharstringValidationData[value];
492 } else if (value >= 32 && value <= 246) {
493 stack[stackSize] = value - 139;
494 stackSize++;
495 } else if (value >= 247 && value <= 254) {
496 stack[stackSize] = value < 251 ? (value - 247 << 8) + data[j] + 108 : -(value - 251 << 8) - data[j] - 108;
497 j++;
498 stackSize++;
499 } else if (value === 255) {
500 stack[stackSize] = (data[j] << 24 | data[j + 1] << 16 | data[j + 2] << 8 | data[j + 3]) / 65536;
501 j += 4;
502 stackSize++;
503 } else if (value === 19 || value === 20) {
504 state.hints += stackSize >> 1;
505 j += state.hints + 7 >> 3;
506 stackSize %= 2;
507 validationCommand = CharstringValidationData[value];
508 } else if (value === 10 || value === 29) {
509 var subrsIndex;
510 if (value === 10) {
511 subrsIndex = localSubrIndex;
512 } else {
513 subrsIndex = globalSubrIndex;
514 }
515 if (!subrsIndex) {
516 validationCommand = CharstringValidationData[value];
517 (0, _util.warn)('Missing subrsIndex for ' + validationCommand.id);
518 return false;
519 }
520 var bias = 32768;
521 if (subrsIndex.count < 1240) {
522 bias = 107;
523 } else if (subrsIndex.count < 33900) {
524 bias = 1131;
525 }
526 var subrNumber = stack[--stackSize] + bias;
527 if (subrNumber < 0 || subrNumber >= subrsIndex.count || isNaN(subrNumber)) {
528 validationCommand = CharstringValidationData[value];
529 (0, _util.warn)('Out of bounds subrIndex for ' + validationCommand.id);
530 return false;
531 }
532 state.stackSize = stackSize;
533 state.callDepth++;
534 var valid = this.parseCharString(state, subrsIndex.get(subrNumber), localSubrIndex, globalSubrIndex);
535 if (!valid) {
536 return false;
537 }
538 state.callDepth--;
539 stackSize = state.stackSize;
540 continue;
541 } else if (value === 11) {
542 state.stackSize = stackSize;
543 return true;
544 } else {
545 validationCommand = CharstringValidationData[value];
546 }
547 if (validationCommand) {
548 if (validationCommand.stem) {
549 state.hints += stackSize >> 1;
550 }
551 if ('min' in validationCommand) {
552 if (!state.undefStack && stackSize < validationCommand.min) {
553 (0, _util.warn)('Not enough parameters for ' + validationCommand.id + '; actual: ' + stackSize + ', expected: ' + validationCommand.min);
554 return false;
555 }
556 }
557 if (state.firstStackClearing && validationCommand.stackClearing) {
558 state.firstStackClearing = false;
559 stackSize -= validationCommand.min;
560 if (stackSize >= 2 && validationCommand.stem) {
561 stackSize %= 2;
562 } else if (stackSize > 1) {
563 (0, _util.warn)('Found too many parameters for stack-clearing command');
564 }
565 if (stackSize > 0 && stack[stackSize - 1] >= 0) {
566 state.width = stack[stackSize - 1];
567 }
568 }
569 if ('stackDelta' in validationCommand) {
570 if ('stackFn' in validationCommand) {
571 validationCommand.stackFn(stack, stackSize);
572 }
573 stackSize += validationCommand.stackDelta;
574 } else if (validationCommand.stackClearing) {
575 stackSize = 0;
576 } else if (validationCommand.resetStack) {
577 stackSize = 0;
578 state.undefStack = false;
579 } else if (validationCommand.undefStack) {
580 stackSize = 0;
581 state.undefStack = true;
582 state.firstStackClearing = false;
583 }
584 }
585 }
586 state.stackSize = stackSize;
587 return true;
588 },
589 parseCharStrings: function parseCharStrings(_ref) {
590 var charStrings = _ref.charStrings,
591 localSubrIndex = _ref.localSubrIndex,
592 globalSubrIndex = _ref.globalSubrIndex,
593 fdSelect = _ref.fdSelect,
594 fdArray = _ref.fdArray,
595 privateDict = _ref.privateDict;
596
597 var seacs = [];
598 var widths = [];
599 var count = charStrings.count;
600 for (var i = 0; i < count; i++) {
601 var charstring = charStrings.get(i);
602 var state = {
603 callDepth: 0,
604 stackSize: 0,
605 stack: [],
606 undefStack: true,
607 hints: 0,
608 firstStackClearing: true,
609 seac: null,
610 width: null
611 };
612 var valid = true;
613 var localSubrToUse = null;
614 var privateDictToUse = privateDict;
615 if (fdSelect && fdArray.length) {
616 var fdIndex = fdSelect.getFDIndex(i);
617 if (fdIndex === -1) {
618 (0, _util.warn)('Glyph index is not in fd select.');
619 valid = false;
620 }
621 if (fdIndex >= fdArray.length) {
622 (0, _util.warn)('Invalid fd index for glyph index.');
623 valid = false;
624 }
625 if (valid) {
626 privateDictToUse = fdArray[fdIndex].privateDict;
627 localSubrToUse = privateDictToUse.subrsIndex;
628 }
629 } else if (localSubrIndex) {
630 localSubrToUse = localSubrIndex;
631 }
632 if (valid) {
633 valid = this.parseCharString(state, charstring, localSubrToUse, globalSubrIndex);
634 }
635 if (state.width !== null) {
636 var nominalWidth = privateDictToUse.getByName('nominalWidthX');
637 widths[i] = nominalWidth + state.width;
638 } else {
639 var defaultWidth = privateDictToUse.getByName('defaultWidthX');
640 widths[i] = defaultWidth;
641 }
642 if (state.seac !== null) {
643 seacs[i] = state.seac;
644 }
645 if (!valid) {
646 charStrings.set(i, new Uint8Array([14]));
647 }
648 }
649 return {
650 charStrings: charStrings,
651 seacs: seacs,
652 widths: widths
653 };
654 },
655
656 emptyPrivateDictionary: function CFFParser_emptyPrivateDictionary(parentDict) {
657 var privateDict = this.createDict(CFFPrivateDict, [], parentDict.strings);
658 parentDict.setByKey(18, [0, 0]);
659 parentDict.privateDict = privateDict;
660 },
661 parsePrivateDict: function CFFParser_parsePrivateDict(parentDict) {
662 if (!parentDict.hasName('Private')) {
663 this.emptyPrivateDictionary(parentDict);
664 return;
665 }
666 var privateOffset = parentDict.getByName('Private');
667 if (!Array.isArray(privateOffset) || privateOffset.length !== 2) {
668 parentDict.removeByName('Private');
669 return;
670 }
671 var size = privateOffset[0];
672 var offset = privateOffset[1];
673 if (size === 0 || offset >= this.bytes.length) {
674 this.emptyPrivateDictionary(parentDict);
675 return;
676 }
677 var privateDictEnd = offset + size;
678 var dictData = this.bytes.subarray(offset, privateDictEnd);
679 var dict = this.parseDict(dictData);
680 var privateDict = this.createDict(CFFPrivateDict, dict, parentDict.strings);
681 parentDict.privateDict = privateDict;
682 if (!privateDict.getByName('Subrs')) {
683 return;
684 }
685 var subrsOffset = privateDict.getByName('Subrs');
686 var relativeOffset = offset + subrsOffset;
687 if (subrsOffset === 0 || relativeOffset >= this.bytes.length) {
688 this.emptyPrivateDictionary(parentDict);
689 return;
690 }
691 var subrsIndex = this.parseIndex(relativeOffset);
692 privateDict.subrsIndex = subrsIndex.obj;
693 },
694 parseCharsets: function CFFParser_parseCharsets(pos, length, strings, cid) {
695 if (pos === 0) {
696 return new CFFCharset(true, CFFCharsetPredefinedTypes.ISO_ADOBE, _charsets.ISOAdobeCharset);
697 } else if (pos === 1) {
698 return new CFFCharset(true, CFFCharsetPredefinedTypes.EXPERT, _charsets.ExpertCharset);
699 } else if (pos === 2) {
700 return new CFFCharset(true, CFFCharsetPredefinedTypes.EXPERT_SUBSET, _charsets.ExpertSubsetCharset);
701 }
702 var bytes = this.bytes;
703 var start = pos;
704 var format = bytes[pos++];
705 var charset = ['.notdef'];
706 var id, count, i;
707 length -= 1;
708 switch (format) {
709 case 0:
710 for (i = 0; i < length; i++) {
711 id = bytes[pos++] << 8 | bytes[pos++];
712 charset.push(cid ? id : strings.get(id));
713 }
714 break;
715 case 1:
716 while (charset.length <= length) {
717 id = bytes[pos++] << 8 | bytes[pos++];
718 count = bytes[pos++];
719 for (i = 0; i <= count; i++) {
720 charset.push(cid ? id++ : strings.get(id++));
721 }
722 }
723 break;
724 case 2:
725 while (charset.length <= length) {
726 id = bytes[pos++] << 8 | bytes[pos++];
727 count = bytes[pos++] << 8 | bytes[pos++];
728 for (i = 0; i <= count; i++) {
729 charset.push(cid ? id++ : strings.get(id++));
730 }
731 }
732 break;
733 default:
734 throw new _util.FormatError('Unknown charset format');
735 }
736 var end = pos;
737 var raw = bytes.subarray(start, end);
738 return new CFFCharset(false, format, charset, raw);
739 },
740 parseEncoding: function CFFParser_parseEncoding(pos, properties, strings, charset) {
741 var encoding = Object.create(null);
742 var bytes = this.bytes;
743 var predefined = false;
744 var format, i, ii;
745 var raw = null;
746 function readSupplement() {
747 var supplementsCount = bytes[pos++];
748 for (i = 0; i < supplementsCount; i++) {
749 var code = bytes[pos++];
750 var sid = (bytes[pos++] << 8) + (bytes[pos++] & 0xff);
751 encoding[code] = charset.indexOf(strings.get(sid));
752 }
753 }
754 if (pos === 0 || pos === 1) {
755 predefined = true;
756 format = pos;
757 var baseEncoding = pos ? _encodings.ExpertEncoding : _encodings.StandardEncoding;
758 for (i = 0, ii = charset.length; i < ii; i++) {
759 var index = baseEncoding.indexOf(charset[i]);
760 if (index !== -1) {
761 encoding[index] = i;
762 }
763 }
764 } else {
765 var dataStart = pos;
766 format = bytes[pos++];
767 switch (format & 0x7f) {
768 case 0:
769 var glyphsCount = bytes[pos++];
770 for (i = 1; i <= glyphsCount; i++) {
771 encoding[bytes[pos++]] = i;
772 }
773 break;
774 case 1:
775 var rangesCount = bytes[pos++];
776 var gid = 1;
777 for (i = 0; i < rangesCount; i++) {
778 var start = bytes[pos++];
779 var left = bytes[pos++];
780 for (var j = start; j <= start + left; j++) {
781 encoding[j] = gid++;
782 }
783 }
784 break;
785 default:
786 throw new _util.FormatError('Unknown encoding format: ' + format + ' in CFF');
787 }
788 var dataEnd = pos;
789 if (format & 0x80) {
790 bytes[dataStart] &= 0x7f;
791 readSupplement();
792 }
793 raw = bytes.subarray(dataStart, dataEnd);
794 }
795 format = format & 0x7f;
796 return new CFFEncoding(predefined, format, encoding, raw);
797 },
798 parseFDSelect: function CFFParser_parseFDSelect(pos, length) {
799 var start = pos;
800 var bytes = this.bytes;
801 var format = bytes[pos++];
802 var fdSelect = [],
803 rawBytes;
804 var i,
805 invalidFirstGID = false;
806 switch (format) {
807 case 0:
808 for (i = 0; i < length; ++i) {
809 var id = bytes[pos++];
810 fdSelect.push(id);
811 }
812 rawBytes = bytes.subarray(start, pos);
813 break;
814 case 3:
815 var rangesCount = bytes[pos++] << 8 | bytes[pos++];
816 for (i = 0; i < rangesCount; ++i) {
817 var first = bytes[pos++] << 8 | bytes[pos++];
818 if (i === 0 && first !== 0) {
819 (0, _util.warn)('parseFDSelect: The first range must have a first GID of 0' + ' -- trying to recover.');
820 invalidFirstGID = true;
821 first = 0;
822 }
823 var fdIndex = bytes[pos++];
824 var next = bytes[pos] << 8 | bytes[pos + 1];
825 for (var j = first; j < next; ++j) {
826 fdSelect.push(fdIndex);
827 }
828 }
829 pos += 2;
830 rawBytes = bytes.subarray(start, pos);
831 if (invalidFirstGID) {
832 rawBytes[3] = rawBytes[4] = 0;
833 }
834 break;
835 default:
836 throw new _util.FormatError('parseFDSelect: Unknown format "' + format + '".');
837 }
838 if (fdSelect.length !== length) {
839 throw new _util.FormatError('parseFDSelect: Invalid font data.');
840 }
841 return new CFFFDSelect(fdSelect, rawBytes);
842 }
843 };
844 return CFFParser;
845}();
846var CFF = function CFFClosure() {
847 function CFF() {
848 this.header = null;
849 this.names = [];
850 this.topDict = null;
851 this.strings = new CFFStrings();
852 this.globalSubrIndex = null;
853 this.encoding = null;
854 this.charset = null;
855 this.charStrings = null;
856 this.fdArray = [];
857 this.fdSelect = null;
858 this.isCIDFont = false;
859 }
860 return CFF;
861}();
862var CFFHeader = function CFFHeaderClosure() {
863 function CFFHeader(major, minor, hdrSize, offSize) {
864 this.major = major;
865 this.minor = minor;
866 this.hdrSize = hdrSize;
867 this.offSize = offSize;
868 }
869 return CFFHeader;
870}();
871var CFFStrings = function CFFStringsClosure() {
872 function CFFStrings() {
873 this.strings = [];
874 }
875 CFFStrings.prototype = {
876 get: function CFFStrings_get(index) {
877 if (index >= 0 && index <= 390) {
878 return CFFStandardStrings[index];
879 }
880 if (index - 391 <= this.strings.length) {
881 return this.strings[index - 391];
882 }
883 return CFFStandardStrings[0];
884 },
885 add: function CFFStrings_add(value) {
886 this.strings.push(value);
887 },
888 get count() {
889 return this.strings.length;
890 }
891 };
892 return CFFStrings;
893}();
894var CFFIndex = function CFFIndexClosure() {
895 function CFFIndex() {
896 this.objects = [];
897 this.length = 0;
898 }
899 CFFIndex.prototype = {
900 add: function CFFIndex_add(data) {
901 this.length += data.length;
902 this.objects.push(data);
903 },
904 set: function CFFIndex_set(index, data) {
905 this.length += data.length - this.objects[index].length;
906 this.objects[index] = data;
907 },
908 get: function CFFIndex_get(index) {
909 return this.objects[index];
910 },
911 get count() {
912 return this.objects.length;
913 }
914 };
915 return CFFIndex;
916}();
917var CFFDict = function CFFDictClosure() {
918 function CFFDict(tables, strings) {
919 this.keyToNameMap = tables.keyToNameMap;
920 this.nameToKeyMap = tables.nameToKeyMap;
921 this.defaults = tables.defaults;
922 this.types = tables.types;
923 this.opcodes = tables.opcodes;
924 this.order = tables.order;
925 this.strings = strings;
926 this.values = Object.create(null);
927 }
928 CFFDict.prototype = {
929 setByKey: function CFFDict_setByKey(key, value) {
930 if (!(key in this.keyToNameMap)) {
931 return false;
932 }
933 var valueLength = value.length;
934 if (valueLength === 0) {
935 return true;
936 }
937 for (var i = 0; i < valueLength; i++) {
938 if (isNaN(value[i])) {
939 (0, _util.warn)('Invalid CFFDict value: "' + value + '" for key "' + key + '".');
940 return true;
941 }
942 }
943 var type = this.types[key];
944 if (type === 'num' || type === 'sid' || type === 'offset') {
945 value = value[0];
946 }
947 this.values[key] = value;
948 return true;
949 },
950 setByName: function CFFDict_setByName(name, value) {
951 if (!(name in this.nameToKeyMap)) {
952 throw new _util.FormatError('Invalid dictionary name "' + name + '"');
953 }
954 this.values[this.nameToKeyMap[name]] = value;
955 },
956 hasName: function CFFDict_hasName(name) {
957 return this.nameToKeyMap[name] in this.values;
958 },
959 getByName: function CFFDict_getByName(name) {
960 if (!(name in this.nameToKeyMap)) {
961 throw new _util.FormatError('Invalid dictionary name ' + name + '"');
962 }
963 var key = this.nameToKeyMap[name];
964 if (!(key in this.values)) {
965 return this.defaults[key];
966 }
967 return this.values[key];
968 },
969 removeByName: function CFFDict_removeByName(name) {
970 delete this.values[this.nameToKeyMap[name]];
971 }
972 };
973 CFFDict.createTables = function CFFDict_createTables(layout) {
974 var tables = {
975 keyToNameMap: {},
976 nameToKeyMap: {},
977 defaults: {},
978 types: {},
979 opcodes: {},
980 order: []
981 };
982 for (var i = 0, ii = layout.length; i < ii; ++i) {
983 var entry = layout[i];
984 var key = Array.isArray(entry[0]) ? (entry[0][0] << 8) + entry[0][1] : entry[0];
985 tables.keyToNameMap[key] = entry[1];
986 tables.nameToKeyMap[entry[1]] = key;
987 tables.types[key] = entry[2];
988 tables.defaults[key] = entry[3];
989 tables.opcodes[key] = Array.isArray(entry[0]) ? entry[0] : [entry[0]];
990 tables.order.push(key);
991 }
992 return tables;
993 };
994 return CFFDict;
995}();
996var CFFTopDict = function CFFTopDictClosure() {
997 var layout = [[[12, 30], 'ROS', ['sid', 'sid', 'num'], null], [[12, 20], 'SyntheticBase', 'num', null], [0, 'version', 'sid', null], [1, 'Notice', 'sid', null], [[12, 0], 'Copyright', 'sid', null], [2, 'FullName', 'sid', null], [3, 'FamilyName', 'sid', null], [4, 'Weight', 'sid', null], [[12, 1], 'isFixedPitch', 'num', 0], [[12, 2], 'ItalicAngle', 'num', 0], [[12, 3], 'UnderlinePosition', 'num', -100], [[12, 4], 'UnderlineThickness', 'num', 50], [[12, 5], 'PaintType', 'num', 0], [[12, 6], 'CharstringType', 'num', 2], [[12, 7], 'FontMatrix', ['num', 'num', 'num', 'num', 'num', 'num'], [0.001, 0, 0, 0.001, 0, 0]], [13, 'UniqueID', 'num', null], [5, 'FontBBox', ['num', 'num', 'num', 'num'], [0, 0, 0, 0]], [[12, 8], 'StrokeWidth', 'num', 0], [14, 'XUID', 'array', null], [15, 'charset', 'offset', 0], [16, 'Encoding', 'offset', 0], [17, 'CharStrings', 'offset', 0], [18, 'Private', ['offset', 'offset'], null], [[12, 21], 'PostScript', 'sid', null], [[12, 22], 'BaseFontName', 'sid', null], [[12, 23], 'BaseFontBlend', 'delta', null], [[12, 31], 'CIDFontVersion', 'num', 0], [[12, 32], 'CIDFontRevision', 'num', 0], [[12, 33], 'CIDFontType', 'num', 0], [[12, 34], 'CIDCount', 'num', 8720], [[12, 35], 'UIDBase', 'num', null], [[12, 37], 'FDSelect', 'offset', null], [[12, 36], 'FDArray', 'offset', null], [[12, 38], 'FontName', 'sid', null]];
998 var tables = null;
999 function CFFTopDict(strings) {
1000 if (tables === null) {
1001 tables = CFFDict.createTables(layout);
1002 }
1003 CFFDict.call(this, tables, strings);
1004 this.privateDict = null;
1005 }
1006 CFFTopDict.prototype = Object.create(CFFDict.prototype);
1007 return CFFTopDict;
1008}();
1009var CFFPrivateDict = function CFFPrivateDictClosure() {
1010 var layout = [[6, 'BlueValues', 'delta', null], [7, 'OtherBlues', 'delta', null], [8, 'FamilyBlues', 'delta', null], [9, 'FamilyOtherBlues', 'delta', null], [[12, 9], 'BlueScale', 'num', 0.039625], [[12, 10], 'BlueShift', 'num', 7], [[12, 11], 'BlueFuzz', 'num', 1], [10, 'StdHW', 'num', null], [11, 'StdVW', 'num', null], [[12, 12], 'StemSnapH', 'delta', null], [[12, 13], 'StemSnapV', 'delta', null], [[12, 14], 'ForceBold', 'num', 0], [[12, 17], 'LanguageGroup', 'num', 0], [[12, 18], 'ExpansionFactor', 'num', 0.06], [[12, 19], 'initialRandomSeed', 'num', 0], [20, 'defaultWidthX', 'num', 0], [21, 'nominalWidthX', 'num', 0], [19, 'Subrs', 'offset', null]];
1011 var tables = null;
1012 function CFFPrivateDict(strings) {
1013 if (tables === null) {
1014 tables = CFFDict.createTables(layout);
1015 }
1016 CFFDict.call(this, tables, strings);
1017 this.subrsIndex = null;
1018 }
1019 CFFPrivateDict.prototype = Object.create(CFFDict.prototype);
1020 return CFFPrivateDict;
1021}();
1022var CFFCharsetPredefinedTypes = {
1023 ISO_ADOBE: 0,
1024 EXPERT: 1,
1025 EXPERT_SUBSET: 2
1026};
1027var CFFCharset = function CFFCharsetClosure() {
1028 function CFFCharset(predefined, format, charset, raw) {
1029 this.predefined = predefined;
1030 this.format = format;
1031 this.charset = charset;
1032 this.raw = raw;
1033 }
1034 return CFFCharset;
1035}();
1036var CFFEncoding = function CFFEncodingClosure() {
1037 function CFFEncoding(predefined, format, encoding, raw) {
1038 this.predefined = predefined;
1039 this.format = format;
1040 this.encoding = encoding;
1041 this.raw = raw;
1042 }
1043 return CFFEncoding;
1044}();
1045var CFFFDSelect = function CFFFDSelectClosure() {
1046 function CFFFDSelect(fdSelect, raw) {
1047 this.fdSelect = fdSelect;
1048 this.raw = raw;
1049 }
1050 CFFFDSelect.prototype = {
1051 getFDIndex: function CFFFDSelect_get(glyphIndex) {
1052 if (glyphIndex < 0 || glyphIndex >= this.fdSelect.length) {
1053 return -1;
1054 }
1055 return this.fdSelect[glyphIndex];
1056 }
1057 };
1058 return CFFFDSelect;
1059}();
1060var CFFOffsetTracker = function CFFOffsetTrackerClosure() {
1061 function CFFOffsetTracker() {
1062 this.offsets = Object.create(null);
1063 }
1064 CFFOffsetTracker.prototype = {
1065 isTracking: function CFFOffsetTracker_isTracking(key) {
1066 return key in this.offsets;
1067 },
1068 track: function CFFOffsetTracker_track(key, location) {
1069 if (key in this.offsets) {
1070 throw new _util.FormatError('Already tracking location of ' + key);
1071 }
1072 this.offsets[key] = location;
1073 },
1074 offset: function CFFOffsetTracker_offset(value) {
1075 for (var key in this.offsets) {
1076 this.offsets[key] += value;
1077 }
1078 },
1079 setEntryLocation: function CFFOffsetTracker_setEntryLocation(key, values, output) {
1080 if (!(key in this.offsets)) {
1081 throw new _util.FormatError('Not tracking location of ' + key);
1082 }
1083 var data = output.data;
1084 var dataOffset = this.offsets[key];
1085 var size = 5;
1086 for (var i = 0, ii = values.length; i < ii; ++i) {
1087 var offset0 = i * size + dataOffset;
1088 var offset1 = offset0 + 1;
1089 var offset2 = offset0 + 2;
1090 var offset3 = offset0 + 3;
1091 var offset4 = offset0 + 4;
1092 if (data[offset0] !== 0x1d || data[offset1] !== 0 || data[offset2] !== 0 || data[offset3] !== 0 || data[offset4] !== 0) {
1093 throw new _util.FormatError('writing to an offset that is not empty');
1094 }
1095 var value = values[i];
1096 data[offset0] = 0x1d;
1097 data[offset1] = value >> 24 & 0xFF;
1098 data[offset2] = value >> 16 & 0xFF;
1099 data[offset3] = value >> 8 & 0xFF;
1100 data[offset4] = value & 0xFF;
1101 }
1102 }
1103 };
1104 return CFFOffsetTracker;
1105}();
1106var CFFCompiler = function CFFCompilerClosure() {
1107 function CFFCompiler(cff) {
1108 this.cff = cff;
1109 }
1110 CFFCompiler.prototype = {
1111 compile: function CFFCompiler_compile() {
1112 var cff = this.cff;
1113 var output = {
1114 data: [],
1115 length: 0,
1116 add: function CFFCompiler_add(data) {
1117 this.data = this.data.concat(data);
1118 this.length = this.data.length;
1119 }
1120 };
1121 var header = this.compileHeader(cff.header);
1122 output.add(header);
1123 var nameIndex = this.compileNameIndex(cff.names);
1124 output.add(nameIndex);
1125 if (cff.isCIDFont) {
1126 if (cff.topDict.hasName('FontMatrix')) {
1127 var base = cff.topDict.getByName('FontMatrix');
1128 cff.topDict.removeByName('FontMatrix');
1129 for (var i = 0, ii = cff.fdArray.length; i < ii; i++) {
1130 var subDict = cff.fdArray[i];
1131 var matrix = base.slice(0);
1132 if (subDict.hasName('FontMatrix')) {
1133 matrix = _util.Util.transform(matrix, subDict.getByName('FontMatrix'));
1134 }
1135 subDict.setByName('FontMatrix', matrix);
1136 }
1137 }
1138 }
1139 var compiled = this.compileTopDicts([cff.topDict], output.length, cff.isCIDFont);
1140 output.add(compiled.output);
1141 var topDictTracker = compiled.trackers[0];
1142 var stringIndex = this.compileStringIndex(cff.strings.strings);
1143 output.add(stringIndex);
1144 var globalSubrIndex = this.compileIndex(cff.globalSubrIndex);
1145 output.add(globalSubrIndex);
1146 if (cff.encoding && cff.topDict.hasName('Encoding')) {
1147 if (cff.encoding.predefined) {
1148 topDictTracker.setEntryLocation('Encoding', [cff.encoding.format], output);
1149 } else {
1150 var encoding = this.compileEncoding(cff.encoding);
1151 topDictTracker.setEntryLocation('Encoding', [output.length], output);
1152 output.add(encoding);
1153 }
1154 }
1155 if (cff.charset && cff.topDict.hasName('charset')) {
1156 if (cff.charset.predefined) {
1157 topDictTracker.setEntryLocation('charset', [cff.charset.format], output);
1158 } else {
1159 var charset = this.compileCharset(cff.charset);
1160 topDictTracker.setEntryLocation('charset', [output.length], output);
1161 output.add(charset);
1162 }
1163 }
1164 var charStrings = this.compileCharStrings(cff.charStrings);
1165 topDictTracker.setEntryLocation('CharStrings', [output.length], output);
1166 output.add(charStrings);
1167 if (cff.isCIDFont) {
1168 topDictTracker.setEntryLocation('FDSelect', [output.length], output);
1169 var fdSelect = this.compileFDSelect(cff.fdSelect.raw);
1170 output.add(fdSelect);
1171 compiled = this.compileTopDicts(cff.fdArray, output.length, true);
1172 topDictTracker.setEntryLocation('FDArray', [output.length], output);
1173 output.add(compiled.output);
1174 var fontDictTrackers = compiled.trackers;
1175 this.compilePrivateDicts(cff.fdArray, fontDictTrackers, output);
1176 }
1177 this.compilePrivateDicts([cff.topDict], [topDictTracker], output);
1178 output.add([0]);
1179 return output.data;
1180 },
1181 encodeNumber: function CFFCompiler_encodeNumber(value) {
1182 if (parseFloat(value) === parseInt(value, 10) && !isNaN(value)) {
1183 return this.encodeInteger(value);
1184 }
1185 return this.encodeFloat(value);
1186 },
1187 encodeFloat: function CFFCompiler_encodeFloat(num) {
1188 var value = num.toString();
1189 var m = /\.(\d*?)(?:9{5,20}|0{5,20})\d{0,2}(?:e(.+)|$)/.exec(value);
1190 if (m) {
1191 var epsilon = parseFloat('1e' + ((m[2] ? +m[2] : 0) + m[1].length));
1192 value = (Math.round(num * epsilon) / epsilon).toString();
1193 }
1194 var nibbles = '';
1195 var i, ii;
1196 for (i = 0, ii = value.length; i < ii; ++i) {
1197 var a = value[i];
1198 if (a === 'e') {
1199 nibbles += value[++i] === '-' ? 'c' : 'b';
1200 } else if (a === '.') {
1201 nibbles += 'a';
1202 } else if (a === '-') {
1203 nibbles += 'e';
1204 } else {
1205 nibbles += a;
1206 }
1207 }
1208 nibbles += nibbles.length & 1 ? 'f' : 'ff';
1209 var out = [30];
1210 for (i = 0, ii = nibbles.length; i < ii; i += 2) {
1211 out.push(parseInt(nibbles.substr(i, 2), 16));
1212 }
1213 return out;
1214 },
1215 encodeInteger: function CFFCompiler_encodeInteger(value) {
1216 var code;
1217 if (value >= -107 && value <= 107) {
1218 code = [value + 139];
1219 } else if (value >= 108 && value <= 1131) {
1220 value = value - 108;
1221 code = [(value >> 8) + 247, value & 0xFF];
1222 } else if (value >= -1131 && value <= -108) {
1223 value = -value - 108;
1224 code = [(value >> 8) + 251, value & 0xFF];
1225 } else if (value >= -32768 && value <= 32767) {
1226 code = [0x1c, value >> 8 & 0xFF, value & 0xFF];
1227 } else {
1228 code = [0x1d, value >> 24 & 0xFF, value >> 16 & 0xFF, value >> 8 & 0xFF, value & 0xFF];
1229 }
1230 return code;
1231 },
1232 compileHeader: function CFFCompiler_compileHeader(header) {
1233 return [header.major, header.minor, header.hdrSize, header.offSize];
1234 },
1235 compileNameIndex: function CFFCompiler_compileNameIndex(names) {
1236 var nameIndex = new CFFIndex();
1237 for (var i = 0, ii = names.length; i < ii; ++i) {
1238 var name = names[i];
1239 var length = Math.min(name.length, 127);
1240 var sanitizedName = new Array(length);
1241 for (var j = 0; j < length; j++) {
1242 var char = name[j];
1243 if (char < '!' || char > '~' || char === '[' || char === ']' || char === '(' || char === ')' || char === '{' || char === '}' || char === '<' || char === '>' || char === '/' || char === '%') {
1244 char = '_';
1245 }
1246 sanitizedName[j] = char;
1247 }
1248 sanitizedName = sanitizedName.join('');
1249 if (sanitizedName === '') {
1250 sanitizedName = 'Bad_Font_Name';
1251 }
1252 nameIndex.add((0, _util.stringToBytes)(sanitizedName));
1253 }
1254 return this.compileIndex(nameIndex);
1255 },
1256 compileTopDicts: function CFFCompiler_compileTopDicts(dicts, length, removeCidKeys) {
1257 var fontDictTrackers = [];
1258 var fdArrayIndex = new CFFIndex();
1259 for (var i = 0, ii = dicts.length; i < ii; ++i) {
1260 var fontDict = dicts[i];
1261 if (removeCidKeys) {
1262 fontDict.removeByName('CIDFontVersion');
1263 fontDict.removeByName('CIDFontRevision');
1264 fontDict.removeByName('CIDFontType');
1265 fontDict.removeByName('CIDCount');
1266 fontDict.removeByName('UIDBase');
1267 }
1268 var fontDictTracker = new CFFOffsetTracker();
1269 var fontDictData = this.compileDict(fontDict, fontDictTracker);
1270 fontDictTrackers.push(fontDictTracker);
1271 fdArrayIndex.add(fontDictData);
1272 fontDictTracker.offset(length);
1273 }
1274 fdArrayIndex = this.compileIndex(fdArrayIndex, fontDictTrackers);
1275 return {
1276 trackers: fontDictTrackers,
1277 output: fdArrayIndex
1278 };
1279 },
1280 compilePrivateDicts: function CFFCompiler_compilePrivateDicts(dicts, trackers, output) {
1281 for (var i = 0, ii = dicts.length; i < ii; ++i) {
1282 var fontDict = dicts[i];
1283 var privateDict = fontDict.privateDict;
1284 if (!privateDict || !fontDict.hasName('Private')) {
1285 throw new _util.FormatError('There must be a private dictionary.');
1286 }
1287 var privateDictTracker = new CFFOffsetTracker();
1288 var privateDictData = this.compileDict(privateDict, privateDictTracker);
1289 var outputLength = output.length;
1290 privateDictTracker.offset(outputLength);
1291 if (!privateDictData.length) {
1292 outputLength = 0;
1293 }
1294 trackers[i].setEntryLocation('Private', [privateDictData.length, outputLength], output);
1295 output.add(privateDictData);
1296 if (privateDict.subrsIndex && privateDict.hasName('Subrs')) {
1297 var subrs = this.compileIndex(privateDict.subrsIndex);
1298 privateDictTracker.setEntryLocation('Subrs', [privateDictData.length], output);
1299 output.add(subrs);
1300 }
1301 }
1302 },
1303 compileDict: function CFFCompiler_compileDict(dict, offsetTracker) {
1304 var out = [];
1305 var order = dict.order;
1306 for (var i = 0; i < order.length; ++i) {
1307 var key = order[i];
1308 if (!(key in dict.values)) {
1309 continue;
1310 }
1311 var values = dict.values[key];
1312 var types = dict.types[key];
1313 if (!Array.isArray(types)) {
1314 types = [types];
1315 }
1316 if (!Array.isArray(values)) {
1317 values = [values];
1318 }
1319 if (values.length === 0) {
1320 continue;
1321 }
1322 for (var j = 0, jj = types.length; j < jj; ++j) {
1323 var type = types[j];
1324 var value = values[j];
1325 switch (type) {
1326 case 'num':
1327 case 'sid':
1328 out = out.concat(this.encodeNumber(value));
1329 break;
1330 case 'offset':
1331 var name = dict.keyToNameMap[key];
1332 if (!offsetTracker.isTracking(name)) {
1333 offsetTracker.track(name, out.length);
1334 }
1335 out = out.concat([0x1d, 0, 0, 0, 0]);
1336 break;
1337 case 'array':
1338 case 'delta':
1339 out = out.concat(this.encodeNumber(value));
1340 for (var k = 1, kk = values.length; k < kk; ++k) {
1341 out = out.concat(this.encodeNumber(values[k]));
1342 }
1343 break;
1344 default:
1345 throw new _util.FormatError('Unknown data type of ' + type);
1346 }
1347 }
1348 out = out.concat(dict.opcodes[key]);
1349 }
1350 return out;
1351 },
1352 compileStringIndex: function CFFCompiler_compileStringIndex(strings) {
1353 var stringIndex = new CFFIndex();
1354 for (var i = 0, ii = strings.length; i < ii; ++i) {
1355 stringIndex.add((0, _util.stringToBytes)(strings[i]));
1356 }
1357 return this.compileIndex(stringIndex);
1358 },
1359 compileGlobalSubrIndex: function CFFCompiler_compileGlobalSubrIndex() {
1360 var globalSubrIndex = this.cff.globalSubrIndex;
1361 this.out.writeByteArray(this.compileIndex(globalSubrIndex));
1362 },
1363 compileCharStrings: function CFFCompiler_compileCharStrings(charStrings) {
1364 return this.compileIndex(charStrings);
1365 },
1366 compileCharset: function CFFCompiler_compileCharset(charset) {
1367 return this.compileTypedArray(charset.raw);
1368 },
1369 compileEncoding: function CFFCompiler_compileEncoding(encoding) {
1370 return this.compileTypedArray(encoding.raw);
1371 },
1372 compileFDSelect: function CFFCompiler_compileFDSelect(fdSelect) {
1373 return this.compileTypedArray(fdSelect);
1374 },
1375 compileTypedArray: function CFFCompiler_compileTypedArray(data) {
1376 var out = [];
1377 for (var i = 0, ii = data.length; i < ii; ++i) {
1378 out[i] = data[i];
1379 }
1380 return out;
1381 },
1382 compileIndex: function CFFCompiler_compileIndex(index, trackers) {
1383 trackers = trackers || [];
1384 var objects = index.objects;
1385 var count = objects.length;
1386 if (count === 0) {
1387 return [0, 0, 0];
1388 }
1389 var data = [count >> 8 & 0xFF, count & 0xff];
1390 var lastOffset = 1,
1391 i;
1392 for (i = 0; i < count; ++i) {
1393 lastOffset += objects[i].length;
1394 }
1395 var offsetSize;
1396 if (lastOffset < 0x100) {
1397 offsetSize = 1;
1398 } else if (lastOffset < 0x10000) {
1399 offsetSize = 2;
1400 } else if (lastOffset < 0x1000000) {
1401 offsetSize = 3;
1402 } else {
1403 offsetSize = 4;
1404 }
1405 data.push(offsetSize);
1406 var relativeOffset = 1;
1407 for (i = 0; i < count + 1; i++) {
1408 if (offsetSize === 1) {
1409 data.push(relativeOffset & 0xFF);
1410 } else if (offsetSize === 2) {
1411 data.push(relativeOffset >> 8 & 0xFF, relativeOffset & 0xFF);
1412 } else if (offsetSize === 3) {
1413 data.push(relativeOffset >> 16 & 0xFF, relativeOffset >> 8 & 0xFF, relativeOffset & 0xFF);
1414 } else {
1415 data.push(relativeOffset >>> 24 & 0xFF, relativeOffset >> 16 & 0xFF, relativeOffset >> 8 & 0xFF, relativeOffset & 0xFF);
1416 }
1417 if (objects[i]) {
1418 relativeOffset += objects[i].length;
1419 }
1420 }
1421 for (i = 0; i < count; i++) {
1422 if (trackers[i]) {
1423 trackers[i].offset(data.length);
1424 }
1425 for (var j = 0, jj = objects[i].length; j < jj; j++) {
1426 data.push(objects[i][j]);
1427 }
1428 }
1429 return data;
1430 }
1431 };
1432 return CFFCompiler;
1433}();
1434exports.CFFStandardStrings = CFFStandardStrings;
1435exports.CFFParser = CFFParser;
1436exports.CFF = CFF;
1437exports.CFFHeader = CFFHeader;
1438exports.CFFStrings = CFFStrings;
1439exports.CFFIndex = CFFIndex;
1440exports.CFFCharset = CFFCharset;
1441exports.CFFTopDict = CFFTopDict;
1442exports.CFFPrivateDict = CFFPrivateDict;
1443exports.CFFCompiler = CFFCompiler;
\No newline at end of file