documents.js 1.33.4 → 1.34.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 +1 -0
- package/dist/index.cjs +624 -727
- package/dist/index.d.cts +13 -645
- package/dist/index.d.ts +13 -645
- package/dist/index.js +607 -780
- package/package.json +5 -2
package/dist/index.js
CHANGED
|
@@ -1,237 +1,31 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AttributeSchema, BinaryPartSchema, CommentSchema, CompactPackageSchema, CompactPartSchema, CompactXmlNodeSchema, DefinedNameSchema, PackageSchema, PartSchema, XmlCdataSchema, XmlCommentSchema, XmlDeclarationSchema, XmlElementSchema, XmlNodeSchema, XmlPartSchema, XmlPiSchema, XmlTextSchema, attr, attr as attr$1, base64ToBytes, base64ToBytes as base64ToBytes$1, buildXml, bytesToBase64, bytesToBase64 as bytesToBase64$1, childrenWithTag, compactCodec, compactPackageCodec, decodeCompactPackage, decodeEntities, decodePackage, decodePackage as decodePackage$1, elementsWithTag, encodeCompactPackage, encodePackage, encodePackage as encodePackage$1, fromCompact, isCompactXmlNode, isXmlNode, packageCodec, parsePackage, parseXml, readDocx, readPptx, resolveRelationships, resolveRelationships as resolveRelationships$1, rootElement, rootElement as rootElement$1, serializePackage, textContent, textContent as textContent$1, toCompact, unzipPackage, walk, xmlCodec, zipPackage } from "ooxml.js";
|
|
2
|
+
import { COLOR_BLACK, ContentBlockSchema, ContentImageBlockSchema, ContentPageBreakSchema, ContentParagraphSchema, ContentRunSchema, ContentSectionSchema, ContentSectionSchema as ContentSectionSchema$1, ContentShapeSchema, ContentSlideSchema, ContentSlideSchema as ContentSlideSchema$1, ContentTableCellSchema, ContentTableRowSchema, ContentTableSchema, DEFAULT_LAYOUT_FONT, LAYOUT_FORMAT_VERSION, LAYOUT_FORMAT_VERSION as LAYOUT_FORMAT_VERSION$1, LayoutDocumentSchema, LayoutMetadataSchema, PAGE_SIZE_A4, PAGE_SIZE_LETTER, SLIDE_SIZE_STANDARD, SLIDE_SIZE_WIDESCREEN, colorToRgbHex, isContentBlock, rgbHexToColor } from "document-content-model";
|
|
2
3
|
import { z } from "zod";
|
|
3
4
|
import { Unzlib, inflateSync, unzlibSync, zlibSync } from "fflate";
|
|
4
|
-
//#region src/model/geometry.ts
|
|
5
|
-
function flipY(box, containerHeightPt) {
|
|
6
|
-
return {
|
|
7
|
-
xPt: box.xPt,
|
|
8
|
-
yPt: containerHeightPt - box.yPt - box.heightPt,
|
|
9
|
-
widthPt: box.widthPt,
|
|
10
|
-
heightPt: box.heightPt
|
|
11
|
-
};
|
|
12
|
-
}
|
|
13
|
-
//#endregion
|
|
14
|
-
//#region src/model/style.ts
|
|
15
|
-
const LayoutFontSchema = z.object({
|
|
16
|
-
family: z.string(),
|
|
17
|
-
weight: z.enum(["normal", "bold"]),
|
|
18
|
-
style: z.enum(["normal", "italic"])
|
|
19
|
-
});
|
|
20
|
-
const DEFAULT_LAYOUT_FONT = {
|
|
21
|
-
family: "Helvetica",
|
|
22
|
-
weight: "normal",
|
|
23
|
-
style: "normal"
|
|
24
|
-
};
|
|
25
|
-
//#endregion
|
|
26
|
-
//#region src/model/layout.ts
|
|
27
|
-
const LAYOUT_FORMAT_VERSION = 1;
|
|
28
|
-
const LayoutTextSchema = z.object({
|
|
29
|
-
kind: z.literal("text"),
|
|
30
|
-
text: z.string(),
|
|
31
|
-
xPt: z.number(),
|
|
32
|
-
yPt: z.number(),
|
|
33
|
-
font: LayoutFontSchema,
|
|
34
|
-
sizePt: z.number().positive(),
|
|
35
|
-
color: LayoutColorSchema,
|
|
36
|
-
widthPt: z.number().nonnegative().optional(),
|
|
37
|
-
rotationDeg: z.number().optional(),
|
|
38
|
-
underline: z.boolean().optional()
|
|
39
|
-
});
|
|
40
|
-
const LayoutImageSchema = z.object({
|
|
41
|
-
kind: z.literal("image"),
|
|
42
|
-
imageId: z.string(),
|
|
43
|
-
xPt: z.number(),
|
|
44
|
-
yPt: z.number(),
|
|
45
|
-
widthPt: z.number().positive(),
|
|
46
|
-
heightPt: z.number().positive(),
|
|
47
|
-
rotationDeg: z.number().optional()
|
|
48
|
-
});
|
|
49
|
-
const LayoutRectSchema = z.object({
|
|
50
|
-
kind: z.literal("rect"),
|
|
51
|
-
xPt: z.number(),
|
|
52
|
-
yPt: z.number(),
|
|
53
|
-
widthPt: z.number().nonnegative(),
|
|
54
|
-
heightPt: z.number().nonnegative(),
|
|
55
|
-
fill: LayoutColorSchema.optional(),
|
|
56
|
-
stroke: z.object({
|
|
57
|
-
color: LayoutColorSchema,
|
|
58
|
-
widthPt: z.number().positive()
|
|
59
|
-
}).optional()
|
|
60
|
-
});
|
|
61
|
-
const LayoutLineSchema = z.object({
|
|
62
|
-
kind: z.literal("line"),
|
|
63
|
-
x1Pt: z.number(),
|
|
64
|
-
y1Pt: z.number(),
|
|
65
|
-
x2Pt: z.number(),
|
|
66
|
-
y2Pt: z.number(),
|
|
67
|
-
color: LayoutColorSchema,
|
|
68
|
-
widthPt: z.number().positive()
|
|
69
|
-
});
|
|
70
|
-
const LayoutEllipseSchema = z.object({
|
|
71
|
-
kind: z.literal("ellipse"),
|
|
72
|
-
xPt: z.number(),
|
|
73
|
-
yPt: z.number(),
|
|
74
|
-
widthPt: z.number().positive(),
|
|
75
|
-
heightPt: z.number().positive(),
|
|
76
|
-
fill: LayoutColorSchema.optional(),
|
|
77
|
-
stroke: z.object({
|
|
78
|
-
color: LayoutColorSchema,
|
|
79
|
-
widthPt: z.number().positive()
|
|
80
|
-
}).optional()
|
|
81
|
-
});
|
|
82
|
-
const LayoutLinkSchema = z.object({
|
|
83
|
-
kind: z.literal("link"),
|
|
84
|
-
uri: z.string(),
|
|
85
|
-
xPt: z.number(),
|
|
86
|
-
yPt: z.number(),
|
|
87
|
-
widthPt: z.number().nonnegative(),
|
|
88
|
-
heightPt: z.number().nonnegative()
|
|
89
|
-
});
|
|
90
|
-
const LayoutItemSchema = z.discriminatedUnion("kind", [
|
|
91
|
-
LayoutTextSchema,
|
|
92
|
-
LayoutImageSchema,
|
|
93
|
-
LayoutRectSchema,
|
|
94
|
-
LayoutLineSchema,
|
|
95
|
-
LayoutEllipseSchema,
|
|
96
|
-
LayoutLinkSchema
|
|
97
|
-
]);
|
|
98
|
-
const LayoutPageSchema = z.object({
|
|
99
|
-
widthPt: z.number().positive(),
|
|
100
|
-
heightPt: z.number().positive(),
|
|
101
|
-
items: z.array(LayoutItemSchema)
|
|
102
|
-
});
|
|
103
|
-
const LayoutImageAssetSchema = z.object({
|
|
104
|
-
format: z.enum(["png", "jpeg"]),
|
|
105
|
-
base64: z.string(),
|
|
106
|
-
widthPx: z.number().int().positive(),
|
|
107
|
-
heightPx: z.number().int().positive()
|
|
108
|
-
});
|
|
109
|
-
const LayoutMetadataSchema = z.object({
|
|
110
|
-
title: z.string().optional(),
|
|
111
|
-
author: z.string().optional(),
|
|
112
|
-
subject: z.string().optional(),
|
|
113
|
-
keywords: z.array(z.string()).optional(),
|
|
114
|
-
creator: z.string().optional(),
|
|
115
|
-
producer: z.string().optional(),
|
|
116
|
-
createdIso: z.string().optional(),
|
|
117
|
-
modifiedIso: z.string().optional()
|
|
118
|
-
});
|
|
119
|
-
const LayoutDocumentSchema = z.object({
|
|
120
|
-
formatVersion: z.literal(1),
|
|
121
|
-
metadata: LayoutMetadataSchema,
|
|
122
|
-
pages: z.array(LayoutPageSchema),
|
|
123
|
-
images: z.record(z.string(), LayoutImageAssetSchema)
|
|
124
|
-
});
|
|
125
|
-
//#endregion
|
|
126
5
|
//#region src/model/content.ts
|
|
127
6
|
const CONTENT_FORMAT_VERSION = 1;
|
|
128
|
-
const ContentRunSchema = z.object({
|
|
129
|
-
text: z.string(),
|
|
130
|
-
bold: z.boolean().optional(),
|
|
131
|
-
italic: z.boolean().optional(),
|
|
132
|
-
underline: z.boolean().optional(),
|
|
133
|
-
strike: z.boolean().optional(),
|
|
134
|
-
fontFamily: z.string().optional(),
|
|
135
|
-
sizePt: z.number().positive().optional(),
|
|
136
|
-
color: LayoutColorSchema.optional(),
|
|
137
|
-
hyperlink: z.string().optional()
|
|
138
|
-
});
|
|
139
|
-
const ContentListMembershipSchema = z.object({
|
|
140
|
-
numId: z.string(),
|
|
141
|
-
level: z.number().int().nonnegative()
|
|
142
|
-
});
|
|
143
|
-
const ContentParagraphSchema = z.object({
|
|
144
|
-
kind: z.literal("paragraph"),
|
|
145
|
-
runs: z.array(ContentRunSchema),
|
|
146
|
-
styleId: z.string().optional(),
|
|
147
|
-
alignment: AlignmentSchema.optional(),
|
|
148
|
-
list: ContentListMembershipSchema.optional(),
|
|
149
|
-
spacingBeforePt: z.number().optional(),
|
|
150
|
-
spacingAfterPt: z.number().optional(),
|
|
151
|
-
lineSpacing: z.number().positive().optional(),
|
|
152
|
-
indentLeftPt: z.number().optional(),
|
|
153
|
-
indentFirstLinePt: z.number().optional()
|
|
154
|
-
});
|
|
155
|
-
const ContentImageBlockSchema = z.object({
|
|
156
|
-
kind: z.literal("image"),
|
|
157
|
-
format: z.enum(["png", "jpeg"]),
|
|
158
|
-
base64: z.string(),
|
|
159
|
-
widthPt: z.number().positive(),
|
|
160
|
-
heightPt: z.number().positive(),
|
|
161
|
-
altText: z.string().optional()
|
|
162
|
-
});
|
|
163
|
-
const ContentPageBreakSchema = z.object({ kind: z.literal("pageBreak") });
|
|
164
|
-
function isRecord(value) {
|
|
165
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
166
|
-
}
|
|
167
|
-
function isContentRun(value) {
|
|
168
|
-
return isRecord(value) && typeof value.text === "string";
|
|
169
|
-
}
|
|
170
|
-
function isContentTableCell(value) {
|
|
171
|
-
return isRecord(value) && Array.isArray(value.blocks) && value.blocks.every(isContentBlock);
|
|
172
|
-
}
|
|
173
|
-
function isContentTableRow(value) {
|
|
174
|
-
return isRecord(value) && Array.isArray(value.cells) && value.cells.every(isContentTableCell) && (value.heightPt === void 0 || typeof value.heightPt === "number");
|
|
175
|
-
}
|
|
176
|
-
function isContentBlock(value) {
|
|
177
|
-
if (!isRecord(value)) return false;
|
|
178
|
-
const kind = value.kind;
|
|
179
|
-
if (kind === "paragraph") return Array.isArray(value.runs) && value.runs.every(isContentRun);
|
|
180
|
-
if (kind === "image") return (value.format === "png" || value.format === "jpeg") && typeof value.base64 === "string" && typeof value.widthPt === "number" && typeof value.heightPt === "number";
|
|
181
|
-
if (kind === "pageBreak") return true;
|
|
182
|
-
if (kind === "table") return Array.isArray(value.rows) && value.rows.every(isContentTableRow) && Array.isArray(value.columnWidthsPt) && value.columnWidthsPt.every((w) => typeof w === "number");
|
|
183
|
-
return false;
|
|
184
|
-
}
|
|
185
|
-
const ContentBlockSchema = z.custom(isContentBlock);
|
|
186
|
-
const ContentTableCellSchema = z.object({
|
|
187
|
-
blocks: z.array(ContentBlockSchema),
|
|
188
|
-
colSpan: z.number().int().positive().optional(),
|
|
189
|
-
rowSpan: z.number().int().positive().optional(),
|
|
190
|
-
background: LayoutColorSchema.optional()
|
|
191
|
-
});
|
|
192
|
-
const ContentTableRowSchema = z.object({
|
|
193
|
-
cells: z.array(ContentTableCellSchema),
|
|
194
|
-
heightPt: z.number().positive().optional()
|
|
195
|
-
});
|
|
196
|
-
const ContentTableSchema = z.object({
|
|
197
|
-
kind: z.literal("table"),
|
|
198
|
-
rows: z.array(ContentTableRowSchema),
|
|
199
|
-
columnWidthsPt: z.array(z.number().positive())
|
|
200
|
-
});
|
|
201
|
-
const ContentSectionSchema = z.object({
|
|
202
|
-
pageSize: PageSizeSchema,
|
|
203
|
-
margins: MarginsSchema,
|
|
204
|
-
blocks: z.array(ContentBlockSchema)
|
|
205
|
-
});
|
|
206
|
-
const ContentShapeSchema = z.object({
|
|
207
|
-
name: z.string().optional(),
|
|
208
|
-
frame: BoxSchema,
|
|
209
|
-
rotationDeg: z.number().optional(),
|
|
210
|
-
insetLeftPt: z.number().nonnegative(),
|
|
211
|
-
insetTopPt: z.number().nonnegative(),
|
|
212
|
-
insetRightPt: z.number().nonnegative(),
|
|
213
|
-
insetBottomPt: z.number().nonnegative(),
|
|
214
|
-
fontScale: z.number().positive().optional(),
|
|
215
|
-
lineSpacingReduction: z.number().nonnegative().optional(),
|
|
216
|
-
blocks: z.array(ContentBlockSchema)
|
|
217
|
-
});
|
|
218
|
-
const ContentSlideSchema = z.object({
|
|
219
|
-
size: PageSizeSchema,
|
|
220
|
-
shapes: z.array(ContentShapeSchema),
|
|
221
|
-
notes: z.string()
|
|
222
|
-
});
|
|
223
7
|
const ContentDocumentSchema = z.discriminatedUnion("kind", [z.object({
|
|
224
8
|
kind: z.literal("wordprocessing"),
|
|
225
9
|
formatVersion: z.literal(1),
|
|
226
10
|
metadata: LayoutMetadataSchema,
|
|
227
|
-
sections: z.array(ContentSectionSchema)
|
|
11
|
+
sections: z.array(ContentSectionSchema$1)
|
|
228
12
|
}), z.object({
|
|
229
13
|
kind: z.literal("presentation"),
|
|
230
14
|
formatVersion: z.literal(1),
|
|
231
15
|
metadata: LayoutMetadataSchema,
|
|
232
|
-
slides: z.array(ContentSlideSchema)
|
|
16
|
+
slides: z.array(ContentSlideSchema$1)
|
|
233
17
|
})]);
|
|
234
18
|
//#endregion
|
|
19
|
+
//#region src/model/geometry.ts
|
|
20
|
+
function flipY(box, containerHeightPt) {
|
|
21
|
+
return {
|
|
22
|
+
xPt: box.xPt,
|
|
23
|
+
yPt: containerHeightPt - box.yPt - box.heightPt,
|
|
24
|
+
widthPt: box.widthPt,
|
|
25
|
+
heightPt: box.heightPt
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
235
29
|
//#region src/model/bytes.ts
|
|
236
30
|
const ZIP_LOCAL_FILE_HEADER = [
|
|
237
31
|
80,
|
|
@@ -5792,411 +5586,83 @@ function runContentStream(bytes, resources, initialState, context, items, depth)
|
|
|
5792
5586
|
}
|
|
5793
5587
|
}
|
|
5794
5588
|
//#endregion
|
|
5795
|
-
//#region src/
|
|
5796
|
-
const
|
|
5797
|
-
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
|
|
5805
|
-
|
|
5806
|
-
|
|
5589
|
+
//#region src/image/png-decode.ts
|
|
5590
|
+
const PNG_SIGNATURE = [
|
|
5591
|
+
137,
|
|
5592
|
+
80,
|
|
5593
|
+
78,
|
|
5594
|
+
71,
|
|
5595
|
+
13,
|
|
5596
|
+
10,
|
|
5597
|
+
26,
|
|
5598
|
+
10
|
|
5599
|
+
];
|
|
5600
|
+
function requireDataView(bytes) {
|
|
5601
|
+
return new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
5807
5602
|
}
|
|
5808
|
-
function
|
|
5809
|
-
const
|
|
5810
|
-
const
|
|
5811
|
-
|
|
5812
|
-
|
|
5813
|
-
|
|
5814
|
-
|
|
5815
|
-
|
|
5816
|
-
|
|
5817
|
-
|
|
5818
|
-
|
|
5819
|
-
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
|
|
5825
|
-
|
|
5826
|
-
|
|
5827
|
-
|
|
5828
|
-
|
|
5829
|
-
}
|
|
5603
|
+
function readChunks(bytes, onWarning) {
|
|
5604
|
+
const chunks = [];
|
|
5605
|
+
const view = requireDataView(bytes);
|
|
5606
|
+
let offset = PNG_SIGNATURE.length;
|
|
5607
|
+
while (offset + 8 <= bytes.length) {
|
|
5608
|
+
const length = view.getUint32(offset);
|
|
5609
|
+
const typeBytes = bytes.subarray(offset + 4, offset + 8);
|
|
5610
|
+
const type = new TextDecoder("latin1").decode(typeBytes);
|
|
5611
|
+
const dataStart = offset + 8;
|
|
5612
|
+
const dataEnd = dataStart + length;
|
|
5613
|
+
if (dataEnd + 4 > bytes.length) throw new Error(`PNG chunk '${type}' declares a length that runs past the end of the file`);
|
|
5614
|
+
const data = bytes.subarray(dataStart, dataEnd);
|
|
5615
|
+
if (onWarning !== void 0) {
|
|
5616
|
+
if (view.getUint32(dataEnd) !== crc32(concatBytes([typeBytes, data]))) onWarning(`PNG chunk '${type}' failed its CRC32 check`);
|
|
5617
|
+
}
|
|
5618
|
+
chunks.push({
|
|
5619
|
+
type,
|
|
5620
|
+
data
|
|
5621
|
+
});
|
|
5622
|
+
offset = dataEnd + 4;
|
|
5623
|
+
if (type === "IEND") break;
|
|
5624
|
+
}
|
|
5625
|
+
return chunks;
|
|
5830
5626
|
}
|
|
5831
|
-
function
|
|
5832
|
-
const
|
|
5833
|
-
if (arr === void 0) return {
|
|
5834
|
-
llx: 0,
|
|
5835
|
-
lly: 0,
|
|
5836
|
-
urx: DEFAULT_PAGE_WIDTH_PT,
|
|
5837
|
-
ury: DEFAULT_PAGE_HEIGHT_PT
|
|
5838
|
-
};
|
|
5839
|
-
const a = asNumber(arr[0]) ?? 0;
|
|
5840
|
-
const b = asNumber(arr[1]) ?? 0;
|
|
5841
|
-
const c = asNumber(arr[2]) ?? DEFAULT_PAGE_WIDTH_PT;
|
|
5842
|
-
const d = asNumber(arr[3]) ?? DEFAULT_PAGE_HEIGHT_PT;
|
|
5627
|
+
function parseIhdr(data) {
|
|
5628
|
+
const view = requireDataView(data);
|
|
5843
5629
|
return {
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
5630
|
+
width: view.getUint32(0),
|
|
5631
|
+
height: view.getUint32(4),
|
|
5632
|
+
bitDepth: data[8],
|
|
5633
|
+
colorType: data[9],
|
|
5634
|
+
interlace: data[12]
|
|
5848
5635
|
};
|
|
5849
5636
|
}
|
|
5850
|
-
function
|
|
5851
|
-
if (
|
|
5852
|
-
|
|
5853
|
-
|
|
5637
|
+
function channelsForColorType(colorType) {
|
|
5638
|
+
if (colorType === 0) return 1;
|
|
5639
|
+
if (colorType === 2) return 3;
|
|
5640
|
+
if (colorType === 3) return 1;
|
|
5641
|
+
if (colorType === 4) return 2;
|
|
5642
|
+
if (colorType === 6) return 4;
|
|
5643
|
+
throw new Error(`unsupported PNG colour type: ${colorType}`);
|
|
5854
5644
|
}
|
|
5855
|
-
function
|
|
5856
|
-
|
|
5857
|
-
matrix: [
|
|
5858
|
-
0,
|
|
5859
|
-
-1,
|
|
5860
|
-
1,
|
|
5861
|
-
0,
|
|
5862
|
-
0,
|
|
5863
|
-
w
|
|
5864
|
-
],
|
|
5865
|
-
widthPt: h,
|
|
5866
|
-
heightPt: w
|
|
5867
|
-
};
|
|
5868
|
-
if (rotation === 180) return {
|
|
5869
|
-
matrix: [
|
|
5870
|
-
-1,
|
|
5871
|
-
0,
|
|
5872
|
-
0,
|
|
5873
|
-
-1,
|
|
5874
|
-
w,
|
|
5875
|
-
h
|
|
5876
|
-
],
|
|
5877
|
-
widthPt: w,
|
|
5878
|
-
heightPt: h
|
|
5879
|
-
};
|
|
5880
|
-
if (rotation === 270) return {
|
|
5881
|
-
matrix: [
|
|
5882
|
-
0,
|
|
5883
|
-
1,
|
|
5884
|
-
-1,
|
|
5885
|
-
0,
|
|
5886
|
-
h,
|
|
5887
|
-
0
|
|
5888
|
-
],
|
|
5889
|
-
widthPt: h,
|
|
5890
|
-
heightPt: w
|
|
5891
|
-
};
|
|
5892
|
-
return {
|
|
5893
|
-
matrix: [
|
|
5894
|
-
1,
|
|
5895
|
-
0,
|
|
5896
|
-
0,
|
|
5897
|
-
1,
|
|
5898
|
-
0,
|
|
5899
|
-
0
|
|
5900
|
-
],
|
|
5901
|
-
widthPt: w,
|
|
5902
|
-
heightPt: h
|
|
5903
|
-
};
|
|
5645
|
+
function filterBpp(bitDepth, channels) {
|
|
5646
|
+
return Math.max(1, Math.ceil(bitDepth * channels / 8));
|
|
5904
5647
|
}
|
|
5905
|
-
function
|
|
5906
|
-
const
|
|
5907
|
-
|
|
5908
|
-
if (
|
|
5909
|
-
|
|
5910
|
-
|
|
5911
|
-
|
|
5912
|
-
|
|
5648
|
+
function unpackRow(rowBytes, width, channels, bitDepth) {
|
|
5649
|
+
const sampleCount = width * channels;
|
|
5650
|
+
const samples = new Array(sampleCount);
|
|
5651
|
+
if (bitDepth === 8) for (let i = 0; i < sampleCount; i++) samples[i] = rowBytes[i];
|
|
5652
|
+
else if (bitDepth === 16) for (let i = 0; i < sampleCount; i++) samples[i] = rowBytes[i * 2];
|
|
5653
|
+
else {
|
|
5654
|
+
const mask = (1 << bitDepth) - 1;
|
|
5655
|
+
for (let i = 0; i < sampleCount; i++) {
|
|
5656
|
+
const bitOffset = i * bitDepth;
|
|
5657
|
+
const byteIndex = bitOffset >> 3;
|
|
5658
|
+
const shift = 8 - bitDepth - (bitOffset & 7);
|
|
5659
|
+
samples[i] = rowBytes[byteIndex] >> shift & mask;
|
|
5913
5660
|
}
|
|
5914
|
-
return concatBytes(chunks);
|
|
5915
5661
|
}
|
|
5916
|
-
return
|
|
5662
|
+
return samples;
|
|
5917
5663
|
}
|
|
5918
|
-
function
|
|
5919
|
-
|
|
5920
|
-
const mediaBox = readMediaBox(page);
|
|
5921
|
-
const rotationResult = pageRotationTransform(normalizeRotation(asNumber(dictGet(page, "Rotate"))), mediaBox.urx - mediaBox.llx, mediaBox.ury - mediaBox.lly);
|
|
5922
|
-
const pageMatrix = multiplyMatrices(translationMatrix(-mediaBox.llx, -mediaBox.lly), rotationResult.matrix);
|
|
5923
|
-
const items = [];
|
|
5924
|
-
if (resources !== void 0) {
|
|
5925
|
-
const extracted = interpretContentStream(readPageContentBytes(page, resolver, sink), resources, {
|
|
5926
|
-
fontMetrics: fontResolver.metrics,
|
|
5927
|
-
resolver,
|
|
5928
|
-
sink
|
|
5929
|
-
});
|
|
5930
|
-
for (const item of extracted) {
|
|
5931
|
-
const converted = convertExtractedItem(item, pageMatrix, fontResolver, images, imageIdCache, resolver, sink);
|
|
5932
|
-
if (converted !== void 0) items.push(converted);
|
|
5933
|
-
}
|
|
5934
|
-
} else sink({
|
|
5935
|
-
code: "pdf/object-missing-value",
|
|
5936
|
-
severity: "warning",
|
|
5937
|
-
message: "page has no /Resources dict; its content stream cannot be interpreted"
|
|
5938
|
-
});
|
|
5939
|
-
items.push(...readLinkAnnotations(page, pageMatrix, resolver));
|
|
5940
|
-
return {
|
|
5941
|
-
widthPt: rotationResult.widthPt,
|
|
5942
|
-
heightPt: rotationResult.heightPt,
|
|
5943
|
-
items
|
|
5944
|
-
};
|
|
5945
|
-
}
|
|
5946
|
-
function convertExtractedItem(item, pageMatrix, fontResolver, images, imageIdCache, resolver, sink) {
|
|
5947
|
-
if (item.kind === "text") return convertText(item, pageMatrix, fontResolver);
|
|
5948
|
-
if (item.kind === "rect") return convertRect(item, pageMatrix);
|
|
5949
|
-
if (item.kind === "image") return convertImage(item, pageMatrix, images, imageIdCache, resolver, sink);
|
|
5950
|
-
return convertInlineImage(item, pageMatrix, images, resolver, sink);
|
|
5951
|
-
}
|
|
5952
|
-
function convertText(item, pageMatrix, fontResolver) {
|
|
5953
|
-
const font = fontResolver.resolve(item.fontResourceName, item.resources);
|
|
5954
|
-
const text = font?.decodeToUnicode(item.codes) ?? "";
|
|
5955
|
-
if (text.length === 0) return;
|
|
5956
|
-
const startTrm = multiplyMatrices(item.startMatrix, pageMatrix);
|
|
5957
|
-
const endTrm = multiplyMatrices(item.endMatrix, pageMatrix);
|
|
5958
|
-
const widthPt = Math.hypot(endTrm[4] - startTrm[4], endTrm[5] - startTrm[5]);
|
|
5959
|
-
const sizePt = matrixScaleX(startTrm);
|
|
5960
|
-
const rotationDeg = matrixRotationDegrees(startTrm);
|
|
5961
|
-
const layoutFont = {
|
|
5962
|
-
family: font?.family ?? "Helvetica",
|
|
5963
|
-
weight: font?.bold === true ? "bold" : "normal",
|
|
5964
|
-
style: font?.italic === true ? "italic" : "normal"
|
|
5965
|
-
};
|
|
5966
|
-
return {
|
|
5967
|
-
kind: "text",
|
|
5968
|
-
text,
|
|
5969
|
-
xPt: startTrm[4],
|
|
5970
|
-
yPt: startTrm[5],
|
|
5971
|
-
font: layoutFont,
|
|
5972
|
-
sizePt: sizePt > 0 ? sizePt : item.sizePt,
|
|
5973
|
-
color: item.color,
|
|
5974
|
-
widthPt,
|
|
5975
|
-
rotationDeg: rotationDeg !== 0 ? rotationDeg : void 0
|
|
5976
|
-
};
|
|
5977
|
-
}
|
|
5978
|
-
function convertRect(item, pageMatrix) {
|
|
5979
|
-
const p1 = applyMatrix(pageMatrix, {
|
|
5980
|
-
x: item.xPt,
|
|
5981
|
-
y: item.yPt
|
|
5982
|
-
});
|
|
5983
|
-
const p2 = applyMatrix(pageMatrix, {
|
|
5984
|
-
x: item.xPt + item.widthPt,
|
|
5985
|
-
y: item.yPt + item.heightPt
|
|
5986
|
-
});
|
|
5987
|
-
return {
|
|
5988
|
-
kind: "rect",
|
|
5989
|
-
xPt: Math.min(p1.x, p2.x),
|
|
5990
|
-
yPt: Math.min(p1.y, p2.y),
|
|
5991
|
-
widthPt: Math.abs(p2.x - p1.x),
|
|
5992
|
-
heightPt: Math.abs(p2.y - p1.y),
|
|
5993
|
-
fill: item.color
|
|
5994
|
-
};
|
|
5995
|
-
}
|
|
5996
|
-
function imagePlacementFrom(matrix) {
|
|
5997
|
-
const rotationDeg = matrixRotationDegrees(matrix);
|
|
5998
|
-
return {
|
|
5999
|
-
xPt: matrix[4],
|
|
6000
|
-
yPt: matrix[5],
|
|
6001
|
-
widthPt: matrixScaleX(matrix),
|
|
6002
|
-
heightPt: matrixScaleY(matrix),
|
|
6003
|
-
rotationDeg: rotationDeg !== 0 ? rotationDeg : void 0
|
|
6004
|
-
};
|
|
6005
|
-
}
|
|
6006
|
-
function registerExtractedImage(format, bytes, widthPx, heightPx, images) {
|
|
6007
|
-
const imageId = `img${crc32(bytes).toString(16)}`;
|
|
6008
|
-
if (!(imageId in images)) images[imageId] = {
|
|
6009
|
-
format,
|
|
6010
|
-
base64: bytesToBase64$1(bytes),
|
|
6011
|
-
widthPx,
|
|
6012
|
-
heightPx
|
|
6013
|
-
};
|
|
6014
|
-
return imageId;
|
|
6015
|
-
}
|
|
6016
|
-
function resolveCachedImageId(dict, raw, images, cache, resolver, sink) {
|
|
6017
|
-
if (cache.has(dict)) return cache.get(dict) ?? void 0;
|
|
6018
|
-
const decoded = readImageXObject(dict, raw, resolver, sink);
|
|
6019
|
-
if (decoded === void 0) {
|
|
6020
|
-
cache.set(dict, null);
|
|
6021
|
-
return;
|
|
6022
|
-
}
|
|
6023
|
-
const imageId = registerExtractedImage(decoded.format, decoded.bytes, decoded.widthPx, decoded.heightPx, images);
|
|
6024
|
-
cache.set(dict, imageId);
|
|
6025
|
-
return imageId;
|
|
6026
|
-
}
|
|
6027
|
-
function convertImage(item, pageMatrix, images, cache, resolver, sink) {
|
|
6028
|
-
const xobjects = resolver.resolveDict(dictGet(item.resources, "XObject"));
|
|
6029
|
-
const xobj = xobjects !== void 0 ? resolver.resolve(dictGet(xobjects, item.resourceName)) : void 0;
|
|
6030
|
-
if (xobj?.kind !== "stream") return;
|
|
6031
|
-
const imageId = resolveCachedImageId(xobj.dict, xobj.raw, images, cache, resolver, sink);
|
|
6032
|
-
if (imageId === void 0) return;
|
|
6033
|
-
return {
|
|
6034
|
-
kind: "image",
|
|
6035
|
-
imageId,
|
|
6036
|
-
...imagePlacementFrom(multiplyMatrices(item.matrix, pageMatrix))
|
|
6037
|
-
};
|
|
6038
|
-
}
|
|
6039
|
-
function convertInlineImage(item, pageMatrix, images, resolver, sink) {
|
|
6040
|
-
const decoded = readImageXObject(item.dict, item.data, resolver, sink);
|
|
6041
|
-
if (decoded === void 0) return;
|
|
6042
|
-
return {
|
|
6043
|
-
kind: "image",
|
|
6044
|
-
imageId: registerExtractedImage(decoded.format, decoded.bytes, decoded.widthPx, decoded.heightPx, images),
|
|
6045
|
-
...imagePlacementFrom(multiplyMatrices(item.matrix, pageMatrix))
|
|
6046
|
-
};
|
|
6047
|
-
}
|
|
6048
|
-
function readLinkAnnotations(page, pageMatrix, resolver) {
|
|
6049
|
-
const annotsArr = asArray(dictGet(page, "Annots"));
|
|
6050
|
-
if (annotsArr === void 0) return [];
|
|
6051
|
-
const links = [];
|
|
6052
|
-
for (const annotRef of annotsArr) {
|
|
6053
|
-
const annot = resolver.resolveDict(annotRef);
|
|
6054
|
-
if (annot === void 0 || asName(dictGet(annot, "Subtype")) !== "Link") continue;
|
|
6055
|
-
const uri = readLinkUri(annot, resolver);
|
|
6056
|
-
const rectArr = asArray(dictGet(annot, "Rect"));
|
|
6057
|
-
if (uri === void 0 || rectArr === void 0) continue;
|
|
6058
|
-
const x1 = asNumber(rectArr[0]) ?? 0;
|
|
6059
|
-
const y1 = asNumber(rectArr[1]) ?? 0;
|
|
6060
|
-
const x2 = asNumber(rectArr[2]) ?? 0;
|
|
6061
|
-
const y2 = asNumber(rectArr[3]) ?? 0;
|
|
6062
|
-
const p1 = applyMatrix(pageMatrix, {
|
|
6063
|
-
x: Math.min(x1, x2),
|
|
6064
|
-
y: Math.min(y1, y2)
|
|
6065
|
-
});
|
|
6066
|
-
const p2 = applyMatrix(pageMatrix, {
|
|
6067
|
-
x: Math.max(x1, x2),
|
|
6068
|
-
y: Math.max(y1, y2)
|
|
6069
|
-
});
|
|
6070
|
-
links.push({
|
|
6071
|
-
kind: "link",
|
|
6072
|
-
uri,
|
|
6073
|
-
xPt: Math.min(p1.x, p2.x),
|
|
6074
|
-
yPt: Math.min(p1.y, p2.y),
|
|
6075
|
-
widthPt: Math.abs(p2.x - p1.x),
|
|
6076
|
-
heightPt: Math.abs(p2.y - p1.y)
|
|
6077
|
-
});
|
|
6078
|
-
}
|
|
6079
|
-
return links;
|
|
6080
|
-
}
|
|
6081
|
-
function readLinkUri(annot, resolver) {
|
|
6082
|
-
const action = resolver.resolveDict(dictGet(annot, "A"));
|
|
6083
|
-
if (action === void 0 || asName(dictGet(action, "S")) !== "URI") return;
|
|
6084
|
-
const uriObj = dictGet(action, "URI");
|
|
6085
|
-
return uriObj?.kind === "string" ? decodePdfString(uriObj.bytes) : void 0;
|
|
6086
|
-
}
|
|
6087
|
-
function decodePdfString(bytes) {
|
|
6088
|
-
if (bytes.length >= 2 && bytes[0] === 254 && bytes[1] === 255) {
|
|
6089
|
-
let out = "";
|
|
6090
|
-
for (let i = 2; i + 1 < bytes.length; i += 2) out += String.fromCharCode((bytes[i] ?? 0) << 8 | (bytes[i + 1] ?? 0));
|
|
6091
|
-
return out;
|
|
6092
|
-
}
|
|
6093
|
-
return Array.from(bytes, (b) => String.fromCharCode(b)).join("");
|
|
6094
|
-
}
|
|
6095
|
-
const PDF_DATE_PATTERN = /^D:(\d{4})(\d{2})?(\d{2})?(\d{2})?(\d{2})?(\d{2})?([+\-Z])?(\d{2})?'?(\d{2})?'?$/;
|
|
6096
|
-
function parsePdfDate(raw) {
|
|
6097
|
-
if (raw === void 0) return;
|
|
6098
|
-
const match = PDF_DATE_PATTERN.exec(raw);
|
|
6099
|
-
if (match === null) return;
|
|
6100
|
-
const [, year, month = "01", day = "01", hour = "00", minute = "00", second = "00", tzSign, tzHour = "00", tzMinute = "00"] = match;
|
|
6101
|
-
return `${year}-${month}-${day}T${hour}:${minute}:${second}${tzSign === void 0 || tzSign === "Z" ? "Z" : `${tzSign}${tzHour}:${tzMinute}`}`;
|
|
6102
|
-
}
|
|
6103
|
-
function readMetadata(trailer, resolver) {
|
|
6104
|
-
const info = resolver.resolveDict(dictGet(trailer, "Info"));
|
|
6105
|
-
if (info === void 0) return {};
|
|
6106
|
-
const stringField = (key) => {
|
|
6107
|
-
const obj = dictGet(info, key);
|
|
6108
|
-
return obj?.kind === "string" ? decodePdfString(obj.bytes) : void 0;
|
|
6109
|
-
};
|
|
6110
|
-
const keywords = stringField("Keywords")?.split(",").map((k) => k.trim()).filter((k) => k.length > 0);
|
|
6111
|
-
return {
|
|
6112
|
-
title: stringField("Title"),
|
|
6113
|
-
author: stringField("Author"),
|
|
6114
|
-
subject: stringField("Subject"),
|
|
6115
|
-
keywords: keywords !== void 0 && keywords.length > 0 ? keywords : void 0,
|
|
6116
|
-
creator: stringField("Creator"),
|
|
6117
|
-
producer: stringField("Producer"),
|
|
6118
|
-
createdIso: parsePdfDate(stringField("CreationDate")),
|
|
6119
|
-
modifiedIso: parsePdfDate(stringField("ModDate"))
|
|
6120
|
-
};
|
|
6121
|
-
}
|
|
6122
|
-
//#endregion
|
|
6123
|
-
//#region src/image/png-decode.ts
|
|
6124
|
-
const PNG_SIGNATURE = [
|
|
6125
|
-
137,
|
|
6126
|
-
80,
|
|
6127
|
-
78,
|
|
6128
|
-
71,
|
|
6129
|
-
13,
|
|
6130
|
-
10,
|
|
6131
|
-
26,
|
|
6132
|
-
10
|
|
6133
|
-
];
|
|
6134
|
-
function requireDataView(bytes) {
|
|
6135
|
-
return new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
6136
|
-
}
|
|
6137
|
-
function readChunks(bytes, onWarning) {
|
|
6138
|
-
const chunks = [];
|
|
6139
|
-
const view = requireDataView(bytes);
|
|
6140
|
-
let offset = PNG_SIGNATURE.length;
|
|
6141
|
-
while (offset + 8 <= bytes.length) {
|
|
6142
|
-
const length = view.getUint32(offset);
|
|
6143
|
-
const typeBytes = bytes.subarray(offset + 4, offset + 8);
|
|
6144
|
-
const type = new TextDecoder("latin1").decode(typeBytes);
|
|
6145
|
-
const dataStart = offset + 8;
|
|
6146
|
-
const dataEnd = dataStart + length;
|
|
6147
|
-
if (dataEnd + 4 > bytes.length) throw new Error(`PNG chunk '${type}' declares a length that runs past the end of the file`);
|
|
6148
|
-
const data = bytes.subarray(dataStart, dataEnd);
|
|
6149
|
-
if (onWarning !== void 0) {
|
|
6150
|
-
if (view.getUint32(dataEnd) !== crc32(concatBytes([typeBytes, data]))) onWarning(`PNG chunk '${type}' failed its CRC32 check`);
|
|
6151
|
-
}
|
|
6152
|
-
chunks.push({
|
|
6153
|
-
type,
|
|
6154
|
-
data
|
|
6155
|
-
});
|
|
6156
|
-
offset = dataEnd + 4;
|
|
6157
|
-
if (type === "IEND") break;
|
|
6158
|
-
}
|
|
6159
|
-
return chunks;
|
|
6160
|
-
}
|
|
6161
|
-
function parseIhdr(data) {
|
|
6162
|
-
const view = requireDataView(data);
|
|
6163
|
-
return {
|
|
6164
|
-
width: view.getUint32(0),
|
|
6165
|
-
height: view.getUint32(4),
|
|
6166
|
-
bitDepth: data[8],
|
|
6167
|
-
colorType: data[9],
|
|
6168
|
-
interlace: data[12]
|
|
6169
|
-
};
|
|
6170
|
-
}
|
|
6171
|
-
function channelsForColorType(colorType) {
|
|
6172
|
-
if (colorType === 0) return 1;
|
|
6173
|
-
if (colorType === 2) return 3;
|
|
6174
|
-
if (colorType === 3) return 1;
|
|
6175
|
-
if (colorType === 4) return 2;
|
|
6176
|
-
if (colorType === 6) return 4;
|
|
6177
|
-
throw new Error(`unsupported PNG colour type: ${colorType}`);
|
|
6178
|
-
}
|
|
6179
|
-
function filterBpp(bitDepth, channels) {
|
|
6180
|
-
return Math.max(1, Math.ceil(bitDepth * channels / 8));
|
|
6181
|
-
}
|
|
6182
|
-
function unpackRow(rowBytes, width, channels, bitDepth) {
|
|
6183
|
-
const sampleCount = width * channels;
|
|
6184
|
-
const samples = new Array(sampleCount);
|
|
6185
|
-
if (bitDepth === 8) for (let i = 0; i < sampleCount; i++) samples[i] = rowBytes[i];
|
|
6186
|
-
else if (bitDepth === 16) for (let i = 0; i < sampleCount; i++) samples[i] = rowBytes[i * 2];
|
|
6187
|
-
else {
|
|
6188
|
-
const mask = (1 << bitDepth) - 1;
|
|
6189
|
-
for (let i = 0; i < sampleCount; i++) {
|
|
6190
|
-
const bitOffset = i * bitDepth;
|
|
6191
|
-
const byteIndex = bitOffset >> 3;
|
|
6192
|
-
const shift = 8 - bitDepth - (bitOffset & 7);
|
|
6193
|
-
samples[i] = rowBytes[byteIndex] >> shift & mask;
|
|
6194
|
-
}
|
|
6195
|
-
}
|
|
6196
|
-
return samples;
|
|
6197
|
-
}
|
|
6198
|
-
function readTrnsGrayValue(trns) {
|
|
6199
|
-
return requireDataView(trns).getUint16(0);
|
|
5664
|
+
function readTrnsGrayValue(trns) {
|
|
5665
|
+
return requireDataView(trns).getUint16(0);
|
|
6200
5666
|
}
|
|
6201
5667
|
function readTrnsRgbKey(trns) {
|
|
6202
5668
|
const view = requireDataView(trns);
|
|
@@ -6671,175 +6137,535 @@ function buildLinkAnnotDict(link) {
|
|
|
6671
6137
|
})
|
|
6672
6138
|
});
|
|
6673
6139
|
}
|
|
6674
|
-
function isLinkItem(item) {
|
|
6675
|
-
return item.kind === "link";
|
|
6140
|
+
function isLinkItem(item) {
|
|
6141
|
+
return item.kind === "link";
|
|
6142
|
+
}
|
|
6143
|
+
const NOTES_ANNOTATION_HIDDEN_FLAG = 2;
|
|
6144
|
+
const NOTES_ANNOTATION_AUTHOR = "documents.js:notes";
|
|
6145
|
+
function buildNotesAnnotDict(notes) {
|
|
6146
|
+
return pdfDict({
|
|
6147
|
+
Type: pdfName("Annot"),
|
|
6148
|
+
Subtype: pdfName("Text"),
|
|
6149
|
+
Rect: pdfArray([
|
|
6150
|
+
0,
|
|
6151
|
+
0,
|
|
6152
|
+
0,
|
|
6153
|
+
0
|
|
6154
|
+
].map((n) => pdfNum(n))),
|
|
6155
|
+
Contents: textToPdfString(notes),
|
|
6156
|
+
T: textToPdfString(NOTES_ANNOTATION_AUTHOR),
|
|
6157
|
+
F: pdfNum(NOTES_ANNOTATION_HIDDEN_FLAG)
|
|
6158
|
+
});
|
|
6159
|
+
}
|
|
6160
|
+
function xrefEntry(offset, generation, inUse) {
|
|
6161
|
+
return `${offset.toString().padStart(10, "0")} ${generation.toString().padStart(5, "0")} ${inUse ? "n" : "f"} \n`;
|
|
6162
|
+
}
|
|
6163
|
+
function writePdf(doc, options = {}) {
|
|
6164
|
+
const compress = options.compress ?? true;
|
|
6165
|
+
const measurer = createStandardFontMeasurer();
|
|
6166
|
+
let nextObjNum = 1;
|
|
6167
|
+
const catalogNum = nextObjNum++;
|
|
6168
|
+
const pagesNum = nextObjNum++;
|
|
6169
|
+
const infoNum = nextObjNum++;
|
|
6170
|
+
const fontNames = /* @__PURE__ */ new Set();
|
|
6171
|
+
const imageIds = /* @__PURE__ */ new Set();
|
|
6172
|
+
for (const page of doc.pages) for (const item of page.items) if (item.kind === "text") fontNames.add(resolveStandardFont(item.font.family, item.font.weight === "bold", item.font.style === "italic").standardName);
|
|
6173
|
+
else if (item.kind === "image") imageIds.add(item.imageId);
|
|
6174
|
+
const fontAllocs = /* @__PURE__ */ new Map();
|
|
6175
|
+
for (const [index, name] of [...fontNames].sort().entries()) {
|
|
6176
|
+
const fontNum = nextObjNum++;
|
|
6177
|
+
const descNum = nextObjNum++;
|
|
6178
|
+
fontAllocs.set(name, {
|
|
6179
|
+
fontNum,
|
|
6180
|
+
descNum,
|
|
6181
|
+
resourceName: `F${index + 1}`
|
|
6182
|
+
});
|
|
6183
|
+
}
|
|
6184
|
+
const imageAllocs = /* @__PURE__ */ new Map();
|
|
6185
|
+
for (const [index, imageId] of [...imageIds].sort().entries()) {
|
|
6186
|
+
const asset = doc.images[imageId];
|
|
6187
|
+
if (asset === void 0) throw new Error(`LayoutDocument references image "${imageId}" but it is not present in images`);
|
|
6188
|
+
const prepared = prepareImage(asset, compress);
|
|
6189
|
+
const imageNum = nextObjNum++;
|
|
6190
|
+
const smaskNum = prepared.alpha === void 0 ? void 0 : nextObjNum++;
|
|
6191
|
+
imageAllocs.set(imageId, {
|
|
6192
|
+
imageNum,
|
|
6193
|
+
smaskNum,
|
|
6194
|
+
resourceName: `Im${index + 1}`,
|
|
6195
|
+
prepared
|
|
6196
|
+
});
|
|
6197
|
+
}
|
|
6198
|
+
const pageAllocs = doc.pages.map(() => ({
|
|
6199
|
+
pageNum: nextObjNum++,
|
|
6200
|
+
contentsNum: nextObjNum++
|
|
6201
|
+
}));
|
|
6202
|
+
const objects = [];
|
|
6203
|
+
objects.push({
|
|
6204
|
+
num: catalogNum,
|
|
6205
|
+
value: pdfDict({
|
|
6206
|
+
Type: pdfName("Catalog"),
|
|
6207
|
+
Pages: pdfRef(pagesNum, 0)
|
|
6208
|
+
})
|
|
6209
|
+
});
|
|
6210
|
+
objects.push({
|
|
6211
|
+
num: pagesNum,
|
|
6212
|
+
value: pdfDict({
|
|
6213
|
+
Type: pdfName("Pages"),
|
|
6214
|
+
Kids: pdfArray(pageAllocs.map((p) => pdfRef(p.pageNum, 0))),
|
|
6215
|
+
Count: pdfNum(doc.pages.length)
|
|
6216
|
+
})
|
|
6217
|
+
});
|
|
6218
|
+
objects.push({
|
|
6219
|
+
num: infoNum,
|
|
6220
|
+
value: buildInfoDict(doc)
|
|
6221
|
+
});
|
|
6222
|
+
for (const [standardName, alloc] of fontAllocs) {
|
|
6223
|
+
const { font, descriptor } = buildFontObjects(standardName, pdfRef(alloc.descNum, 0));
|
|
6224
|
+
objects.push({
|
|
6225
|
+
num: alloc.fontNum,
|
|
6226
|
+
value: font
|
|
6227
|
+
});
|
|
6228
|
+
objects.push({
|
|
6229
|
+
num: alloc.descNum,
|
|
6230
|
+
value: descriptor
|
|
6231
|
+
});
|
|
6232
|
+
}
|
|
6233
|
+
for (const alloc of imageAllocs.values()) {
|
|
6234
|
+
if (alloc.smaskNum !== void 0 && alloc.prepared.alpha !== void 0) {
|
|
6235
|
+
alloc.prepared.dict.entries.set("SMask", pdfRef(alloc.smaskNum, 0));
|
|
6236
|
+
objects.push({
|
|
6237
|
+
num: alloc.smaskNum,
|
|
6238
|
+
value: pdfStream(alloc.prepared.alpha.dict, alloc.prepared.alpha.raw)
|
|
6239
|
+
});
|
|
6240
|
+
}
|
|
6241
|
+
objects.push({
|
|
6242
|
+
num: alloc.imageNum,
|
|
6243
|
+
value: pdfStream(alloc.prepared.dict, alloc.prepared.raw)
|
|
6244
|
+
});
|
|
6245
|
+
}
|
|
6246
|
+
const resourceEntries = /* @__PURE__ */ new Map();
|
|
6247
|
+
if (fontAllocs.size > 0) resourceEntries.set("Font", pdfDict(new Map([...fontAllocs.values()].map((alloc) => [alloc.resourceName, pdfRef(alloc.fontNum, 0)]))));
|
|
6248
|
+
if (imageAllocs.size > 0) resourceEntries.set("XObject", pdfDict(new Map([...imageAllocs.values()].map((alloc) => [alloc.resourceName, pdfRef(alloc.imageNum, 0)]))));
|
|
6249
|
+
const resourcesDict = pdfDict(resourceEntries);
|
|
6250
|
+
const context = {
|
|
6251
|
+
measurer,
|
|
6252
|
+
resolveFont: (font) => {
|
|
6253
|
+
const standardName = resolveStandardFont(font.family, font.weight === "bold", font.style === "italic").standardName;
|
|
6254
|
+
const alloc = fontAllocs.get(standardName);
|
|
6255
|
+
if (alloc === void 0) throw new Error(`font "${standardName}" was not pre-allocated -- this is a writePdf internal invariant violation`);
|
|
6256
|
+
return {
|
|
6257
|
+
resourceName: alloc.resourceName,
|
|
6258
|
+
standardName
|
|
6259
|
+
};
|
|
6260
|
+
},
|
|
6261
|
+
resolveImage: (imageId) => {
|
|
6262
|
+
const alloc = imageAllocs.get(imageId);
|
|
6263
|
+
if (alloc === void 0) throw new Error(`image "${imageId}" was not pre-allocated -- this is a writePdf internal invariant violation`);
|
|
6264
|
+
return { resourceName: alloc.resourceName };
|
|
6265
|
+
}
|
|
6266
|
+
};
|
|
6267
|
+
doc.pages.forEach((page, pageIndex) => {
|
|
6268
|
+
throwIfAborted(options.signal);
|
|
6269
|
+
const { pageNum, contentsNum } = pageAllocs[pageIndex];
|
|
6270
|
+
const { bytes: contentBytes, substitutions } = writeContentStream(page.items, context);
|
|
6271
|
+
for (const substitution of substitutions) options.onSubstitution?.(substitution, { pageIndex });
|
|
6272
|
+
const finalContentBytes = compress ? deflate(contentBytes) : contentBytes;
|
|
6273
|
+
const contentsDict = pdfDict(compress ? { Filter: pdfName("FlateDecode") } : {});
|
|
6274
|
+
objects.push({
|
|
6275
|
+
num: contentsNum,
|
|
6276
|
+
value: pdfStream(contentsDict, finalContentBytes)
|
|
6277
|
+
});
|
|
6278
|
+
const annots = page.items.filter(isLinkItem).map((link) => buildLinkAnnotDict(link));
|
|
6279
|
+
if (page.notes !== void 0 && page.notes.length > 0) annots.push(buildNotesAnnotDict(page.notes));
|
|
6280
|
+
const pageEntries = /* @__PURE__ */ new Map([
|
|
6281
|
+
["Type", pdfName("Page")],
|
|
6282
|
+
["Parent", pdfRef(pagesNum, 0)],
|
|
6283
|
+
["MediaBox", pdfArray([
|
|
6284
|
+
0,
|
|
6285
|
+
0,
|
|
6286
|
+
page.widthPt,
|
|
6287
|
+
page.heightPt
|
|
6288
|
+
].map((n) => pdfNum(n)))],
|
|
6289
|
+
["Resources", resourcesDict],
|
|
6290
|
+
["Contents", pdfRef(contentsNum, 0)]
|
|
6291
|
+
]);
|
|
6292
|
+
if (annots.length > 0) pageEntries.set("Annots", pdfArray(annots));
|
|
6293
|
+
objects.push({
|
|
6294
|
+
num: pageNum,
|
|
6295
|
+
value: pdfDict(pageEntries)
|
|
6296
|
+
});
|
|
6297
|
+
});
|
|
6298
|
+
const writer = new ByteWriter();
|
|
6299
|
+
writer.writeAscii("%PDF-1.7\n");
|
|
6300
|
+
const offsets = /* @__PURE__ */ new Map();
|
|
6301
|
+
for (const { num, value } of objects) {
|
|
6302
|
+
offsets.set(num, writer.length);
|
|
6303
|
+
writer.writeAscii(`${num} 0 obj\n`);
|
|
6304
|
+
writeObject(writer, value);
|
|
6305
|
+
writer.writeAscii("\nendobj\n");
|
|
6306
|
+
}
|
|
6307
|
+
const maxObjNum = nextObjNum - 1;
|
|
6308
|
+
const xrefOffset = writer.length;
|
|
6309
|
+
writer.writeAscii("xref\n");
|
|
6310
|
+
writer.writeAscii(`0 ${maxObjNum + 1}\n`);
|
|
6311
|
+
writer.writeAscii(xrefEntry(0, 65535, false));
|
|
6312
|
+
for (let num = 1; num <= maxObjNum; num++) {
|
|
6313
|
+
const offset = offsets.get(num);
|
|
6314
|
+
if (offset === void 0) throw new Error(`object ${num} was allocated but never written -- this is a writePdf internal invariant violation`);
|
|
6315
|
+
writer.writeAscii(xrefEntry(offset, 0, true));
|
|
6316
|
+
}
|
|
6317
|
+
writer.writeAscii("trailer\n");
|
|
6318
|
+
writeObject(writer, pdfDict({
|
|
6319
|
+
Size: pdfNum(maxObjNum + 1),
|
|
6320
|
+
Root: pdfRef(catalogNum, 0),
|
|
6321
|
+
Info: pdfRef(infoNum, 0)
|
|
6322
|
+
}));
|
|
6323
|
+
writer.writeAscii("\nstartxref\n");
|
|
6324
|
+
writer.writeAscii(`${xrefOffset}\n`);
|
|
6325
|
+
writer.writeAscii("%%EOF");
|
|
6326
|
+
return writer.toBytes();
|
|
6327
|
+
}
|
|
6328
|
+
//#endregion
|
|
6329
|
+
//#region src/pdf/read.ts
|
|
6330
|
+
const PDF_HEADER_BYTES = new TextEncoder().encode("%PDF-");
|
|
6331
|
+
const HEADER_SEARCH_WINDOW = 1024;
|
|
6332
|
+
const DEFAULT_PAGE_WIDTH_PT = 612;
|
|
6333
|
+
const DEFAULT_PAGE_HEIGHT_PT = 792;
|
|
6334
|
+
function hasPdfHeader(bytes) {
|
|
6335
|
+
const window = bytes.subarray(0, Math.min(HEADER_SEARCH_WINDOW, bytes.length));
|
|
6336
|
+
outer: for (let i = 0; i <= window.length - PDF_HEADER_BYTES.length; i++) {
|
|
6337
|
+
for (let j = 0; j < PDF_HEADER_BYTES.length; j++) if (window[i + j] !== PDF_HEADER_BYTES[j]) continue outer;
|
|
6338
|
+
return true;
|
|
6339
|
+
}
|
|
6340
|
+
return false;
|
|
6341
|
+
}
|
|
6342
|
+
function readPdf(bytes, options) {
|
|
6343
|
+
const sink = options?.sink ?? NOOP_DIAGNOSTIC_SINK;
|
|
6344
|
+
const signal = options?.signal;
|
|
6345
|
+
if (!hasPdfHeader(bytes)) throw new PdfParseError("pdf/no-header", "no \"%PDF-\" header found within the first bytes of the file; this does not look like a PDF at all");
|
|
6346
|
+
const doc = openPdfDocument(bytes, sink);
|
|
6347
|
+
const resolver = doc;
|
|
6348
|
+
const fontResolver = createFontResolver({
|
|
6349
|
+
resolver,
|
|
6350
|
+
sink
|
|
6351
|
+
});
|
|
6352
|
+
const images = {};
|
|
6353
|
+
const imageIdCache = /* @__PURE__ */ new Map();
|
|
6354
|
+
const pages = doc.pages().map((pageDict) => {
|
|
6355
|
+
throwIfAborted(signal);
|
|
6356
|
+
return readPage(pageDict, resolver, fontResolver, images, imageIdCache, sink);
|
|
6357
|
+
});
|
|
6358
|
+
return {
|
|
6359
|
+
formatVersion: LAYOUT_FORMAT_VERSION$1,
|
|
6360
|
+
metadata: readMetadata(doc.trailer, resolver),
|
|
6361
|
+
pages,
|
|
6362
|
+
images
|
|
6363
|
+
};
|
|
6364
|
+
}
|
|
6365
|
+
function readMediaBox(page) {
|
|
6366
|
+
const arr = asArray(dictGet(page, "MediaBox"));
|
|
6367
|
+
if (arr === void 0) return {
|
|
6368
|
+
llx: 0,
|
|
6369
|
+
lly: 0,
|
|
6370
|
+
urx: DEFAULT_PAGE_WIDTH_PT,
|
|
6371
|
+
ury: DEFAULT_PAGE_HEIGHT_PT
|
|
6372
|
+
};
|
|
6373
|
+
const a = asNumber(arr[0]) ?? 0;
|
|
6374
|
+
const b = asNumber(arr[1]) ?? 0;
|
|
6375
|
+
const c = asNumber(arr[2]) ?? DEFAULT_PAGE_WIDTH_PT;
|
|
6376
|
+
const d = asNumber(arr[3]) ?? DEFAULT_PAGE_HEIGHT_PT;
|
|
6377
|
+
return {
|
|
6378
|
+
llx: Math.min(a, c),
|
|
6379
|
+
lly: Math.min(b, d),
|
|
6380
|
+
urx: Math.max(a, c),
|
|
6381
|
+
ury: Math.max(b, d)
|
|
6382
|
+
};
|
|
6383
|
+
}
|
|
6384
|
+
function normalizeRotation(rotate) {
|
|
6385
|
+
if (rotate === void 0) return 0;
|
|
6386
|
+
const normalized = (Math.round(rotate / 90) * 90 % 360 + 360) % 360;
|
|
6387
|
+
return normalized === 90 || normalized === 180 || normalized === 270 ? normalized : 0;
|
|
6388
|
+
}
|
|
6389
|
+
function pageRotationTransform(rotation, w, h) {
|
|
6390
|
+
if (rotation === 90) return {
|
|
6391
|
+
matrix: [
|
|
6392
|
+
0,
|
|
6393
|
+
-1,
|
|
6394
|
+
1,
|
|
6395
|
+
0,
|
|
6396
|
+
0,
|
|
6397
|
+
w
|
|
6398
|
+
],
|
|
6399
|
+
widthPt: h,
|
|
6400
|
+
heightPt: w
|
|
6401
|
+
};
|
|
6402
|
+
if (rotation === 180) return {
|
|
6403
|
+
matrix: [
|
|
6404
|
+
-1,
|
|
6405
|
+
0,
|
|
6406
|
+
0,
|
|
6407
|
+
-1,
|
|
6408
|
+
w,
|
|
6409
|
+
h
|
|
6410
|
+
],
|
|
6411
|
+
widthPt: w,
|
|
6412
|
+
heightPt: h
|
|
6413
|
+
};
|
|
6414
|
+
if (rotation === 270) return {
|
|
6415
|
+
matrix: [
|
|
6416
|
+
0,
|
|
6417
|
+
1,
|
|
6418
|
+
-1,
|
|
6419
|
+
0,
|
|
6420
|
+
h,
|
|
6421
|
+
0
|
|
6422
|
+
],
|
|
6423
|
+
widthPt: h,
|
|
6424
|
+
heightPt: w
|
|
6425
|
+
};
|
|
6426
|
+
return {
|
|
6427
|
+
matrix: [
|
|
6428
|
+
1,
|
|
6429
|
+
0,
|
|
6430
|
+
0,
|
|
6431
|
+
1,
|
|
6432
|
+
0,
|
|
6433
|
+
0
|
|
6434
|
+
],
|
|
6435
|
+
widthPt: w,
|
|
6436
|
+
heightPt: h
|
|
6437
|
+
};
|
|
6438
|
+
}
|
|
6439
|
+
function readPageContentBytes(page, resolver, sink) {
|
|
6440
|
+
const contentsObj = resolver.resolve(dictGet(page, "Contents"));
|
|
6441
|
+
if (contentsObj?.kind === "stream") return decodeStream(contentsObj.raw, contentsObj.dict, sink).bytes;
|
|
6442
|
+
if (contentsObj?.kind === "array") {
|
|
6443
|
+
const chunks = [];
|
|
6444
|
+
for (const item of contentsObj.items) {
|
|
6445
|
+
const streamObj = resolver.resolve(item);
|
|
6446
|
+
if (streamObj?.kind === "stream") chunks.push(decodeStream(streamObj.raw, streamObj.dict, sink).bytes, new Uint8Array([10]));
|
|
6447
|
+
}
|
|
6448
|
+
return concatBytes(chunks);
|
|
6449
|
+
}
|
|
6450
|
+
return /* @__PURE__ */ new Uint8Array(0);
|
|
6451
|
+
}
|
|
6452
|
+
function readPage(page, resolver, fontResolver, images, imageIdCache, sink) {
|
|
6453
|
+
const resources = resolver.resolveDict(dictGet(page, "Resources"));
|
|
6454
|
+
const mediaBox = readMediaBox(page);
|
|
6455
|
+
const rotationResult = pageRotationTransform(normalizeRotation(asNumber(dictGet(page, "Rotate"))), mediaBox.urx - mediaBox.llx, mediaBox.ury - mediaBox.lly);
|
|
6456
|
+
const pageMatrix = multiplyMatrices(translationMatrix(-mediaBox.llx, -mediaBox.lly), rotationResult.matrix);
|
|
6457
|
+
const items = [];
|
|
6458
|
+
if (resources !== void 0) {
|
|
6459
|
+
const extracted = interpretContentStream(readPageContentBytes(page, resolver, sink), resources, {
|
|
6460
|
+
fontMetrics: fontResolver.metrics,
|
|
6461
|
+
resolver,
|
|
6462
|
+
sink
|
|
6463
|
+
});
|
|
6464
|
+
for (const item of extracted) {
|
|
6465
|
+
const converted = convertExtractedItem(item, pageMatrix, fontResolver, images, imageIdCache, resolver, sink);
|
|
6466
|
+
if (converted !== void 0) items.push(converted);
|
|
6467
|
+
}
|
|
6468
|
+
} else sink({
|
|
6469
|
+
code: "pdf/object-missing-value",
|
|
6470
|
+
severity: "warning",
|
|
6471
|
+
message: "page has no /Resources dict; its content stream cannot be interpreted"
|
|
6472
|
+
});
|
|
6473
|
+
items.push(...readLinkAnnotations(page, pageMatrix, resolver));
|
|
6474
|
+
const notes = readPageNotes(page, resolver);
|
|
6475
|
+
return {
|
|
6476
|
+
widthPt: rotationResult.widthPt,
|
|
6477
|
+
heightPt: rotationResult.heightPt,
|
|
6478
|
+
items,
|
|
6479
|
+
...notes !== void 0 ? { notes } : {}
|
|
6480
|
+
};
|
|
6481
|
+
}
|
|
6482
|
+
function convertExtractedItem(item, pageMatrix, fontResolver, images, imageIdCache, resolver, sink) {
|
|
6483
|
+
if (item.kind === "text") return convertText(item, pageMatrix, fontResolver);
|
|
6484
|
+
if (item.kind === "rect") return convertRect(item, pageMatrix);
|
|
6485
|
+
if (item.kind === "image") return convertImage(item, pageMatrix, images, imageIdCache, resolver, sink);
|
|
6486
|
+
return convertInlineImage(item, pageMatrix, images, resolver, sink);
|
|
6487
|
+
}
|
|
6488
|
+
function convertText(item, pageMatrix, fontResolver) {
|
|
6489
|
+
const font = fontResolver.resolve(item.fontResourceName, item.resources);
|
|
6490
|
+
const text = font?.decodeToUnicode(item.codes) ?? "";
|
|
6491
|
+
if (text.length === 0) return;
|
|
6492
|
+
const startTrm = multiplyMatrices(item.startMatrix, pageMatrix);
|
|
6493
|
+
const endTrm = multiplyMatrices(item.endMatrix, pageMatrix);
|
|
6494
|
+
const widthPt = Math.hypot(endTrm[4] - startTrm[4], endTrm[5] - startTrm[5]);
|
|
6495
|
+
const sizePt = matrixScaleX(startTrm);
|
|
6496
|
+
const rotationDeg = matrixRotationDegrees(startTrm);
|
|
6497
|
+
const layoutFont = {
|
|
6498
|
+
family: font?.family ?? "Helvetica",
|
|
6499
|
+
weight: font?.bold === true ? "bold" : "normal",
|
|
6500
|
+
style: font?.italic === true ? "italic" : "normal"
|
|
6501
|
+
};
|
|
6502
|
+
return {
|
|
6503
|
+
kind: "text",
|
|
6504
|
+
text,
|
|
6505
|
+
xPt: startTrm[4],
|
|
6506
|
+
yPt: startTrm[5],
|
|
6507
|
+
font: layoutFont,
|
|
6508
|
+
sizePt: sizePt > 0 ? sizePt : item.sizePt,
|
|
6509
|
+
color: item.color,
|
|
6510
|
+
widthPt,
|
|
6511
|
+
rotationDeg: rotationDeg !== 0 ? rotationDeg : void 0
|
|
6512
|
+
};
|
|
6513
|
+
}
|
|
6514
|
+
function convertRect(item, pageMatrix) {
|
|
6515
|
+
const p1 = applyMatrix(pageMatrix, {
|
|
6516
|
+
x: item.xPt,
|
|
6517
|
+
y: item.yPt
|
|
6518
|
+
});
|
|
6519
|
+
const p2 = applyMatrix(pageMatrix, {
|
|
6520
|
+
x: item.xPt + item.widthPt,
|
|
6521
|
+
y: item.yPt + item.heightPt
|
|
6522
|
+
});
|
|
6523
|
+
return {
|
|
6524
|
+
kind: "rect",
|
|
6525
|
+
xPt: Math.min(p1.x, p2.x),
|
|
6526
|
+
yPt: Math.min(p1.y, p2.y),
|
|
6527
|
+
widthPt: Math.abs(p2.x - p1.x),
|
|
6528
|
+
heightPt: Math.abs(p2.y - p1.y),
|
|
6529
|
+
fill: item.color
|
|
6530
|
+
};
|
|
6531
|
+
}
|
|
6532
|
+
function imagePlacementFrom(matrix) {
|
|
6533
|
+
const rotationDeg = matrixRotationDegrees(matrix);
|
|
6534
|
+
return {
|
|
6535
|
+
xPt: matrix[4],
|
|
6536
|
+
yPt: matrix[5],
|
|
6537
|
+
widthPt: matrixScaleX(matrix),
|
|
6538
|
+
heightPt: matrixScaleY(matrix),
|
|
6539
|
+
rotationDeg: rotationDeg !== 0 ? rotationDeg : void 0
|
|
6540
|
+
};
|
|
6541
|
+
}
|
|
6542
|
+
function registerExtractedImage(format, bytes, widthPx, heightPx, images) {
|
|
6543
|
+
const imageId = `img${crc32(bytes).toString(16)}`;
|
|
6544
|
+
if (!(imageId in images)) images[imageId] = {
|
|
6545
|
+
format,
|
|
6546
|
+
base64: bytesToBase64$1(bytes),
|
|
6547
|
+
widthPx,
|
|
6548
|
+
heightPx
|
|
6549
|
+
};
|
|
6550
|
+
return imageId;
|
|
6551
|
+
}
|
|
6552
|
+
function resolveCachedImageId(dict, raw, images, cache, resolver, sink) {
|
|
6553
|
+
if (cache.has(dict)) return cache.get(dict) ?? void 0;
|
|
6554
|
+
const decoded = readImageXObject(dict, raw, resolver, sink);
|
|
6555
|
+
if (decoded === void 0) {
|
|
6556
|
+
cache.set(dict, null);
|
|
6557
|
+
return;
|
|
6558
|
+
}
|
|
6559
|
+
const imageId = registerExtractedImage(decoded.format, decoded.bytes, decoded.widthPx, decoded.heightPx, images);
|
|
6560
|
+
cache.set(dict, imageId);
|
|
6561
|
+
return imageId;
|
|
6562
|
+
}
|
|
6563
|
+
function convertImage(item, pageMatrix, images, cache, resolver, sink) {
|
|
6564
|
+
const xobjects = resolver.resolveDict(dictGet(item.resources, "XObject"));
|
|
6565
|
+
const xobj = xobjects !== void 0 ? resolver.resolve(dictGet(xobjects, item.resourceName)) : void 0;
|
|
6566
|
+
if (xobj?.kind !== "stream") return;
|
|
6567
|
+
const imageId = resolveCachedImageId(xobj.dict, xobj.raw, images, cache, resolver, sink);
|
|
6568
|
+
if (imageId === void 0) return;
|
|
6569
|
+
return {
|
|
6570
|
+
kind: "image",
|
|
6571
|
+
imageId,
|
|
6572
|
+
...imagePlacementFrom(multiplyMatrices(item.matrix, pageMatrix))
|
|
6573
|
+
};
|
|
6676
6574
|
}
|
|
6677
|
-
function
|
|
6678
|
-
|
|
6575
|
+
function convertInlineImage(item, pageMatrix, images, resolver, sink) {
|
|
6576
|
+
const decoded = readImageXObject(item.dict, item.data, resolver, sink);
|
|
6577
|
+
if (decoded === void 0) return;
|
|
6578
|
+
return {
|
|
6579
|
+
kind: "image",
|
|
6580
|
+
imageId: registerExtractedImage(decoded.format, decoded.bytes, decoded.widthPx, decoded.heightPx, images),
|
|
6581
|
+
...imagePlacementFrom(multiplyMatrices(item.matrix, pageMatrix))
|
|
6582
|
+
};
|
|
6679
6583
|
}
|
|
6680
|
-
function
|
|
6681
|
-
const
|
|
6682
|
-
|
|
6683
|
-
|
|
6684
|
-
const
|
|
6685
|
-
|
|
6686
|
-
|
|
6687
|
-
|
|
6688
|
-
|
|
6689
|
-
|
|
6690
|
-
|
|
6691
|
-
|
|
6692
|
-
|
|
6693
|
-
const
|
|
6694
|
-
const
|
|
6695
|
-
|
|
6696
|
-
|
|
6697
|
-
descNum,
|
|
6698
|
-
resourceName: `F${index + 1}`
|
|
6699
|
-
});
|
|
6700
|
-
}
|
|
6701
|
-
const imageAllocs = /* @__PURE__ */ new Map();
|
|
6702
|
-
for (const [index, imageId] of [...imageIds].sort().entries()) {
|
|
6703
|
-
const asset = doc.images[imageId];
|
|
6704
|
-
if (asset === void 0) throw new Error(`LayoutDocument references image "${imageId}" but it is not present in images`);
|
|
6705
|
-
const prepared = prepareImage(asset, compress);
|
|
6706
|
-
const imageNum = nextObjNum++;
|
|
6707
|
-
const smaskNum = prepared.alpha === void 0 ? void 0 : nextObjNum++;
|
|
6708
|
-
imageAllocs.set(imageId, {
|
|
6709
|
-
imageNum,
|
|
6710
|
-
smaskNum,
|
|
6711
|
-
resourceName: `Im${index + 1}`,
|
|
6712
|
-
prepared
|
|
6713
|
-
});
|
|
6714
|
-
}
|
|
6715
|
-
const pageAllocs = doc.pages.map(() => ({
|
|
6716
|
-
pageNum: nextObjNum++,
|
|
6717
|
-
contentsNum: nextObjNum++
|
|
6718
|
-
}));
|
|
6719
|
-
const objects = [];
|
|
6720
|
-
objects.push({
|
|
6721
|
-
num: catalogNum,
|
|
6722
|
-
value: pdfDict({
|
|
6723
|
-
Type: pdfName("Catalog"),
|
|
6724
|
-
Pages: pdfRef(pagesNum, 0)
|
|
6725
|
-
})
|
|
6726
|
-
});
|
|
6727
|
-
objects.push({
|
|
6728
|
-
num: pagesNum,
|
|
6729
|
-
value: pdfDict({
|
|
6730
|
-
Type: pdfName("Pages"),
|
|
6731
|
-
Kids: pdfArray(pageAllocs.map((p) => pdfRef(p.pageNum, 0))),
|
|
6732
|
-
Count: pdfNum(doc.pages.length)
|
|
6733
|
-
})
|
|
6734
|
-
});
|
|
6735
|
-
objects.push({
|
|
6736
|
-
num: infoNum,
|
|
6737
|
-
value: buildInfoDict(doc)
|
|
6738
|
-
});
|
|
6739
|
-
for (const [standardName, alloc] of fontAllocs) {
|
|
6740
|
-
const { font, descriptor } = buildFontObjects(standardName, pdfRef(alloc.descNum, 0));
|
|
6741
|
-
objects.push({
|
|
6742
|
-
num: alloc.fontNum,
|
|
6743
|
-
value: font
|
|
6584
|
+
function readLinkAnnotations(page, pageMatrix, resolver) {
|
|
6585
|
+
const annotsArr = asArray(dictGet(page, "Annots"));
|
|
6586
|
+
if (annotsArr === void 0) return [];
|
|
6587
|
+
const links = [];
|
|
6588
|
+
for (const annotRef of annotsArr) {
|
|
6589
|
+
const annot = resolver.resolveDict(annotRef);
|
|
6590
|
+
if (annot === void 0 || asName(dictGet(annot, "Subtype")) !== "Link") continue;
|
|
6591
|
+
const uri = readLinkUri(annot, resolver);
|
|
6592
|
+
const rectArr = asArray(dictGet(annot, "Rect"));
|
|
6593
|
+
if (uri === void 0 || rectArr === void 0) continue;
|
|
6594
|
+
const x1 = asNumber(rectArr[0]) ?? 0;
|
|
6595
|
+
const y1 = asNumber(rectArr[1]) ?? 0;
|
|
6596
|
+
const x2 = asNumber(rectArr[2]) ?? 0;
|
|
6597
|
+
const y2 = asNumber(rectArr[3]) ?? 0;
|
|
6598
|
+
const p1 = applyMatrix(pageMatrix, {
|
|
6599
|
+
x: Math.min(x1, x2),
|
|
6600
|
+
y: Math.min(y1, y2)
|
|
6744
6601
|
});
|
|
6745
|
-
|
|
6746
|
-
|
|
6747
|
-
|
|
6602
|
+
const p2 = applyMatrix(pageMatrix, {
|
|
6603
|
+
x: Math.max(x1, x2),
|
|
6604
|
+
y: Math.max(y1, y2)
|
|
6748
6605
|
});
|
|
6749
|
-
|
|
6750
|
-
|
|
6751
|
-
|
|
6752
|
-
|
|
6753
|
-
|
|
6754
|
-
|
|
6755
|
-
|
|
6756
|
-
});
|
|
6757
|
-
}
|
|
6758
|
-
objects.push({
|
|
6759
|
-
num: alloc.imageNum,
|
|
6760
|
-
value: pdfStream(alloc.prepared.dict, alloc.prepared.raw)
|
|
6606
|
+
links.push({
|
|
6607
|
+
kind: "link",
|
|
6608
|
+
uri,
|
|
6609
|
+
xPt: Math.min(p1.x, p2.x),
|
|
6610
|
+
yPt: Math.min(p1.y, p2.y),
|
|
6611
|
+
widthPt: Math.abs(p2.x - p1.x),
|
|
6612
|
+
heightPt: Math.abs(p2.y - p1.y)
|
|
6761
6613
|
});
|
|
6762
6614
|
}
|
|
6763
|
-
|
|
6764
|
-
|
|
6765
|
-
|
|
6766
|
-
const
|
|
6767
|
-
|
|
6768
|
-
|
|
6769
|
-
|
|
6770
|
-
|
|
6771
|
-
|
|
6772
|
-
|
|
6773
|
-
|
|
6774
|
-
|
|
6775
|
-
|
|
6776
|
-
|
|
6777
|
-
|
|
6778
|
-
|
|
6779
|
-
|
|
6780
|
-
|
|
6781
|
-
return { resourceName: alloc.resourceName };
|
|
6782
|
-
}
|
|
6783
|
-
};
|
|
6784
|
-
doc.pages.forEach((page, pageIndex) => {
|
|
6785
|
-
throwIfAborted(options.signal);
|
|
6786
|
-
const { pageNum, contentsNum } = pageAllocs[pageIndex];
|
|
6787
|
-
const { bytes: contentBytes, substitutions } = writeContentStream(page.items, context);
|
|
6788
|
-
for (const substitution of substitutions) options.onSubstitution?.(substitution, { pageIndex });
|
|
6789
|
-
const finalContentBytes = compress ? deflate(contentBytes) : contentBytes;
|
|
6790
|
-
const contentsDict = pdfDict(compress ? { Filter: pdfName("FlateDecode") } : {});
|
|
6791
|
-
objects.push({
|
|
6792
|
-
num: contentsNum,
|
|
6793
|
-
value: pdfStream(contentsDict, finalContentBytes)
|
|
6794
|
-
});
|
|
6795
|
-
const annots = page.items.filter(isLinkItem).map((link) => buildLinkAnnotDict(link));
|
|
6796
|
-
const pageEntries = /* @__PURE__ */ new Map([
|
|
6797
|
-
["Type", pdfName("Page")],
|
|
6798
|
-
["Parent", pdfRef(pagesNum, 0)],
|
|
6799
|
-
["MediaBox", pdfArray([
|
|
6800
|
-
0,
|
|
6801
|
-
0,
|
|
6802
|
-
page.widthPt,
|
|
6803
|
-
page.heightPt
|
|
6804
|
-
].map((n) => pdfNum(n)))],
|
|
6805
|
-
["Resources", resourcesDict],
|
|
6806
|
-
["Contents", pdfRef(contentsNum, 0)]
|
|
6807
|
-
]);
|
|
6808
|
-
if (annots.length > 0) pageEntries.set("Annots", pdfArray(annots));
|
|
6809
|
-
objects.push({
|
|
6810
|
-
num: pageNum,
|
|
6811
|
-
value: pdfDict(pageEntries)
|
|
6812
|
-
});
|
|
6813
|
-
});
|
|
6814
|
-
const writer = new ByteWriter();
|
|
6815
|
-
writer.writeAscii("%PDF-1.7\n");
|
|
6816
|
-
const offsets = /* @__PURE__ */ new Map();
|
|
6817
|
-
for (const { num, value } of objects) {
|
|
6818
|
-
offsets.set(num, writer.length);
|
|
6819
|
-
writer.writeAscii(`${num} 0 obj\n`);
|
|
6820
|
-
writeObject(writer, value);
|
|
6821
|
-
writer.writeAscii("\nendobj\n");
|
|
6615
|
+
return links;
|
|
6616
|
+
}
|
|
6617
|
+
function readLinkUri(annot, resolver) {
|
|
6618
|
+
const action = resolver.resolveDict(dictGet(annot, "A"));
|
|
6619
|
+
if (action === void 0 || asName(dictGet(action, "S")) !== "URI") return;
|
|
6620
|
+
const uriObj = dictGet(action, "URI");
|
|
6621
|
+
return uriObj?.kind === "string" ? decodePdfString(uriObj.bytes) : void 0;
|
|
6622
|
+
}
|
|
6623
|
+
function readPageNotes(page, resolver) {
|
|
6624
|
+
const annotsArr = asArray(dictGet(page, "Annots"));
|
|
6625
|
+
if (annotsArr === void 0) return;
|
|
6626
|
+
for (const annotRef of annotsArr) {
|
|
6627
|
+
const annot = resolver.resolveDict(annotRef);
|
|
6628
|
+
if (annot === void 0 || asName(dictGet(annot, "Subtype")) !== "Text") continue;
|
|
6629
|
+
const titleObj = dictGet(annot, "T");
|
|
6630
|
+
if ((titleObj?.kind === "string" ? decodePdfString(titleObj.bytes) : void 0) !== "documents.js:notes") continue;
|
|
6631
|
+
const contentsObj = dictGet(annot, "Contents");
|
|
6632
|
+
if (contentsObj?.kind === "string") return decodePdfString(contentsObj.bytes);
|
|
6822
6633
|
}
|
|
6823
|
-
|
|
6824
|
-
|
|
6825
|
-
|
|
6826
|
-
|
|
6827
|
-
|
|
6828
|
-
|
|
6829
|
-
const offset = offsets.get(num);
|
|
6830
|
-
if (offset === void 0) throw new Error(`object ${num} was allocated but never written -- this is a writePdf internal invariant violation`);
|
|
6831
|
-
writer.writeAscii(xrefEntry(offset, 0, true));
|
|
6634
|
+
}
|
|
6635
|
+
function decodePdfString(bytes) {
|
|
6636
|
+
if (bytes.length >= 2 && bytes[0] === 254 && bytes[1] === 255) {
|
|
6637
|
+
let out = "";
|
|
6638
|
+
for (let i = 2; i + 1 < bytes.length; i += 2) out += String.fromCharCode((bytes[i] ?? 0) << 8 | (bytes[i + 1] ?? 0));
|
|
6639
|
+
return out;
|
|
6832
6640
|
}
|
|
6833
|
-
|
|
6834
|
-
|
|
6835
|
-
|
|
6836
|
-
|
|
6837
|
-
|
|
6838
|
-
|
|
6839
|
-
|
|
6840
|
-
|
|
6841
|
-
|
|
6842
|
-
|
|
6641
|
+
return Array.from(bytes, (b) => String.fromCharCode(b)).join("");
|
|
6642
|
+
}
|
|
6643
|
+
const PDF_DATE_PATTERN = /^D:(\d{4})(\d{2})?(\d{2})?(\d{2})?(\d{2})?(\d{2})?([+\-Z])?(\d{2})?'?(\d{2})?'?$/;
|
|
6644
|
+
function parsePdfDate(raw) {
|
|
6645
|
+
if (raw === void 0) return;
|
|
6646
|
+
const match = PDF_DATE_PATTERN.exec(raw);
|
|
6647
|
+
if (match === null) return;
|
|
6648
|
+
const [, year, month = "01", day = "01", hour = "00", minute = "00", second = "00", tzSign, tzHour = "00", tzMinute = "00"] = match;
|
|
6649
|
+
return `${year}-${month}-${day}T${hour}:${minute}:${second}${tzSign === void 0 || tzSign === "Z" ? "Z" : `${tzSign}${tzHour}:${tzMinute}`}`;
|
|
6650
|
+
}
|
|
6651
|
+
function readMetadata(trailer, resolver) {
|
|
6652
|
+
const info = resolver.resolveDict(dictGet(trailer, "Info"));
|
|
6653
|
+
if (info === void 0) return {};
|
|
6654
|
+
const stringField = (key) => {
|
|
6655
|
+
const obj = dictGet(info, key);
|
|
6656
|
+
return obj?.kind === "string" ? decodePdfString(obj.bytes) : void 0;
|
|
6657
|
+
};
|
|
6658
|
+
const keywords = stringField("Keywords")?.split(",").map((k) => k.trim()).filter((k) => k.length > 0);
|
|
6659
|
+
return {
|
|
6660
|
+
title: stringField("Title"),
|
|
6661
|
+
author: stringField("Author"),
|
|
6662
|
+
subject: stringField("Subject"),
|
|
6663
|
+
keywords: keywords !== void 0 && keywords.length > 0 ? keywords : void 0,
|
|
6664
|
+
creator: stringField("Creator"),
|
|
6665
|
+
producer: stringField("Producer"),
|
|
6666
|
+
createdIso: parsePdfDate(stringField("CreationDate")),
|
|
6667
|
+
modifiedIso: parsePdfDate(stringField("ModDate"))
|
|
6668
|
+
};
|
|
6843
6669
|
}
|
|
6844
6670
|
//#endregion
|
|
6845
6671
|
//#region src/pdf/codec.ts
|
|
@@ -7328,7 +7154,7 @@ function convertWordprocessingToLayout(doc, options) {
|
|
|
7328
7154
|
const pages = [];
|
|
7329
7155
|
for (const section of doc.sections) paginateSection(section, options.measurer, images, pages);
|
|
7330
7156
|
return {
|
|
7331
|
-
formatVersion: 1,
|
|
7157
|
+
formatVersion: LAYOUT_FORMAT_VERSION$1,
|
|
7332
7158
|
metadata: doc.metadata,
|
|
7333
7159
|
pages,
|
|
7334
7160
|
images
|
|
@@ -7471,14 +7297,15 @@ function convertSlide(slide, measurer, images) {
|
|
|
7471
7297
|
return {
|
|
7472
7298
|
widthPt: slide.size.widthPt,
|
|
7473
7299
|
heightPt: slide.size.heightPt,
|
|
7474
|
-
items
|
|
7300
|
+
items,
|
|
7301
|
+
...slide.notes.length > 0 ? { notes: slide.notes } : {}
|
|
7475
7302
|
};
|
|
7476
7303
|
}
|
|
7477
7304
|
function convertPresentationToLayout(doc, options) {
|
|
7478
7305
|
const images = {};
|
|
7479
7306
|
const pages = doc.slides.map((slide) => convertSlide(slide, options.measurer, images));
|
|
7480
7307
|
return {
|
|
7481
|
-
formatVersion: 1,
|
|
7308
|
+
formatVersion: LAYOUT_FORMAT_VERSION$1,
|
|
7482
7309
|
metadata: doc.metadata,
|
|
7483
7310
|
pages,
|
|
7484
7311
|
images
|
|
@@ -7709,7 +7536,7 @@ function reconstructSlide(page, images) {
|
|
|
7709
7536
|
heightPt: page.heightPt
|
|
7710
7537
|
},
|
|
7711
7538
|
shapes: [...imageShapes, ...textShapes],
|
|
7712
|
-
notes: ""
|
|
7539
|
+
notes: page.notes ?? ""
|
|
7713
7540
|
};
|
|
7714
7541
|
}
|
|
7715
7542
|
function splitLineByLargeGaps(line) {
|