pdf-parser.js 5.2.27 → 5.2.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{afm-widths-BYiX2L9D.cjs → afm-widths-BHD2HZIL.cjs} +553 -642
- package/dist/{afm-widths-blUDDjlI.js → afm-widths-B_IGbGky.js} +553 -642
- package/dist/afm-widths.cjs +1 -1
- package/dist/afm-widths.js +1 -1
- package/dist/annotations.cjs +5 -3
- package/dist/annotations.js +5 -3
- package/dist/builtin-encoding.cjs +17 -6
- package/dist/builtin-encoding.js +17 -6
- package/dist/bytes/crc32.cjs +5 -3
- package/dist/bytes/crc32.js +5 -3
- package/dist/bytes/flate.cjs +0 -1
- package/dist/bytes/flate.js +0 -1
- package/dist/cff-bounds.cjs +42 -24
- package/dist/cff-bounds.js +42 -24
- package/dist/cff.cjs +13 -6
- package/dist/cff.js +13 -6
- package/dist/cmap-table.cjs +54 -26
- package/dist/cmap-table.js +54 -26
- package/dist/cmap.cjs +5 -3
- package/dist/cmap.js +5 -3
- package/dist/content-write.cjs +3 -2
- package/dist/content-write.js +3 -2
- package/dist/crypto/aes.cjs +76 -55
- package/dist/crypto/aes.js +76 -55
- package/dist/crypto/md5.cjs +345 -149
- package/dist/crypto/md5.js +345 -149
- package/dist/crypto/rc4.cjs +5 -4
- package/dist/crypto/rc4.js +5 -4
- package/dist/crypto/sha2.cjs +92 -43
- package/dist/crypto/sha2.js +92 -43
- package/dist/embedded-font.cjs +4 -2
- package/dist/embedded-font.js +4 -2
- package/dist/encoding.cjs +1 -1
- package/dist/encoding.js +1 -1
- package/dist/encrypt-write.cjs +25 -21
- package/dist/encrypt-write.js +25 -21
- package/dist/encrypt.cjs +43 -64
- package/dist/encrypt.js +43 -64
- package/dist/filters.cjs +40 -20
- package/dist/filters.js +40 -20
- package/dist/font-face.cjs +2 -1
- package/dist/font-face.js +2 -1
- package/dist/font-read.cjs +12 -6
- package/dist/font-read.js +12 -6
- package/dist/font-tables.cjs +55 -28
- package/dist/font-tables.js +55 -28
- package/dist/gdef-table.cjs +6 -3
- package/dist/gdef-table.js +6 -3
- package/dist/glyf-contours.cjs +3 -1
- package/dist/glyf-contours.js +3 -1
- package/dist/glyf.cjs +22 -11
- package/dist/glyf.js +22 -11
- package/dist/gpos-table.cjs +37 -19
- package/dist/gpos-table.js +37 -19
- package/dist/gsub-table.cjs +28 -17
- package/dist/gsub-table.js +28 -17
- package/dist/hmtx-table.cjs +5 -3
- package/dist/hmtx-table.js +5 -3
- package/dist/image/ccitt-encode.cjs +8 -5
- package/dist/image/ccitt-encode.js +8 -5
- package/dist/image/ccitt.cjs +801 -209
- package/dist/image/ccitt.js +801 -209
- package/dist/image/jbig2-arith.cjs +340 -306
- package/dist/image/jbig2-arith.js +340 -306
- package/dist/image/jbig2-bitmap.cjs +8 -5
- package/dist/image/jbig2-bitmap.js +8 -5
- package/dist/image/jbig2-generic.cjs +240 -69
- package/dist/image/jbig2-generic.js +240 -69
- package/dist/image/jbig2-text.cjs +4 -2
- package/dist/image/jbig2-text.js +4 -2
- package/dist/image/jbig2.cjs +69 -31
- package/dist/image/jbig2.js +69 -31
- package/dist/image/jp2-boxes.cjs +52 -27
- package/dist/image/jp2-boxes.js +52 -27
- package/dist/image/jpeg-info.cjs +29 -24
- package/dist/image/jpeg-info.js +29 -24
- package/dist/image/jpeg2000-codestream.cjs +44 -24
- package/dist/image/jpeg2000-codestream.js +44 -24
- package/dist/image/jpeg2000-dwt.cjs +2 -1
- package/dist/image/jpeg2000-dwt.js +2 -1
- package/dist/image/jpeg2000-t1.cjs +21 -11
- package/dist/image/jpeg2000-t1.js +21 -11
- package/dist/image/jpeg2000-t2.cjs +12 -5
- package/dist/image/jpeg2000-t2.js +12 -5
- package/dist/image/jpeg2000-tagtree.cjs +8 -4
- package/dist/image/jpeg2000-tagtree.js +8 -4
- package/dist/image/jpeg2000.cjs +11 -6
- package/dist/image/jpeg2000.js +11 -6
- package/dist/image/png-decode.cjs +62 -39
- package/dist/image/png-decode.d.cts +3 -2
- package/dist/image/png-decode.d.ts +3 -2
- package/dist/image/png-decode.js +62 -39
- package/dist/image/png-filter.cjs +23 -15
- package/dist/image/png-filter.js +23 -15
- package/dist/images-read.cjs +41 -36
- package/dist/images-read.js +41 -36
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/interpret.cjs +39 -24
- package/dist/interpret.js +39 -24
- package/dist/lexer.cjs +86 -51
- package/dist/lexer.js +86 -51
- package/dist/math-content-write.cjs +9 -5
- package/dist/math-content-write.js +9 -5
- package/dist/math-font-write.cjs +2 -1
- package/dist/math-font-write.js +2 -1
- package/dist/math-font.cjs +16 -7
- package/dist/math-font.js +16 -7
- package/dist/math-table.cjs +25 -13
- package/dist/math-table.js +25 -13
- package/dist/matrix.cjs +3 -2
- package/dist/matrix.js +3 -2
- package/dist/measure.cjs +1 -1
- package/dist/measure.js +1 -1
- package/dist/navigation.cjs +10 -5
- package/dist/navigation.js +10 -5
- package/dist/ot-layout-common.cjs +4 -2
- package/dist/ot-layout-common.js +4 -2
- package/dist/parse.cjs +7 -5
- package/dist/parse.js +7 -5
- package/dist/pdf-text.cjs +5 -2
- package/dist/pdf-text.js +5 -2
- package/dist/predictors.cjs +14 -9
- package/dist/predictors.js +14 -9
- package/dist/raster.cjs +30 -19
- package/dist/raster.js +30 -19
- package/dist/read.cjs +17 -8
- package/dist/read.js +17 -8
- package/dist/serialize.cjs +6 -2
- package/dist/serialize.js +6 -2
- package/dist/sfnt-subset.cjs +25 -13
- package/dist/sfnt-subset.js +25 -13
- package/dist/sfnt.cjs +20 -10
- package/dist/sfnt.js +20 -10
- package/dist/text-group.cjs +0 -1
- package/dist/text-group.js +0 -1
- package/dist/tounicode.cjs +4 -2
- package/dist/tounicode.js +4 -2
- package/dist/winansi.cjs +1 -1
- package/dist/winansi.js +1 -1
- package/dist/write.cjs +20 -12
- package/dist/write.js +20 -12
- package/dist/xref.cjs +5 -2
- package/dist/xref.js +5 -2
- package/package.json +2 -2
package/dist/raster.cjs
CHANGED
|
@@ -17,13 +17,16 @@ const require_read = require("./read.cjs");
|
|
|
17
17
|
const require_glyf = require("./glyf.cjs");
|
|
18
18
|
const require_glyf_contours = require("./glyf-contours.cjs");
|
|
19
19
|
//#region src/raster.ts
|
|
20
|
+
const PIXEL_ROUNDING_EPSILON = 1e-9;
|
|
21
|
+
const POINTS_PER_INCH = 72;
|
|
22
|
+
const EXTENT_ZERO_EPSILON = 1e-9;
|
|
20
23
|
function regionPixels(extentPt, scale) {
|
|
21
|
-
return Math.max(1, Math.ceil(extentPt * scale -
|
|
24
|
+
return Math.max(1, Math.ceil(extentPt * scale - PIXEL_ROUNDING_EPSILON));
|
|
22
25
|
}
|
|
23
26
|
function renderPdfPage(pdfBytes, pageIndex, options, rasteriser) {
|
|
24
27
|
const sink = options.sink ?? require_diagnostics.NOOP_DIAGNOSTIC_SINK;
|
|
25
28
|
if (options.scale !== void 0 && options.dpi !== void 0) throw new Error("renderPdfPage accepts at most one of scale and dpi; they name the same factor in different units (dpi = 72 x scale)");
|
|
26
|
-
const scale = options.dpi !== void 0 ? options.dpi /
|
|
29
|
+
const scale = options.dpi !== void 0 ? options.dpi / POINTS_PER_INCH : options.scale;
|
|
27
30
|
if (scale !== void 0 && !(scale > 0)) throw new Error(`renderPdfPage requires a positive scale factor (received scale ${options.scale}, dpi ${options.dpi})`);
|
|
28
31
|
const pixelsPerPt = scale ?? 1;
|
|
29
32
|
if (options.clipPt !== void 0 && (!(options.clipPt.widthPt > 0) || !(options.clipPt.heightPt > 0))) throw new Error("renderPdfPage clipPt must carry positive widthPt and heightPt; a zero-extent region has no pixels to render");
|
|
@@ -195,6 +198,7 @@ function drawRect(item, matrix, pixelsPerPt, rasteriser) {
|
|
|
195
198
|
stroke: strokeSpec(item.stroke, void 0, pixelsPerPt)
|
|
196
199
|
});
|
|
197
200
|
}
|
|
201
|
+
const ELLIPSE_QUADRANT_COUNT = 4;
|
|
198
202
|
function drawEllipse(item, matrix, pixelsPerPt, rasteriser) {
|
|
199
203
|
const cx = item.xPt + item.widthPt / 2;
|
|
200
204
|
const cy = item.yPt + item.heightPt / 2;
|
|
@@ -224,8 +228,8 @@ function drawEllipse(item, matrix, pixelsPerPt, rasteriser) {
|
|
|
224
228
|
});
|
|
225
229
|
const start = transformed(cardinalPoints[0]);
|
|
226
230
|
const segments = [];
|
|
227
|
-
for (let i = 0; i <
|
|
228
|
-
const end = transformed(cardinalPoints[(i + 1) %
|
|
231
|
+
for (let i = 0; i < ELLIPSE_QUADRANT_COUNT; i++) {
|
|
232
|
+
const end = transformed(cardinalPoints[(i + 1) % ELLIPSE_QUADRANT_COUNT]);
|
|
229
233
|
const c1 = transformed(controls[i * 2]);
|
|
230
234
|
const c2 = transformed(controls[i * 2 + 1]);
|
|
231
235
|
segments.push({
|
|
@@ -402,12 +406,13 @@ function drawImage(image, ctm, interpretToDeviceMatrix, rasteriser) {
|
|
|
402
406
|
matrix
|
|
403
407
|
});
|
|
404
408
|
}
|
|
409
|
+
const DASHED_STROKE_WIDTH_MULTIPLE = 3;
|
|
405
410
|
function strokeSpec(stroke, style, pixelsPerPt) {
|
|
406
411
|
const widthPx = stroke.widthPt * pixelsPerPt;
|
|
407
412
|
if (style === "dashed") return {
|
|
408
413
|
color: stroke.color,
|
|
409
414
|
widthPx,
|
|
410
|
-
dashPx: [widthPx *
|
|
415
|
+
dashPx: [widthPx * DASHED_STROKE_WIDTH_MULTIPLE, widthPx * DASHED_STROKE_WIDTH_MULTIPLE]
|
|
411
416
|
};
|
|
412
417
|
return {
|
|
413
418
|
color: stroke.color,
|
|
@@ -475,6 +480,7 @@ function flattenCubic(p0, c1, c2, p1) {
|
|
|
475
480
|
flatten(p0, c1, c2, p1, 0);
|
|
476
481
|
return points;
|
|
477
482
|
}
|
|
483
|
+
const CFF_HEADER_SIZE = 4;
|
|
478
484
|
function openEmbeddedProgram(descriptorOwner, resolver) {
|
|
479
485
|
const descriptor = resolver.resolveDict(require_objects.dictGet(descriptorOwner, "FontDescriptor"));
|
|
480
486
|
if (descriptor === void 0) return { kind: "absent" };
|
|
@@ -482,7 +488,7 @@ function openEmbeddedProgram(descriptorOwner, resolver) {
|
|
|
482
488
|
const stream = resolver.resolve(require_objects.dictGet(descriptor, key));
|
|
483
489
|
if (stream?.kind !== "stream") continue;
|
|
484
490
|
const bytes = require_filters.decodeStream(stream.raw, stream.dict, require_diagnostics.NOOP_DIAGNOSTIC_SINK).bytes;
|
|
485
|
-
if (bytes[0] === 1 && bytes[1] === 0 && bytes[2] ===
|
|
491
|
+
if (bytes[0] === 1 && bytes[1] === 0 && bytes[2] === CFF_HEADER_SIZE) return { kind: "cff" };
|
|
486
492
|
const sfnt = require_sfnt.parseSfnt(bytes);
|
|
487
493
|
if (sfnt === void 0) continue;
|
|
488
494
|
if (sfnt.tables.has("CFF ")) return { kind: "cff" };
|
|
@@ -514,6 +520,7 @@ function resolveTextOutlineFace(fontResourceName, resources, resolver, fontResol
|
|
|
514
520
|
outlineFaces.set(fontDict, face);
|
|
515
521
|
return face;
|
|
516
522
|
}
|
|
523
|
+
const BITS_PER_BYTE = 8;
|
|
517
524
|
function buildTextOutlineFace(fontDict, resolver, fontResolver, fontResourceName, resources, sink) {
|
|
518
525
|
const faceName = require_objects.asName(require_objects.dictGet(fontDict, "BaseFont")) ?? require_objects.asName(require_objects.dictGet(fontDict, "Subtype")) ?? "font";
|
|
519
526
|
const unavailable = (reason) => {
|
|
@@ -548,12 +555,12 @@ function buildTextOutlineFace(fontDict, resolver, fontResolver, fontResourceName
|
|
|
548
555
|
if (cidToGidMap?.kind === "stream") {
|
|
549
556
|
const decodedBytes = require_filters.decodeStream(cidToGidMap.raw, cidToGidMap.dict, require_diagnostics.NOOP_DIAGNOSTIC_SINK).bytes;
|
|
550
557
|
const entries = [];
|
|
551
|
-
for (let i = 0; i + 1 < decodedBytes.length; i += 2) entries.push(decodedBytes[i] <<
|
|
558
|
+
for (let i = 0; i + 1 < decodedBytes.length; i += 2) entries.push(decodedBytes[i] << BITS_PER_BYTE | decodedBytes[i + 1]);
|
|
552
559
|
return {
|
|
553
560
|
glyf: program.face.glyf,
|
|
554
561
|
unitsPerEm: program.face.unitsPerEm,
|
|
555
562
|
glyphIdOf: (codes, offset) => {
|
|
556
|
-
const cid = codes[offset] <<
|
|
563
|
+
const cid = codes[offset] << BITS_PER_BYTE | codes[offset + 1];
|
|
557
564
|
return entries[cid];
|
|
558
565
|
}
|
|
559
566
|
};
|
|
@@ -561,7 +568,7 @@ function buildTextOutlineFace(fontDict, resolver, fontResolver, fontResourceName
|
|
|
561
568
|
return {
|
|
562
569
|
glyf: program.face.glyf,
|
|
563
570
|
unitsPerEm: program.face.unitsPerEm,
|
|
564
|
-
glyphIdOf: (codes, offset) => codes[offset] <<
|
|
571
|
+
glyphIdOf: (codes, offset) => codes[offset] << BITS_PER_BYTE | codes[offset + 1]
|
|
565
572
|
};
|
|
566
573
|
}
|
|
567
574
|
if (subtype === "TrueType") {
|
|
@@ -585,6 +592,7 @@ function buildTextOutlineFace(fontDict, resolver, fontResolver, fontResourceName
|
|
|
585
592
|
}
|
|
586
593
|
return unavailable(`a font of subtype ${subtype ?? "(none)"} (a Type3 glyph-procedure font has no static outline at all)`);
|
|
587
594
|
}
|
|
595
|
+
const METRICS_PER_1000_SCALE = 1e3;
|
|
588
596
|
function drawTextRun(item, fontResolver, resolver, outlineFaces, interpretToDeviceMatrix, rasteriser, sink) {
|
|
589
597
|
const face = resolveTextOutlineFace(item.fontResourceName, item.resources, resolver, fontResolver, outlineFaces, sink);
|
|
590
598
|
if (face === void 0) return;
|
|
@@ -597,10 +605,10 @@ function drawTextRun(item, fontResolver, resolver, outlineFaces, interpretToDevi
|
|
|
597
605
|
placements.push({
|
|
598
606
|
glyphId: face.glyphIdOf(item.codes, offset),
|
|
599
607
|
advance: cumulative,
|
|
600
|
-
positionX: (advance.vertical?.positionXPer1000 ?? 0) /
|
|
601
|
-
positionY: (advance.vertical?.positionYPer1000 ?? 0) /
|
|
608
|
+
positionX: (advance.vertical?.positionXPer1000 ?? 0) / METRICS_PER_1000_SCALE,
|
|
609
|
+
positionY: (advance.vertical?.positionYPer1000 ?? 0) / METRICS_PER_1000_SCALE
|
|
602
610
|
});
|
|
603
|
-
cumulative += (advance.vertical?.displacementPer1000 ?? advance.widthPer1000) /
|
|
611
|
+
cumulative += (advance.vertical?.displacementPer1000 ?? advance.widthPer1000) / METRICS_PER_1000_SCALE * item.sizePt;
|
|
604
612
|
offset += byteLength;
|
|
605
613
|
}
|
|
606
614
|
const startDeviceMatrix = require_matrix.multiplyMatrices(item.startMatrix, interpretToDeviceMatrix);
|
|
@@ -623,7 +631,7 @@ function drawTextRun(item, fontResolver, resolver, outlineFaces, interpretToDevi
|
|
|
623
631
|
});
|
|
624
632
|
const rawExtent = Math.hypot(rawEndPoint.x - startPoint.x, rawEndPoint.y - startPoint.y);
|
|
625
633
|
const actualExtent = Math.hypot(endPoint.x - startPoint.x, endPoint.y - startPoint.y);
|
|
626
|
-
const correction = rawExtent >
|
|
634
|
+
const correction = rawExtent > EXTENT_ZERO_EPSILON ? actualExtent / rawExtent : 1;
|
|
627
635
|
const firstPlacement = placements[0];
|
|
628
636
|
const glyphScale = require_matrix.scaleMatrix(1 / face.unitsPerEm, 1 / face.unitsPerEm);
|
|
629
637
|
for (const placement of placements) {
|
|
@@ -646,10 +654,12 @@ function drawGlyphOutline(outline, glyphMatrix, color, rasteriser) {
|
|
|
646
654
|
}
|
|
647
655
|
});
|
|
648
656
|
}
|
|
657
|
+
const MIN_CONTOUR_POINTS = 3;
|
|
658
|
+
const QUADRATIC_TO_CUBIC_CONTROL_FRACTION = 2 / 3;
|
|
649
659
|
function glyphOutlineSubpaths(outline, matrix) {
|
|
650
660
|
const subpaths = [];
|
|
651
661
|
for (const contour of outline.contours) {
|
|
652
|
-
if (contour.length <
|
|
662
|
+
if (contour.length < MIN_CONTOUR_POINTS) continue;
|
|
653
663
|
const firstOn = contour.findIndex((point) => point.onCurve);
|
|
654
664
|
const contourPoints = contour.map((point) => ({
|
|
655
665
|
x: point.x,
|
|
@@ -674,10 +684,10 @@ function glyphOutlineSubpaths(outline, matrix) {
|
|
|
674
684
|
const p1 = require_matrix.applyMatrix(matrix, to);
|
|
675
685
|
segments.push({
|
|
676
686
|
kind: "cubic",
|
|
677
|
-
c1xPx: p0.x +
|
|
678
|
-
c1yPx: p0.y +
|
|
679
|
-
c2xPx: p1.x +
|
|
680
|
-
c2yPx: p1.y +
|
|
687
|
+
c1xPx: p0.x + QUADRATIC_TO_CUBIC_CONTROL_FRACTION * (q.x - p0.x),
|
|
688
|
+
c1yPx: p0.y + QUADRATIC_TO_CUBIC_CONTROL_FRACTION * (q.y - p0.y),
|
|
689
|
+
c2xPx: p1.x + QUADRATIC_TO_CUBIC_CONTROL_FRACTION * (q.x - p1.x),
|
|
690
|
+
c2yPx: p1.y + QUADRATIC_TO_CUBIC_CONTROL_FRACTION * (q.y - p1.y),
|
|
681
691
|
xPx: p1.x,
|
|
682
692
|
yPx: p1.y
|
|
683
693
|
});
|
|
@@ -765,6 +775,7 @@ function rotatedRectBounds(rect, matrix) {
|
|
|
765
775
|
maxY: Math.max(...ys)
|
|
766
776
|
};
|
|
767
777
|
}
|
|
778
|
+
const NEWLINE_BYTE = 10;
|
|
768
779
|
function readPageContentBytes(page, resolver, sink) {
|
|
769
780
|
const contentsObj = resolver.resolve(require_objects.dictGet(page, "Contents"));
|
|
770
781
|
if (contentsObj?.kind === "stream") return require_filters.decodeStream(contentsObj.raw, contentsObj.dict, sink).bytes;
|
|
@@ -772,7 +783,7 @@ function readPageContentBytes(page, resolver, sink) {
|
|
|
772
783
|
const chunks = [];
|
|
773
784
|
for (const entry of contentsObj.items) {
|
|
774
785
|
const streamObj = resolver.resolve(entry);
|
|
775
|
-
if (streamObj?.kind === "stream") chunks.push(require_filters.decodeStream(streamObj.raw, streamObj.dict, sink).bytes, new Uint8Array([
|
|
786
|
+
if (streamObj?.kind === "stream") chunks.push(require_filters.decodeStream(streamObj.raw, streamObj.dict, sink).bytes, new Uint8Array([NEWLINE_BYTE]));
|
|
776
787
|
}
|
|
777
788
|
return require_bytes_writer.concatBytes(chunks);
|
|
778
789
|
}
|
package/dist/raster.js
CHANGED
|
@@ -16,13 +16,16 @@ import { normalizeRotation, pageRotationTransform } from "./read.js";
|
|
|
16
16
|
import { parseGlyf } from "./glyf.js";
|
|
17
17
|
import { decodeGlyphOutline } from "./glyf-contours.js";
|
|
18
18
|
//#region src/raster.ts
|
|
19
|
+
const PIXEL_ROUNDING_EPSILON = 1e-9;
|
|
20
|
+
const POINTS_PER_INCH = 72;
|
|
21
|
+
const EXTENT_ZERO_EPSILON = 1e-9;
|
|
19
22
|
function regionPixels(extentPt, scale) {
|
|
20
|
-
return Math.max(1, Math.ceil(extentPt * scale -
|
|
23
|
+
return Math.max(1, Math.ceil(extentPt * scale - PIXEL_ROUNDING_EPSILON));
|
|
21
24
|
}
|
|
22
25
|
function renderPdfPage(pdfBytes, pageIndex, options, rasteriser) {
|
|
23
26
|
const sink = options.sink ?? NOOP_DIAGNOSTIC_SINK;
|
|
24
27
|
if (options.scale !== void 0 && options.dpi !== void 0) throw new Error("renderPdfPage accepts at most one of scale and dpi; they name the same factor in different units (dpi = 72 x scale)");
|
|
25
|
-
const scale = options.dpi !== void 0 ? options.dpi /
|
|
28
|
+
const scale = options.dpi !== void 0 ? options.dpi / POINTS_PER_INCH : options.scale;
|
|
26
29
|
if (scale !== void 0 && !(scale > 0)) throw new Error(`renderPdfPage requires a positive scale factor (received scale ${options.scale}, dpi ${options.dpi})`);
|
|
27
30
|
const pixelsPerPt = scale ?? 1;
|
|
28
31
|
if (options.clipPt !== void 0 && (!(options.clipPt.widthPt > 0) || !(options.clipPt.heightPt > 0))) throw new Error("renderPdfPage clipPt must carry positive widthPt and heightPt; a zero-extent region has no pixels to render");
|
|
@@ -194,6 +197,7 @@ function drawRect(item, matrix, pixelsPerPt, rasteriser) {
|
|
|
194
197
|
stroke: strokeSpec(item.stroke, void 0, pixelsPerPt)
|
|
195
198
|
});
|
|
196
199
|
}
|
|
200
|
+
const ELLIPSE_QUADRANT_COUNT = 4;
|
|
197
201
|
function drawEllipse(item, matrix, pixelsPerPt, rasteriser) {
|
|
198
202
|
const cx = item.xPt + item.widthPt / 2;
|
|
199
203
|
const cy = item.yPt + item.heightPt / 2;
|
|
@@ -223,8 +227,8 @@ function drawEllipse(item, matrix, pixelsPerPt, rasteriser) {
|
|
|
223
227
|
});
|
|
224
228
|
const start = transformed(cardinalPoints[0]);
|
|
225
229
|
const segments = [];
|
|
226
|
-
for (let i = 0; i <
|
|
227
|
-
const end = transformed(cardinalPoints[(i + 1) %
|
|
230
|
+
for (let i = 0; i < ELLIPSE_QUADRANT_COUNT; i++) {
|
|
231
|
+
const end = transformed(cardinalPoints[(i + 1) % ELLIPSE_QUADRANT_COUNT]);
|
|
228
232
|
const c1 = transformed(controls[i * 2]);
|
|
229
233
|
const c2 = transformed(controls[i * 2 + 1]);
|
|
230
234
|
segments.push({
|
|
@@ -401,12 +405,13 @@ function drawImage(image, ctm, interpretToDeviceMatrix, rasteriser) {
|
|
|
401
405
|
matrix
|
|
402
406
|
});
|
|
403
407
|
}
|
|
408
|
+
const DASHED_STROKE_WIDTH_MULTIPLE = 3;
|
|
404
409
|
function strokeSpec(stroke, style, pixelsPerPt) {
|
|
405
410
|
const widthPx = stroke.widthPt * pixelsPerPt;
|
|
406
411
|
if (style === "dashed") return {
|
|
407
412
|
color: stroke.color,
|
|
408
413
|
widthPx,
|
|
409
|
-
dashPx: [widthPx *
|
|
414
|
+
dashPx: [widthPx * DASHED_STROKE_WIDTH_MULTIPLE, widthPx * DASHED_STROKE_WIDTH_MULTIPLE]
|
|
410
415
|
};
|
|
411
416
|
return {
|
|
412
417
|
color: stroke.color,
|
|
@@ -474,6 +479,7 @@ function flattenCubic(p0, c1, c2, p1) {
|
|
|
474
479
|
flatten(p0, c1, c2, p1, 0);
|
|
475
480
|
return points;
|
|
476
481
|
}
|
|
482
|
+
const CFF_HEADER_SIZE = 4;
|
|
477
483
|
function openEmbeddedProgram(descriptorOwner, resolver) {
|
|
478
484
|
const descriptor = resolver.resolveDict(dictGet(descriptorOwner, "FontDescriptor"));
|
|
479
485
|
if (descriptor === void 0) return { kind: "absent" };
|
|
@@ -481,7 +487,7 @@ function openEmbeddedProgram(descriptorOwner, resolver) {
|
|
|
481
487
|
const stream = resolver.resolve(dictGet(descriptor, key));
|
|
482
488
|
if (stream?.kind !== "stream") continue;
|
|
483
489
|
const bytes = decodeStream(stream.raw, stream.dict, NOOP_DIAGNOSTIC_SINK).bytes;
|
|
484
|
-
if (bytes[0] === 1 && bytes[1] === 0 && bytes[2] ===
|
|
490
|
+
if (bytes[0] === 1 && bytes[1] === 0 && bytes[2] === CFF_HEADER_SIZE) return { kind: "cff" };
|
|
485
491
|
const sfnt = parseSfnt(bytes);
|
|
486
492
|
if (sfnt === void 0) continue;
|
|
487
493
|
if (sfnt.tables.has("CFF ")) return { kind: "cff" };
|
|
@@ -513,6 +519,7 @@ function resolveTextOutlineFace(fontResourceName, resources, resolver, fontResol
|
|
|
513
519
|
outlineFaces.set(fontDict, face);
|
|
514
520
|
return face;
|
|
515
521
|
}
|
|
522
|
+
const BITS_PER_BYTE = 8;
|
|
516
523
|
function buildTextOutlineFace(fontDict, resolver, fontResolver, fontResourceName, resources, sink) {
|
|
517
524
|
const faceName = asName(dictGet(fontDict, "BaseFont")) ?? asName(dictGet(fontDict, "Subtype")) ?? "font";
|
|
518
525
|
const unavailable = (reason) => {
|
|
@@ -547,12 +554,12 @@ function buildTextOutlineFace(fontDict, resolver, fontResolver, fontResourceName
|
|
|
547
554
|
if (cidToGidMap?.kind === "stream") {
|
|
548
555
|
const decodedBytes = decodeStream(cidToGidMap.raw, cidToGidMap.dict, NOOP_DIAGNOSTIC_SINK).bytes;
|
|
549
556
|
const entries = [];
|
|
550
|
-
for (let i = 0; i + 1 < decodedBytes.length; i += 2) entries.push(decodedBytes[i] <<
|
|
557
|
+
for (let i = 0; i + 1 < decodedBytes.length; i += 2) entries.push(decodedBytes[i] << BITS_PER_BYTE | decodedBytes[i + 1]);
|
|
551
558
|
return {
|
|
552
559
|
glyf: program.face.glyf,
|
|
553
560
|
unitsPerEm: program.face.unitsPerEm,
|
|
554
561
|
glyphIdOf: (codes, offset) => {
|
|
555
|
-
const cid = codes[offset] <<
|
|
562
|
+
const cid = codes[offset] << BITS_PER_BYTE | codes[offset + 1];
|
|
556
563
|
return entries[cid];
|
|
557
564
|
}
|
|
558
565
|
};
|
|
@@ -560,7 +567,7 @@ function buildTextOutlineFace(fontDict, resolver, fontResolver, fontResourceName
|
|
|
560
567
|
return {
|
|
561
568
|
glyf: program.face.glyf,
|
|
562
569
|
unitsPerEm: program.face.unitsPerEm,
|
|
563
|
-
glyphIdOf: (codes, offset) => codes[offset] <<
|
|
570
|
+
glyphIdOf: (codes, offset) => codes[offset] << BITS_PER_BYTE | codes[offset + 1]
|
|
564
571
|
};
|
|
565
572
|
}
|
|
566
573
|
if (subtype === "TrueType") {
|
|
@@ -584,6 +591,7 @@ function buildTextOutlineFace(fontDict, resolver, fontResolver, fontResourceName
|
|
|
584
591
|
}
|
|
585
592
|
return unavailable(`a font of subtype ${subtype ?? "(none)"} (a Type3 glyph-procedure font has no static outline at all)`);
|
|
586
593
|
}
|
|
594
|
+
const METRICS_PER_1000_SCALE = 1e3;
|
|
587
595
|
function drawTextRun(item, fontResolver, resolver, outlineFaces, interpretToDeviceMatrix, rasteriser, sink) {
|
|
588
596
|
const face = resolveTextOutlineFace(item.fontResourceName, item.resources, resolver, fontResolver, outlineFaces, sink);
|
|
589
597
|
if (face === void 0) return;
|
|
@@ -596,10 +604,10 @@ function drawTextRun(item, fontResolver, resolver, outlineFaces, interpretToDevi
|
|
|
596
604
|
placements.push({
|
|
597
605
|
glyphId: face.glyphIdOf(item.codes, offset),
|
|
598
606
|
advance: cumulative,
|
|
599
|
-
positionX: (advance.vertical?.positionXPer1000 ?? 0) /
|
|
600
|
-
positionY: (advance.vertical?.positionYPer1000 ?? 0) /
|
|
607
|
+
positionX: (advance.vertical?.positionXPer1000 ?? 0) / METRICS_PER_1000_SCALE,
|
|
608
|
+
positionY: (advance.vertical?.positionYPer1000 ?? 0) / METRICS_PER_1000_SCALE
|
|
601
609
|
});
|
|
602
|
-
cumulative += (advance.vertical?.displacementPer1000 ?? advance.widthPer1000) /
|
|
610
|
+
cumulative += (advance.vertical?.displacementPer1000 ?? advance.widthPer1000) / METRICS_PER_1000_SCALE * item.sizePt;
|
|
603
611
|
offset += byteLength;
|
|
604
612
|
}
|
|
605
613
|
const startDeviceMatrix = multiplyMatrices(item.startMatrix, interpretToDeviceMatrix);
|
|
@@ -622,7 +630,7 @@ function drawTextRun(item, fontResolver, resolver, outlineFaces, interpretToDevi
|
|
|
622
630
|
});
|
|
623
631
|
const rawExtent = Math.hypot(rawEndPoint.x - startPoint.x, rawEndPoint.y - startPoint.y);
|
|
624
632
|
const actualExtent = Math.hypot(endPoint.x - startPoint.x, endPoint.y - startPoint.y);
|
|
625
|
-
const correction = rawExtent >
|
|
633
|
+
const correction = rawExtent > EXTENT_ZERO_EPSILON ? actualExtent / rawExtent : 1;
|
|
626
634
|
const firstPlacement = placements[0];
|
|
627
635
|
const glyphScale = scaleMatrix(1 / face.unitsPerEm, 1 / face.unitsPerEm);
|
|
628
636
|
for (const placement of placements) {
|
|
@@ -645,10 +653,12 @@ function drawGlyphOutline(outline, glyphMatrix, color, rasteriser) {
|
|
|
645
653
|
}
|
|
646
654
|
});
|
|
647
655
|
}
|
|
656
|
+
const MIN_CONTOUR_POINTS = 3;
|
|
657
|
+
const QUADRATIC_TO_CUBIC_CONTROL_FRACTION = 2 / 3;
|
|
648
658
|
function glyphOutlineSubpaths(outline, matrix) {
|
|
649
659
|
const subpaths = [];
|
|
650
660
|
for (const contour of outline.contours) {
|
|
651
|
-
if (contour.length <
|
|
661
|
+
if (contour.length < MIN_CONTOUR_POINTS) continue;
|
|
652
662
|
const firstOn = contour.findIndex((point) => point.onCurve);
|
|
653
663
|
const contourPoints = contour.map((point) => ({
|
|
654
664
|
x: point.x,
|
|
@@ -673,10 +683,10 @@ function glyphOutlineSubpaths(outline, matrix) {
|
|
|
673
683
|
const p1 = applyMatrix(matrix, to);
|
|
674
684
|
segments.push({
|
|
675
685
|
kind: "cubic",
|
|
676
|
-
c1xPx: p0.x +
|
|
677
|
-
c1yPx: p0.y +
|
|
678
|
-
c2xPx: p1.x +
|
|
679
|
-
c2yPx: p1.y +
|
|
686
|
+
c1xPx: p0.x + QUADRATIC_TO_CUBIC_CONTROL_FRACTION * (q.x - p0.x),
|
|
687
|
+
c1yPx: p0.y + QUADRATIC_TO_CUBIC_CONTROL_FRACTION * (q.y - p0.y),
|
|
688
|
+
c2xPx: p1.x + QUADRATIC_TO_CUBIC_CONTROL_FRACTION * (q.x - p1.x),
|
|
689
|
+
c2yPx: p1.y + QUADRATIC_TO_CUBIC_CONTROL_FRACTION * (q.y - p1.y),
|
|
680
690
|
xPx: p1.x,
|
|
681
691
|
yPx: p1.y
|
|
682
692
|
});
|
|
@@ -764,6 +774,7 @@ function rotatedRectBounds(rect, matrix) {
|
|
|
764
774
|
maxY: Math.max(...ys)
|
|
765
775
|
};
|
|
766
776
|
}
|
|
777
|
+
const NEWLINE_BYTE = 10;
|
|
767
778
|
function readPageContentBytes(page, resolver, sink) {
|
|
768
779
|
const contentsObj = resolver.resolve(dictGet(page, "Contents"));
|
|
769
780
|
if (contentsObj?.kind === "stream") return decodeStream(contentsObj.raw, contentsObj.dict, sink).bytes;
|
|
@@ -771,7 +782,7 @@ function readPageContentBytes(page, resolver, sink) {
|
|
|
771
782
|
const chunks = [];
|
|
772
783
|
for (const entry of contentsObj.items) {
|
|
773
784
|
const streamObj = resolver.resolve(entry);
|
|
774
|
-
if (streamObj?.kind === "stream") chunks.push(decodeStream(streamObj.raw, streamObj.dict, sink).bytes, new Uint8Array([
|
|
785
|
+
if (streamObj?.kind === "stream") chunks.push(decodeStream(streamObj.raw, streamObj.dict, sink).bytes, new Uint8Array([NEWLINE_BYTE]));
|
|
775
786
|
}
|
|
776
787
|
return concatBytes(chunks);
|
|
777
788
|
}
|
package/dist/read.cjs
CHANGED
|
@@ -121,13 +121,20 @@ function rotatedRectBounds(rect, matrix) {
|
|
|
121
121
|
maxY: Math.max(...ys)
|
|
122
122
|
};
|
|
123
123
|
}
|
|
124
|
+
const QUARTER_TURN_DEG = 90;
|
|
125
|
+
const HALF_TURN_DEG = 180;
|
|
126
|
+
const THREE_QUARTER_TURN_DEG = 270;
|
|
127
|
+
const DEGREES_PER_TURN = 360;
|
|
128
|
+
function isNonZeroPageRotation(n) {
|
|
129
|
+
return n === QUARTER_TURN_DEG || n === HALF_TURN_DEG || n === THREE_QUARTER_TURN_DEG;
|
|
130
|
+
}
|
|
124
131
|
function normalizeRotation(rotate) {
|
|
125
132
|
if (rotate === void 0) return 0;
|
|
126
|
-
const normalized = (Math.round(rotate /
|
|
127
|
-
return normalized
|
|
133
|
+
const normalized = (Math.round(rotate / QUARTER_TURN_DEG) * QUARTER_TURN_DEG % DEGREES_PER_TURN + DEGREES_PER_TURN) % DEGREES_PER_TURN;
|
|
134
|
+
return isNonZeroPageRotation(normalized) ? normalized : 0;
|
|
128
135
|
}
|
|
129
136
|
function pageRotationTransform(rotation, w, h) {
|
|
130
|
-
if (rotation ===
|
|
137
|
+
if (rotation === QUARTER_TURN_DEG) return {
|
|
131
138
|
matrix: [
|
|
132
139
|
0,
|
|
133
140
|
-1,
|
|
@@ -139,7 +146,7 @@ function pageRotationTransform(rotation, w, h) {
|
|
|
139
146
|
widthPt: h,
|
|
140
147
|
heightPt: w
|
|
141
148
|
};
|
|
142
|
-
if (rotation ===
|
|
149
|
+
if (rotation === HALF_TURN_DEG) return {
|
|
143
150
|
matrix: [
|
|
144
151
|
-1,
|
|
145
152
|
0,
|
|
@@ -151,7 +158,7 @@ function pageRotationTransform(rotation, w, h) {
|
|
|
151
158
|
widthPt: w,
|
|
152
159
|
heightPt: h
|
|
153
160
|
};
|
|
154
|
-
if (rotation ===
|
|
161
|
+
if (rotation === THREE_QUARTER_TURN_DEG) return {
|
|
155
162
|
matrix: [
|
|
156
163
|
0,
|
|
157
164
|
1,
|
|
@@ -176,6 +183,7 @@ function pageRotationTransform(rotation, w, h) {
|
|
|
176
183
|
heightPt: h
|
|
177
184
|
};
|
|
178
185
|
}
|
|
186
|
+
const CONTENT_STREAM_JOIN_BYTE = 10;
|
|
179
187
|
function readPageContentBytes(page, resolver, sink) {
|
|
180
188
|
const contentsObj = resolver.resolve(require_objects.dictGet(page, "Contents"));
|
|
181
189
|
if (contentsObj?.kind === "stream") return require_filters.decodeStream(contentsObj.raw, contentsObj.dict, sink).bytes;
|
|
@@ -183,7 +191,7 @@ function readPageContentBytes(page, resolver, sink) {
|
|
|
183
191
|
const chunks = [];
|
|
184
192
|
for (const item of contentsObj.items) {
|
|
185
193
|
const streamObj = resolver.resolve(item);
|
|
186
|
-
if (streamObj?.kind === "stream") chunks.push(require_filters.decodeStream(streamObj.raw, streamObj.dict, sink).bytes, new Uint8Array([
|
|
194
|
+
if (streamObj?.kind === "stream") chunks.push(require_filters.decodeStream(streamObj.raw, streamObj.dict, sink).bytes, new Uint8Array([CONTENT_STREAM_JOIN_BYTE]));
|
|
187
195
|
}
|
|
188
196
|
return require_bytes_writer.concatBytes(chunks);
|
|
189
197
|
}
|
|
@@ -198,7 +206,7 @@ function contentItemBounds(item) {
|
|
|
198
206
|
maxY: yPt + heightPt
|
|
199
207
|
});
|
|
200
208
|
if (item.kind === "text" || item.kind === "image" || item.kind === "rect" || item.kind === "ellipse") {
|
|
201
|
-
if (item.kind !== "text" || item.rotationDeg === void 0 || item.rotationDeg %
|
|
209
|
+
if (item.kind !== "text" || item.rotationDeg === void 0 || item.rotationDeg % HALF_TURN_DEG === 0) return frameBounds(item.xPt, item.yPt, item.widthPt ?? 0, item.kind === "text" ? item.sizePt : item.heightPt);
|
|
202
210
|
const rotationDeg = item.rotationDeg;
|
|
203
211
|
const anchorX = item.xPt;
|
|
204
212
|
const anchorY = item.yPt;
|
|
@@ -502,8 +510,9 @@ function imagePlacementFrom(matrix) {
|
|
|
502
510
|
rotationDeg: rotationDeg !== 0 ? rotationDeg : void 0
|
|
503
511
|
};
|
|
504
512
|
}
|
|
513
|
+
const HEX_RADIX = 16;
|
|
505
514
|
function registerExtractedImage(format, bytes, widthPx, heightPx, images, original) {
|
|
506
|
-
const imageId = `img${require_bytes_crc32.crc32(bytes).toString(
|
|
515
|
+
const imageId = `img${require_bytes_crc32.crc32(bytes).toString(HEX_RADIX)}`;
|
|
507
516
|
if (!(imageId in images)) images[imageId] = {
|
|
508
517
|
format,
|
|
509
518
|
base64: (0, byte_codec.bytesToBase64)(bytes),
|
package/dist/read.js
CHANGED
|
@@ -120,13 +120,20 @@ function rotatedRectBounds(rect, matrix) {
|
|
|
120
120
|
maxY: Math.max(...ys)
|
|
121
121
|
};
|
|
122
122
|
}
|
|
123
|
+
const QUARTER_TURN_DEG = 90;
|
|
124
|
+
const HALF_TURN_DEG = 180;
|
|
125
|
+
const THREE_QUARTER_TURN_DEG = 270;
|
|
126
|
+
const DEGREES_PER_TURN = 360;
|
|
127
|
+
function isNonZeroPageRotation(n) {
|
|
128
|
+
return n === QUARTER_TURN_DEG || n === HALF_TURN_DEG || n === THREE_QUARTER_TURN_DEG;
|
|
129
|
+
}
|
|
123
130
|
function normalizeRotation(rotate) {
|
|
124
131
|
if (rotate === void 0) return 0;
|
|
125
|
-
const normalized = (Math.round(rotate /
|
|
126
|
-
return normalized
|
|
132
|
+
const normalized = (Math.round(rotate / QUARTER_TURN_DEG) * QUARTER_TURN_DEG % DEGREES_PER_TURN + DEGREES_PER_TURN) % DEGREES_PER_TURN;
|
|
133
|
+
return isNonZeroPageRotation(normalized) ? normalized : 0;
|
|
127
134
|
}
|
|
128
135
|
function pageRotationTransform(rotation, w, h) {
|
|
129
|
-
if (rotation ===
|
|
136
|
+
if (rotation === QUARTER_TURN_DEG) return {
|
|
130
137
|
matrix: [
|
|
131
138
|
0,
|
|
132
139
|
-1,
|
|
@@ -138,7 +145,7 @@ function pageRotationTransform(rotation, w, h) {
|
|
|
138
145
|
widthPt: h,
|
|
139
146
|
heightPt: w
|
|
140
147
|
};
|
|
141
|
-
if (rotation ===
|
|
148
|
+
if (rotation === HALF_TURN_DEG) return {
|
|
142
149
|
matrix: [
|
|
143
150
|
-1,
|
|
144
151
|
0,
|
|
@@ -150,7 +157,7 @@ function pageRotationTransform(rotation, w, h) {
|
|
|
150
157
|
widthPt: w,
|
|
151
158
|
heightPt: h
|
|
152
159
|
};
|
|
153
|
-
if (rotation ===
|
|
160
|
+
if (rotation === THREE_QUARTER_TURN_DEG) return {
|
|
154
161
|
matrix: [
|
|
155
162
|
0,
|
|
156
163
|
1,
|
|
@@ -175,6 +182,7 @@ function pageRotationTransform(rotation, w, h) {
|
|
|
175
182
|
heightPt: h
|
|
176
183
|
};
|
|
177
184
|
}
|
|
185
|
+
const CONTENT_STREAM_JOIN_BYTE = 10;
|
|
178
186
|
function readPageContentBytes(page, resolver, sink) {
|
|
179
187
|
const contentsObj = resolver.resolve(dictGet(page, "Contents"));
|
|
180
188
|
if (contentsObj?.kind === "stream") return decodeStream(contentsObj.raw, contentsObj.dict, sink).bytes;
|
|
@@ -182,7 +190,7 @@ function readPageContentBytes(page, resolver, sink) {
|
|
|
182
190
|
const chunks = [];
|
|
183
191
|
for (const item of contentsObj.items) {
|
|
184
192
|
const streamObj = resolver.resolve(item);
|
|
185
|
-
if (streamObj?.kind === "stream") chunks.push(decodeStream(streamObj.raw, streamObj.dict, sink).bytes, new Uint8Array([
|
|
193
|
+
if (streamObj?.kind === "stream") chunks.push(decodeStream(streamObj.raw, streamObj.dict, sink).bytes, new Uint8Array([CONTENT_STREAM_JOIN_BYTE]));
|
|
186
194
|
}
|
|
187
195
|
return concatBytes(chunks);
|
|
188
196
|
}
|
|
@@ -197,7 +205,7 @@ function contentItemBounds(item) {
|
|
|
197
205
|
maxY: yPt + heightPt
|
|
198
206
|
});
|
|
199
207
|
if (item.kind === "text" || item.kind === "image" || item.kind === "rect" || item.kind === "ellipse") {
|
|
200
|
-
if (item.kind !== "text" || item.rotationDeg === void 0 || item.rotationDeg %
|
|
208
|
+
if (item.kind !== "text" || item.rotationDeg === void 0 || item.rotationDeg % HALF_TURN_DEG === 0) return frameBounds(item.xPt, item.yPt, item.widthPt ?? 0, item.kind === "text" ? item.sizePt : item.heightPt);
|
|
201
209
|
const rotationDeg = item.rotationDeg;
|
|
202
210
|
const anchorX = item.xPt;
|
|
203
211
|
const anchorY = item.yPt;
|
|
@@ -501,8 +509,9 @@ function imagePlacementFrom(matrix) {
|
|
|
501
509
|
rotationDeg: rotationDeg !== 0 ? rotationDeg : void 0
|
|
502
510
|
};
|
|
503
511
|
}
|
|
512
|
+
const HEX_RADIX = 16;
|
|
504
513
|
function registerExtractedImage(format, bytes, widthPx, heightPx, images, original) {
|
|
505
|
-
const imageId = `img${crc32(bytes).toString(
|
|
514
|
+
const imageId = `img${crc32(bytes).toString(HEX_RADIX)}`;
|
|
506
515
|
if (!(imageId in images)) images[imageId] = {
|
|
507
516
|
format,
|
|
508
517
|
base64: bytesToBase64(bytes),
|
package/dist/serialize.cjs
CHANGED
|
@@ -8,18 +8,22 @@ function formatNumber(n) {
|
|
|
8
8
|
if (Math.abs(n) < NUMBER_EPSILON) return "0";
|
|
9
9
|
return n.toFixed(NUMBER_DECIMAL_PLACES).replace(/0+$/, "").replace(/\.$/, "");
|
|
10
10
|
}
|
|
11
|
+
const NAME_SAFE_RANGE_START = 33;
|
|
12
|
+
const NAME_SAFE_RANGE_END = 126;
|
|
13
|
+
const HEX_RADIX = 16;
|
|
14
|
+
const HEX_DIGIT_WIDTH = 2;
|
|
11
15
|
function escapeName(name) {
|
|
12
16
|
let out = "";
|
|
13
17
|
for (const ch of name) {
|
|
14
18
|
const code = ch.codePointAt(0);
|
|
15
|
-
if (code <
|
|
19
|
+
if (code < NAME_SAFE_RANGE_START || code > NAME_SAFE_RANGE_END || "#()<>[]{}/%".includes(ch)) out += `#${code.toString(HEX_RADIX).padStart(HEX_DIGIT_WIDTH, "0")}`;
|
|
16
20
|
else out += ch;
|
|
17
21
|
}
|
|
18
22
|
return out;
|
|
19
23
|
}
|
|
20
24
|
function bytesToHex(bytes) {
|
|
21
25
|
let out = "";
|
|
22
|
-
for (const byte of bytes) out += byte.toString(
|
|
26
|
+
for (const byte of bytes) out += byte.toString(HEX_RADIX).padStart(HEX_DIGIT_WIDTH, "0");
|
|
23
27
|
return out;
|
|
24
28
|
}
|
|
25
29
|
function writeDictBody(writer, entries) {
|
package/dist/serialize.js
CHANGED
|
@@ -7,18 +7,22 @@ function formatNumber(n) {
|
|
|
7
7
|
if (Math.abs(n) < NUMBER_EPSILON) return "0";
|
|
8
8
|
return n.toFixed(NUMBER_DECIMAL_PLACES).replace(/0+$/, "").replace(/\.$/, "");
|
|
9
9
|
}
|
|
10
|
+
const NAME_SAFE_RANGE_START = 33;
|
|
11
|
+
const NAME_SAFE_RANGE_END = 126;
|
|
12
|
+
const HEX_RADIX = 16;
|
|
13
|
+
const HEX_DIGIT_WIDTH = 2;
|
|
10
14
|
function escapeName(name) {
|
|
11
15
|
let out = "";
|
|
12
16
|
for (const ch of name) {
|
|
13
17
|
const code = ch.codePointAt(0);
|
|
14
|
-
if (code <
|
|
18
|
+
if (code < NAME_SAFE_RANGE_START || code > NAME_SAFE_RANGE_END || "#()<>[]{}/%".includes(ch)) out += `#${code.toString(HEX_RADIX).padStart(HEX_DIGIT_WIDTH, "0")}`;
|
|
15
19
|
else out += ch;
|
|
16
20
|
}
|
|
17
21
|
return out;
|
|
18
22
|
}
|
|
19
23
|
function bytesToHex(bytes) {
|
|
20
24
|
let out = "";
|
|
21
|
-
for (const byte of bytes) out += byte.toString(
|
|
25
|
+
for (const byte of bytes) out += byte.toString(HEX_RADIX).padStart(HEX_DIGIT_WIDTH, "0");
|
|
22
26
|
return out;
|
|
23
27
|
}
|
|
24
28
|
function writeDictBody(writer, entries) {
|
package/dist/sfnt-subset.cjs
CHANGED
|
@@ -21,6 +21,13 @@ const LEFT_SIDE_BEARING_SIZE = 2;
|
|
|
21
21
|
const LOCA_LONG_ENTRY_SIZE = 4;
|
|
22
22
|
const TABLE_DIRECTORY_HEADER_SIZE = 12;
|
|
23
23
|
const TABLE_RECORD_SIZE = 16;
|
|
24
|
+
const SFNT_HEADER_NUM_TABLES_OFFSET = 4;
|
|
25
|
+
const SFNT_HEADER_SEARCH_RANGE_OFFSET = 6;
|
|
26
|
+
const SFNT_HEADER_ENTRY_SELECTOR_OFFSET = 8;
|
|
27
|
+
const SFNT_HEADER_RANGE_SHIFT_OFFSET = 10;
|
|
28
|
+
const TABLE_RECORD_CHECKSUM_OFFSET = 4;
|
|
29
|
+
const TABLE_RECORD_TABLE_OFFSET_OFFSET = 8;
|
|
30
|
+
const TABLE_RECORD_LENGTH_OFFSET = 12;
|
|
24
31
|
const SFNT_VERSION_TRUETYPE = 65536;
|
|
25
32
|
const CHECKSUM_ADJUSTMENT_MAGIC = 2981146554;
|
|
26
33
|
const GLYPH_ALIGNMENT = 4;
|
|
@@ -32,15 +39,20 @@ const COPIED_TAGS = [
|
|
|
32
39
|
function alignUp(value) {
|
|
33
40
|
return Math.ceil(value / GLYPH_ALIGNMENT) * GLYPH_ALIGNMENT;
|
|
34
41
|
}
|
|
42
|
+
const BITS_PER_BYTE = 8;
|
|
43
|
+
const BYTE_MASK = 255;
|
|
44
|
+
const U32_BYTE_2_SHIFT = 16;
|
|
45
|
+
const U32_BYTE_1_SHIFT = 24;
|
|
46
|
+
const U32_LAST_BYTE_OFFSET = 3;
|
|
35
47
|
function writeU16(bytes, offset, value) {
|
|
36
|
-
bytes[offset] = value >>>
|
|
37
|
-
bytes[offset + 1] = value &
|
|
48
|
+
bytes[offset] = value >>> BITS_PER_BYTE & BYTE_MASK;
|
|
49
|
+
bytes[offset + 1] = value & BYTE_MASK;
|
|
38
50
|
}
|
|
39
51
|
function writeU32(bytes, offset, value) {
|
|
40
|
-
bytes[offset] = value >>>
|
|
41
|
-
bytes[offset + 1] = value >>>
|
|
42
|
-
bytes[offset + 2] = value >>>
|
|
43
|
-
bytes[offset +
|
|
52
|
+
bytes[offset] = value >>> U32_BYTE_1_SHIFT & BYTE_MASK;
|
|
53
|
+
bytes[offset + 1] = value >>> U32_BYTE_2_SHIFT & BYTE_MASK;
|
|
54
|
+
bytes[offset + 2] = value >>> BITS_PER_BYTE & BYTE_MASK;
|
|
55
|
+
bytes[offset + U32_LAST_BYTE_OFFSET] = value & BYTE_MASK;
|
|
44
56
|
}
|
|
45
57
|
function checksum(bytes, offset, length) {
|
|
46
58
|
let sum = 0;
|
|
@@ -230,19 +242,19 @@ function subsetSfnt(font, codePoints, extraGlyphIds = []) {
|
|
|
230
242
|
}
|
|
231
243
|
const file = new Uint8Array(fileLength);
|
|
232
244
|
writeU32(file, 0, SFNT_VERSION_TRUETYPE);
|
|
233
|
-
writeU16(file,
|
|
234
|
-
writeU16(file,
|
|
235
|
-
writeU16(file,
|
|
236
|
-
writeU16(file,
|
|
245
|
+
writeU16(file, SFNT_HEADER_NUM_TABLES_OFFSET, numTables);
|
|
246
|
+
writeU16(file, SFNT_HEADER_SEARCH_RANGE_OFFSET, searchRange);
|
|
247
|
+
writeU16(file, SFNT_HEADER_ENTRY_SELECTOR_OFFSET, entrySelector);
|
|
248
|
+
writeU16(file, SFNT_HEADER_RANGE_SHIFT_OFFSET, rangeShift);
|
|
237
249
|
for (let i = 0; i < numTables; i++) {
|
|
238
250
|
const table = tables[i];
|
|
239
251
|
const tableOffset = offsets[i];
|
|
240
252
|
file.set(table.data, tableOffset);
|
|
241
253
|
const recordOffset = TABLE_DIRECTORY_HEADER_SIZE + i * TABLE_RECORD_SIZE;
|
|
242
254
|
for (let c = 0; c < table.tag.length; c++) file[recordOffset + c] = table.tag.charCodeAt(c);
|
|
243
|
-
writeU32(file, recordOffset +
|
|
244
|
-
writeU32(file, recordOffset +
|
|
245
|
-
writeU32(file, recordOffset +
|
|
255
|
+
writeU32(file, recordOffset + TABLE_RECORD_CHECKSUM_OFFSET, checksum(file, tableOffset, alignUp(table.data.length)));
|
|
256
|
+
writeU32(file, recordOffset + TABLE_RECORD_TABLE_OFFSET_OFFSET, tableOffset);
|
|
257
|
+
writeU32(file, recordOffset + TABLE_RECORD_LENGTH_OFFSET, table.data.length);
|
|
246
258
|
}
|
|
247
259
|
const headOffsetInFile = offsets[tables.findIndex((table) => table.tag === "head")];
|
|
248
260
|
writeU32(file, headOffsetInFile + HEAD_CHECKSUM_ADJUSTMENT_OFFSET, CHECKSUM_ADJUSTMENT_MAGIC - checksum(file, 0, fileLength) >>> 0);
|