pdf-codec 4.0.11 → 4.0.12
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/README.md +1 -1
- package/dist/interpret.cjs +20 -6
- package/dist/interpret.d.cts +3 -1
- package/dist/interpret.d.ts +3 -1
- package/dist/interpret.js +20 -6
- package/dist/read.cjs +2 -0
- package/dist/read.js +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -269,7 +269,7 @@ Dependency direction is strictly downward and checkable: `layout` imports only `
|
|
|
269
269
|
- **The OpenType `MATH` table's `MathVariants` is parsed, stretchy-glyph assembly implemented, and the result genuinely drawable** via `MathAssembledGlyphs` (glyph-ID-addressed placements, since most construction glyphs have no Unicode code point — they draw directly because CID == GID here). An unencoded glyph gets no ToUnicode entry; the construction is wrapped in an `/ActualText` span carrying the operator's own text so it still extracts as `(`. `MathConstants` and `MathGlyphInfo` (italics correction, top-accent attachment) are parsed in full.
|
|
270
270
|
- **What this package draws for a stretchy glyph is decided entirely by its caller.** Which operators a document actually stretches is a layout-engine decision — `documents.js`'s own `src/mathml/layout.ts` currently stretches vertical fences in an `mrow` and nothing else.
|
|
271
271
|
- **Real per-glyph ink bounds are measured from the outline (`inkAscentPt`/`inkDescentPt`), computed by walking each glyph's Type 2 charstring.** `ascentPerEm`/`descentPerEm` remain alongside them as the uniform face-wide figure, still the right measure for anything sized against the font rather than particular characters, and the fallback for a glyph with no outline to measure. An ink box is genuinely tight, which for a math font is often _larger_ than the nominal metrics (over a tenth of STIX Two Math's repertoire draws above its nominal ascent). `inkDescentPt` is negative where the glyph's lowest ink sits above the baseline.
|
|
272
|
-
- **A `LayoutLine`/`LayoutPath` `style` of `dashed`/`dotted` becomes a real dash-array (`d`) operator scaled to the stroke's own width; `double` becomes two genuinely separate offset strokes.** Dash lengths are stroke-width multiples so a hairline and a thick rule both read as recognisably dashed: `dashed` emits `[3w 3w] 0 d`, `dotted` emits `[0 2w] 0 d` with a `1 J` round cap (the zero on-length under a round cap paints a filled circle — exactly a dot; under PDF's default butt cap it paints nothing). Both are reset immediately after the paint operator (`[] 0 d`, `0 J`) since the graphics state persists for the whole content stream. `double` has no PDF operator and is drawn as geometry: width `w` splits into three equal bands, each rule `w/3` wide with centreline `w/3` offset, outer edges matching the single stroke's.
|
|
272
|
+
- **A `LayoutLine`/`LayoutPath` `style` of `dashed`/`dotted` becomes a real dash-array (`d`) operator scaled to the stroke's own width; `double` becomes two genuinely separate offset strokes.** Dash lengths are stroke-width multiples so a hairline and a thick rule both read as recognisably dashed: `dashed` emits `[3w 3w] 0 d`, `dotted` emits `[0 2w] 0 d` with a `1 J` round cap (the zero on-length under a round cap paints a filled circle — exactly a dot; under PDF's default butt cap it paints nothing). Both are reset immediately after the paint operator (`[] 0 d`, `0 J`) since the graphics state persists for the whole content stream. `double` has no PDF operator and is drawn as geometry: width `w` splits into three equal bands, each rule `w/3` wide with centreline `w/3` offset, outer edges matching the single stroke's. The read side tracks the `d` operator's own dash array as graphics state and recovers `dashed`/`dotted` back onto a stroked `LayoutLine`/`LayoutPath` from it — a zero on-length reads as `dotted`, any other non-empty array as `dashed`, an empty array (or none at all) as `solid`. `double` has no operator of its own to recover from, so it reads back as whatever geometry its two offset strokes actually are (typically two plain solid lines or paths), not as `double`.
|
|
273
273
|
- **An embedded `CIDFontType2` program needs no `cmap` table of its own**, and `sfnt-subset.ts`'s output doesn't carry one — character code → CID goes through the `Type0` font's `/Encoding` (Identity-H, so CID == character code), and CID → GID through `/CIDToGIDMap /Identity` (matching the GID-preserving design). Both happen inside the PDF's object graph, before the embedded font program is consulted (ISO 32000-1 9.7.4.2).
|
|
274
274
|
- **`GPOS` pair kerning is read and applied for an embedded face; no other OpenType layout feature is.** `GSUB` (ligatures, contextual alternates, small caps) is never applied — a face's `fi` ligature is drawn as two separate glyphs. The legacy `kern` table is not read either (neither Carlito nor Caladea ships one). A kerned run is shown with `TJ`, and the sign of a `TJ` number is the opposite of the adjustment it expresses (ISO 32000-1 9.4.3: a positive number moves the next glyph closer). A run with no kerning pairs stays as one unsplit hex string with `Tj`.
|
|
275
275
|
- **Kerning applies to whole shown strings, so a wrap decision does not see a pair straddling the boundary between two separately-measured words.** The width a line reports is the width the page draws; making the wrap decision itself exact would mean widening the `TextMeasurer` port for a sub-point difference that only ever errs towards breaking a line early.
|
package/dist/interpret.cjs
CHANGED
|
@@ -250,7 +250,11 @@ function detectEllipse(subpath, paint) {
|
|
|
250
250
|
stroke: paint.stroke
|
|
251
251
|
};
|
|
252
252
|
}
|
|
253
|
-
function
|
|
253
|
+
function strokeStyleFromDashArray(dashArray) {
|
|
254
|
+
if (dashArray.length === 0) return;
|
|
255
|
+
return dashArray.length === 2 && dashArray[0] === 0 ? "dotted" : "dashed";
|
|
256
|
+
}
|
|
257
|
+
function detectLine(subpath, paint, style) {
|
|
254
258
|
const segment = subpath.segments[0];
|
|
255
259
|
if (subpath.closed || subpath.segments.length !== 1 || segment?.kind !== "line") return;
|
|
256
260
|
if (paint.fill !== void 0 || paint.stroke === void 0) return;
|
|
@@ -261,13 +265,14 @@ function detectLine(subpath, paint) {
|
|
|
261
265
|
x2Pt: segment.xPt,
|
|
262
266
|
y2Pt: segment.yPt,
|
|
263
267
|
color: paint.stroke.color,
|
|
264
|
-
widthPt: paint.stroke.widthPt
|
|
268
|
+
widthPt: paint.stroke.widthPt,
|
|
269
|
+
...style !== void 0 ? { style } : {}
|
|
265
270
|
};
|
|
266
271
|
}
|
|
267
|
-
function classifyShape(subpaths, paint) {
|
|
272
|
+
function classifyShape(subpaths, paint, style) {
|
|
268
273
|
const subpath = subpaths[0];
|
|
269
274
|
if (subpaths.length !== 1 || subpath === void 0) return;
|
|
270
|
-
return detectRect(subpath, paint) ?? detectEllipse(subpath, paint) ?? detectLine(subpath, paint);
|
|
275
|
+
return detectRect(subpath, paint) ?? detectEllipse(subpath, paint) ?? detectLine(subpath, paint, style);
|
|
271
276
|
}
|
|
272
277
|
function interpretContentStream(bytes, resources, context) {
|
|
273
278
|
const items = [];
|
|
@@ -276,6 +281,7 @@ function interpretContentStream(bytes, resources, context) {
|
|
|
276
281
|
fillColor: document_schema_js.COLOR_BLACK,
|
|
277
282
|
strokeColor: document_schema_js.COLOR_BLACK,
|
|
278
283
|
lineWidth: DEFAULT_LINE_WIDTH_PT,
|
|
284
|
+
dashArray: [],
|
|
279
285
|
...initialTextParameters()
|
|
280
286
|
}, context, items, 0, []);
|
|
281
287
|
return items;
|
|
@@ -402,11 +408,13 @@ function runContentStream(bytes, resources, initialState, context, items, depth,
|
|
|
402
408
|
widthPt: gs.lineWidth
|
|
403
409
|
} : void 0
|
|
404
410
|
};
|
|
405
|
-
|
|
411
|
+
const strokeStyle = isStrokeOp ? strokeStyleFromDashArray(gs.dashArray) : void 0;
|
|
412
|
+
pushItem(classifyShape(pathSubpaths, paint, strokeStyle) ?? {
|
|
406
413
|
kind: "path",
|
|
407
414
|
subpaths: pathSubpaths,
|
|
408
415
|
fillRule: paintFillRuleFor(operator),
|
|
409
|
-
...paint
|
|
416
|
+
...paint,
|
|
417
|
+
...strokeStyle !== void 0 ? { style: strokeStyle } : {}
|
|
410
418
|
});
|
|
411
419
|
}
|
|
412
420
|
resetPath();
|
|
@@ -705,6 +713,12 @@ function runContentStream(bytes, resources, initialState, context, items, depth,
|
|
|
705
713
|
lineWidth: numAt(operands, 0)
|
|
706
714
|
};
|
|
707
715
|
break;
|
|
716
|
+
case "d":
|
|
717
|
+
gs = {
|
|
718
|
+
...gs,
|
|
719
|
+
dashArray: (require_objects.asArray(operands[0]) ?? []).map((entry) => require_objects.asNumber(entry) ?? 0)
|
|
720
|
+
};
|
|
721
|
+
break;
|
|
708
722
|
case "f":
|
|
709
723
|
case "F":
|
|
710
724
|
case "f*":
|
package/dist/interpret.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { PdfDiagnosticSink } from "./diagnostics.cjs";
|
|
2
2
|
import { r as Matrix } from "./matrix-B7_SBKQ5.cjs";
|
|
3
3
|
import { PdfDict, PdfObject } from "./objects.cjs";
|
|
4
|
-
import { Color } from "document-schema.js";
|
|
4
|
+
import { Color, ContentStrokeStyle } from "document-schema.js";
|
|
5
5
|
//#region src/interpret.d.ts
|
|
6
6
|
interface ExtractedTextRun {
|
|
7
7
|
readonly kind: "text";
|
|
@@ -48,6 +48,7 @@ interface ExtractedLine {
|
|
|
48
48
|
readonly y2Pt: number;
|
|
49
49
|
readonly color: Color;
|
|
50
50
|
readonly widthPt: number;
|
|
51
|
+
readonly style?: ContentStrokeStyle;
|
|
51
52
|
readonly layerName?: string;
|
|
52
53
|
readonly mcid?: number;
|
|
53
54
|
}
|
|
@@ -74,6 +75,7 @@ interface ExtractedPath extends ExtractedPaint {
|
|
|
74
75
|
readonly kind: "path";
|
|
75
76
|
readonly subpaths: readonly ExtractedSubpath[];
|
|
76
77
|
readonly fillRule: "nonzero" | "evenodd";
|
|
78
|
+
readonly style?: ContentStrokeStyle;
|
|
77
79
|
}
|
|
78
80
|
interface ExtractedImage {
|
|
79
81
|
readonly kind: "image";
|
package/dist/interpret.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { PdfDiagnosticSink } from "./diagnostics.js";
|
|
2
2
|
import { r as Matrix } from "./matrix-B7_SBKQ5.js";
|
|
3
3
|
import { PdfDict, PdfObject } from "./objects.js";
|
|
4
|
-
import { Color } from "document-schema.js";
|
|
4
|
+
import { Color, ContentStrokeStyle } from "document-schema.js";
|
|
5
5
|
//#region src/interpret.d.ts
|
|
6
6
|
interface ExtractedTextRun {
|
|
7
7
|
readonly kind: "text";
|
|
@@ -48,6 +48,7 @@ interface ExtractedLine {
|
|
|
48
48
|
readonly y2Pt: number;
|
|
49
49
|
readonly color: Color;
|
|
50
50
|
readonly widthPt: number;
|
|
51
|
+
readonly style?: ContentStrokeStyle;
|
|
51
52
|
readonly layerName?: string;
|
|
52
53
|
readonly mcid?: number;
|
|
53
54
|
}
|
|
@@ -74,6 +75,7 @@ interface ExtractedPath extends ExtractedPaint {
|
|
|
74
75
|
readonly kind: "path";
|
|
75
76
|
readonly subpaths: readonly ExtractedSubpath[];
|
|
76
77
|
readonly fillRule: "nonzero" | "evenodd";
|
|
78
|
+
readonly style?: ContentStrokeStyle;
|
|
77
79
|
}
|
|
78
80
|
interface ExtractedImage {
|
|
79
81
|
readonly kind: "image";
|
package/dist/interpret.js
CHANGED
|
@@ -249,7 +249,11 @@ function detectEllipse(subpath, paint) {
|
|
|
249
249
|
stroke: paint.stroke
|
|
250
250
|
};
|
|
251
251
|
}
|
|
252
|
-
function
|
|
252
|
+
function strokeStyleFromDashArray(dashArray) {
|
|
253
|
+
if (dashArray.length === 0) return;
|
|
254
|
+
return dashArray.length === 2 && dashArray[0] === 0 ? "dotted" : "dashed";
|
|
255
|
+
}
|
|
256
|
+
function detectLine(subpath, paint, style) {
|
|
253
257
|
const segment = subpath.segments[0];
|
|
254
258
|
if (subpath.closed || subpath.segments.length !== 1 || segment?.kind !== "line") return;
|
|
255
259
|
if (paint.fill !== void 0 || paint.stroke === void 0) return;
|
|
@@ -260,13 +264,14 @@ function detectLine(subpath, paint) {
|
|
|
260
264
|
x2Pt: segment.xPt,
|
|
261
265
|
y2Pt: segment.yPt,
|
|
262
266
|
color: paint.stroke.color,
|
|
263
|
-
widthPt: paint.stroke.widthPt
|
|
267
|
+
widthPt: paint.stroke.widthPt,
|
|
268
|
+
...style !== void 0 ? { style } : {}
|
|
264
269
|
};
|
|
265
270
|
}
|
|
266
|
-
function classifyShape(subpaths, paint) {
|
|
271
|
+
function classifyShape(subpaths, paint, style) {
|
|
267
272
|
const subpath = subpaths[0];
|
|
268
273
|
if (subpaths.length !== 1 || subpath === void 0) return;
|
|
269
|
-
return detectRect(subpath, paint) ?? detectEllipse(subpath, paint) ?? detectLine(subpath, paint);
|
|
274
|
+
return detectRect(subpath, paint) ?? detectEllipse(subpath, paint) ?? detectLine(subpath, paint, style);
|
|
270
275
|
}
|
|
271
276
|
function interpretContentStream(bytes, resources, context) {
|
|
272
277
|
const items = [];
|
|
@@ -275,6 +280,7 @@ function interpretContentStream(bytes, resources, context) {
|
|
|
275
280
|
fillColor: COLOR_BLACK,
|
|
276
281
|
strokeColor: COLOR_BLACK,
|
|
277
282
|
lineWidth: DEFAULT_LINE_WIDTH_PT,
|
|
283
|
+
dashArray: [],
|
|
278
284
|
...initialTextParameters()
|
|
279
285
|
}, context, items, 0, []);
|
|
280
286
|
return items;
|
|
@@ -401,11 +407,13 @@ function runContentStream(bytes, resources, initialState, context, items, depth,
|
|
|
401
407
|
widthPt: gs.lineWidth
|
|
402
408
|
} : void 0
|
|
403
409
|
};
|
|
404
|
-
|
|
410
|
+
const strokeStyle = isStrokeOp ? strokeStyleFromDashArray(gs.dashArray) : void 0;
|
|
411
|
+
pushItem(classifyShape(pathSubpaths, paint, strokeStyle) ?? {
|
|
405
412
|
kind: "path",
|
|
406
413
|
subpaths: pathSubpaths,
|
|
407
414
|
fillRule: paintFillRuleFor(operator),
|
|
408
|
-
...paint
|
|
415
|
+
...paint,
|
|
416
|
+
...strokeStyle !== void 0 ? { style: strokeStyle } : {}
|
|
409
417
|
});
|
|
410
418
|
}
|
|
411
419
|
resetPath();
|
|
@@ -704,6 +712,12 @@ function runContentStream(bytes, resources, initialState, context, items, depth,
|
|
|
704
712
|
lineWidth: numAt(operands, 0)
|
|
705
713
|
};
|
|
706
714
|
break;
|
|
715
|
+
case "d":
|
|
716
|
+
gs = {
|
|
717
|
+
...gs,
|
|
718
|
+
dashArray: (asArray(operands[0]) ?? []).map((entry) => asNumber(entry) ?? 0)
|
|
719
|
+
};
|
|
720
|
+
break;
|
|
707
721
|
case "f":
|
|
708
722
|
case "F":
|
|
709
723
|
case "f*":
|
package/dist/read.cjs
CHANGED
|
@@ -426,6 +426,7 @@ function convertLine(item, pageMatrix) {
|
|
|
426
426
|
y2Pt: p2.y,
|
|
427
427
|
color: item.color,
|
|
428
428
|
widthPt: item.widthPt,
|
|
429
|
+
...item.style !== void 0 ? { style: item.style } : {},
|
|
429
430
|
...item.layerName !== void 0 ? { layer: item.layerName } : {}
|
|
430
431
|
};
|
|
431
432
|
}
|
|
@@ -482,6 +483,7 @@ function convertPath(item, pageMatrix) {
|
|
|
482
483
|
...item.fill !== void 0 ? { fill: item.fill } : {},
|
|
483
484
|
...item.fill !== void 0 && item.fillRule === "evenodd" ? { fillRule: "evenodd" } : {},
|
|
484
485
|
...item.stroke !== void 0 ? { stroke: item.stroke } : {},
|
|
486
|
+
...item.style !== void 0 ? { style: item.style } : {},
|
|
485
487
|
...item.layerName !== void 0 ? { layer: item.layerName } : {}
|
|
486
488
|
};
|
|
487
489
|
}
|
package/dist/read.js
CHANGED
|
@@ -425,6 +425,7 @@ function convertLine(item, pageMatrix) {
|
|
|
425
425
|
y2Pt: p2.y,
|
|
426
426
|
color: item.color,
|
|
427
427
|
widthPt: item.widthPt,
|
|
428
|
+
...item.style !== void 0 ? { style: item.style } : {},
|
|
428
429
|
...item.layerName !== void 0 ? { layer: item.layerName } : {}
|
|
429
430
|
};
|
|
430
431
|
}
|
|
@@ -481,6 +482,7 @@ function convertPath(item, pageMatrix) {
|
|
|
481
482
|
...item.fill !== void 0 ? { fill: item.fill } : {},
|
|
482
483
|
...item.fill !== void 0 && item.fillRule === "evenodd" ? { fillRule: "evenodd" } : {},
|
|
483
484
|
...item.stroke !== void 0 ? { stroke: item.stroke } : {},
|
|
485
|
+
...item.style !== void 0 ? { style: item.style } : {},
|
|
484
486
|
...item.layerName !== void 0 ? { layer: item.layerName } : {}
|
|
485
487
|
};
|
|
486
488
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pdf-codec",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.12",
|
|
4
4
|
"description": "Hand-written, dependency-minimal PDF codec: parses arbitrary real-world PDFs and generates new ones, built on its own codec-owned LayoutDocument item model and Zod 4 codecs.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|