reamkit 1.23.0 → 1.25.0
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 +22 -11
- package/dist/esm/core/bmp.d.ts +33 -0
- package/dist/esm/core/bmp.js +276 -0
- package/dist/esm/core/converter/facade.d.ts +3 -3
- package/dist/esm/core/converter/facade.js +12 -0
- package/dist/esm/core/converter/ream.d.ts +48 -4
- package/dist/esm/core/converter/ream.js +25 -4
- package/dist/esm/core/document-model/index.d.ts +1 -1
- package/dist/esm/core/document-model/types.d.ts +54 -0
- package/dist/esm/core/drawingml/chart-geometry.js +3 -2
- package/dist/esm/core/drawingml/diagram/colors.d.ts +52 -0
- package/dist/esm/core/drawingml/diagram/colors.js +125 -0
- package/dist/esm/core/drawingml/diagram/data-model.d.ts +47 -0
- package/dist/esm/core/drawingml/diagram/data-model.js +119 -0
- package/dist/esm/core/drawingml/diagram/layout-engine.d.ts +46 -0
- package/dist/esm/core/drawingml/diagram/layout-engine.js +948 -0
- package/dist/esm/core/drawingml/diagram/run.d.ts +37 -0
- package/dist/esm/core/drawingml/diagram/run.js +50 -0
- package/dist/esm/core/drawingml/diagram/to-drawing.d.ts +16 -0
- package/dist/esm/core/drawingml/diagram/to-drawing.js +86 -0
- package/dist/esm/core/drawingml/preset-geometry.js +116 -6
- package/dist/esm/core/drawingml/text-warp.d.ts +58 -0
- package/dist/esm/core/drawingml/text-warp.js +355 -0
- package/dist/esm/core/drawingml/theme-parser.js +9 -3
- package/dist/esm/core/font/measure.d.ts +12 -0
- package/dist/esm/core/font/measure.js +36 -0
- package/dist/esm/core/images.d.ts +30 -2
- package/dist/esm/core/images.js +121 -5
- package/dist/esm/core/metafile/emf.js +144 -16
- package/dist/esm/core/metafile/picture.d.ts +49 -0
- package/dist/esm/core/metafile/picture.js +70 -1
- package/dist/esm/core/metafile/wmf.js +53 -4
- package/dist/esm/core/ole/escher-blip.js +11 -1
- package/dist/esm/core/outline.d.ts +17 -0
- package/dist/esm/core/outline.js +30 -0
- package/dist/esm/core/style-cascade/resolver.js +1 -0
- package/dist/esm/core/style-cascade/types.d.ts +3 -1
- package/dist/esm/excel/print-model.js +2 -2
- package/dist/esm/excel/sheet-drawing.js +1 -1
- package/dist/esm/excel/sheet-to-flow.d.ts +10 -0
- package/dist/esm/excel/sheet-to-flow.js +30 -3
- package/dist/esm/html/html-writer.js +5 -3
- package/dist/esm/index.d.ts +3 -0
- package/dist/esm/index.js +2 -1
- package/dist/esm/layout/page-doc.d.ts +35 -0
- package/dist/esm/layout/page-doc.js +14 -1
- package/dist/esm/layout/styled-layout.js +217 -30
- package/dist/esm/markdown/markdown-writer.d.ts +41 -0
- package/dist/esm/markdown/markdown-writer.js +733 -0
- package/dist/esm/pdf/shading.js +8 -6
- package/dist/esm/pdf/styled-page-emitter.js +62 -4
- package/dist/esm/pdf/vector-graphics.js +1 -1
- package/dist/esm/pdf-reader/annots.d.ts +24 -0
- package/dist/esm/pdf-reader/annots.js +126 -0
- package/dist/esm/pdf-reader/content.d.ts +131 -5
- package/dist/esm/pdf-reader/content.js +169 -12
- package/dist/esm/pdf-reader/display.d.ts +56 -0
- package/dist/esm/pdf-reader/display.js +162 -0
- package/dist/esm/pdf-reader/document.d.ts +36 -1
- package/dist/esm/pdf-reader/document.js +92 -25
- package/dist/esm/pdf-reader/embedded-fonts.d.ts +31 -0
- package/dist/esm/pdf-reader/embedded-fonts.js +94 -0
- package/dist/esm/pdf-reader/flow-build.d.ts +61 -6
- package/dist/esm/pdf-reader/flow-build.js +128 -22
- package/dist/esm/pdf-reader/font.js +185 -4
- package/dist/esm/pdf-reader/image-decode.js +56 -5
- package/dist/esm/pdf-reader/images.d.ts +6 -0
- package/dist/esm/pdf-reader/images.js +25 -5
- package/dist/esm/pdf-reader/jpeg.d.ts +18 -0
- package/dist/esm/pdf-reader/jpeg.js +419 -0
- package/dist/esm/pdf-reader/layout.d.ts +1 -1
- package/dist/esm/pdf-reader/layout.js +221 -32
- package/dist/esm/pdf-reader/pattern-tint.d.ts +17 -0
- package/dist/esm/pdf-reader/pattern-tint.js +181 -0
- package/dist/esm/pdf-reader/reader.d.ts +9 -1
- package/dist/esm/pdf-reader/reader.js +22 -6
- package/dist/esm/pdf-reader/shading.d.ts +14 -0
- package/dist/esm/pdf-reader/shading.js +27 -1
- package/dist/esm/pdf-reader/tagged.js +156 -17
- package/dist/esm/pdf-reader/text.d.ts +13 -1
- package/dist/esm/pdf-reader/text.js +70 -3
- package/dist/esm/pdf-reader/vector.d.ts +25 -1
- package/dist/esm/pdf-reader/vector.js +168 -12
- package/dist/esm/pptx/ppt/ppt-reader.js +95 -27
- package/dist/esm/pptx/ppt/ppt-text.d.ts +41 -0
- package/dist/esm/pptx/ppt/ppt-text.js +327 -37
- package/dist/esm/pptx/pptx-reader.js +90 -10
- package/dist/esm/pptx/slide-parser.d.ts +4 -1
- package/dist/esm/pptx/slide-parser.js +104 -13
- package/dist/esm/pptx/sp-helpers.d.ts +7 -3
- package/dist/esm/pptx/sp-helpers.js +45 -7
- package/dist/esm/pptx/table-style.d.ts +7 -0
- package/dist/esm/pptx/table-style.js +59 -7
- package/dist/esm/svg/svg-writer.js +1 -0
- package/dist/esm/word/document-parser.d.ts +4 -1
- package/dist/esm/word/document-parser.js +4 -1
- package/dist/esm/word/docx-reader.js +26 -13
- package/dist/esm/word/docx-writer.js +15 -1
- package/dist/esm/word/drawing-parser.d.ts +20 -0
- package/dist/esm/word/drawing-parser.js +44 -5
- package/dist/esm/word/table-parser.js +3 -0
- package/package.json +1 -1
package/dist/esm/pdf/shading.js
CHANGED
|
@@ -284,14 +284,16 @@ function normalizeStops(stops) {
|
|
|
284
284
|
colorHex: "000000",
|
|
285
285
|
alpha: 1
|
|
286
286
|
}];
|
|
287
|
-
out[0]
|
|
288
|
-
|
|
287
|
+
const first = out[0];
|
|
288
|
+
if (first.offset > 0) out.unshift({
|
|
289
|
+
...first,
|
|
289
290
|
offset: 0
|
|
290
|
-
};
|
|
291
|
-
out[out.length - 1]
|
|
292
|
-
|
|
291
|
+
});
|
|
292
|
+
const last = out[out.length - 1];
|
|
293
|
+
if (last.offset < 1) out.push({
|
|
294
|
+
...last,
|
|
293
295
|
offset: 1
|
|
294
|
-
};
|
|
296
|
+
});
|
|
295
297
|
return out;
|
|
296
298
|
}
|
|
297
299
|
function rgb01(hex) {
|
|
@@ -7,6 +7,7 @@ import { GLUE_SHRINK_RATIO } from "../layout/styled-layout.js";
|
|
|
7
7
|
import { paintPlan } from "../layout/page-doc.js";
|
|
8
8
|
import { emitClipPath, emitVectorShape, shadowBlurLayers } from "./vector-graphics.js";
|
|
9
9
|
import { buildGradientAlphaMask, buildGradientPattern, shapeBbox } from "./shading.js";
|
|
10
|
+
import { warpGlyphMatrix } from "../core/drawingml/text-warp.js";
|
|
10
11
|
import { sanitizeHref } from "../core/links.js";
|
|
11
12
|
import { embedAssociatedFile } from "./embedded-file.js";
|
|
12
13
|
import { buildSrgbIccProfile } from "./icc-profile.js";
|
|
@@ -111,7 +112,11 @@ function assembleStyledPdf(laid, options, doc) {
|
|
|
111
112
|
if (!pdfaProfile) for (const page of renderedPages) {
|
|
112
113
|
const ph = page.height;
|
|
113
114
|
const plan = paintPlan(page.commands);
|
|
114
|
-
const shapesNeedingPatterns = [...plan.shapes, ...[
|
|
115
|
+
const shapesNeedingPatterns = [...plan.shapes, ...[
|
|
116
|
+
...plan.behind,
|
|
117
|
+
...plan.pictures.flat(),
|
|
118
|
+
...plan.ordered.flat()
|
|
119
|
+
].filter((i) => i.type === "shape")];
|
|
115
120
|
for (const item of shapesNeedingPatterns) {
|
|
116
121
|
const gradient = item.shape.fillGradient;
|
|
117
122
|
if (!gradient) continue;
|
|
@@ -211,7 +216,11 @@ function assembleStyledPdf(laid, options, doc) {
|
|
|
211
216
|
wantAlpha(img.alpha);
|
|
212
217
|
wantDuotone(img, page.height);
|
|
213
218
|
}
|
|
214
|
-
for (const item of [
|
|
219
|
+
for (const item of [
|
|
220
|
+
...plan.behind,
|
|
221
|
+
...plan.pictures.flat(),
|
|
222
|
+
...plan.ordered.flat()
|
|
223
|
+
]) {
|
|
215
224
|
if (item.type === "image") {
|
|
216
225
|
wantAlpha(washVeil(item.wash)?.alpha);
|
|
217
226
|
wantAlpha(item.alpha);
|
|
@@ -644,6 +653,7 @@ function emitPageContent(page, tagging, gradientNames, alphaStateNames, duotoneS
|
|
|
644
653
|
let lastSize = -1;
|
|
645
654
|
let lastColor = "";
|
|
646
655
|
let lastFauxWidth = 0;
|
|
656
|
+
let lastStroke;
|
|
647
657
|
let lastTz = 100;
|
|
648
658
|
const switchFontIfNeeded = (tok) => {
|
|
649
659
|
const fontKey = tok.font.resourceName;
|
|
@@ -652,11 +662,20 @@ function emitPageContent(page, tagging, gradientNames, alphaStateNames, duotoneS
|
|
|
652
662
|
lastFont = fontKey;
|
|
653
663
|
lastSize = tok.fontSizePt;
|
|
654
664
|
}
|
|
655
|
-
const
|
|
665
|
+
const outline = tok.resolvedRun.textOutline;
|
|
666
|
+
const fauxWidth = outline !== void 0 ? outline.widthPt : tok.synthetic?.bold === true ? tok.fontSizePt * .03 : 0;
|
|
656
667
|
if (fauxWidth !== lastFauxWidth) {
|
|
657
668
|
out.push(fauxWidth > 0 ? `2 Tr ${formatNumber(fauxWidth)} w` : "0 Tr");
|
|
658
669
|
lastFauxWidth = fauxWidth;
|
|
659
670
|
}
|
|
671
|
+
const strokeHex = outline?.colorHex;
|
|
672
|
+
if (strokeHex !== lastStroke) {
|
|
673
|
+
if (strokeHex !== void 0) {
|
|
674
|
+
const [sr, sg, sb] = hexToRgb01(strokeHex);
|
|
675
|
+
out.push(`${formatNumber(sr)} ${formatNumber(sg)} ${formatNumber(sb)} RG`);
|
|
676
|
+
}
|
|
677
|
+
lastStroke = strokeHex;
|
|
678
|
+
}
|
|
660
679
|
const tz = Math.round((tok.synthetic?.widthScale ?? 1) * 100);
|
|
661
680
|
if (tz !== lastTz) {
|
|
662
681
|
out.push(`${formatNumber(tz)} Tz`);
|
|
@@ -713,6 +732,8 @@ function emitPageContent(page, tagging, gradientNames, alphaStateNames, duotoneS
|
|
|
713
732
|
}
|
|
714
733
|
lastFont = "";
|
|
715
734
|
lastFauxWidth = 0;
|
|
735
|
+
lastStroke = void 0;
|
|
736
|
+
lastStroke = void 0;
|
|
716
737
|
lastTz = 100;
|
|
717
738
|
lastSize = -1;
|
|
718
739
|
lastColor = "";
|
|
@@ -761,6 +782,7 @@ function emitPageContent(page, tagging, gradientNames, alphaStateNames, duotoneS
|
|
|
761
782
|
}
|
|
762
783
|
lastFont = "";
|
|
763
784
|
lastFauxWidth = 0;
|
|
785
|
+
lastStroke = void 0;
|
|
764
786
|
lastTz = 100;
|
|
765
787
|
lastSize = -1;
|
|
766
788
|
lastColor = "";
|
|
@@ -795,6 +817,8 @@ function emitPageContent(page, tagging, gradientNames, alphaStateNames, duotoneS
|
|
|
795
817
|
out.push("Q");
|
|
796
818
|
lastFont = "";
|
|
797
819
|
lastFauxWidth = 0;
|
|
820
|
+
lastStroke = void 0;
|
|
821
|
+
lastStroke = void 0;
|
|
798
822
|
lastTz = 100;
|
|
799
823
|
lastSize = -1;
|
|
800
824
|
lastColor = "";
|
|
@@ -822,6 +846,8 @@ function emitPageContent(page, tagging, gradientNames, alphaStateNames, duotoneS
|
|
|
822
846
|
for (const op of emitVectorShape(shape)) out.push(op);
|
|
823
847
|
lastFont = "";
|
|
824
848
|
lastFauxWidth = 0;
|
|
849
|
+
lastStroke = void 0;
|
|
850
|
+
lastStroke = void 0;
|
|
825
851
|
lastTz = 100;
|
|
826
852
|
lastSize = -1;
|
|
827
853
|
lastColor = "";
|
|
@@ -841,6 +867,7 @@ function emitPageContent(page, tagging, gradientNames, alphaStateNames, duotoneS
|
|
|
841
867
|
out.push(`${formatNumber(clip.x)} ${formatNumber(H - clip.y - clip.height)} ${formatNumber(clip.width)} ${formatNumber(clip.height)} re W n`);
|
|
842
868
|
lastFont = "";
|
|
843
869
|
lastFauxWidth = 0;
|
|
870
|
+
lastStroke = void 0;
|
|
844
871
|
lastTz = 100;
|
|
845
872
|
lastSize = -1;
|
|
846
873
|
lastColor = "";
|
|
@@ -855,6 +882,7 @@ function emitPageContent(page, tagging, gradientNames, alphaStateNames, duotoneS
|
|
|
855
882
|
out.push("Q");
|
|
856
883
|
lastFont = "";
|
|
857
884
|
lastFauxWidth = 0;
|
|
885
|
+
lastStroke = void 0;
|
|
858
886
|
lastTz = 100;
|
|
859
887
|
lastSize = -1;
|
|
860
888
|
lastColor = "";
|
|
@@ -1009,6 +1037,34 @@ function emitPageContent(page, tagging, gradientNames, alphaStateNames, duotoneS
|
|
|
1009
1037
|
}
|
|
1010
1038
|
const hasRise = line.tokens.some((t) => t.kind === "text" && t.risePt !== void 0);
|
|
1011
1039
|
const hasFauxItalic = line.tokens.some((t) => t.kind === "text" && t.synthetic?.italic === true);
|
|
1040
|
+
const warp = cmd.warp;
|
|
1041
|
+
if (warp) {
|
|
1042
|
+
if (!inBT) {
|
|
1043
|
+
out.push("BT");
|
|
1044
|
+
inBT = true;
|
|
1045
|
+
}
|
|
1046
|
+
let cursor = originX;
|
|
1047
|
+
for (const tok of line.tokens) {
|
|
1048
|
+
if (tok.kind !== "text" || tok.isSpace) {
|
|
1049
|
+
cursor += tok.widthPt;
|
|
1050
|
+
continue;
|
|
1051
|
+
}
|
|
1052
|
+
switchFontIfNeeded(tok);
|
|
1053
|
+
const chars = [...tok.text];
|
|
1054
|
+
const raw = chars.map((c) => tok.font.measure.textWidthPt(c, tok.fontSizePt));
|
|
1055
|
+
const total = raw.reduce((a, b) => a + b, 0);
|
|
1056
|
+
const k = total > 0 ? tok.widthPt / total : 1;
|
|
1057
|
+
for (let i = 0; i < chars.length; i++) {
|
|
1058
|
+
const adv = raw[i] * k;
|
|
1059
|
+
const m = warpGlyphMatrix(warp, cursor, adv, cmd.baselineY);
|
|
1060
|
+
cursor += adv;
|
|
1061
|
+
if (!m) continue;
|
|
1062
|
+
out.push(`${formatNumber(m[0])} ${formatNumber(-m[1])} ${formatNumber(m[2])} ${formatNumber(-m[3])} ${formatNumber(m[4])} ${formatNumber(H - m[5])} Tm`);
|
|
1063
|
+
out.push(tok.font.measure.showText(chars[i]));
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
return;
|
|
1067
|
+
}
|
|
1012
1068
|
if (rotationDeg !== 0 && !hasImageToken && !hasMathToken) {
|
|
1013
1069
|
if (!inBT) {
|
|
1014
1070
|
out.push("BT");
|
|
@@ -1104,6 +1160,7 @@ function emitPageContent(page, tagging, gradientNames, alphaStateNames, duotoneS
|
|
|
1104
1160
|
if (item.type === "shape") {
|
|
1105
1161
|
const t = item.shape.transform;
|
|
1106
1162
|
const fillAlpha = item.shape.fillAlpha;
|
|
1163
|
+
const layer = item.shape.shadow ? shadowBlurLayers(item.shape.shadow) : void 0;
|
|
1107
1164
|
for (const op of emitVectorShape({
|
|
1108
1165
|
...item.shape,
|
|
1109
1166
|
transform: [
|
|
@@ -1114,7 +1171,7 @@ function emitPageContent(page, tagging, gradientNames, alphaStateNames, duotoneS
|
|
|
1114
1171
|
t[4],
|
|
1115
1172
|
H - t[5]
|
|
1116
1173
|
]
|
|
1117
|
-
}, gradientNames?.get(item.shape), void 0, gradientMaskNames?.get(item.shape) ?? (fillAlpha !== void 0 && fillAlpha < 1 ? alphaStateNames?.get(Math.round(fillAlpha * 1e3) / 1e3) : void 0))) out.push(op);
|
|
1174
|
+
}, gradientNames?.get(item.shape), layer && layer.alpha < 1 ? alphaStateNames?.get(Math.round(layer.alpha * 1e3) / 1e3) : void 0, gradientMaskNames?.get(item.shape) ?? (fillAlpha !== void 0 && fillAlpha < 1 ? alphaStateNames?.get(Math.round(fillAlpha * 1e3) / 1e3) : void 0))) out.push(op);
|
|
1118
1175
|
return;
|
|
1119
1176
|
}
|
|
1120
1177
|
if (item.type === "image") {
|
|
@@ -1287,6 +1344,7 @@ function emitPageContent(page, tagging, gradientNames, alphaStateNames, duotoneS
|
|
|
1287
1344
|
for (const item of run) emitInOrder(item);
|
|
1288
1345
|
if (tagging) out.push("EMC");
|
|
1289
1346
|
}
|
|
1347
|
+
for (const run of plan.ordered) for (const item of run) emitInOrder(item);
|
|
1290
1348
|
emitLinesPass();
|
|
1291
1349
|
return {
|
|
1292
1350
|
content: encoder.encode(out.join("\n")),
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Matrix } from './content.js';
|
|
2
|
+
import { PdfDict, PdfStream } from '../pdf/objects.js';
|
|
3
|
+
import { PdfFile, PdfPage } from './document.js';
|
|
4
|
+
/** One annotation's normal appearance, ready to interpret in page space. */
|
|
5
|
+
export interface Appearance {
|
|
6
|
+
readonly stream: PdfStream;
|
|
7
|
+
/** Maps the appearance's own space onto the page (§12.5.5). */
|
|
8
|
+
readonly ctm: Matrix;
|
|
9
|
+
/** The appearance's `/Resources`, when it states its own. */
|
|
10
|
+
readonly resources: PdfDict | undefined;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Every annotation appearance the page shows, in `/Annots` order — which is
|
|
14
|
+
* the order they paint in, over the page's own content.
|
|
15
|
+
*
|
|
16
|
+
* A `Popup` is a note's window and is never part of the page. An annotation
|
|
17
|
+
* flagged Hidden or NoView paints nothing. An `/AP` `/N` may be a stream or a
|
|
18
|
+
* dictionary of states, in which case `/AS` names the one in force.
|
|
19
|
+
*
|
|
20
|
+
* @param file The owning file, for resolving references.
|
|
21
|
+
* @param page The page whose annotations are wanted.
|
|
22
|
+
* @returns The appearances, each with the matrix that places it on the page.
|
|
23
|
+
*/
|
|
24
|
+
export declare function collectPageAppearances(file: PdfFile, page: PdfPage): Array<Appearance>;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { PDF_NULL, PdfName, PdfStream } from "../pdf/objects.js";
|
|
2
|
+
import { IDENTITY, multiply } from "./content.js";
|
|
3
|
+
//#region src/pdf-reader/annots.ts
|
|
4
|
+
/** §12.5.3 `/F` — the annotation is not painted at all. */
|
|
5
|
+
var FLAG_HIDDEN = 2;
|
|
6
|
+
var FLAG_NOVIEW = 32;
|
|
7
|
+
/**
|
|
8
|
+
* Every annotation appearance the page shows, in `/Annots` order — which is
|
|
9
|
+
* the order they paint in, over the page's own content.
|
|
10
|
+
*
|
|
11
|
+
* A `Popup` is a note's window and is never part of the page. An annotation
|
|
12
|
+
* flagged Hidden or NoView paints nothing. An `/AP` `/N` may be a stream or a
|
|
13
|
+
* dictionary of states, in which case `/AS` names the one in force.
|
|
14
|
+
*
|
|
15
|
+
* @param file The owning file, for resolving references.
|
|
16
|
+
* @param page The page whose annotations are wanted.
|
|
17
|
+
* @returns The appearances, each with the matrix that places it on the page.
|
|
18
|
+
*/
|
|
19
|
+
function collectPageAppearances(file, page) {
|
|
20
|
+
const annots = file.get(page.dict, "Annots");
|
|
21
|
+
if (!Array.isArray(annots)) return [];
|
|
22
|
+
const out = [];
|
|
23
|
+
for (const entry of annots) {
|
|
24
|
+
const annot = file.resolve(entry);
|
|
25
|
+
if (!(annot instanceof Map)) continue;
|
|
26
|
+
const subtype = file.get(annot, "Subtype");
|
|
27
|
+
if (subtype instanceof PdfName && subtype.value === "Popup") continue;
|
|
28
|
+
const flags = file.get(annot, "F");
|
|
29
|
+
if (typeof flags === "number" && (flags & FLAG_HIDDEN || flags & FLAG_NOVIEW)) continue;
|
|
30
|
+
const stream = normalAppearance(file, annot);
|
|
31
|
+
if (!stream) continue;
|
|
32
|
+
const rect = rectangle(file.get(annot, "Rect"));
|
|
33
|
+
if (!rect) continue;
|
|
34
|
+
const matrix = matrixOf(file, stream.dict);
|
|
35
|
+
const bbox = rectangle(file.get(stream.dict, "BBox"));
|
|
36
|
+
const resources = file.get(stream.dict, "Resources");
|
|
37
|
+
out.push({
|
|
38
|
+
stream,
|
|
39
|
+
ctm: multiply(matrix, fitToRect(bbox, matrix, rect)),
|
|
40
|
+
resources: resources instanceof Map ? resources : void 0
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
return out;
|
|
44
|
+
}
|
|
45
|
+
/** §12.5.5 `/AP` `/N` — the normal appearance, through `/AS` when it is a set. */
|
|
46
|
+
function normalAppearance(file, annot) {
|
|
47
|
+
const ap = file.get(annot, "AP");
|
|
48
|
+
if (!(ap instanceof Map)) return void 0;
|
|
49
|
+
const normal = file.get(ap, "N");
|
|
50
|
+
if (normal instanceof PdfStream) return normal;
|
|
51
|
+
if (!(normal instanceof Map)) return void 0;
|
|
52
|
+
const state = file.get(annot, "AS");
|
|
53
|
+
const picked = state instanceof PdfName ? file.resolve(normal.get(state.value) ?? PDF_NULL) : PDF_NULL;
|
|
54
|
+
if (picked instanceof PdfStream) return picked;
|
|
55
|
+
const only = [...normal.values()].map((v) => file.resolve(v)).filter((v) => v instanceof PdfStream);
|
|
56
|
+
return only.length === 1 ? only[0] : void 0;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* §12.5.5 — the matrix taking the form's `/Matrix`-transformed `/BBox` onto the
|
|
60
|
+
* annotation's `/Rect`: the two are fitted corner to corner, so an appearance
|
|
61
|
+
* authored at any size lands exactly in the rectangle that owns it.
|
|
62
|
+
*/
|
|
63
|
+
function fitToRect(bbox, matrix, rect) {
|
|
64
|
+
if (!bbox) return [
|
|
65
|
+
1,
|
|
66
|
+
0,
|
|
67
|
+
0,
|
|
68
|
+
1,
|
|
69
|
+
rect[0],
|
|
70
|
+
rect[1]
|
|
71
|
+
];
|
|
72
|
+
const xs = [];
|
|
73
|
+
const ys = [];
|
|
74
|
+
for (const [x, y] of [
|
|
75
|
+
[bbox[0], bbox[1]],
|
|
76
|
+
[bbox[2], bbox[1]],
|
|
77
|
+
[bbox[2], bbox[3]],
|
|
78
|
+
[bbox[0], bbox[3]]
|
|
79
|
+
]) {
|
|
80
|
+
xs.push(matrix[0] * x + matrix[2] * y + matrix[4]);
|
|
81
|
+
ys.push(matrix[1] * x + matrix[3] * y + matrix[5]);
|
|
82
|
+
}
|
|
83
|
+
const bw = Math.max(...xs) - Math.min(...xs);
|
|
84
|
+
const bh = Math.max(...ys) - Math.min(...ys);
|
|
85
|
+
const sx = bw > 0 ? (rect[2] - rect[0]) / bw : 1;
|
|
86
|
+
const sy = bh > 0 ? (rect[3] - rect[1]) / bh : 1;
|
|
87
|
+
return [
|
|
88
|
+
sx,
|
|
89
|
+
0,
|
|
90
|
+
0,
|
|
91
|
+
sy,
|
|
92
|
+
rect[0] - Math.min(...xs) * sx,
|
|
93
|
+
rect[1] - Math.min(...ys) * sy
|
|
94
|
+
];
|
|
95
|
+
}
|
|
96
|
+
/** §8.10.2 `/Matrix`, or the identity when the form states none. */
|
|
97
|
+
function matrixOf(file, dict) {
|
|
98
|
+
const m = file.resolve(dict.get("Matrix") ?? PDF_NULL);
|
|
99
|
+
if (!Array.isArray(m) || m.length !== 6) return IDENTITY;
|
|
100
|
+
const n = m.map((v) => {
|
|
101
|
+
const r = file.resolve(v);
|
|
102
|
+
return typeof r === "number" ? r : 0;
|
|
103
|
+
});
|
|
104
|
+
return [
|
|
105
|
+
n[0],
|
|
106
|
+
n[1],
|
|
107
|
+
n[2],
|
|
108
|
+
n[3],
|
|
109
|
+
n[4],
|
|
110
|
+
n[5]
|
|
111
|
+
];
|
|
112
|
+
}
|
|
113
|
+
/** A four-number array as an ordered rectangle, or `undefined`. */
|
|
114
|
+
function rectangle(v) {
|
|
115
|
+
if (!Array.isArray(v) || v.length < 4) return void 0;
|
|
116
|
+
const n = v.slice(0, 4).map((x) => typeof x === "number" ? x : NaN);
|
|
117
|
+
if (n.some((x) => !Number.isFinite(x))) return void 0;
|
|
118
|
+
return [
|
|
119
|
+
Math.min(n[0], n[2]),
|
|
120
|
+
Math.min(n[1], n[3]),
|
|
121
|
+
Math.max(n[0], n[2]),
|
|
122
|
+
Math.max(n[1], n[3])
|
|
123
|
+
];
|
|
124
|
+
}
|
|
125
|
+
//#endregion
|
|
126
|
+
export { collectPageAppearances };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ShapeGradient } from '../core/vector.js';
|
|
2
|
+
import { PdfDict, PdfStream } from '../pdf/objects.js';
|
|
2
3
|
/**
|
|
3
4
|
* A page font as the interpreter needs it (built from the font dictionaries in
|
|
4
5
|
* EP2b): how wide each code is, and how a run of codes decodes to Unicode. An
|
|
@@ -12,6 +13,27 @@ export interface ContentFont {
|
|
|
12
13
|
decode: (codes: ReadonlyArray<number>) => string;
|
|
13
14
|
/** Glyph advance for one code, in 1000-unit text space. */
|
|
14
15
|
width: (code: number) => number;
|
|
16
|
+
/** §9.6.2 — the face's own `/BaseFont` name, for a document that embeds it. */
|
|
17
|
+
readonly name?: string;
|
|
18
|
+
/** §9.8.1 — the face is a bold one (weight, the ForceBold flag, or its name). */
|
|
19
|
+
readonly bold?: boolean;
|
|
20
|
+
/** §9.8.1 — the face is slanted (`/ItalicAngle`, the Italic flag, or its name). */
|
|
21
|
+
readonly italic?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* §9.6.5 — a Type 3 face, whose glyphs are content streams rather than
|
|
24
|
+
* outlines. What such a font draws is not type at all: it is whatever the
|
|
25
|
+
* procedure paints, in the resources the font states.
|
|
26
|
+
*/
|
|
27
|
+
readonly type3?: Type3Face;
|
|
28
|
+
}
|
|
29
|
+
/** §9.6.5 — the parts of a Type 3 font a caller needs to run its glyphs. */
|
|
30
|
+
export interface Type3Face {
|
|
31
|
+
/** `/FontMatrix` — glyph space to text space. */
|
|
32
|
+
readonly matrix: Matrix;
|
|
33
|
+
/** `/Encoding` + `/CharProcs` — the content stream one code draws. */
|
|
34
|
+
readonly proc: (code: number) => PdfStream | undefined;
|
|
35
|
+
/** `/Resources` the procedures draw with, when the font states its own. */
|
|
36
|
+
readonly resources: PdfDict | undefined;
|
|
15
37
|
}
|
|
16
38
|
/**
|
|
17
39
|
* One positioned text run emitted by a show operator: its decoded text, the
|
|
@@ -24,8 +46,63 @@ export interface TextRun {
|
|
|
24
46
|
readonly x: number;
|
|
25
47
|
/** Glyph origin y in page space (points). */
|
|
26
48
|
readonly y: number;
|
|
49
|
+
/**
|
|
50
|
+
* Where the pen stood after the last glyph, in page space (§9.4.4). The
|
|
51
|
+
* interpreter advances the text matrix by the font's own widths, so this is
|
|
52
|
+
* a measurement, not the half-em-per-character guess the reader used to make
|
|
53
|
+
* — and the difference between a word space and a table column is exactly
|
|
54
|
+
* the kind of thing a guess gets wrong.
|
|
55
|
+
*/
|
|
56
|
+
readonly endX: number;
|
|
57
|
+
/** The pen's y after the last glyph — with {@link endX}, the whole advance. */
|
|
58
|
+
readonly endY: number;
|
|
59
|
+
/**
|
|
60
|
+
* The baseline's direction in page space, degrees counter-clockwise from
|
|
61
|
+
* left-to-right (§9.4.2 — the text matrix may turn as well as move). Absent
|
|
62
|
+
* for ordinary upright text, which is nearly all of it.
|
|
63
|
+
*/
|
|
64
|
+
readonly angleDeg?: number;
|
|
27
65
|
readonly fontSizePt: number;
|
|
28
66
|
readonly fontKey: string;
|
|
67
|
+
/**
|
|
68
|
+
* §9.6.2 `/BaseFont` — the face's own name, subset prefix dropped and
|
|
69
|
+
* lowercased, or absent when the font states none. This is what a rebuilt run
|
|
70
|
+
* asks for, so a page whose faces the file EMBEDS is re-set in them rather
|
|
71
|
+
* than in a substitute (see `./embedded-fonts`).
|
|
72
|
+
*/
|
|
73
|
+
readonly fontName?: string;
|
|
74
|
+
/** §9.8.1 — the face the glyphs were shown in is a bold one. */
|
|
75
|
+
readonly bold?: boolean;
|
|
76
|
+
/**
|
|
77
|
+
* §8.6.6.2 — the glyphs are filled with a tiling PATTERN, named here for the
|
|
78
|
+
* caller to resolve: a pattern is a content stream, not a colour, and the
|
|
79
|
+
* fill colour still standing from before is not what the page shows.
|
|
80
|
+
*/
|
|
81
|
+
readonly fillPatternName?: string;
|
|
82
|
+
/**
|
|
83
|
+
* §9.3.6 — the page painted these glyphs NOWHERE: mode 3 shows nothing and
|
|
84
|
+
* mode 7 only adds to the clip. A scanned page carries its recognised words
|
|
85
|
+
* that way, under the picture of the page — so the run is kept, because it
|
|
86
|
+
* is the only text such a document has, and a reader reproducing the page
|
|
87
|
+
* leaves it to the picture.
|
|
88
|
+
*/
|
|
89
|
+
readonly invisible?: boolean;
|
|
90
|
+
/**
|
|
91
|
+
* §9.3.6 — the colour the glyphs are STROKED in, when the rendering mode
|
|
92
|
+
* asks for a stroke, and how wide the pen is.
|
|
93
|
+
*/
|
|
94
|
+
readonly outlineHex?: string;
|
|
95
|
+
readonly outlineWidthPt?: number;
|
|
96
|
+
/** §9.8.1 — the face the glyphs were shown in is a slanted one. */
|
|
97
|
+
readonly italic?: boolean;
|
|
98
|
+
/**
|
|
99
|
+
* §9.6.5 — the face is a Type 3 one, so what the page SHOWS here is the
|
|
100
|
+
* glyph procedures, not type. The run is kept for its words; a reader that
|
|
101
|
+
* reproduces the page draws the procedures instead of re-setting it.
|
|
102
|
+
*/
|
|
103
|
+
readonly type3?: boolean;
|
|
104
|
+
/** §8.6.8 — the non-stroking colour the glyphs were painted in (6-hex). */
|
|
105
|
+
readonly colorHex: string;
|
|
29
106
|
/**
|
|
30
107
|
* The marked-content id of the enclosing `BDC` sequence (§14.6), if any — the
|
|
31
108
|
* link from this text to the structure element that owns it (E-PDF EP3).
|
|
@@ -43,6 +120,8 @@ export interface TextRun {
|
|
|
43
120
|
* `mcid` links the paint to its structure element (a `/Figure`, E-PDF EP6).
|
|
44
121
|
*/
|
|
45
122
|
export interface ImagePlacement {
|
|
123
|
+
/** Where the `Do` fell in the stream's painting order — see {@link VectorPlacement.order}. */
|
|
124
|
+
readonly order: number;
|
|
46
125
|
/** XObject resource name (no leading slash). */
|
|
47
126
|
readonly name: string;
|
|
48
127
|
readonly ctm: Matrix;
|
|
@@ -72,17 +151,38 @@ export type PathSeg = {
|
|
|
72
151
|
readonly op: 'close';
|
|
73
152
|
};
|
|
74
153
|
/**
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
* a fill colour/gradient (EP10/EP16c) and/or a stroke colour + width (EP11),
|
|
78
|
-
* plus the enclosing structure id.
|
|
154
|
+
* §8.5.4 — the clipping region in force when a path was painted: the path that
|
|
155
|
+
* `W`/`W*` installed, plus its page-space bounding box.
|
|
79
156
|
*/
|
|
157
|
+
export interface ClipRegion {
|
|
158
|
+
readonly segs: ReadonlyArray<PathSeg>;
|
|
159
|
+
readonly minX: number;
|
|
160
|
+
readonly minY: number;
|
|
161
|
+
readonly maxX: number;
|
|
162
|
+
readonly maxY: number;
|
|
163
|
+
}
|
|
80
164
|
export interface VectorPlacement {
|
|
165
|
+
/**
|
|
166
|
+
* Where this fell in the stream's painting order (§8.5.3): later covers
|
|
167
|
+
* earlier, and a `Do` of a form is numbered here too, so a caller walking
|
|
168
|
+
* into that form knows exactly where its marks belong among these.
|
|
169
|
+
*/
|
|
170
|
+
readonly order: number;
|
|
81
171
|
readonly segs: ReadonlyArray<PathSeg>;
|
|
172
|
+
/** §8.5.4 — the clip in force when it was painted, when there was one. */
|
|
173
|
+
readonly clip?: ClipRegion;
|
|
82
174
|
/** Fill colour (6-hex), present iff the path is filled (`f` / `F` / `f*` / `B` / `b`). */
|
|
83
175
|
readonly fillHex?: string;
|
|
84
176
|
/** Shading pattern, present iff filled with one (EP16c). */
|
|
85
177
|
readonly gradient?: ShapeGradient;
|
|
178
|
+
/** §11.6.4.4 `/ca` — how opaque the fill is, when the page asked for less. */
|
|
179
|
+
readonly alpha?: number;
|
|
180
|
+
/**
|
|
181
|
+
* §8.7.3 — the TILING pattern resource name the path is filled with. Its
|
|
182
|
+
* content is a stream of its own, so what the fill actually shows is only
|
|
183
|
+
* known by walking into it; the `fillHex` beside this is not the fill.
|
|
184
|
+
*/
|
|
185
|
+
readonly patternName?: string;
|
|
86
186
|
/** Stroke colour (6-hex), present iff the path is stroked (`S` / `s` / `B` / `b`) — EP11. */
|
|
87
187
|
readonly strokeHex?: string;
|
|
88
188
|
/** Stroke width in page-space points — EP11. */
|
|
@@ -94,6 +194,19 @@ export interface InterpretResult {
|
|
|
94
194
|
readonly texts: Array<TextRun>;
|
|
95
195
|
readonly images: Array<ImagePlacement>;
|
|
96
196
|
readonly vectors: Array<VectorPlacement>;
|
|
197
|
+
/** §9.6.5 — every Type 3 glyph the stream showed, with where to run it. */
|
|
198
|
+
readonly glyphs: Array<Type3Call>;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* §9.6.5 — one showing of a Type 3 glyph: which procedure, and the matrix that
|
|
202
|
+
* puts glyph space on the page.
|
|
203
|
+
*/
|
|
204
|
+
export interface Type3Call {
|
|
205
|
+
readonly stream: PdfStream;
|
|
206
|
+
readonly resources: PdfDict | undefined;
|
|
207
|
+
readonly ctm: Matrix;
|
|
208
|
+
/** §8.5.3 — its place in the stream's painting order, as a form call has. */
|
|
209
|
+
readonly order: number;
|
|
97
210
|
}
|
|
98
211
|
/**
|
|
99
212
|
* 2D affine matrix `[a b c d e f]`, row-vector convention (`[x y 1] · M`):
|
|
@@ -120,6 +233,19 @@ export declare function multiply(a: Matrix, b: Matrix): Matrix;
|
|
|
120
233
|
* an unmapped key falls back to Latin-1 with a half-em advance.
|
|
121
234
|
* @param initialCtm The starting CTM mapping user space to page space.
|
|
122
235
|
* @param shadings Shading patterns by name, selected by `scn`/`sc` (EP16c).
|
|
236
|
+
* @param alphas Constant fill alphas by `/ExtGState` name, selected by `gs`.
|
|
123
237
|
* @returns The extracted text runs, image placements and vector paths.
|
|
124
238
|
*/
|
|
125
|
-
export declare function interpretContent(bytes: Uint8Array, fonts: ReadonlyMap<string, ContentFont>, initialCtm?: Matrix, shadings?: ReadonlyMap<string, ShapeGradient>): InterpretResult;
|
|
239
|
+
export declare function interpretContent(bytes: Uint8Array, fonts: ReadonlyMap<string, ContentFont>, initialCtm?: Matrix, shadings?: ReadonlyMap<string, ShapeGradient>, alphas?: ReadonlyMap<string, number>): InterpretResult;
|
|
240
|
+
/**
|
|
241
|
+
* Whether a string is wholly right-to-left: at least one letter of an RTL
|
|
242
|
+
* script and nothing of any other, spaces and joiners aside.
|
|
243
|
+
*
|
|
244
|
+
* Anything mixed — a number inside an Arabic sentence runs left to right —
|
|
245
|
+
* needs the full bidi algorithm, and guessing at it would be worse than
|
|
246
|
+
* leaving it alone.
|
|
247
|
+
*
|
|
248
|
+
* @param text The string to judge.
|
|
249
|
+
* @returns Whether it is one run of right-to-left script.
|
|
250
|
+
*/
|
|
251
|
+
export declare function isRightToLeft(text: string): boolean;
|