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.FontRendererFactory = void 0;
|
28 |
|
29 | var _util = require("../shared/util.js");
|
30 |
|
31 | var _cff_parser = require("./cff_parser.js");
|
32 |
|
33 | var _glyphlist = require("./glyphlist.js");
|
34 |
|
35 | var _encodings = require("./encodings.js");
|
36 |
|
37 | var _stream = require("./stream.js");
|
38 |
|
39 | function getUint32(data, offset) {
|
40 | return (data[offset] << 24 | data[offset + 1] << 16 | data[offset + 2] << 8 | data[offset + 3]) >>> 0;
|
41 | }
|
42 |
|
43 | function getUint16(data, offset) {
|
44 | return data[offset] << 8 | data[offset + 1];
|
45 | }
|
46 |
|
47 | function getInt16(data, offset) {
|
48 | return (data[offset] << 24 | data[offset + 1] << 16) >> 16;
|
49 | }
|
50 |
|
51 | function getInt8(data, offset) {
|
52 | return data[offset] << 24 >> 24;
|
53 | }
|
54 |
|
55 | function getFloat214(data, offset) {
|
56 | return getInt16(data, offset) / 16384;
|
57 | }
|
58 |
|
59 | function getSubroutineBias(subrs) {
|
60 | const numSubrs = subrs.length;
|
61 | let bias = 32768;
|
62 |
|
63 | if (numSubrs < 1240) {
|
64 | bias = 107;
|
65 | } else if (numSubrs < 33900) {
|
66 | bias = 1131;
|
67 | }
|
68 |
|
69 | return bias;
|
70 | }
|
71 |
|
72 | function parseCmap(data, start, end) {
|
73 | const offset = getUint16(data, start + 2) === 1 ? getUint32(data, start + 8) : getUint32(data, start + 16);
|
74 | const format = getUint16(data, start + offset);
|
75 | let ranges, p, i;
|
76 |
|
77 | if (format === 4) {
|
78 | getUint16(data, start + offset + 2);
|
79 | const segCount = getUint16(data, start + offset + 6) >> 1;
|
80 | p = start + offset + 14;
|
81 | ranges = [];
|
82 |
|
83 | for (i = 0; i < segCount; i++, p += 2) {
|
84 | ranges[i] = {
|
85 | end: getUint16(data, p)
|
86 | };
|
87 | }
|
88 |
|
89 | p += 2;
|
90 |
|
91 | for (i = 0; i < segCount; i++, p += 2) {
|
92 | ranges[i].start = getUint16(data, p);
|
93 | }
|
94 |
|
95 | for (i = 0; i < segCount; i++, p += 2) {
|
96 | ranges[i].idDelta = getUint16(data, p);
|
97 | }
|
98 |
|
99 | for (i = 0; i < segCount; i++, p += 2) {
|
100 | let idOffset = getUint16(data, p);
|
101 |
|
102 | if (idOffset === 0) {
|
103 | continue;
|
104 | }
|
105 |
|
106 | ranges[i].ids = [];
|
107 |
|
108 | for (let j = 0, jj = ranges[i].end - ranges[i].start + 1; j < jj; j++) {
|
109 | ranges[i].ids[j] = getUint16(data, p + idOffset);
|
110 | idOffset += 2;
|
111 | }
|
112 | }
|
113 |
|
114 | return ranges;
|
115 | } else if (format === 12) {
|
116 | const groups = getUint32(data, start + offset + 12);
|
117 | p = start + offset + 16;
|
118 | ranges = [];
|
119 |
|
120 | for (i = 0; i < groups; i++) {
|
121 | start = getUint32(data, p);
|
122 | ranges.push({
|
123 | start,
|
124 | end: getUint32(data, p + 4),
|
125 | idDelta: getUint32(data, p + 8) - start
|
126 | });
|
127 | p += 12;
|
128 | }
|
129 |
|
130 | return ranges;
|
131 | }
|
132 |
|
133 | throw new _util.FormatError(`unsupported cmap: ${format}`);
|
134 | }
|
135 |
|
136 | function parseCff(data, start, end, seacAnalysisEnabled) {
|
137 | const properties = {};
|
138 | const parser = new _cff_parser.CFFParser(new _stream.Stream(data, start, end - start), properties, seacAnalysisEnabled);
|
139 | const cff = parser.parse();
|
140 | return {
|
141 | glyphs: cff.charStrings.objects,
|
142 | subrs: cff.topDict.privateDict && cff.topDict.privateDict.subrsIndex && cff.topDict.privateDict.subrsIndex.objects,
|
143 | gsubrs: cff.globalSubrIndex && cff.globalSubrIndex.objects,
|
144 | isCFFCIDFont: cff.isCIDFont,
|
145 | fdSelect: cff.fdSelect,
|
146 | fdArray: cff.fdArray
|
147 | };
|
148 | }
|
149 |
|
150 | function parseGlyfTable(glyf, loca, isGlyphLocationsLong) {
|
151 | let itemSize, itemDecode;
|
152 |
|
153 | if (isGlyphLocationsLong) {
|
154 | itemSize = 4;
|
155 | itemDecode = getUint32;
|
156 | } else {
|
157 | itemSize = 2;
|
158 |
|
159 | itemDecode = (data, offset) => 2 * getUint16(data, offset);
|
160 | }
|
161 |
|
162 | const glyphs = [];
|
163 | let startOffset = itemDecode(loca, 0);
|
164 |
|
165 | for (let j = itemSize; j < loca.length; j += itemSize) {
|
166 | const endOffset = itemDecode(loca, j);
|
167 | glyphs.push(glyf.subarray(startOffset, endOffset));
|
168 | startOffset = endOffset;
|
169 | }
|
170 |
|
171 | return glyphs;
|
172 | }
|
173 |
|
174 | function lookupCmap(ranges, unicode) {
|
175 | const code = unicode.codePointAt(0);
|
176 | let gid = 0,
|
177 | l = 0,
|
178 | r = ranges.length - 1;
|
179 |
|
180 | while (l < r) {
|
181 | const c = l + r + 1 >> 1;
|
182 |
|
183 | if (code < ranges[c].start) {
|
184 | r = c - 1;
|
185 | } else {
|
186 | l = c;
|
187 | }
|
188 | }
|
189 |
|
190 | if (ranges[l].start <= code && code <= ranges[l].end) {
|
191 | gid = ranges[l].idDelta + (ranges[l].ids ? ranges[l].ids[code - ranges[l].start] : code) & 0xffff;
|
192 | }
|
193 |
|
194 | return {
|
195 | charCode: code,
|
196 | glyphId: gid
|
197 | };
|
198 | }
|
199 |
|
200 | function compileGlyf(code, cmds, font) {
|
201 | function moveTo(x, y) {
|
202 | cmds.push({
|
203 | cmd: "moveTo",
|
204 | args: [x, y]
|
205 | });
|
206 | }
|
207 |
|
208 | function lineTo(x, y) {
|
209 | cmds.push({
|
210 | cmd: "lineTo",
|
211 | args: [x, y]
|
212 | });
|
213 | }
|
214 |
|
215 | function quadraticCurveTo(xa, ya, x, y) {
|
216 | cmds.push({
|
217 | cmd: "quadraticCurveTo",
|
218 | args: [xa, ya, x, y]
|
219 | });
|
220 | }
|
221 |
|
222 | let i = 0;
|
223 | const numberOfContours = getInt16(code, i);
|
224 | let flags;
|
225 | let x = 0,
|
226 | y = 0;
|
227 | i += 10;
|
228 |
|
229 | if (numberOfContours < 0) {
|
230 | do {
|
231 | flags = getUint16(code, i);
|
232 | const glyphIndex = getUint16(code, i + 2);
|
233 | i += 4;
|
234 | let arg1, arg2;
|
235 |
|
236 | if (flags & 0x01) {
|
237 | if (flags & 0x02) {
|
238 | arg1 = getInt16(code, i);
|
239 | arg2 = getInt16(code, i + 2);
|
240 | } else {
|
241 | arg1 = getUint16(code, i);
|
242 | arg2 = getUint16(code, i + 2);
|
243 | }
|
244 |
|
245 | i += 4;
|
246 | } else {
|
247 | if (flags & 0x02) {
|
248 | arg1 = getInt8(code, i++);
|
249 | arg2 = getInt8(code, i++);
|
250 | } else {
|
251 | arg1 = code[i++];
|
252 | arg2 = code[i++];
|
253 | }
|
254 | }
|
255 |
|
256 | if (flags & 0x02) {
|
257 | x = arg1;
|
258 | y = arg2;
|
259 | } else {
|
260 | x = 0;
|
261 | y = 0;
|
262 | }
|
263 |
|
264 | let scaleX = 1,
|
265 | scaleY = 1,
|
266 | scale01 = 0,
|
267 | scale10 = 0;
|
268 |
|
269 | if (flags & 0x08) {
|
270 | scaleX = scaleY = getFloat214(code, i);
|
271 | i += 2;
|
272 | } else if (flags & 0x40) {
|
273 | scaleX = getFloat214(code, i);
|
274 | scaleY = getFloat214(code, i + 2);
|
275 | i += 4;
|
276 | } else if (flags & 0x80) {
|
277 | scaleX = getFloat214(code, i);
|
278 | scale01 = getFloat214(code, i + 2);
|
279 | scale10 = getFloat214(code, i + 4);
|
280 | scaleY = getFloat214(code, i + 6);
|
281 | i += 8;
|
282 | }
|
283 |
|
284 | const subglyph = font.glyphs[glyphIndex];
|
285 |
|
286 | if (subglyph) {
|
287 | cmds.push({
|
288 | cmd: "save"
|
289 | }, {
|
290 | cmd: "transform",
|
291 | args: [scaleX, scale01, scale10, scaleY, x, y]
|
292 | });
|
293 |
|
294 | if (!(flags & 0x02)) {}
|
295 |
|
296 | compileGlyf(subglyph, cmds, font);
|
297 | cmds.push({
|
298 | cmd: "restore"
|
299 | });
|
300 | }
|
301 | } while (flags & 0x20);
|
302 | } else {
|
303 | const endPtsOfContours = [];
|
304 | let j, jj;
|
305 |
|
306 | for (j = 0; j < numberOfContours; j++) {
|
307 | endPtsOfContours.push(getUint16(code, i));
|
308 | i += 2;
|
309 | }
|
310 |
|
311 | const instructionLength = getUint16(code, i);
|
312 | i += 2 + instructionLength;
|
313 | const numberOfPoints = endPtsOfContours.at(-1) + 1;
|
314 | const points = [];
|
315 |
|
316 | while (points.length < numberOfPoints) {
|
317 | flags = code[i++];
|
318 | let repeat = 1;
|
319 |
|
320 | if (flags & 0x08) {
|
321 | repeat += code[i++];
|
322 | }
|
323 |
|
324 | while (repeat-- > 0) {
|
325 | points.push({
|
326 | flags
|
327 | });
|
328 | }
|
329 | }
|
330 |
|
331 | for (j = 0; j < numberOfPoints; j++) {
|
332 | switch (points[j].flags & 0x12) {
|
333 | case 0x00:
|
334 | x += getInt16(code, i);
|
335 | i += 2;
|
336 | break;
|
337 |
|
338 | case 0x02:
|
339 | x -= code[i++];
|
340 | break;
|
341 |
|
342 | case 0x12:
|
343 | x += code[i++];
|
344 | break;
|
345 | }
|
346 |
|
347 | points[j].x = x;
|
348 | }
|
349 |
|
350 | for (j = 0; j < numberOfPoints; j++) {
|
351 | switch (points[j].flags & 0x24) {
|
352 | case 0x00:
|
353 | y += getInt16(code, i);
|
354 | i += 2;
|
355 | break;
|
356 |
|
357 | case 0x04:
|
358 | y -= code[i++];
|
359 | break;
|
360 |
|
361 | case 0x24:
|
362 | y += code[i++];
|
363 | break;
|
364 | }
|
365 |
|
366 | points[j].y = y;
|
367 | }
|
368 |
|
369 | let startPoint = 0;
|
370 |
|
371 | for (i = 0; i < numberOfContours; i++) {
|
372 | const endPoint = endPtsOfContours[i];
|
373 | const contour = points.slice(startPoint, endPoint + 1);
|
374 |
|
375 | if (contour[0].flags & 1) {
|
376 | contour.push(contour[0]);
|
377 | } else if (contour.at(-1).flags & 1) {
|
378 | contour.unshift(contour.at(-1));
|
379 | } else {
|
380 | const p = {
|
381 | flags: 1,
|
382 | x: (contour[0].x + contour.at(-1).x) / 2,
|
383 | y: (contour[0].y + contour.at(-1).y) / 2
|
384 | };
|
385 | contour.unshift(p);
|
386 | contour.push(p);
|
387 | }
|
388 |
|
389 | moveTo(contour[0].x, contour[0].y);
|
390 |
|
391 | for (j = 1, jj = contour.length; j < jj; j++) {
|
392 | if (contour[j].flags & 1) {
|
393 | lineTo(contour[j].x, contour[j].y);
|
394 | } else if (contour[j + 1].flags & 1) {
|
395 | quadraticCurveTo(contour[j].x, contour[j].y, contour[j + 1].x, contour[j + 1].y);
|
396 | j++;
|
397 | } else {
|
398 | quadraticCurveTo(contour[j].x, contour[j].y, (contour[j].x + contour[j + 1].x) / 2, (contour[j].y + contour[j + 1].y) / 2);
|
399 | }
|
400 | }
|
401 |
|
402 | startPoint = endPoint + 1;
|
403 | }
|
404 | }
|
405 | }
|
406 |
|
407 | function compileCharString(charStringCode, cmds, font, glyphId) {
|
408 | function moveTo(x, y) {
|
409 | cmds.push({
|
410 | cmd: "moveTo",
|
411 | args: [x, y]
|
412 | });
|
413 | }
|
414 |
|
415 | function lineTo(x, y) {
|
416 | cmds.push({
|
417 | cmd: "lineTo",
|
418 | args: [x, y]
|
419 | });
|
420 | }
|
421 |
|
422 | function bezierCurveTo(x1, y1, x2, y2, x, y) {
|
423 | cmds.push({
|
424 | cmd: "bezierCurveTo",
|
425 | args: [x1, y1, x2, y2, x, y]
|
426 | });
|
427 | }
|
428 |
|
429 | const stack = [];
|
430 | let x = 0,
|
431 | y = 0;
|
432 | let stems = 0;
|
433 |
|
434 | function parse(code) {
|
435 | let i = 0;
|
436 |
|
437 | while (i < code.length) {
|
438 | let stackClean = false;
|
439 | let v = code[i++];
|
440 | let xa, xb, ya, yb, y1, y2, y3, n, subrCode;
|
441 |
|
442 | switch (v) {
|
443 | case 1:
|
444 | stems += stack.length >> 1;
|
445 | stackClean = true;
|
446 | break;
|
447 |
|
448 | case 3:
|
449 | stems += stack.length >> 1;
|
450 | stackClean = true;
|
451 | break;
|
452 |
|
453 | case 4:
|
454 | y += stack.pop();
|
455 | moveTo(x, y);
|
456 | stackClean = true;
|
457 | break;
|
458 |
|
459 | case 5:
|
460 | while (stack.length > 0) {
|
461 | x += stack.shift();
|
462 | y += stack.shift();
|
463 | lineTo(x, y);
|
464 | }
|
465 |
|
466 | break;
|
467 |
|
468 | case 6:
|
469 | while (stack.length > 0) {
|
470 | x += stack.shift();
|
471 | lineTo(x, y);
|
472 |
|
473 | if (stack.length === 0) {
|
474 | break;
|
475 | }
|
476 |
|
477 | y += stack.shift();
|
478 | lineTo(x, y);
|
479 | }
|
480 |
|
481 | break;
|
482 |
|
483 | case 7:
|
484 | while (stack.length > 0) {
|
485 | y += stack.shift();
|
486 | lineTo(x, y);
|
487 |
|
488 | if (stack.length === 0) {
|
489 | break;
|
490 | }
|
491 |
|
492 | x += stack.shift();
|
493 | lineTo(x, y);
|
494 | }
|
495 |
|
496 | break;
|
497 |
|
498 | case 8:
|
499 | while (stack.length > 0) {
|
500 | xa = x + stack.shift();
|
501 | ya = y + stack.shift();
|
502 | xb = xa + stack.shift();
|
503 | yb = ya + stack.shift();
|
504 | x = xb + stack.shift();
|
505 | y = yb + stack.shift();
|
506 | bezierCurveTo(xa, ya, xb, yb, x, y);
|
507 | }
|
508 |
|
509 | break;
|
510 |
|
511 | case 10:
|
512 | n = stack.pop();
|
513 | subrCode = null;
|
514 |
|
515 | if (font.isCFFCIDFont) {
|
516 | const fdIndex = font.fdSelect.getFDIndex(glyphId);
|
517 |
|
518 | if (fdIndex >= 0 && fdIndex < font.fdArray.length) {
|
519 | const fontDict = font.fdArray[fdIndex];
|
520 | let subrs;
|
521 |
|
522 | if (fontDict.privateDict && fontDict.privateDict.subrsIndex) {
|
523 | subrs = fontDict.privateDict.subrsIndex.objects;
|
524 | }
|
525 |
|
526 | if (subrs) {
|
527 | n += getSubroutineBias(subrs);
|
528 | subrCode = subrs[n];
|
529 | }
|
530 | } else {
|
531 | (0, _util.warn)("Invalid fd index for glyph index.");
|
532 | }
|
533 | } else {
|
534 | subrCode = font.subrs[n + font.subrsBias];
|
535 | }
|
536 |
|
537 | if (subrCode) {
|
538 | parse(subrCode);
|
539 | }
|
540 |
|
541 | break;
|
542 |
|
543 | case 11:
|
544 | return;
|
545 |
|
546 | case 12:
|
547 | v = code[i++];
|
548 |
|
549 | switch (v) {
|
550 | case 34:
|
551 | xa = x + stack.shift();
|
552 | xb = xa + stack.shift();
|
553 | y1 = y + stack.shift();
|
554 | x = xb + stack.shift();
|
555 | bezierCurveTo(xa, y, xb, y1, x, y1);
|
556 | xa = x + stack.shift();
|
557 | xb = xa + stack.shift();
|
558 | x = xb + stack.shift();
|
559 | bezierCurveTo(xa, y1, xb, y, x, y);
|
560 | break;
|
561 |
|
562 | case 35:
|
563 | xa = x + stack.shift();
|
564 | ya = y + stack.shift();
|
565 | xb = xa + stack.shift();
|
566 | yb = ya + stack.shift();
|
567 | x = xb + stack.shift();
|
568 | y = yb + stack.shift();
|
569 | bezierCurveTo(xa, ya, xb, yb, x, y);
|
570 | xa = x + stack.shift();
|
571 | ya = y + stack.shift();
|
572 | xb = xa + stack.shift();
|
573 | yb = ya + stack.shift();
|
574 | x = xb + stack.shift();
|
575 | y = yb + stack.shift();
|
576 | bezierCurveTo(xa, ya, xb, yb, x, y);
|
577 | stack.pop();
|
578 | break;
|
579 |
|
580 | case 36:
|
581 | xa = x + stack.shift();
|
582 | y1 = y + stack.shift();
|
583 | xb = xa + stack.shift();
|
584 | y2 = y1 + stack.shift();
|
585 | x = xb + stack.shift();
|
586 | bezierCurveTo(xa, y1, xb, y2, x, y2);
|
587 | xa = x + stack.shift();
|
588 | xb = xa + stack.shift();
|
589 | y3 = y2 + stack.shift();
|
590 | x = xb + stack.shift();
|
591 | bezierCurveTo(xa, y2, xb, y3, x, y);
|
592 | break;
|
593 |
|
594 | case 37:
|
595 | const x0 = x,
|
596 | y0 = y;
|
597 | xa = x + stack.shift();
|
598 | ya = y + stack.shift();
|
599 | xb = xa + stack.shift();
|
600 | yb = ya + stack.shift();
|
601 | x = xb + stack.shift();
|
602 | y = yb + stack.shift();
|
603 | bezierCurveTo(xa, ya, xb, yb, x, y);
|
604 | xa = x + stack.shift();
|
605 | ya = y + stack.shift();
|
606 | xb = xa + stack.shift();
|
607 | yb = ya + stack.shift();
|
608 | x = xb;
|
609 | y = yb;
|
610 |
|
611 | if (Math.abs(x - x0) > Math.abs(y - y0)) {
|
612 | x += stack.shift();
|
613 | } else {
|
614 | y += stack.shift();
|
615 | }
|
616 |
|
617 | bezierCurveTo(xa, ya, xb, yb, x, y);
|
618 | break;
|
619 |
|
620 | default:
|
621 | throw new _util.FormatError(`unknown operator: 12 ${v}`);
|
622 | }
|
623 |
|
624 | break;
|
625 |
|
626 | case 14:
|
627 | if (stack.length >= 4) {
|
628 | const achar = stack.pop();
|
629 | const bchar = stack.pop();
|
630 | y = stack.pop();
|
631 | x = stack.pop();
|
632 | cmds.push({
|
633 | cmd: "save"
|
634 | }, {
|
635 | cmd: "translate",
|
636 | args: [x, y]
|
637 | });
|
638 | let cmap = lookupCmap(font.cmap, String.fromCharCode(font.glyphNameMap[_encodings.StandardEncoding[achar]]));
|
639 | compileCharString(font.glyphs[cmap.glyphId], cmds, font, cmap.glyphId);
|
640 | cmds.push({
|
641 | cmd: "restore"
|
642 | });
|
643 | cmap = lookupCmap(font.cmap, String.fromCharCode(font.glyphNameMap[_encodings.StandardEncoding[bchar]]));
|
644 | compileCharString(font.glyphs[cmap.glyphId], cmds, font, cmap.glyphId);
|
645 | }
|
646 |
|
647 | return;
|
648 |
|
649 | case 18:
|
650 | stems += stack.length >> 1;
|
651 | stackClean = true;
|
652 | break;
|
653 |
|
654 | case 19:
|
655 | stems += stack.length >> 1;
|
656 | i += stems + 7 >> 3;
|
657 | stackClean = true;
|
658 | break;
|
659 |
|
660 | case 20:
|
661 | stems += stack.length >> 1;
|
662 | i += stems + 7 >> 3;
|
663 | stackClean = true;
|
664 | break;
|
665 |
|
666 | case 21:
|
667 | y += stack.pop();
|
668 | x += stack.pop();
|
669 | moveTo(x, y);
|
670 | stackClean = true;
|
671 | break;
|
672 |
|
673 | case 22:
|
674 | x += stack.pop();
|
675 | moveTo(x, y);
|
676 | stackClean = true;
|
677 | break;
|
678 |
|
679 | case 23:
|
680 | stems += stack.length >> 1;
|
681 | stackClean = true;
|
682 | break;
|
683 |
|
684 | case 24:
|
685 | while (stack.length > 2) {
|
686 | xa = x + stack.shift();
|
687 | ya = y + stack.shift();
|
688 | xb = xa + stack.shift();
|
689 | yb = ya + stack.shift();
|
690 | x = xb + stack.shift();
|
691 | y = yb + stack.shift();
|
692 | bezierCurveTo(xa, ya, xb, yb, x, y);
|
693 | }
|
694 |
|
695 | x += stack.shift();
|
696 | y += stack.shift();
|
697 | lineTo(x, y);
|
698 | break;
|
699 |
|
700 | case 25:
|
701 | while (stack.length > 6) {
|
702 | x += stack.shift();
|
703 | y += stack.shift();
|
704 | lineTo(x, y);
|
705 | }
|
706 |
|
707 | xa = x + stack.shift();
|
708 | ya = y + stack.shift();
|
709 | xb = xa + stack.shift();
|
710 | yb = ya + stack.shift();
|
711 | x = xb + stack.shift();
|
712 | y = yb + stack.shift();
|
713 | bezierCurveTo(xa, ya, xb, yb, x, y);
|
714 | break;
|
715 |
|
716 | case 26:
|
717 | if (stack.length % 2) {
|
718 | x += stack.shift();
|
719 | }
|
720 |
|
721 | while (stack.length > 0) {
|
722 | xa = x;
|
723 | ya = y + stack.shift();
|
724 | xb = xa + stack.shift();
|
725 | yb = ya + stack.shift();
|
726 | x = xb;
|
727 | y = yb + stack.shift();
|
728 | bezierCurveTo(xa, ya, xb, yb, x, y);
|
729 | }
|
730 |
|
731 | break;
|
732 |
|
733 | case 27:
|
734 | if (stack.length % 2) {
|
735 | y += stack.shift();
|
736 | }
|
737 |
|
738 | while (stack.length > 0) {
|
739 | xa = x + stack.shift();
|
740 | ya = y;
|
741 | xb = xa + stack.shift();
|
742 | yb = ya + stack.shift();
|
743 | x = xb + stack.shift();
|
744 | y = yb;
|
745 | bezierCurveTo(xa, ya, xb, yb, x, y);
|
746 | }
|
747 |
|
748 | break;
|
749 |
|
750 | case 28:
|
751 | stack.push((code[i] << 24 | code[i + 1] << 16) >> 16);
|
752 | i += 2;
|
753 | break;
|
754 |
|
755 | case 29:
|
756 | n = stack.pop() + font.gsubrsBias;
|
757 | subrCode = font.gsubrs[n];
|
758 |
|
759 | if (subrCode) {
|
760 | parse(subrCode);
|
761 | }
|
762 |
|
763 | break;
|
764 |
|
765 | case 30:
|
766 | while (stack.length > 0) {
|
767 | xa = x;
|
768 | ya = y + stack.shift();
|
769 | xb = xa + stack.shift();
|
770 | yb = ya + stack.shift();
|
771 | x = xb + stack.shift();
|
772 | y = yb + (stack.length === 1 ? stack.shift() : 0);
|
773 | bezierCurveTo(xa, ya, xb, yb, x, y);
|
774 |
|
775 | if (stack.length === 0) {
|
776 | break;
|
777 | }
|
778 |
|
779 | xa = x + stack.shift();
|
780 | ya = y;
|
781 | xb = xa + stack.shift();
|
782 | yb = ya + stack.shift();
|
783 | y = yb + stack.shift();
|
784 | x = xb + (stack.length === 1 ? stack.shift() : 0);
|
785 | bezierCurveTo(xa, ya, xb, yb, x, y);
|
786 | }
|
787 |
|
788 | break;
|
789 |
|
790 | case 31:
|
791 | while (stack.length > 0) {
|
792 | xa = x + stack.shift();
|
793 | ya = y;
|
794 | xb = xa + stack.shift();
|
795 | yb = ya + stack.shift();
|
796 | y = yb + stack.shift();
|
797 | x = xb + (stack.length === 1 ? stack.shift() : 0);
|
798 | bezierCurveTo(xa, ya, xb, yb, x, y);
|
799 |
|
800 | if (stack.length === 0) {
|
801 | break;
|
802 | }
|
803 |
|
804 | xa = x;
|
805 | ya = y + stack.shift();
|
806 | xb = xa + stack.shift();
|
807 | yb = ya + stack.shift();
|
808 | x = xb + stack.shift();
|
809 | y = yb + (stack.length === 1 ? stack.shift() : 0);
|
810 | bezierCurveTo(xa, ya, xb, yb, x, y);
|
811 | }
|
812 |
|
813 | break;
|
814 |
|
815 | default:
|
816 | if (v < 32) {
|
817 | throw new _util.FormatError(`unknown operator: ${v}`);
|
818 | }
|
819 |
|
820 | if (v < 247) {
|
821 | stack.push(v - 139);
|
822 | } else if (v < 251) {
|
823 | stack.push((v - 247) * 256 + code[i++] + 108);
|
824 | } else if (v < 255) {
|
825 | stack.push(-(v - 251) * 256 - code[i++] - 108);
|
826 | } else {
|
827 | stack.push((code[i] << 24 | code[i + 1] << 16 | code[i + 2] << 8 | code[i + 3]) / 65536);
|
828 | i += 4;
|
829 | }
|
830 |
|
831 | break;
|
832 | }
|
833 |
|
834 | if (stackClean) {
|
835 | stack.length = 0;
|
836 | }
|
837 | }
|
838 | }
|
839 |
|
840 | parse(charStringCode);
|
841 | }
|
842 |
|
843 | const NOOP = [];
|
844 |
|
845 | class CompiledFont {
|
846 | constructor(fontMatrix) {
|
847 | if (this.constructor === CompiledFont) {
|
848 | (0, _util.unreachable)("Cannot initialize CompiledFont.");
|
849 | }
|
850 |
|
851 | this.fontMatrix = fontMatrix;
|
852 | this.compiledGlyphs = Object.create(null);
|
853 | this.compiledCharCodeToGlyphId = Object.create(null);
|
854 | }
|
855 |
|
856 | getPathJs(unicode) {
|
857 | const {
|
858 | charCode,
|
859 | glyphId
|
860 | } = lookupCmap(this.cmap, unicode);
|
861 | let fn = this.compiledGlyphs[glyphId];
|
862 |
|
863 | if (!fn) {
|
864 | try {
|
865 | fn = this.compileGlyph(this.glyphs[glyphId], glyphId);
|
866 | this.compiledGlyphs[glyphId] = fn;
|
867 | } catch (ex) {
|
868 | this.compiledGlyphs[glyphId] = NOOP;
|
869 |
|
870 | if (this.compiledCharCodeToGlyphId[charCode] === undefined) {
|
871 | this.compiledCharCodeToGlyphId[charCode] = glyphId;
|
872 | }
|
873 |
|
874 | throw ex;
|
875 | }
|
876 | }
|
877 |
|
878 | if (this.compiledCharCodeToGlyphId[charCode] === undefined) {
|
879 | this.compiledCharCodeToGlyphId[charCode] = glyphId;
|
880 | }
|
881 |
|
882 | return fn;
|
883 | }
|
884 |
|
885 | compileGlyph(code, glyphId) {
|
886 | if (!code || code.length === 0 || code[0] === 14) {
|
887 | return NOOP;
|
888 | }
|
889 |
|
890 | let fontMatrix = this.fontMatrix;
|
891 |
|
892 | if (this.isCFFCIDFont) {
|
893 | const fdIndex = this.fdSelect.getFDIndex(glyphId);
|
894 |
|
895 | if (fdIndex >= 0 && fdIndex < this.fdArray.length) {
|
896 | const fontDict = this.fdArray[fdIndex];
|
897 | fontMatrix = fontDict.getByName("FontMatrix") || _util.FONT_IDENTITY_MATRIX;
|
898 | } else {
|
899 | (0, _util.warn)("Invalid fd index for glyph index.");
|
900 | }
|
901 | }
|
902 |
|
903 | const cmds = [{
|
904 | cmd: "save"
|
905 | }, {
|
906 | cmd: "transform",
|
907 | args: fontMatrix.slice()
|
908 | }, {
|
909 | cmd: "scale",
|
910 | args: ["size", "-size"]
|
911 | }];
|
912 | this.compileGlyphImpl(code, cmds, glyphId);
|
913 | cmds.push({
|
914 | cmd: "restore"
|
915 | });
|
916 | return cmds;
|
917 | }
|
918 |
|
919 | compileGlyphImpl() {
|
920 | (0, _util.unreachable)("Children classes should implement this.");
|
921 | }
|
922 |
|
923 | hasBuiltPath(unicode) {
|
924 | const {
|
925 | charCode,
|
926 | glyphId
|
927 | } = lookupCmap(this.cmap, unicode);
|
928 | return this.compiledGlyphs[glyphId] !== undefined && this.compiledCharCodeToGlyphId[charCode] !== undefined;
|
929 | }
|
930 |
|
931 | }
|
932 |
|
933 | class TrueTypeCompiled extends CompiledFont {
|
934 | constructor(glyphs, cmap, fontMatrix) {
|
935 | super(fontMatrix || [0.000488, 0, 0, 0.000488, 0, 0]);
|
936 | this.glyphs = glyphs;
|
937 | this.cmap = cmap;
|
938 | }
|
939 |
|
940 | compileGlyphImpl(code, cmds) {
|
941 | compileGlyf(code, cmds, this);
|
942 | }
|
943 |
|
944 | }
|
945 |
|
946 | class Type2Compiled extends CompiledFont {
|
947 | constructor(cffInfo, cmap, fontMatrix, glyphNameMap) {
|
948 | super(fontMatrix || [0.001, 0, 0, 0.001, 0, 0]);
|
949 | this.glyphs = cffInfo.glyphs;
|
950 | this.gsubrs = cffInfo.gsubrs || [];
|
951 | this.subrs = cffInfo.subrs || [];
|
952 | this.cmap = cmap;
|
953 | this.glyphNameMap = glyphNameMap || (0, _glyphlist.getGlyphsUnicode)();
|
954 | this.gsubrsBias = getSubroutineBias(this.gsubrs);
|
955 | this.subrsBias = getSubroutineBias(this.subrs);
|
956 | this.isCFFCIDFont = cffInfo.isCFFCIDFont;
|
957 | this.fdSelect = cffInfo.fdSelect;
|
958 | this.fdArray = cffInfo.fdArray;
|
959 | }
|
960 |
|
961 | compileGlyphImpl(code, cmds, glyphId) {
|
962 | compileCharString(code, cmds, this, glyphId);
|
963 | }
|
964 |
|
965 | }
|
966 |
|
967 | class FontRendererFactory {
|
968 | static create(font, seacAnalysisEnabled) {
|
969 | const data = new Uint8Array(font.data);
|
970 | let cmap, glyf, loca, cff, indexToLocFormat, unitsPerEm;
|
971 | const numTables = getUint16(data, 4);
|
972 |
|
973 | for (let i = 0, p = 12; i < numTables; i++, p += 16) {
|
974 | const tag = (0, _util.bytesToString)(data.subarray(p, p + 4));
|
975 | const offset = getUint32(data, p + 8);
|
976 | const length = getUint32(data, p + 12);
|
977 |
|
978 | switch (tag) {
|
979 | case "cmap":
|
980 | cmap = parseCmap(data, offset, offset + length);
|
981 | break;
|
982 |
|
983 | case "glyf":
|
984 | glyf = data.subarray(offset, offset + length);
|
985 | break;
|
986 |
|
987 | case "loca":
|
988 | loca = data.subarray(offset, offset + length);
|
989 | break;
|
990 |
|
991 | case "head":
|
992 | unitsPerEm = getUint16(data, offset + 18);
|
993 | indexToLocFormat = getUint16(data, offset + 50);
|
994 | break;
|
995 |
|
996 | case "CFF ":
|
997 | cff = parseCff(data, offset, offset + length, seacAnalysisEnabled);
|
998 | break;
|
999 | }
|
1000 | }
|
1001 |
|
1002 | if (glyf) {
|
1003 | const fontMatrix = !unitsPerEm ? font.fontMatrix : [1 / unitsPerEm, 0, 0, 1 / unitsPerEm, 0, 0];
|
1004 | return new TrueTypeCompiled(parseGlyfTable(glyf, loca, indexToLocFormat), cmap, fontMatrix);
|
1005 | }
|
1006 |
|
1007 | return new Type2Compiled(cff, cmap, font.fontMatrix, font.glyphNameMap);
|
1008 | }
|
1009 |
|
1010 | }
|
1011 |
|
1012 | exports.FontRendererFactory = FontRendererFactory; |
\ | No newline at end of file |