documents.js 1.45.0 → 1.46.1
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 +4 -3
- package/dist/index.cjs +265 -13
- package/dist/index.js +265 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -30,7 +30,7 @@ npm install documents.js
|
|
|
30
30
|
|
|
31
31
|
## Usage
|
|
32
32
|
|
|
33
|
-
The eight round-trip ergonomic conversions (docx/pptx/odt/odp ⇄ PDF), plus `odsToPdf`/`odgToPdf`'s one-directional additions (there is no `pdfToOds` yet — PDF
|
|
33
|
+
The eight round-trip ergonomic conversions (docx/pptx/odt/odp ⇄ PDF), plus `odsToPdf`/`odgToPdf`'s one-directional additions (there is no `pdfToOds`/`pdfToOdg` yet — the PDF reader's general vector-path tracking both directions need now exists (`src/pdf/interpret.ts`), but the reconstruction algorithms themselves, `reconstructSpreadsheet`/`reconstructDrawing`, are not yet built — see [Gotchas](#gotchas-and-quirks)):
|
|
34
34
|
|
|
35
35
|
```ts
|
|
36
36
|
import { docxToPdf, odgToPdf, odpToPdf, odsToPdf, odtToPdf, pdfToDocx, pdfToOdp, pdfToOdt, pptxToPdf, pdfToPptx } from 'documents.js';
|
|
@@ -206,8 +206,9 @@ To run a single test file: `pnpm vitest run src/path/to/file.test.ts`.
|
|
|
206
206
|
- **The docx⇄PDF and pptx⇄PDF conversions are explicitly not round-trip-lossless** — in deliberate contrast to `ooxml.js`'s own `packageCodec`, which is byte/part-faithful by design. See [Fidelity](#fidelity).
|
|
207
207
|
- **`odpToPdf`/`pdfToOdp` needed zero new layout code.** `readOdpContent` (`src/odf/odp/read.ts`) produces the identical `presentation` `ContentDocument` shape `readPptxContent` does, so it feeds `convertPresentationToLayout` unmodified — including the existing hidden-annotation speaker-notes mechanism below, which carries odp's `presentation:notes` through to the PDF with no new notes-handling code at all; `pdfToOdp` reuses `reconstructPresentation` unmodified too, the same architectural bet `pdfToOdt` already proved for `reconstructWordprocessing`. The genuinely new work for the reverse direction was the live-view editor itself (`src/edit/odp/*`) — see Architecture above.
|
|
208
208
|
- **`OdpShape.rotationDeg` writes a real `draw:transform`, built on `odf.js`'s own transform machinery.** It is the write-side inverse of `odf.js`'s `resolveOdfShapeGeometry` (`typed/shared/transform.ts`), built on that module's own exported `applyOdfTransform` rather than a hand-rolled rotation matrix, so it inherits that module's own empirically-verified rotate/translate composition order and sign convention by construction. Unlike `PptxShape` (see the `colSpan`/`rowSpan` gotcha below, which pptx still has and odp does not), `buildOdpPackage` writes a rotated shape's rotation back correctly — verified both by this package's own tests and by opening a fresh, editor-built `.odp` in actual LibreOffice.
|
|
209
|
-
- **`
|
|
210
|
-
- **`
|
|
209
|
+
- **`src/pdf/interpret.ts` now tracks general vector paths, not just axis-aligned `re` rectangles.** `m`/`l`/`c`/`v`/`y`/`h` (and `re` itself, per its own ISO 32000-1 definition as a 4-point rectangle subpath) accumulate real subpaths — CTM-transformed line/cubic segments, open or closed — and any paint operator (`f`/`F`/`f*`/`S`/`s`/`B`/`B*`/`b`/`b*`) emits a `LayoutPath` item when the path isn't reducible to the simple single-`re`-on-an-axis-aligned-CTM case (which still takes the original, unchanged `LayoutRect` fast path). Verified both by dedicated tests and by a genuine `writePath` → `writePdf` → `readPdf` round trip recovering the original `LayoutPath` value exactly. This is the shared infrastructure both `pdfToOds` and `pdfToOdg` below need — it exists now, but neither conversion function itself does yet.
|
|
210
|
+
- **`odsToPdf` is one-directional — there is no `pdfToOds` yet, and no `reconstructSpreadsheet`.** The PDF-side blocker (general vector-path tracking, needed to detect a reconstructed sheet's gridlines from recovered geometry) is resolved — see the `interpret.ts` gotcha above. What's still missing is `reconstructSpreadsheet` itself: the actual gridline-detection/text-clustering-into-a-grid algorithm. `buildOdsPackage` (`src/edit/ods/content.ts`) is built and exported, ready for `pdfToOds` to call the moment that algorithm lands; nothing currently calls it.
|
|
211
|
+
- **`odgToPdf` is one-directional — there is no `pdfToOdg`, and no `reconstructDrawing`, even though the odg live-view editor and `buildOdgPackage` both now exist.** The PDF-side blocker (general vector-path tracking) is resolved — see the `interpret.ts` gotcha above. What's still missing is `reconstructDrawing` itself: mapping recovered `LayoutRect`/`LayoutPath`/`LayoutText`/`LayoutImage` geometry back onto ODF shapes (which recovered path is a rect vs. a genuine curve, where one shape ends and another begins) — a fundamentally different, unstarted problem from `reconstructWordprocessing`/`reconstructPresentation`'s own paragraph/shape geometry clustering, though genuinely more tractable since a drawing has no semantic structure to infer, only shapes to map near-1:1. `buildOdgPackage` (`src/edit/odg/content.ts`) is built and exported, ready for `pdfToOdg` to call the moment that algorithm lands; nothing currently calls it, mirroring `buildOdsPackage`'s own identical situation.
|
|
211
212
|
- **A vector primitive's own fill/stroke needed a self-contained graphic-family style writer, not `odf.js`'s own `StyleRegistry`.** `'graphic'` is a recognised `StyleFamily` member (`odf.js`'s `src/styles/registry.ts`), but `StylePropertiesSchema`/`buildStylePropertyElements` (`properties.ts`/`serialize.ts`) only ever model text/paragraph formatting and never emit a `style:graphic-properties` element for any family — extending that shared package for one narrow, documents.js-local need (`draw:fill(-color)`/`draw:stroke` + `svg:stroke-color`/`svg:stroke-width`) would be scope creep into a foreign package for a two-attribute-group writer this package can express directly. `src/edit/odg/style.ts` is that writer: it still reuses `odf.js`'s general append-only style-editing invariant (a setter always mints a fresh `style:style` and repoints `draw:style-name`, never mutates an existing entry — verified by the same `assertAutomaticStylesOnlyAppended` helper `OdpEditor`'s own live-view fidelity test uses) and `src/edit/odt/automatic-styles.ts`'s `ensureAutomaticStyles`/`nextStyleName` (the "find-or-create `office:automatic-styles`, mint the next unused name" logic every other hand-rolled style helper in this package already shares), rather than a third reimplementation of either.
|
|
212
213
|
- **A path vector's own `svg:d` is cross-checked against `odf.js`'s real parser, not merely asserted to "look plausible".** `src/edit/odg/svg-path.ts`'s `buildSvgPathData` is the write-side inverse of `odf.js`'s `parseOdfPathData`; `OdgPathVector.subpaths` re-derives its value by reparsing the actual written `svg:viewBox`/`svg:d` through that exact function (plus `parseOdfViewBox`/`buildOdfSubpaths`) on every read, rather than echoing back whatever `ContentSubpath[]` the caller originally passed to `addPath` — so every read is itself a live round-trip proof, and this module's own test suite additionally feeds `buildSvgPathData`'s output straight into `parseOdfPathData` to confirm point-for-point recovery.
|
|
213
214
|
- **A newly added vector/shape's paint order is expressed purely as document order, with no `draw:z-index` ever written.** This matches `odf.js`'s own reader-side convention exactly (`typed/draw/shapes.ts`'s `paintOrderKey`: honour an explicit `draw:z-index` when present, otherwise fall back to document order — and real LibreOffice output never emits one, it reorders elements instead), so `OdgPage.addRect`/`addEllipse`/`addLine`/`addPath`/`addTextBox`/`addImage` simply append to `draw:page`'s own children in call order and nothing more is needed for a later `add*` call to paint in front of an earlier one.
|
package/dist/index.cjs
CHANGED
|
@@ -7544,6 +7544,7 @@ function readInlineImage(reader, sink) {
|
|
|
7544
7544
|
//#region src/pdf/interpret.ts
|
|
7545
7545
|
const MAX_FORM_XOBJECT_DEPTH = 12;
|
|
7546
7546
|
const FALLBACK_GLYPH_WIDTH_PER_1000 = 500;
|
|
7547
|
+
const DEFAULT_LINE_WIDTH_PT = 1;
|
|
7547
7548
|
function defaultTextState() {
|
|
7548
7549
|
return {
|
|
7549
7550
|
tm: IDENTITY_MATRIX,
|
|
@@ -7636,16 +7637,118 @@ function interpretContentStream(bytes, resources, context) {
|
|
|
7636
7637
|
runContentStream(bytes, resources, {
|
|
7637
7638
|
ctm: IDENTITY_MATRIX,
|
|
7638
7639
|
fillColor: document_content_model.COLOR_BLACK,
|
|
7639
|
-
strokeColor: document_content_model.COLOR_BLACK
|
|
7640
|
+
strokeColor: document_content_model.COLOR_BLACK,
|
|
7641
|
+
lineWidth: DEFAULT_LINE_WIDTH_PT
|
|
7640
7642
|
}, context, items, 0);
|
|
7641
7643
|
return items;
|
|
7642
7644
|
}
|
|
7645
|
+
function lastPointOf(subpath) {
|
|
7646
|
+
const last = subpath.segments[subpath.segments.length - 1];
|
|
7647
|
+
return last === void 0 ? {
|
|
7648
|
+
x: subpath.startXPt,
|
|
7649
|
+
y: subpath.startYPt
|
|
7650
|
+
} : {
|
|
7651
|
+
x: last.xPt,
|
|
7652
|
+
y: last.yPt
|
|
7653
|
+
};
|
|
7654
|
+
}
|
|
7643
7655
|
function runContentStream(bytes, resources, initialState, context, items, depth) {
|
|
7644
7656
|
const operations = readContentStream(bytes, context.sink);
|
|
7645
7657
|
const gsStack = [];
|
|
7646
7658
|
let gs = initialState;
|
|
7647
7659
|
let ts = defaultTextState();
|
|
7648
7660
|
let pendingRect;
|
|
7661
|
+
let pathSubpaths = [];
|
|
7662
|
+
let currentSubpath;
|
|
7663
|
+
const finalizeCurrentSubpath = () => {
|
|
7664
|
+
if (currentSubpath !== void 0) {
|
|
7665
|
+
pathSubpaths.push(currentSubpath);
|
|
7666
|
+
currentSubpath = void 0;
|
|
7667
|
+
}
|
|
7668
|
+
};
|
|
7669
|
+
const resetPath = () => {
|
|
7670
|
+
pendingRect = void 0;
|
|
7671
|
+
pathSubpaths = [];
|
|
7672
|
+
currentSubpath = void 0;
|
|
7673
|
+
};
|
|
7674
|
+
const appendRectSubpath = (operands, ctm) => {
|
|
7675
|
+
finalizeCurrentSubpath();
|
|
7676
|
+
const x = numAt(operands, 0);
|
|
7677
|
+
const y = numAt(operands, 1);
|
|
7678
|
+
const w = numAt(operands, 2);
|
|
7679
|
+
const h = numAt(operands, 3);
|
|
7680
|
+
const p1 = applyMatrix(ctm, {
|
|
7681
|
+
x,
|
|
7682
|
+
y
|
|
7683
|
+
});
|
|
7684
|
+
const p2 = applyMatrix(ctm, {
|
|
7685
|
+
x: x + w,
|
|
7686
|
+
y
|
|
7687
|
+
});
|
|
7688
|
+
const p3 = applyMatrix(ctm, {
|
|
7689
|
+
x: x + w,
|
|
7690
|
+
y: y + h
|
|
7691
|
+
});
|
|
7692
|
+
const p4 = applyMatrix(ctm, {
|
|
7693
|
+
x,
|
|
7694
|
+
y: y + h
|
|
7695
|
+
});
|
|
7696
|
+
pathSubpaths.push({
|
|
7697
|
+
startXPt: p1.x,
|
|
7698
|
+
startYPt: p1.y,
|
|
7699
|
+
segments: [
|
|
7700
|
+
{
|
|
7701
|
+
kind: "line",
|
|
7702
|
+
xPt: p2.x,
|
|
7703
|
+
yPt: p2.y
|
|
7704
|
+
},
|
|
7705
|
+
{
|
|
7706
|
+
kind: "line",
|
|
7707
|
+
xPt: p3.x,
|
|
7708
|
+
yPt: p3.y
|
|
7709
|
+
},
|
|
7710
|
+
{
|
|
7711
|
+
kind: "line",
|
|
7712
|
+
xPt: p4.x,
|
|
7713
|
+
yPt: p4.y
|
|
7714
|
+
}
|
|
7715
|
+
],
|
|
7716
|
+
closed: true
|
|
7717
|
+
});
|
|
7718
|
+
};
|
|
7719
|
+
const paintFillRuleFor = (operator) => operator.endsWith("*") ? "evenodd" : "nonzero";
|
|
7720
|
+
const emitPaint = (operator) => {
|
|
7721
|
+
if (operator === "n") {
|
|
7722
|
+
resetPath();
|
|
7723
|
+
return;
|
|
7724
|
+
}
|
|
7725
|
+
if ((operator === "s" || operator === "b" || operator === "b*") && currentSubpath !== void 0) currentSubpath.closed = true;
|
|
7726
|
+
if ((operator === "f" || operator === "F" || operator === "f*") && currentSubpath === void 0 && pathSubpaths.length === 1 && pendingRect !== void 0) {
|
|
7727
|
+
items.push({
|
|
7728
|
+
kind: "rect",
|
|
7729
|
+
...pendingRect,
|
|
7730
|
+
color: gs.fillColor
|
|
7731
|
+
});
|
|
7732
|
+
resetPath();
|
|
7733
|
+
return;
|
|
7734
|
+
}
|
|
7735
|
+
finalizeCurrentSubpath();
|
|
7736
|
+
if (pathSubpaths.length > 0) {
|
|
7737
|
+
const isFillOp = operator === "f" || operator === "F" || operator === "f*" || operator === "B" || operator === "B*" || operator === "b" || operator === "b*";
|
|
7738
|
+
const isStrokeOp = operator === "S" || operator === "s" || operator === "B" || operator === "B*" || operator === "b" || operator === "b*";
|
|
7739
|
+
items.push({
|
|
7740
|
+
kind: "path",
|
|
7741
|
+
subpaths: pathSubpaths,
|
|
7742
|
+
fillRule: paintFillRuleFor(operator),
|
|
7743
|
+
fill: isFillOp ? gs.fillColor : void 0,
|
|
7744
|
+
stroke: isStrokeOp ? {
|
|
7745
|
+
color: gs.strokeColor,
|
|
7746
|
+
widthPt: gs.lineWidth
|
|
7747
|
+
} : void 0
|
|
7748
|
+
});
|
|
7749
|
+
}
|
|
7750
|
+
resetPath();
|
|
7751
|
+
};
|
|
7649
7752
|
const advanceThroughString = (codes) => {
|
|
7650
7753
|
if (ts.fontResourceName === void 0) return;
|
|
7651
7754
|
let offset = 0;
|
|
@@ -7829,25 +7932,118 @@ function runContentStream(bytes, resources, initialState, context, items, depth)
|
|
|
7829
7932
|
}
|
|
7830
7933
|
case "re":
|
|
7831
7934
|
pendingRect = rectFromOperands(operands, gs.ctm);
|
|
7935
|
+
appendRectSubpath(operands, gs.ctm);
|
|
7936
|
+
break;
|
|
7937
|
+
case "m": {
|
|
7938
|
+
finalizeCurrentSubpath();
|
|
7939
|
+
const p = applyMatrix(gs.ctm, {
|
|
7940
|
+
x: numAt(operands, 0),
|
|
7941
|
+
y: numAt(operands, 1)
|
|
7942
|
+
});
|
|
7943
|
+
currentSubpath = {
|
|
7944
|
+
startXPt: p.x,
|
|
7945
|
+
startYPt: p.y,
|
|
7946
|
+
segments: [],
|
|
7947
|
+
closed: false
|
|
7948
|
+
};
|
|
7949
|
+
pendingRect = void 0;
|
|
7950
|
+
break;
|
|
7951
|
+
}
|
|
7952
|
+
case "l": {
|
|
7953
|
+
const p = applyMatrix(gs.ctm, {
|
|
7954
|
+
x: numAt(operands, 0),
|
|
7955
|
+
y: numAt(operands, 1)
|
|
7956
|
+
});
|
|
7957
|
+
currentSubpath?.segments.push({
|
|
7958
|
+
kind: "line",
|
|
7959
|
+
xPt: p.x,
|
|
7960
|
+
yPt: p.y
|
|
7961
|
+
});
|
|
7962
|
+
pendingRect = void 0;
|
|
7832
7963
|
break;
|
|
7833
|
-
|
|
7834
|
-
case "
|
|
7835
|
-
|
|
7964
|
+
}
|
|
7965
|
+
case "c": {
|
|
7966
|
+
const c1 = applyMatrix(gs.ctm, {
|
|
7967
|
+
x: numAt(operands, 0),
|
|
7968
|
+
y: numAt(operands, 1)
|
|
7969
|
+
});
|
|
7970
|
+
const c2 = applyMatrix(gs.ctm, {
|
|
7971
|
+
x: numAt(operands, 2),
|
|
7972
|
+
y: numAt(operands, 3)
|
|
7973
|
+
});
|
|
7974
|
+
const p = applyMatrix(gs.ctm, {
|
|
7975
|
+
x: numAt(operands, 4),
|
|
7976
|
+
y: numAt(operands, 5)
|
|
7977
|
+
});
|
|
7978
|
+
currentSubpath?.segments.push({
|
|
7979
|
+
kind: "cubic",
|
|
7980
|
+
c1xPt: c1.x,
|
|
7981
|
+
c1yPt: c1.y,
|
|
7982
|
+
c2xPt: c2.x,
|
|
7983
|
+
c2yPt: c2.y,
|
|
7984
|
+
xPt: p.x,
|
|
7985
|
+
yPt: p.y
|
|
7986
|
+
});
|
|
7987
|
+
pendingRect = void 0;
|
|
7988
|
+
break;
|
|
7989
|
+
}
|
|
7836
7990
|
case "v":
|
|
7837
|
-
|
|
7991
|
+
if (currentSubpath !== void 0) {
|
|
7992
|
+
const cur = lastPointOf(currentSubpath);
|
|
7993
|
+
const c2 = applyMatrix(gs.ctm, {
|
|
7994
|
+
x: numAt(operands, 0),
|
|
7995
|
+
y: numAt(operands, 1)
|
|
7996
|
+
});
|
|
7997
|
+
const p = applyMatrix(gs.ctm, {
|
|
7998
|
+
x: numAt(operands, 2),
|
|
7999
|
+
y: numAt(operands, 3)
|
|
8000
|
+
});
|
|
8001
|
+
currentSubpath.segments.push({
|
|
8002
|
+
kind: "cubic",
|
|
8003
|
+
c1xPt: cur.x,
|
|
8004
|
+
c1yPt: cur.y,
|
|
8005
|
+
c2xPt: c2.x,
|
|
8006
|
+
c2yPt: c2.y,
|
|
8007
|
+
xPt: p.x,
|
|
8008
|
+
yPt: p.y
|
|
8009
|
+
});
|
|
8010
|
+
}
|
|
8011
|
+
pendingRect = void 0;
|
|
8012
|
+
break;
|
|
8013
|
+
case "y": {
|
|
8014
|
+
const c1 = applyMatrix(gs.ctm, {
|
|
8015
|
+
x: numAt(operands, 0),
|
|
8016
|
+
y: numAt(operands, 1)
|
|
8017
|
+
});
|
|
8018
|
+
const p = applyMatrix(gs.ctm, {
|
|
8019
|
+
x: numAt(operands, 2),
|
|
8020
|
+
y: numAt(operands, 3)
|
|
8021
|
+
});
|
|
8022
|
+
currentSubpath?.segments.push({
|
|
8023
|
+
kind: "cubic",
|
|
8024
|
+
c1xPt: c1.x,
|
|
8025
|
+
c1yPt: c1.y,
|
|
8026
|
+
c2xPt: p.x,
|
|
8027
|
+
c2yPt: p.y,
|
|
8028
|
+
xPt: p.x,
|
|
8029
|
+
yPt: p.y
|
|
8030
|
+
});
|
|
8031
|
+
pendingRect = void 0;
|
|
8032
|
+
break;
|
|
8033
|
+
}
|
|
7838
8034
|
case "h":
|
|
8035
|
+
if (currentSubpath !== void 0) currentSubpath.closed = true;
|
|
7839
8036
|
pendingRect = void 0;
|
|
7840
8037
|
break;
|
|
8038
|
+
case "w":
|
|
8039
|
+
gs = {
|
|
8040
|
+
...gs,
|
|
8041
|
+
lineWidth: numAt(operands, 0)
|
|
8042
|
+
};
|
|
8043
|
+
break;
|
|
7841
8044
|
case "f":
|
|
7842
8045
|
case "F":
|
|
7843
8046
|
case "f*":
|
|
7844
|
-
if (pendingRect !== void 0) items.push({
|
|
7845
|
-
kind: "rect",
|
|
7846
|
-
...pendingRect,
|
|
7847
|
-
color: gs.fillColor
|
|
7848
|
-
});
|
|
7849
|
-
pendingRect = void 0;
|
|
7850
|
-
break;
|
|
7851
8047
|
case "S":
|
|
7852
8048
|
case "s":
|
|
7853
8049
|
case "B":
|
|
@@ -7855,7 +8051,7 @@ function runContentStream(bytes, resources, initialState, context, items, depth)
|
|
|
7855
8051
|
case "b":
|
|
7856
8052
|
case "b*":
|
|
7857
8053
|
case "n":
|
|
7858
|
-
|
|
8054
|
+
emitPaint(operator);
|
|
7859
8055
|
break;
|
|
7860
8056
|
case "BT":
|
|
7861
8057
|
ts = defaultTextState();
|
|
@@ -8837,6 +9033,7 @@ function readPage(page, resolver, fontResolver, images, imageIdCache, sink) {
|
|
|
8837
9033
|
function convertExtractedItem(item, pageMatrix, fontResolver, images, imageIdCache, resolver, sink) {
|
|
8838
9034
|
if (item.kind === "text") return convertText(item, pageMatrix, fontResolver);
|
|
8839
9035
|
if (item.kind === "rect") return convertRect(item, pageMatrix);
|
|
9036
|
+
if (item.kind === "path") return convertPath(item, pageMatrix);
|
|
8840
9037
|
if (item.kind === "image") return convertImage(item, pageMatrix, images, imageIdCache, resolver, sink);
|
|
8841
9038
|
return convertInlineImage(item, pageMatrix, images, resolver, sink);
|
|
8842
9039
|
}
|
|
@@ -8884,6 +9081,61 @@ function convertRect(item, pageMatrix) {
|
|
|
8884
9081
|
fill: item.color
|
|
8885
9082
|
};
|
|
8886
9083
|
}
|
|
9084
|
+
function transformSubpath(subpath, pageMatrix) {
|
|
9085
|
+
const start = applyMatrix(pageMatrix, {
|
|
9086
|
+
x: subpath.startXPt,
|
|
9087
|
+
y: subpath.startYPt
|
|
9088
|
+
});
|
|
9089
|
+
const segments = subpath.segments.map((segment) => {
|
|
9090
|
+
if (segment.kind === "line") {
|
|
9091
|
+
const p = applyMatrix(pageMatrix, {
|
|
9092
|
+
x: segment.xPt,
|
|
9093
|
+
y: segment.yPt
|
|
9094
|
+
});
|
|
9095
|
+
return {
|
|
9096
|
+
kind: "line",
|
|
9097
|
+
xPt: p.x,
|
|
9098
|
+
yPt: p.y
|
|
9099
|
+
};
|
|
9100
|
+
}
|
|
9101
|
+
const c1 = applyMatrix(pageMatrix, {
|
|
9102
|
+
x: segment.c1xPt,
|
|
9103
|
+
y: segment.c1yPt
|
|
9104
|
+
});
|
|
9105
|
+
const c2 = applyMatrix(pageMatrix, {
|
|
9106
|
+
x: segment.c2xPt,
|
|
9107
|
+
y: segment.c2yPt
|
|
9108
|
+
});
|
|
9109
|
+
const p = applyMatrix(pageMatrix, {
|
|
9110
|
+
x: segment.xPt,
|
|
9111
|
+
y: segment.yPt
|
|
9112
|
+
});
|
|
9113
|
+
return {
|
|
9114
|
+
kind: "cubic",
|
|
9115
|
+
c1xPt: c1.x,
|
|
9116
|
+
c1yPt: c1.y,
|
|
9117
|
+
c2xPt: c2.x,
|
|
9118
|
+
c2yPt: c2.y,
|
|
9119
|
+
xPt: p.x,
|
|
9120
|
+
yPt: p.y
|
|
9121
|
+
};
|
|
9122
|
+
});
|
|
9123
|
+
return {
|
|
9124
|
+
startXPt: start.x,
|
|
9125
|
+
startYPt: start.y,
|
|
9126
|
+
segments,
|
|
9127
|
+
closed: subpath.closed
|
|
9128
|
+
};
|
|
9129
|
+
}
|
|
9130
|
+
function convertPath(item, pageMatrix) {
|
|
9131
|
+
return {
|
|
9132
|
+
kind: "path",
|
|
9133
|
+
subpaths: item.subpaths.map((subpath) => transformSubpath(subpath, pageMatrix)),
|
|
9134
|
+
...item.fill !== void 0 ? { fill: item.fill } : {},
|
|
9135
|
+
...item.fill !== void 0 && item.fillRule === "evenodd" ? { fillRule: "evenodd" } : {},
|
|
9136
|
+
...item.stroke !== void 0 ? { stroke: item.stroke } : {}
|
|
9137
|
+
};
|
|
9138
|
+
}
|
|
8887
9139
|
function imagePlacementFrom(matrix) {
|
|
8888
9140
|
const rotationDeg = matrixRotationDegrees(matrix);
|
|
8889
9141
|
return {
|
package/dist/index.js
CHANGED
|
@@ -7543,6 +7543,7 @@ function readInlineImage(reader, sink) {
|
|
|
7543
7543
|
//#region src/pdf/interpret.ts
|
|
7544
7544
|
const MAX_FORM_XOBJECT_DEPTH = 12;
|
|
7545
7545
|
const FALLBACK_GLYPH_WIDTH_PER_1000 = 500;
|
|
7546
|
+
const DEFAULT_LINE_WIDTH_PT = 1;
|
|
7546
7547
|
function defaultTextState() {
|
|
7547
7548
|
return {
|
|
7548
7549
|
tm: IDENTITY_MATRIX,
|
|
@@ -7635,16 +7636,118 @@ function interpretContentStream(bytes, resources, context) {
|
|
|
7635
7636
|
runContentStream(bytes, resources, {
|
|
7636
7637
|
ctm: IDENTITY_MATRIX,
|
|
7637
7638
|
fillColor: COLOR_BLACK,
|
|
7638
|
-
strokeColor: COLOR_BLACK
|
|
7639
|
+
strokeColor: COLOR_BLACK,
|
|
7640
|
+
lineWidth: DEFAULT_LINE_WIDTH_PT
|
|
7639
7641
|
}, context, items, 0);
|
|
7640
7642
|
return items;
|
|
7641
7643
|
}
|
|
7644
|
+
function lastPointOf(subpath) {
|
|
7645
|
+
const last = subpath.segments[subpath.segments.length - 1];
|
|
7646
|
+
return last === void 0 ? {
|
|
7647
|
+
x: subpath.startXPt,
|
|
7648
|
+
y: subpath.startYPt
|
|
7649
|
+
} : {
|
|
7650
|
+
x: last.xPt,
|
|
7651
|
+
y: last.yPt
|
|
7652
|
+
};
|
|
7653
|
+
}
|
|
7642
7654
|
function runContentStream(bytes, resources, initialState, context, items, depth) {
|
|
7643
7655
|
const operations = readContentStream(bytes, context.sink);
|
|
7644
7656
|
const gsStack = [];
|
|
7645
7657
|
let gs = initialState;
|
|
7646
7658
|
let ts = defaultTextState();
|
|
7647
7659
|
let pendingRect;
|
|
7660
|
+
let pathSubpaths = [];
|
|
7661
|
+
let currentSubpath;
|
|
7662
|
+
const finalizeCurrentSubpath = () => {
|
|
7663
|
+
if (currentSubpath !== void 0) {
|
|
7664
|
+
pathSubpaths.push(currentSubpath);
|
|
7665
|
+
currentSubpath = void 0;
|
|
7666
|
+
}
|
|
7667
|
+
};
|
|
7668
|
+
const resetPath = () => {
|
|
7669
|
+
pendingRect = void 0;
|
|
7670
|
+
pathSubpaths = [];
|
|
7671
|
+
currentSubpath = void 0;
|
|
7672
|
+
};
|
|
7673
|
+
const appendRectSubpath = (operands, ctm) => {
|
|
7674
|
+
finalizeCurrentSubpath();
|
|
7675
|
+
const x = numAt(operands, 0);
|
|
7676
|
+
const y = numAt(operands, 1);
|
|
7677
|
+
const w = numAt(operands, 2);
|
|
7678
|
+
const h = numAt(operands, 3);
|
|
7679
|
+
const p1 = applyMatrix(ctm, {
|
|
7680
|
+
x,
|
|
7681
|
+
y
|
|
7682
|
+
});
|
|
7683
|
+
const p2 = applyMatrix(ctm, {
|
|
7684
|
+
x: x + w,
|
|
7685
|
+
y
|
|
7686
|
+
});
|
|
7687
|
+
const p3 = applyMatrix(ctm, {
|
|
7688
|
+
x: x + w,
|
|
7689
|
+
y: y + h
|
|
7690
|
+
});
|
|
7691
|
+
const p4 = applyMatrix(ctm, {
|
|
7692
|
+
x,
|
|
7693
|
+
y: y + h
|
|
7694
|
+
});
|
|
7695
|
+
pathSubpaths.push({
|
|
7696
|
+
startXPt: p1.x,
|
|
7697
|
+
startYPt: p1.y,
|
|
7698
|
+
segments: [
|
|
7699
|
+
{
|
|
7700
|
+
kind: "line",
|
|
7701
|
+
xPt: p2.x,
|
|
7702
|
+
yPt: p2.y
|
|
7703
|
+
},
|
|
7704
|
+
{
|
|
7705
|
+
kind: "line",
|
|
7706
|
+
xPt: p3.x,
|
|
7707
|
+
yPt: p3.y
|
|
7708
|
+
},
|
|
7709
|
+
{
|
|
7710
|
+
kind: "line",
|
|
7711
|
+
xPt: p4.x,
|
|
7712
|
+
yPt: p4.y
|
|
7713
|
+
}
|
|
7714
|
+
],
|
|
7715
|
+
closed: true
|
|
7716
|
+
});
|
|
7717
|
+
};
|
|
7718
|
+
const paintFillRuleFor = (operator) => operator.endsWith("*") ? "evenodd" : "nonzero";
|
|
7719
|
+
const emitPaint = (operator) => {
|
|
7720
|
+
if (operator === "n") {
|
|
7721
|
+
resetPath();
|
|
7722
|
+
return;
|
|
7723
|
+
}
|
|
7724
|
+
if ((operator === "s" || operator === "b" || operator === "b*") && currentSubpath !== void 0) currentSubpath.closed = true;
|
|
7725
|
+
if ((operator === "f" || operator === "F" || operator === "f*") && currentSubpath === void 0 && pathSubpaths.length === 1 && pendingRect !== void 0) {
|
|
7726
|
+
items.push({
|
|
7727
|
+
kind: "rect",
|
|
7728
|
+
...pendingRect,
|
|
7729
|
+
color: gs.fillColor
|
|
7730
|
+
});
|
|
7731
|
+
resetPath();
|
|
7732
|
+
return;
|
|
7733
|
+
}
|
|
7734
|
+
finalizeCurrentSubpath();
|
|
7735
|
+
if (pathSubpaths.length > 0) {
|
|
7736
|
+
const isFillOp = operator === "f" || operator === "F" || operator === "f*" || operator === "B" || operator === "B*" || operator === "b" || operator === "b*";
|
|
7737
|
+
const isStrokeOp = operator === "S" || operator === "s" || operator === "B" || operator === "B*" || operator === "b" || operator === "b*";
|
|
7738
|
+
items.push({
|
|
7739
|
+
kind: "path",
|
|
7740
|
+
subpaths: pathSubpaths,
|
|
7741
|
+
fillRule: paintFillRuleFor(operator),
|
|
7742
|
+
fill: isFillOp ? gs.fillColor : void 0,
|
|
7743
|
+
stroke: isStrokeOp ? {
|
|
7744
|
+
color: gs.strokeColor,
|
|
7745
|
+
widthPt: gs.lineWidth
|
|
7746
|
+
} : void 0
|
|
7747
|
+
});
|
|
7748
|
+
}
|
|
7749
|
+
resetPath();
|
|
7750
|
+
};
|
|
7648
7751
|
const advanceThroughString = (codes) => {
|
|
7649
7752
|
if (ts.fontResourceName === void 0) return;
|
|
7650
7753
|
let offset = 0;
|
|
@@ -7828,25 +7931,118 @@ function runContentStream(bytes, resources, initialState, context, items, depth)
|
|
|
7828
7931
|
}
|
|
7829
7932
|
case "re":
|
|
7830
7933
|
pendingRect = rectFromOperands(operands, gs.ctm);
|
|
7934
|
+
appendRectSubpath(operands, gs.ctm);
|
|
7935
|
+
break;
|
|
7936
|
+
case "m": {
|
|
7937
|
+
finalizeCurrentSubpath();
|
|
7938
|
+
const p = applyMatrix(gs.ctm, {
|
|
7939
|
+
x: numAt(operands, 0),
|
|
7940
|
+
y: numAt(operands, 1)
|
|
7941
|
+
});
|
|
7942
|
+
currentSubpath = {
|
|
7943
|
+
startXPt: p.x,
|
|
7944
|
+
startYPt: p.y,
|
|
7945
|
+
segments: [],
|
|
7946
|
+
closed: false
|
|
7947
|
+
};
|
|
7948
|
+
pendingRect = void 0;
|
|
7949
|
+
break;
|
|
7950
|
+
}
|
|
7951
|
+
case "l": {
|
|
7952
|
+
const p = applyMatrix(gs.ctm, {
|
|
7953
|
+
x: numAt(operands, 0),
|
|
7954
|
+
y: numAt(operands, 1)
|
|
7955
|
+
});
|
|
7956
|
+
currentSubpath?.segments.push({
|
|
7957
|
+
kind: "line",
|
|
7958
|
+
xPt: p.x,
|
|
7959
|
+
yPt: p.y
|
|
7960
|
+
});
|
|
7961
|
+
pendingRect = void 0;
|
|
7831
7962
|
break;
|
|
7832
|
-
|
|
7833
|
-
case "
|
|
7834
|
-
|
|
7963
|
+
}
|
|
7964
|
+
case "c": {
|
|
7965
|
+
const c1 = applyMatrix(gs.ctm, {
|
|
7966
|
+
x: numAt(operands, 0),
|
|
7967
|
+
y: numAt(operands, 1)
|
|
7968
|
+
});
|
|
7969
|
+
const c2 = applyMatrix(gs.ctm, {
|
|
7970
|
+
x: numAt(operands, 2),
|
|
7971
|
+
y: numAt(operands, 3)
|
|
7972
|
+
});
|
|
7973
|
+
const p = applyMatrix(gs.ctm, {
|
|
7974
|
+
x: numAt(operands, 4),
|
|
7975
|
+
y: numAt(operands, 5)
|
|
7976
|
+
});
|
|
7977
|
+
currentSubpath?.segments.push({
|
|
7978
|
+
kind: "cubic",
|
|
7979
|
+
c1xPt: c1.x,
|
|
7980
|
+
c1yPt: c1.y,
|
|
7981
|
+
c2xPt: c2.x,
|
|
7982
|
+
c2yPt: c2.y,
|
|
7983
|
+
xPt: p.x,
|
|
7984
|
+
yPt: p.y
|
|
7985
|
+
});
|
|
7986
|
+
pendingRect = void 0;
|
|
7987
|
+
break;
|
|
7988
|
+
}
|
|
7835
7989
|
case "v":
|
|
7836
|
-
|
|
7990
|
+
if (currentSubpath !== void 0) {
|
|
7991
|
+
const cur = lastPointOf(currentSubpath);
|
|
7992
|
+
const c2 = applyMatrix(gs.ctm, {
|
|
7993
|
+
x: numAt(operands, 0),
|
|
7994
|
+
y: numAt(operands, 1)
|
|
7995
|
+
});
|
|
7996
|
+
const p = applyMatrix(gs.ctm, {
|
|
7997
|
+
x: numAt(operands, 2),
|
|
7998
|
+
y: numAt(operands, 3)
|
|
7999
|
+
});
|
|
8000
|
+
currentSubpath.segments.push({
|
|
8001
|
+
kind: "cubic",
|
|
8002
|
+
c1xPt: cur.x,
|
|
8003
|
+
c1yPt: cur.y,
|
|
8004
|
+
c2xPt: c2.x,
|
|
8005
|
+
c2yPt: c2.y,
|
|
8006
|
+
xPt: p.x,
|
|
8007
|
+
yPt: p.y
|
|
8008
|
+
});
|
|
8009
|
+
}
|
|
8010
|
+
pendingRect = void 0;
|
|
8011
|
+
break;
|
|
8012
|
+
case "y": {
|
|
8013
|
+
const c1 = applyMatrix(gs.ctm, {
|
|
8014
|
+
x: numAt(operands, 0),
|
|
8015
|
+
y: numAt(operands, 1)
|
|
8016
|
+
});
|
|
8017
|
+
const p = applyMatrix(gs.ctm, {
|
|
8018
|
+
x: numAt(operands, 2),
|
|
8019
|
+
y: numAt(operands, 3)
|
|
8020
|
+
});
|
|
8021
|
+
currentSubpath?.segments.push({
|
|
8022
|
+
kind: "cubic",
|
|
8023
|
+
c1xPt: c1.x,
|
|
8024
|
+
c1yPt: c1.y,
|
|
8025
|
+
c2xPt: p.x,
|
|
8026
|
+
c2yPt: p.y,
|
|
8027
|
+
xPt: p.x,
|
|
8028
|
+
yPt: p.y
|
|
8029
|
+
});
|
|
8030
|
+
pendingRect = void 0;
|
|
8031
|
+
break;
|
|
8032
|
+
}
|
|
7837
8033
|
case "h":
|
|
8034
|
+
if (currentSubpath !== void 0) currentSubpath.closed = true;
|
|
7838
8035
|
pendingRect = void 0;
|
|
7839
8036
|
break;
|
|
8037
|
+
case "w":
|
|
8038
|
+
gs = {
|
|
8039
|
+
...gs,
|
|
8040
|
+
lineWidth: numAt(operands, 0)
|
|
8041
|
+
};
|
|
8042
|
+
break;
|
|
7840
8043
|
case "f":
|
|
7841
8044
|
case "F":
|
|
7842
8045
|
case "f*":
|
|
7843
|
-
if (pendingRect !== void 0) items.push({
|
|
7844
|
-
kind: "rect",
|
|
7845
|
-
...pendingRect,
|
|
7846
|
-
color: gs.fillColor
|
|
7847
|
-
});
|
|
7848
|
-
pendingRect = void 0;
|
|
7849
|
-
break;
|
|
7850
8046
|
case "S":
|
|
7851
8047
|
case "s":
|
|
7852
8048
|
case "B":
|
|
@@ -7854,7 +8050,7 @@ function runContentStream(bytes, resources, initialState, context, items, depth)
|
|
|
7854
8050
|
case "b":
|
|
7855
8051
|
case "b*":
|
|
7856
8052
|
case "n":
|
|
7857
|
-
|
|
8053
|
+
emitPaint(operator);
|
|
7858
8054
|
break;
|
|
7859
8055
|
case "BT":
|
|
7860
8056
|
ts = defaultTextState();
|
|
@@ -8836,6 +9032,7 @@ function readPage(page, resolver, fontResolver, images, imageIdCache, sink) {
|
|
|
8836
9032
|
function convertExtractedItem(item, pageMatrix, fontResolver, images, imageIdCache, resolver, sink) {
|
|
8837
9033
|
if (item.kind === "text") return convertText(item, pageMatrix, fontResolver);
|
|
8838
9034
|
if (item.kind === "rect") return convertRect(item, pageMatrix);
|
|
9035
|
+
if (item.kind === "path") return convertPath(item, pageMatrix);
|
|
8839
9036
|
if (item.kind === "image") return convertImage(item, pageMatrix, images, imageIdCache, resolver, sink);
|
|
8840
9037
|
return convertInlineImage(item, pageMatrix, images, resolver, sink);
|
|
8841
9038
|
}
|
|
@@ -8883,6 +9080,61 @@ function convertRect(item, pageMatrix) {
|
|
|
8883
9080
|
fill: item.color
|
|
8884
9081
|
};
|
|
8885
9082
|
}
|
|
9083
|
+
function transformSubpath(subpath, pageMatrix) {
|
|
9084
|
+
const start = applyMatrix(pageMatrix, {
|
|
9085
|
+
x: subpath.startXPt,
|
|
9086
|
+
y: subpath.startYPt
|
|
9087
|
+
});
|
|
9088
|
+
const segments = subpath.segments.map((segment) => {
|
|
9089
|
+
if (segment.kind === "line") {
|
|
9090
|
+
const p = applyMatrix(pageMatrix, {
|
|
9091
|
+
x: segment.xPt,
|
|
9092
|
+
y: segment.yPt
|
|
9093
|
+
});
|
|
9094
|
+
return {
|
|
9095
|
+
kind: "line",
|
|
9096
|
+
xPt: p.x,
|
|
9097
|
+
yPt: p.y
|
|
9098
|
+
};
|
|
9099
|
+
}
|
|
9100
|
+
const c1 = applyMatrix(pageMatrix, {
|
|
9101
|
+
x: segment.c1xPt,
|
|
9102
|
+
y: segment.c1yPt
|
|
9103
|
+
});
|
|
9104
|
+
const c2 = applyMatrix(pageMatrix, {
|
|
9105
|
+
x: segment.c2xPt,
|
|
9106
|
+
y: segment.c2yPt
|
|
9107
|
+
});
|
|
9108
|
+
const p = applyMatrix(pageMatrix, {
|
|
9109
|
+
x: segment.xPt,
|
|
9110
|
+
y: segment.yPt
|
|
9111
|
+
});
|
|
9112
|
+
return {
|
|
9113
|
+
kind: "cubic",
|
|
9114
|
+
c1xPt: c1.x,
|
|
9115
|
+
c1yPt: c1.y,
|
|
9116
|
+
c2xPt: c2.x,
|
|
9117
|
+
c2yPt: c2.y,
|
|
9118
|
+
xPt: p.x,
|
|
9119
|
+
yPt: p.y
|
|
9120
|
+
};
|
|
9121
|
+
});
|
|
9122
|
+
return {
|
|
9123
|
+
startXPt: start.x,
|
|
9124
|
+
startYPt: start.y,
|
|
9125
|
+
segments,
|
|
9126
|
+
closed: subpath.closed
|
|
9127
|
+
};
|
|
9128
|
+
}
|
|
9129
|
+
function convertPath(item, pageMatrix) {
|
|
9130
|
+
return {
|
|
9131
|
+
kind: "path",
|
|
9132
|
+
subpaths: item.subpaths.map((subpath) => transformSubpath(subpath, pageMatrix)),
|
|
9133
|
+
...item.fill !== void 0 ? { fill: item.fill } : {},
|
|
9134
|
+
...item.fill !== void 0 && item.fillRule === "evenodd" ? { fillRule: "evenodd" } : {},
|
|
9135
|
+
...item.stroke !== void 0 ? { stroke: item.stroke } : {}
|
|
9136
|
+
};
|
|
9137
|
+
}
|
|
8886
9138
|
function imagePlacementFrom(matrix) {
|
|
8887
9139
|
const rotationDeg = matrixRotationDegrees(matrix);
|
|
8888
9140
|
return {
|
package/package.json
CHANGED