UNPKG

19 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.Type1Parser = void 0;
28
29var _util = require("../shared/util");
30
31var _encodings = require("./encodings");
32
33var _stream = require("./stream");
34
35var HINTING_ENABLED = false;
36
37var 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
340var 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
708exports.Type1Parser = Type1Parser;
\No newline at end of file