1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 | "use strict";
|
23 |
|
24 | Object.defineProperty(exports, "__esModule", {
|
25 | value: true
|
26 | });
|
27 | exports.Type1Parser = void 0;
|
28 |
|
29 | var _util = require("../shared/util");
|
30 |
|
31 | var _encodings = require("./encodings");
|
32 |
|
33 | var _stream = require("./stream");
|
34 |
|
35 | var HINTING_ENABLED = false;
|
36 |
|
37 | var Type1CharString = function Type1CharStringClosure() {
|
38 | var COMMAND_MAP = {
|
39 | 'hstem': [1],
|
40 | 'vstem': [3],
|
41 | 'vmoveto': [4],
|
42 | 'rlineto': [5],
|
43 | 'hlineto': [6],
|
44 | 'vlineto': [7],
|
45 | 'rrcurveto': [8],
|
46 | 'callsubr': [10],
|
47 | 'flex': [12, 35],
|
48 | 'drop': [12, 18],
|
49 | 'endchar': [14],
|
50 | 'rmoveto': [21],
|
51 | 'hmoveto': [22],
|
52 | 'vhcurveto': [30],
|
53 | 'hvcurveto': [31]
|
54 | };
|
55 |
|
56 | function Type1CharString() {
|
57 | this.width = 0;
|
58 | this.lsb = 0;
|
59 | this.flexing = false;
|
60 | this.output = [];
|
61 | this.stack = [];
|
62 | }
|
63 |
|
64 | Type1CharString.prototype = {
|
65 | convert: function Type1CharString_convert(encoded, subrs, seacAnalysisEnabled) {
|
66 | var count = encoded.length;
|
67 | var error = false;
|
68 | var wx, sbx, subrNumber;
|
69 |
|
70 | for (var i = 0; i < count; i++) {
|
71 | var value = encoded[i];
|
72 |
|
73 | if (value < 32) {
|
74 | if (value === 12) {
|
75 | value = (value << 8) + encoded[++i];
|
76 | }
|
77 |
|
78 | switch (value) {
|
79 | case 1:
|
80 | if (!HINTING_ENABLED) {
|
81 | this.stack = [];
|
82 | break;
|
83 | }
|
84 |
|
85 | error = this.executeCommand(2, COMMAND_MAP.hstem);
|
86 | break;
|
87 |
|
88 | case 3:
|
89 | if (!HINTING_ENABLED) {
|
90 | this.stack = [];
|
91 | break;
|
92 | }
|
93 |
|
94 | error = this.executeCommand(2, COMMAND_MAP.vstem);
|
95 | break;
|
96 |
|
97 | case 4:
|
98 | if (this.flexing) {
|
99 | if (this.stack.length < 1) {
|
100 | error = true;
|
101 | break;
|
102 | }
|
103 |
|
104 | var dy = this.stack.pop();
|
105 | this.stack.push(0, dy);
|
106 | break;
|
107 | }
|
108 |
|
109 | error = this.executeCommand(1, COMMAND_MAP.vmoveto);
|
110 | break;
|
111 |
|
112 | case 5:
|
113 | error = this.executeCommand(2, COMMAND_MAP.rlineto);
|
114 | break;
|
115 |
|
116 | case 6:
|
117 | error = this.executeCommand(1, COMMAND_MAP.hlineto);
|
118 | break;
|
119 |
|
120 | case 7:
|
121 | error = this.executeCommand(1, COMMAND_MAP.vlineto);
|
122 | break;
|
123 |
|
124 | case 8:
|
125 | error = this.executeCommand(6, COMMAND_MAP.rrcurveto);
|
126 | break;
|
127 |
|
128 | case 9:
|
129 | this.stack = [];
|
130 | break;
|
131 |
|
132 | case 10:
|
133 | if (this.stack.length < 1) {
|
134 | error = true;
|
135 | break;
|
136 | }
|
137 |
|
138 | subrNumber = this.stack.pop();
|
139 |
|
140 | if (!subrs[subrNumber]) {
|
141 | error = true;
|
142 | break;
|
143 | }
|
144 |
|
145 | error = this.convert(subrs[subrNumber], subrs, seacAnalysisEnabled);
|
146 | break;
|
147 |
|
148 | case 11:
|
149 | return error;
|
150 |
|
151 | case 13:
|
152 | if (this.stack.length < 2) {
|
153 | error = true;
|
154 | break;
|
155 | }
|
156 |
|
157 | wx = this.stack.pop();
|
158 | sbx = this.stack.pop();
|
159 | this.lsb = sbx;
|
160 | this.width = wx;
|
161 | this.stack.push(wx, sbx);
|
162 | error = this.executeCommand(2, COMMAND_MAP.hmoveto);
|
163 | break;
|
164 |
|
165 | case 14:
|
166 | this.output.push(COMMAND_MAP.endchar[0]);
|
167 | break;
|
168 |
|
169 | case 21:
|
170 | if (this.flexing) {
|
171 | break;
|
172 | }
|
173 |
|
174 | error = this.executeCommand(2, COMMAND_MAP.rmoveto);
|
175 | break;
|
176 |
|
177 | case 22:
|
178 | if (this.flexing) {
|
179 | this.stack.push(0);
|
180 | break;
|
181 | }
|
182 |
|
183 | error = this.executeCommand(1, COMMAND_MAP.hmoveto);
|
184 | break;
|
185 |
|
186 | case 30:
|
187 | error = this.executeCommand(4, COMMAND_MAP.vhcurveto);
|
188 | break;
|
189 |
|
190 | case 31:
|
191 | error = this.executeCommand(4, COMMAND_MAP.hvcurveto);
|
192 | break;
|
193 |
|
194 | case (12 << 8) + 0:
|
195 | this.stack = [];
|
196 | break;
|
197 |
|
198 | case (12 << 8) + 1:
|
199 | if (!HINTING_ENABLED) {
|
200 | this.stack = [];
|
201 | break;
|
202 | }
|
203 |
|
204 | error = this.executeCommand(2, COMMAND_MAP.vstem);
|
205 | break;
|
206 |
|
207 | case (12 << 8) + 2:
|
208 | if (!HINTING_ENABLED) {
|
209 | this.stack = [];
|
210 | break;
|
211 | }
|
212 |
|
213 | error = this.executeCommand(2, COMMAND_MAP.hstem);
|
214 | break;
|
215 |
|
216 | case (12 << 8) + 6:
|
217 | if (seacAnalysisEnabled) {
|
218 | this.seac = this.stack.splice(-4, 4);
|
219 | error = this.executeCommand(0, COMMAND_MAP.endchar);
|
220 | } else {
|
221 | error = this.executeCommand(4, COMMAND_MAP.endchar);
|
222 | }
|
223 |
|
224 | break;
|
225 |
|
226 | case (12 << 8) + 7:
|
227 | if (this.stack.length < 4) {
|
228 | error = true;
|
229 | break;
|
230 | }
|
231 |
|
232 | this.stack.pop();
|
233 | wx = this.stack.pop();
|
234 | var sby = this.stack.pop();
|
235 | sbx = this.stack.pop();
|
236 | this.lsb = sbx;
|
237 | this.width = wx;
|
238 | this.stack.push(wx, sbx, sby);
|
239 | error = this.executeCommand(3, COMMAND_MAP.rmoveto);
|
240 | break;
|
241 |
|
242 | case (12 << 8) + 12:
|
243 | if (this.stack.length < 2) {
|
244 | error = true;
|
245 | break;
|
246 | }
|
247 |
|
248 | var num2 = this.stack.pop();
|
249 | var num1 = this.stack.pop();
|
250 | this.stack.push(num1 / num2);
|
251 | break;
|
252 |
|
253 | case (12 << 8) + 16:
|
254 | if (this.stack.length < 2) {
|
255 | error = true;
|
256 | break;
|
257 | }
|
258 |
|
259 | subrNumber = this.stack.pop();
|
260 | var numArgs = this.stack.pop();
|
261 |
|
262 | if (subrNumber === 0 && numArgs === 3) {
|
263 | var flexArgs = this.stack.splice(this.stack.length - 17, 17);
|
264 | this.stack.push(flexArgs[2] + flexArgs[0], flexArgs[3] + flexArgs[1], flexArgs[4], flexArgs[5], flexArgs[6], flexArgs[7], flexArgs[8], flexArgs[9], flexArgs[10], flexArgs[11], flexArgs[12], flexArgs[13], flexArgs[14]);
|
265 | error = this.executeCommand(13, COMMAND_MAP.flex, true);
|
266 | this.flexing = false;
|
267 | this.stack.push(flexArgs[15], flexArgs[16]);
|
268 | } else if (subrNumber === 1 && numArgs === 0) {
|
269 | this.flexing = true;
|
270 | }
|
271 |
|
272 | break;
|
273 |
|
274 | case (12 << 8) + 17:
|
275 | break;
|
276 |
|
277 | case (12 << 8) + 33:
|
278 | this.stack = [];
|
279 | break;
|
280 |
|
281 | default:
|
282 | (0, _util.warn)('Unknown type 1 charstring command of "' + value + '"');
|
283 | break;
|
284 | }
|
285 |
|
286 | if (error) {
|
287 | break;
|
288 | }
|
289 |
|
290 | continue;
|
291 | } else if (value <= 246) {
|
292 | value = value - 139;
|
293 | } else if (value <= 250) {
|
294 | value = (value - 247) * 256 + encoded[++i] + 108;
|
295 | } else if (value <= 254) {
|
296 | value = -((value - 251) * 256) - encoded[++i] - 108;
|
297 | } else {
|
298 | value = (encoded[++i] & 0xff) << 24 | (encoded[++i] & 0xff) << 16 | (encoded[++i] & 0xff) << 8 | (encoded[++i] & 0xff) << 0;
|
299 | }
|
300 |
|
301 | this.stack.push(value);
|
302 | }
|
303 |
|
304 | return error;
|
305 | },
|
306 | executeCommand: function executeCommand(howManyArgs, command, keepStack) {
|
307 | var stackLength = this.stack.length;
|
308 |
|
309 | if (howManyArgs > stackLength) {
|
310 | return true;
|
311 | }
|
312 |
|
313 | var start = stackLength - howManyArgs;
|
314 |
|
315 | for (var i = start; i < stackLength; i++) {
|
316 | var value = this.stack[i];
|
317 |
|
318 | if (Number.isInteger(value)) {
|
319 | this.output.push(28, value >> 8 & 0xff, value & 0xff);
|
320 | } else {
|
321 | value = 65536 * value | 0;
|
322 | this.output.push(255, value >> 24 & 0xFF, value >> 16 & 0xFF, value >> 8 & 0xFF, value & 0xFF);
|
323 | }
|
324 | }
|
325 |
|
326 | this.output.push.apply(this.output, command);
|
327 |
|
328 | if (keepStack) {
|
329 | this.stack.splice(start, howManyArgs);
|
330 | } else {
|
331 | this.stack.length = 0;
|
332 | }
|
333 |
|
334 | return false;
|
335 | }
|
336 | };
|
337 | return Type1CharString;
|
338 | }();
|
339 |
|
340 | var Type1Parser = function Type1ParserClosure() {
|
341 | var EEXEC_ENCRYPT_KEY = 55665;
|
342 | var CHAR_STRS_ENCRYPT_KEY = 4330;
|
343 |
|
344 | function isHexDigit(code) {
|
345 | return code >= 48 && code <= 57 || code >= 65 && code <= 70 || code >= 97 && code <= 102;
|
346 | }
|
347 |
|
348 | function decrypt(data, key, discardNumber) {
|
349 | if (discardNumber >= data.length) {
|
350 | return new Uint8Array(0);
|
351 | }
|
352 |
|
353 | var r = key | 0,
|
354 | c1 = 52845,
|
355 | c2 = 22719,
|
356 | i,
|
357 | j;
|
358 |
|
359 | for (i = 0; i < discardNumber; i++) {
|
360 | r = (data[i] + r) * c1 + c2 & (1 << 16) - 1;
|
361 | }
|
362 |
|
363 | var count = data.length - discardNumber;
|
364 | var decrypted = new Uint8Array(count);
|
365 |
|
366 | for (i = discardNumber, j = 0; j < count; i++, j++) {
|
367 | var value = data[i];
|
368 | decrypted[j] = value ^ r >> 8;
|
369 | r = (value + r) * c1 + c2 & (1 << 16) - 1;
|
370 | }
|
371 |
|
372 | return decrypted;
|
373 | }
|
374 |
|
375 | function decryptAscii(data, key, discardNumber) {
|
376 | var r = key | 0,
|
377 | c1 = 52845,
|
378 | c2 = 22719;
|
379 | var count = data.length,
|
380 | maybeLength = count >>> 1;
|
381 | var decrypted = new Uint8Array(maybeLength);
|
382 | var i, j;
|
383 |
|
384 | for (i = 0, j = 0; i < count; i++) {
|
385 | var digit1 = data[i];
|
386 |
|
387 | if (!isHexDigit(digit1)) {
|
388 | continue;
|
389 | }
|
390 |
|
391 | i++;
|
392 | var digit2;
|
393 |
|
394 | while (i < count && !isHexDigit(digit2 = data[i])) {
|
395 | i++;
|
396 | }
|
397 |
|
398 | if (i < count) {
|
399 | var value = parseInt(String.fromCharCode(digit1, digit2), 16);
|
400 | decrypted[j++] = value ^ r >> 8;
|
401 | r = (value + r) * c1 + c2 & (1 << 16) - 1;
|
402 | }
|
403 | }
|
404 |
|
405 | return Array.prototype.slice.call(decrypted, discardNumber, j);
|
406 | }
|
407 |
|
408 | function isSpecial(c) {
|
409 | return c === 0x2F || c === 0x5B || c === 0x5D || c === 0x7B || c === 0x7D || c === 0x28 || c === 0x29;
|
410 | }
|
411 |
|
412 | function Type1Parser(stream, encrypted, seacAnalysisEnabled) {
|
413 | if (encrypted) {
|
414 | var data = stream.getBytes();
|
415 | var isBinary = !(isHexDigit(data[0]) && isHexDigit(data[1]) && isHexDigit(data[2]) && isHexDigit(data[3]));
|
416 | stream = new _stream.Stream(isBinary ? decrypt(data, EEXEC_ENCRYPT_KEY, 4) : decryptAscii(data, EEXEC_ENCRYPT_KEY, 4));
|
417 | }
|
418 |
|
419 | this.seacAnalysisEnabled = !!seacAnalysisEnabled;
|
420 | this.stream = stream;
|
421 | this.nextChar();
|
422 | }
|
423 |
|
424 | Type1Parser.prototype = {
|
425 | readNumberArray: function Type1Parser_readNumberArray() {
|
426 | this.getToken();
|
427 | var array = [];
|
428 |
|
429 | while (true) {
|
430 | var token = this.getToken();
|
431 |
|
432 | if (token === null || token === ']' || token === '}') {
|
433 | break;
|
434 | }
|
435 |
|
436 | array.push(parseFloat(token || 0));
|
437 | }
|
438 |
|
439 | return array;
|
440 | },
|
441 | readNumber: function Type1Parser_readNumber() {
|
442 | var token = this.getToken();
|
443 | return parseFloat(token || 0);
|
444 | },
|
445 | readInt: function Type1Parser_readInt() {
|
446 | var token = this.getToken();
|
447 | return parseInt(token || 0, 10) | 0;
|
448 | },
|
449 | readBoolean: function Type1Parser_readBoolean() {
|
450 | var token = this.getToken();
|
451 | return token === 'true' ? 1 : 0;
|
452 | },
|
453 | nextChar: function Type1_nextChar() {
|
454 | return this.currentChar = this.stream.getByte();
|
455 | },
|
456 | getToken: function Type1Parser_getToken() {
|
457 | var comment = false;
|
458 | var ch = this.currentChar;
|
459 |
|
460 | while (true) {
|
461 | if (ch === -1) {
|
462 | return null;
|
463 | }
|
464 |
|
465 | if (comment) {
|
466 | if (ch === 0x0A || ch === 0x0D) {
|
467 | comment = false;
|
468 | }
|
469 | } else if (ch === 0x25) {
|
470 | comment = true;
|
471 | } else if (!(0, _util.isSpace)(ch)) {
|
472 | break;
|
473 | }
|
474 |
|
475 | ch = this.nextChar();
|
476 | }
|
477 |
|
478 | if (isSpecial(ch)) {
|
479 | this.nextChar();
|
480 | return String.fromCharCode(ch);
|
481 | }
|
482 |
|
483 | var token = '';
|
484 |
|
485 | do {
|
486 | token += String.fromCharCode(ch);
|
487 | ch = this.nextChar();
|
488 | } while (ch >= 0 && !(0, _util.isSpace)(ch) && !isSpecial(ch));
|
489 |
|
490 | return token;
|
491 | },
|
492 | readCharStrings: function Type1Parser_readCharStrings(bytes, lenIV) {
|
493 | if (lenIV === -1) {
|
494 | return bytes;
|
495 | }
|
496 |
|
497 | return decrypt(bytes, CHAR_STRS_ENCRYPT_KEY, lenIV);
|
498 | },
|
499 | extractFontProgram: function Type1Parser_extractFontProgram() {
|
500 | var stream = this.stream;
|
501 | var subrs = [],
|
502 | charstrings = [];
|
503 | var privateData = Object.create(null);
|
504 | privateData['lenIV'] = 4;
|
505 | var program = {
|
506 | subrs: [],
|
507 | charstrings: [],
|
508 | properties: {
|
509 | 'privateData': privateData
|
510 | }
|
511 | };
|
512 | var token, length, data, lenIV, encoded;
|
513 |
|
514 | while ((token = this.getToken()) !== null) {
|
515 | if (token !== '/') {
|
516 | continue;
|
517 | }
|
518 |
|
519 | token = this.getToken();
|
520 |
|
521 | switch (token) {
|
522 | case 'CharStrings':
|
523 | this.getToken();
|
524 | this.getToken();
|
525 | this.getToken();
|
526 | this.getToken();
|
527 |
|
528 | while (true) {
|
529 | token = this.getToken();
|
530 |
|
531 | if (token === null || token === 'end') {
|
532 | break;
|
533 | }
|
534 |
|
535 | if (token !== '/') {
|
536 | continue;
|
537 | }
|
538 |
|
539 | var glyph = this.getToken();
|
540 | length = this.readInt();
|
541 | this.getToken();
|
542 | data = length > 0 ? stream.getBytes(length) : new Uint8Array(0);
|
543 | lenIV = program.properties.privateData['lenIV'];
|
544 | encoded = this.readCharStrings(data, lenIV);
|
545 | this.nextChar();
|
546 | token = this.getToken();
|
547 |
|
548 | if (token === 'noaccess') {
|
549 | this.getToken();
|
550 | }
|
551 |
|
552 | charstrings.push({
|
553 | glyph: glyph,
|
554 | encoded: encoded
|
555 | });
|
556 | }
|
557 |
|
558 | break;
|
559 |
|
560 | case 'Subrs':
|
561 | this.readInt();
|
562 | this.getToken();
|
563 |
|
564 | while (this.getToken() === 'dup') {
|
565 | var index = this.readInt();
|
566 | length = this.readInt();
|
567 | this.getToken();
|
568 | data = length > 0 ? stream.getBytes(length) : new Uint8Array(0);
|
569 | lenIV = program.properties.privateData['lenIV'];
|
570 | encoded = this.readCharStrings(data, lenIV);
|
571 | this.nextChar();
|
572 | token = this.getToken();
|
573 |
|
574 | if (token === 'noaccess') {
|
575 | this.getToken();
|
576 | }
|
577 |
|
578 | subrs[index] = encoded;
|
579 | }
|
580 |
|
581 | break;
|
582 |
|
583 | case 'BlueValues':
|
584 | case 'OtherBlues':
|
585 | case 'FamilyBlues':
|
586 | case 'FamilyOtherBlues':
|
587 | var blueArray = this.readNumberArray();
|
588 |
|
589 | if (blueArray.length > 0 && blueArray.length % 2 === 0 && HINTING_ENABLED) {
|
590 | program.properties.privateData[token] = blueArray;
|
591 | }
|
592 |
|
593 | break;
|
594 |
|
595 | case 'StemSnapH':
|
596 | case 'StemSnapV':
|
597 | program.properties.privateData[token] = this.readNumberArray();
|
598 | break;
|
599 |
|
600 | case 'StdHW':
|
601 | case 'StdVW':
|
602 | program.properties.privateData[token] = this.readNumberArray()[0];
|
603 | break;
|
604 |
|
605 | case 'BlueShift':
|
606 | case 'lenIV':
|
607 | case 'BlueFuzz':
|
608 | case 'BlueScale':
|
609 | case 'LanguageGroup':
|
610 | case 'ExpansionFactor':
|
611 | program.properties.privateData[token] = this.readNumber();
|
612 | break;
|
613 |
|
614 | case 'ForceBold':
|
615 | program.properties.privateData[token] = this.readBoolean();
|
616 | break;
|
617 | }
|
618 | }
|
619 |
|
620 | for (var i = 0; i < charstrings.length; i++) {
|
621 | glyph = charstrings[i].glyph;
|
622 | encoded = charstrings[i].encoded;
|
623 | var charString = new Type1CharString();
|
624 | var error = charString.convert(encoded, subrs, this.seacAnalysisEnabled);
|
625 | var output = charString.output;
|
626 |
|
627 | if (error) {
|
628 | output = [14];
|
629 | }
|
630 |
|
631 | program.charstrings.push({
|
632 | glyphName: glyph,
|
633 | charstring: output,
|
634 | width: charString.width,
|
635 | lsb: charString.lsb,
|
636 | seac: charString.seac
|
637 | });
|
638 | }
|
639 |
|
640 | return program;
|
641 | },
|
642 | extractFontHeader: function Type1Parser_extractFontHeader(properties) {
|
643 | var token;
|
644 |
|
645 | while ((token = this.getToken()) !== null) {
|
646 | if (token !== '/') {
|
647 | continue;
|
648 | }
|
649 |
|
650 | token = this.getToken();
|
651 |
|
652 | switch (token) {
|
653 | case 'FontMatrix':
|
654 | var matrix = this.readNumberArray();
|
655 | properties.fontMatrix = matrix;
|
656 | break;
|
657 |
|
658 | case 'Encoding':
|
659 | var encodingArg = this.getToken();
|
660 | var encoding;
|
661 |
|
662 | if (!/^\d+$/.test(encodingArg)) {
|
663 | encoding = (0, _encodings.getEncoding)(encodingArg);
|
664 | } else {
|
665 | encoding = [];
|
666 | var size = parseInt(encodingArg, 10) | 0;
|
667 | this.getToken();
|
668 |
|
669 | for (var j = 0; j < size; j++) {
|
670 | token = this.getToken();
|
671 |
|
672 | while (token !== 'dup' && token !== 'def') {
|
673 | token = this.getToken();
|
674 |
|
675 | if (token === null) {
|
676 | return;
|
677 | }
|
678 | }
|
679 |
|
680 | if (token === 'def') {
|
681 | break;
|
682 | }
|
683 |
|
684 | var index = this.readInt();
|
685 | this.getToken();
|
686 | var glyph = this.getToken();
|
687 | encoding[index] = glyph;
|
688 | this.getToken();
|
689 | }
|
690 | }
|
691 |
|
692 | properties.builtInEncoding = encoding;
|
693 | break;
|
694 |
|
695 | case 'FontBBox':
|
696 | var fontBBox = this.readNumberArray();
|
697 | properties.ascent = Math.max(fontBBox[3], fontBBox[1]);
|
698 | properties.descent = Math.min(fontBBox[1], fontBBox[3]);
|
699 | properties.ascentScaled = true;
|
700 | break;
|
701 | }
|
702 | }
|
703 | }
|
704 | };
|
705 | return Type1Parser;
|
706 | }();
|
707 |
|
708 | exports.Type1Parser = Type1Parser; |
\ | No newline at end of file |