reamkit 1.2.0 → 1.4.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 +10 -6
- package/dist/esm/core/converter/facade.d.ts +2 -2
- package/dist/esm/core/converter/facade.js +12 -0
- package/dist/esm/core/converter/ream.d.ts +1 -1
- package/dist/esm/core/converter/ream.js +14 -3
- package/dist/esm/core/document-model/types.d.ts +2 -0
- package/dist/esm/core/drawingml/chart-geometry.d.ts +1 -1
- package/dist/esm/core/drawingml/chart-geometry.js +79 -117
- package/dist/esm/core/drawingml/chart-parser.d.ts +3 -0
- package/dist/esm/core/drawingml/chart-parser.js +28 -1
- package/dist/esm/core/numbering/apply.js +2 -1
- package/dist/esm/core/opc/index.d.ts +3 -0
- package/dist/esm/core/opc/index.js +1 -0
- package/dist/esm/core/opc/opc-writer.d.ts +28 -0
- package/dist/esm/core/opc/opc-writer.js +60 -0
- package/dist/esm/core/opc/package.js +3 -2
- package/dist/esm/core/opc/relationship-types.d.ts +1 -0
- package/dist/esm/core/opc/relationship-types.js +5 -2
- package/dist/esm/excel/print-model.d.ts +3 -0
- package/dist/esm/excel/print-model.js +4 -8
- package/dist/esm/excel/sheet-drawing.d.ts +9 -0
- package/dist/esm/excel/sheet-drawing.js +113 -0
- package/dist/esm/excel/worksheet-parser.d.ts +1 -0
- package/dist/esm/excel/worksheet-parser.js +4 -1
- package/dist/esm/excel/xlsx-reader.js +41 -0
- package/dist/esm/excel/xlsx-to-pdf.js +25 -18
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +2 -1
- package/dist/esm/layout/page-doc.d.ts +1 -0
- package/dist/esm/layout/styled-layout.d.ts +2 -0
- package/dist/esm/layout/styled-layout.js +321 -171
- package/dist/esm/pdf/encryption.d.ts +34 -0
- package/dist/esm/pdf/encryption.js +143 -0
- package/dist/esm/pdf/index.d.ts +2 -1
- package/dist/esm/pdf/styled-page-emitter.d.ts +2 -0
- package/dist/esm/pdf/styled-page-emitter.js +25 -7
- package/dist/esm/pdf/styled-page-renderer.d.ts +1 -0
- package/dist/esm/pdf/styled-page-renderer.js +17 -2
- package/dist/esm/pdf/writer.d.ts +2 -0
- package/dist/esm/pdf/writer.js +6 -1
- package/dist/esm/word/document-parser.js +51 -13
- package/dist/esm/word/docx-reader.js +7 -11
- package/dist/esm/word/docx-to-pdf.js +23 -13
- package/dist/esm/word/docx-writer.d.ts +4 -0
- package/dist/esm/word/docx-writer.js +602 -0
- package/dist/esm/word/drawing-parser.d.ts +1 -0
- package/dist/esm/word/drawing-parser.js +37 -2
- package/package.json +4 -3
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { PdfValue } from './objects.js';
|
|
2
|
+
export interface PdfPermissions {
|
|
3
|
+
/** Bit 3 — print the document (default true). */
|
|
4
|
+
readonly printing?: boolean;
|
|
5
|
+
/** Bit 4 — modify contents (default true). */
|
|
6
|
+
readonly modifying?: boolean;
|
|
7
|
+
/** Bit 5 — copy text and graphics (default true). */
|
|
8
|
+
readonly copying?: boolean;
|
|
9
|
+
/** Bit 6 — add/modify annotations (default true). */
|
|
10
|
+
readonly annotating?: boolean;
|
|
11
|
+
/** Bit 9 — fill in form fields (default true). */
|
|
12
|
+
readonly fillingForms?: boolean;
|
|
13
|
+
/** Bit 10 — extraction for accessibility (default true; forced by PDF/UA). */
|
|
14
|
+
readonly contentAccessibility?: boolean;
|
|
15
|
+
/** Bit 11 — assemble (insert/rotate/delete pages) (default true). */
|
|
16
|
+
readonly documentAssembly?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface PdfEncryptOptions {
|
|
19
|
+
/** Required to OPEN the document. Empty string = opens without a prompt. */
|
|
20
|
+
readonly userPassword?: string;
|
|
21
|
+
/** Overrides the permission restrictions. Defaults to the user password. */
|
|
22
|
+
readonly ownerPassword?: string;
|
|
23
|
+
readonly permissions?: PdfPermissions;
|
|
24
|
+
}
|
|
25
|
+
export interface PreparedEncryption {
|
|
26
|
+
readonly fileKey: Uint8Array;
|
|
27
|
+
readonly encryptDict: PdfValue;
|
|
28
|
+
}
|
|
29
|
+
export declare function permissionBits(p: PdfPermissions | undefined): number;
|
|
30
|
+
export declare function aesCbcNoPadDecrypt(key: Uint8Array, iv: Uint8Array, data: Uint8Array): Promise<Uint8Array>;
|
|
31
|
+
export declare function hardHash(password: Uint8Array, salt: Uint8Array, userData: Uint8Array): Promise<Uint8Array>;
|
|
32
|
+
export declare function preparePdfEncryption(options: PdfEncryptOptions): Promise<PreparedEncryption>;
|
|
33
|
+
export declare function decryptBytes(fileKey: Uint8Array, data: Uint8Array): Promise<Uint8Array>;
|
|
34
|
+
export declare function encryptObjectGraph(value: PdfValue, fileKey: Uint8Array): Promise<PdfValue>;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { PdfHexString, PdfStream, dict, name } from "./objects.js";
|
|
2
|
+
//#region src/pdf/encryption.ts
|
|
3
|
+
var encoder = new TextEncoder();
|
|
4
|
+
var subtle = globalThis.crypto.subtle;
|
|
5
|
+
function permissionBits(p) {
|
|
6
|
+
let bits = -4;
|
|
7
|
+
const off = (bit) => bits &= ~(1 << bit - 1);
|
|
8
|
+
if (p?.printing === false) {
|
|
9
|
+
off(3);
|
|
10
|
+
off(12);
|
|
11
|
+
}
|
|
12
|
+
if (p?.modifying === false) off(4);
|
|
13
|
+
if (p?.copying === false) off(5);
|
|
14
|
+
if (p?.annotating === false) off(6);
|
|
15
|
+
if (p?.fillingForms === false) off(9);
|
|
16
|
+
if (p?.contentAccessibility === false) off(10);
|
|
17
|
+
if (p?.documentAssembly === false) off(11);
|
|
18
|
+
return bits | 0;
|
|
19
|
+
}
|
|
20
|
+
function passwordBytes(password) {
|
|
21
|
+
return encoder.encode(password).slice(0, 127);
|
|
22
|
+
}
|
|
23
|
+
function concat(...parts) {
|
|
24
|
+
const out = new Uint8Array(parts.reduce((s, p) => s + p.length, 0));
|
|
25
|
+
let off = 0;
|
|
26
|
+
for (const p of parts) {
|
|
27
|
+
out.set(p, off);
|
|
28
|
+
off += p.length;
|
|
29
|
+
}
|
|
30
|
+
return out;
|
|
31
|
+
}
|
|
32
|
+
async function sha(algo, data) {
|
|
33
|
+
return new Uint8Array(await subtle.digest(algo, data));
|
|
34
|
+
}
|
|
35
|
+
async function aesCbcNoPad(key, iv, data) {
|
|
36
|
+
const k = await subtle.importKey("raw", key, "AES-CBC", false, ["encrypt"]);
|
|
37
|
+
return new Uint8Array(await subtle.encrypt({
|
|
38
|
+
name: "AES-CBC",
|
|
39
|
+
iv
|
|
40
|
+
}, k, data)).slice(0, data.length);
|
|
41
|
+
}
|
|
42
|
+
async function hardHash(password, salt, userData) {
|
|
43
|
+
let k = await sha("SHA-256", concat(password, salt, userData));
|
|
44
|
+
for (let round = 0;; round++) {
|
|
45
|
+
const block = concat(password, k, userData);
|
|
46
|
+
const k1 = new Uint8Array(block.length * 64);
|
|
47
|
+
for (let i = 0; i < 64; i++) k1.set(block, i * block.length);
|
|
48
|
+
const e = await aesCbcNoPad(k.slice(0, 16), k.slice(16, 32), k1);
|
|
49
|
+
let mod = 0;
|
|
50
|
+
for (let i = 0; i < 16; i++) mod = (mod + e[i]) % 3;
|
|
51
|
+
k = (await sha(mod === 0 ? "SHA-256" : mod === 1 ? "SHA-384" : "SHA-512", e)).slice(0, 64);
|
|
52
|
+
if (round >= 63 && e[e.length - 1] <= round - 31) break;
|
|
53
|
+
}
|
|
54
|
+
return k.slice(0, 32);
|
|
55
|
+
}
|
|
56
|
+
async function preparePdfEncryption(options) {
|
|
57
|
+
const user = passwordBytes(options.userPassword ?? "");
|
|
58
|
+
const owner = passwordBytes(options.ownerPassword ?? options.userPassword ?? "");
|
|
59
|
+
const fileKey = new Uint8Array(32);
|
|
60
|
+
globalThis.crypto.getRandomValues(fileKey);
|
|
61
|
+
const salts = new Uint8Array(36);
|
|
62
|
+
globalThis.crypto.getRandomValues(salts);
|
|
63
|
+
const uvs = salts.slice(0, 8);
|
|
64
|
+
const uks = salts.slice(8, 16);
|
|
65
|
+
const ovs = salts.slice(16, 24);
|
|
66
|
+
const oks = salts.slice(24, 32);
|
|
67
|
+
const U = concat(await hardHash(user, uvs, new Uint8Array(0)), uvs, uks);
|
|
68
|
+
const UE = await aesCbcNoPad(await hardHash(user, uks, new Uint8Array(0)), new Uint8Array(16), fileKey);
|
|
69
|
+
const O = concat(await hardHash(owner, ovs, U), ovs, oks);
|
|
70
|
+
const OE = await aesCbcNoPad(await hardHash(owner, oks, U), new Uint8Array(16), fileKey);
|
|
71
|
+
const p = permissionBits(options.permissions);
|
|
72
|
+
const permsPlain = new Uint8Array(16);
|
|
73
|
+
new DataView(permsPlain.buffer).setInt32(0, p, true);
|
|
74
|
+
permsPlain.set([
|
|
75
|
+
255,
|
|
76
|
+
255,
|
|
77
|
+
255,
|
|
78
|
+
255
|
|
79
|
+
], 4);
|
|
80
|
+
permsPlain.set([
|
|
81
|
+
84,
|
|
82
|
+
97,
|
|
83
|
+
100,
|
|
84
|
+
98
|
|
85
|
+
], 8);
|
|
86
|
+
permsPlain.set(salts.slice(32, 36), 12);
|
|
87
|
+
const perms = await aesCbcNoPad(fileKey, new Uint8Array(16), permsPlain);
|
|
88
|
+
return {
|
|
89
|
+
fileKey,
|
|
90
|
+
encryptDict: dict({
|
|
91
|
+
Filter: name("Standard"),
|
|
92
|
+
V: 5,
|
|
93
|
+
R: 6,
|
|
94
|
+
Length: 256,
|
|
95
|
+
CF: dict({ StdCF: dict({
|
|
96
|
+
CFM: name("AESV3"),
|
|
97
|
+
AuthEvent: name("DocOpen"),
|
|
98
|
+
Length: 32
|
|
99
|
+
}) }),
|
|
100
|
+
StmF: name("StdCF"),
|
|
101
|
+
StrF: name("StdCF"),
|
|
102
|
+
U: new PdfHexString(U),
|
|
103
|
+
UE: new PdfHexString(UE),
|
|
104
|
+
O: new PdfHexString(O),
|
|
105
|
+
OE: new PdfHexString(OE),
|
|
106
|
+
P: p,
|
|
107
|
+
Perms: new PdfHexString(perms),
|
|
108
|
+
EncryptMetadata: true
|
|
109
|
+
})
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
async function encryptBytes(fileKey, data) {
|
|
113
|
+
const iv = new Uint8Array(16);
|
|
114
|
+
globalThis.crypto.getRandomValues(iv);
|
|
115
|
+
const k = await subtle.importKey("raw", fileKey, "AES-CBC", false, ["encrypt"]);
|
|
116
|
+
return concat(iv, new Uint8Array(await subtle.encrypt({
|
|
117
|
+
name: "AES-CBC",
|
|
118
|
+
iv
|
|
119
|
+
}, k, data)));
|
|
120
|
+
}
|
|
121
|
+
async function encryptObjectGraph(value, fileKey) {
|
|
122
|
+
if (typeof value === "string") return new PdfHexString(await encryptBytes(fileKey, encoder.encode(value)));
|
|
123
|
+
if (value instanceof PdfHexString) return new PdfHexString(await encryptBytes(fileKey, value.bytes));
|
|
124
|
+
if (value instanceof PdfStream) {
|
|
125
|
+
const data = await encryptBytes(fileKey, value.data);
|
|
126
|
+
const d = new Map(value.dict);
|
|
127
|
+
d.set("Length", data.length);
|
|
128
|
+
return new PdfStream(d, data);
|
|
129
|
+
}
|
|
130
|
+
if (Array.isArray(value)) {
|
|
131
|
+
const out = [];
|
|
132
|
+
for (const v of value) out.push(await encryptObjectGraph(v, fileKey));
|
|
133
|
+
return out;
|
|
134
|
+
}
|
|
135
|
+
if (value instanceof Map) {
|
|
136
|
+
const out = /* @__PURE__ */ new Map();
|
|
137
|
+
for (const [k, v] of value) out.set(k, await encryptObjectGraph(v, fileKey));
|
|
138
|
+
return out;
|
|
139
|
+
}
|
|
140
|
+
return value;
|
|
141
|
+
}
|
|
142
|
+
//#endregion
|
|
143
|
+
export { encryptObjectGraph, preparePdfEncryption };
|
package/dist/esm/pdf/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { renderPlainTextPdf } from './text-page-renderer.js';
|
|
2
2
|
export type { TextRenderOptions, FontInput } from './text-page-renderer.js';
|
|
3
|
-
export { renderStyledPdf } from './styled-page-renderer.js';
|
|
3
|
+
export { renderStyledPdf, renderStyledPdfEncrypted } from './styled-page-renderer.js';
|
|
4
|
+
export type { PdfEncryptOptions, PdfPermissions } from './encryption.js';
|
|
4
5
|
export type { DocumentInfo, StyledRenderOptions, PdfALevel } from './styled-page-renderer.js';
|
|
5
6
|
export { signPdf } from './signature.js';
|
|
6
7
|
export type { SignaturePlaceholder, SignerCredentials, SignatureOptions } from './signature.js';
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { LaidOutPdfDocument, StyledRenderOptions } from '../layout/styled-layout.js';
|
|
2
2
|
import { PdfDocument } from './writer.js';
|
|
3
|
+
import { PdfEncryptOptions } from './encryption.js';
|
|
3
4
|
type EmitOptions = Pick<StyledRenderOptions, 'attachments' | 'info' | 'language' | 'pdfUA' | 'signaturePlaceholder'>;
|
|
4
5
|
export declare function emitStyledPdf(laid: LaidOutPdfDocument, options: EmitOptions, doc: PdfDocument): Uint8Array;
|
|
6
|
+
export declare function emitStyledPdfEncrypted(laid: LaidOutPdfDocument, options: EmitOptions, doc: PdfDocument, encrypt: PdfEncryptOptions): Promise<Uint8Array>;
|
|
5
7
|
export interface PageTagging {
|
|
6
8
|
next: number;
|
|
7
9
|
assigned: boolean;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { dict, name, ref, stream, unicodeString } from "./objects.js";
|
|
2
2
|
import { embedTtfFont } from "./cid-font.js";
|
|
3
|
+
import { preparePdfEncryption } from "./encryption.js";
|
|
3
4
|
import { reorderVisual } from "../core/bidi/algorithm.js";
|
|
4
5
|
import { reverseByCodePoint } from "../core/bidi/index.js";
|
|
5
6
|
import "../layout/styled-layout.js";
|
|
@@ -14,6 +15,21 @@ import { buildXmpPacket } from "./xmp.js";
|
|
|
14
15
|
//#region src/pdf/styled-page-emitter.ts
|
|
15
16
|
var encoder = new TextEncoder();
|
|
16
17
|
function emitStyledPdf(laid, options, doc) {
|
|
18
|
+
const a = assembleStyledPdf(laid, options, doc);
|
|
19
|
+
return doc.build(a.catalogRef, a.infoRef, a.buildOptions);
|
|
20
|
+
}
|
|
21
|
+
async function emitStyledPdfEncrypted(laid, options, doc, encrypt) {
|
|
22
|
+
const a = assembleStyledPdf(laid, options, doc);
|
|
23
|
+
const prepared = await preparePdfEncryption(encrypt);
|
|
24
|
+
await doc.encryptAll(prepared.fileKey);
|
|
25
|
+
const encryptRef = doc.add(prepared.encryptDict);
|
|
26
|
+
return doc.build(a.catalogRef, a.infoRef, {
|
|
27
|
+
...a.buildOptions,
|
|
28
|
+
id: true,
|
|
29
|
+
encrypt: encryptRef
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
function assembleStyledPdf(laid, options, doc) {
|
|
17
33
|
const { pages: renderedPages } = laid;
|
|
18
34
|
const { structBuilder, sectionCtxs, pdfaProfile, tagged } = laid.pdf;
|
|
19
35
|
const docInfo = options.pdfUA && !options.info?.title ? {
|
|
@@ -210,12 +226,14 @@ function emitStyledPdf(laid, options, doc) {
|
|
|
210
226
|
else firstPageDict.set("Annots", [ref(fieldRef.id)]);
|
|
211
227
|
catalogEntries["AcroForm"] = acroForm;
|
|
212
228
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
229
|
+
return {
|
|
230
|
+
catalogRef: doc.add(dict(catalogEntries)),
|
|
231
|
+
infoRef: buildInfoDict(doc, docInfo),
|
|
232
|
+
buildOptions: { ...pdfaProfile ? {
|
|
233
|
+
version: pdfaProfile.version,
|
|
234
|
+
id: true
|
|
235
|
+
} : {} }
|
|
236
|
+
};
|
|
219
237
|
}
|
|
220
238
|
var DEFAULT_PRODUCER = "Ream";
|
|
221
239
|
function xmpFromInfo(info, p, pdfUA = false) {
|
|
@@ -686,4 +704,4 @@ function formatNumber(n) {
|
|
|
686
704
|
return n.toFixed(3).replace(/0+$/, "").replace(/\.$/, "");
|
|
687
705
|
}
|
|
688
706
|
//#endregion
|
|
689
|
-
export { emitStyledPdf };
|
|
707
|
+
export { emitStyledPdf, emitStyledPdfEncrypted };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { BodyElement } from '../core/document-model/index.js';
|
|
2
2
|
import { StyledRenderOptions } from '../layout/styled-layout.js';
|
|
3
3
|
export declare function renderStyledPdf(body: ReadonlyArray<BodyElement>, options: StyledRenderOptions): Uint8Array;
|
|
4
|
+
export declare function renderStyledPdfEncrypted(body: ReadonlyArray<BodyElement>, options: StyledRenderOptions): Promise<Uint8Array>;
|
|
4
5
|
export * from '../layout/page-doc.js';
|
|
5
6
|
export * from '../layout/styled-layout.js';
|
|
@@ -1,10 +1,25 @@
|
|
|
1
1
|
import { PdfDocument } from "./writer.js";
|
|
2
2
|
import { layoutStyledDocument } from "../layout/styled-layout.js";
|
|
3
3
|
import "../layout/page-doc.js";
|
|
4
|
-
import { emitStyledPdf } from "./styled-page-emitter.js";
|
|
4
|
+
import { emitStyledPdf, emitStyledPdfEncrypted } from "./styled-page-emitter.js";
|
|
5
5
|
//#region src/pdf/styled-page-renderer.ts
|
|
6
6
|
function renderStyledPdf(body, options) {
|
|
7
|
+
if (options.encrypt) throw new Error("PDF encryption requires the async conversion path (WebCrypto)");
|
|
7
8
|
return emitStyledPdf(layoutStyledDocument(body, options), options, new PdfDocument());
|
|
8
9
|
}
|
|
10
|
+
async function renderStyledPdfEncrypted(body, options) {
|
|
11
|
+
const encrypt = options.encrypt;
|
|
12
|
+
if (!encrypt) return renderStyledPdf(body, options);
|
|
13
|
+
if (options.pdfA) throw new Error("PDF/A forbids encryption (ISO 19005)");
|
|
14
|
+
if (options.signaturePlaceholder) throw new Error("encryption combined with a digital signature is not supported (v1)");
|
|
15
|
+
const effective = options.pdfUA === true ? {
|
|
16
|
+
...encrypt,
|
|
17
|
+
permissions: {
|
|
18
|
+
...encrypt.permissions,
|
|
19
|
+
contentAccessibility: true
|
|
20
|
+
}
|
|
21
|
+
} : encrypt;
|
|
22
|
+
return emitStyledPdfEncrypted(layoutStyledDocument(body, options), options, new PdfDocument(), effective);
|
|
23
|
+
}
|
|
9
24
|
//#endregion
|
|
10
|
-
export { renderStyledPdf };
|
|
25
|
+
export { renderStyledPdf, renderStyledPdfEncrypted };
|
package/dist/esm/pdf/writer.d.ts
CHANGED
|
@@ -2,9 +2,11 @@ import { PdfValue, PdfRef } from './objects.js';
|
|
|
2
2
|
export interface BuildOptions {
|
|
3
3
|
readonly version?: string;
|
|
4
4
|
readonly id?: boolean;
|
|
5
|
+
readonly encrypt?: PdfRef;
|
|
5
6
|
}
|
|
6
7
|
export declare class PdfDocument {
|
|
7
8
|
private readonly objects;
|
|
8
9
|
add(value: PdfValue): PdfRef;
|
|
10
|
+
encryptAll(fileKey: Uint8Array): Promise<void>;
|
|
9
11
|
build(root: PdfRef, info?: PdfRef, options?: BuildOptions): Uint8Array;
|
|
10
12
|
}
|
package/dist/esm/pdf/writer.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { PdfRef } from "./objects.js";
|
|
2
|
+
import { encryptObjectGraph } from "./encryption.js";
|
|
2
3
|
import { serializeIndirectObject } from "./serialize.js";
|
|
3
4
|
//#region src/pdf/writer.ts
|
|
4
5
|
var encoder = new TextEncoder();
|
|
@@ -20,6 +21,9 @@ var PdfDocument = class {
|
|
|
20
21
|
});
|
|
21
22
|
return new PdfRef(id);
|
|
22
23
|
}
|
|
24
|
+
async encryptAll(fileKey) {
|
|
25
|
+
for (const obj of this.objects) obj.value = await encryptObjectGraph(obj.value, fileKey);
|
|
26
|
+
}
|
|
23
27
|
build(root, info, options = {}) {
|
|
24
28
|
const parts = [];
|
|
25
29
|
const offsets = new Array(this.objects.length + 1).fill(0);
|
|
@@ -43,12 +47,13 @@ var PdfDocument = class {
|
|
|
43
47
|
}
|
|
44
48
|
push(encoder.encode(xref));
|
|
45
49
|
const infoEntry = info ? ` /Info ${info.id} ${info.generation} R` : "";
|
|
50
|
+
const encryptEntry = options.encrypt ? ` /Encrypt ${options.encrypt.id} ${options.encrypt.generation} R` : "";
|
|
46
51
|
let idEntry = "";
|
|
47
52
|
if (options.id) {
|
|
48
53
|
const hashHex = hash128Hex(parts);
|
|
49
54
|
idEntry = ` /ID [<${hashHex}> <${hashHex}>]`;
|
|
50
55
|
}
|
|
51
|
-
const trailer = `trailer\n<</Size ${totalObjects} /Root ${root.id} ${root.generation} R${infoEntry}${idEntry}>>\nstartxref\n${xrefOffset}\n%%EOF\n`;
|
|
56
|
+
const trailer = `trailer\n<</Size ${totalObjects} /Root ${root.id} ${root.generation} R${infoEntry}${encryptEntry}${idEntry}>>\nstartxref\n${xrefOffset}\n%%EOF\n`;
|
|
52
57
|
push(encoder.encode(trailer));
|
|
53
58
|
const out = new Uint8Array(pos);
|
|
54
59
|
let off = 0;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { twipsToPt } from "../core/ir/units.js";
|
|
2
|
-
import { poAttr, poChildren, poFindByPath, poIntAttr, poIs, poText } from "../core/po-helpers.js";
|
|
2
|
+
import { poAttr, poChildren, poFindByPath, poFindDescendant, poIntAttr, poIs, poText } from "../core/po-helpers.js";
|
|
3
3
|
import { defaultColorResolver } from "../core/drawingml/colors.js";
|
|
4
4
|
import { parseOMath } from "./omml-parser.js";
|
|
5
|
-
import { expandMcChildren, parseDrawing } from "./drawing-parser.js";
|
|
5
|
+
import { expandMcChildren, parseDrawing, parseVmlPicture } from "./drawing-parser.js";
|
|
6
6
|
import { parseRunProperties } from "./run-properties.js";
|
|
7
7
|
import { parseParagraphProperties } from "./paragraph-properties.js";
|
|
8
8
|
import { poElementToFlat } from "./po-to-flat.js";
|
|
@@ -172,22 +172,50 @@ function parseBodyElements(children, ctx = DEFAULT_PARSE_CONTEXT) {
|
|
|
172
172
|
kind: "table",
|
|
173
173
|
table: parseTable(child, ctx)
|
|
174
174
|
});
|
|
175
|
+
else if (poIs(child, "w:sdt")) {
|
|
176
|
+
const content = poChildren(child).find((c) => poIs(c, "w:sdtContent"));
|
|
177
|
+
if (content) out.push(...parseBodyElements(poChildren(content), ctx));
|
|
178
|
+
}
|
|
175
179
|
return out;
|
|
176
180
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
181
|
+
var COLLAPSE_TRANSPARENT_TAGS = new Set([
|
|
182
|
+
"w:ins",
|
|
183
|
+
"w:moveTo",
|
|
184
|
+
"w:sdt",
|
|
185
|
+
"w:sdtContent",
|
|
186
|
+
"w:smartTag"
|
|
187
|
+
]);
|
|
188
|
+
function scanForLoneDrawing(container, acc) {
|
|
189
|
+
for (const child of expandMcChildren(poChildren(container))) {
|
|
190
|
+
if (poIs(child, "w:pPr")) continue;
|
|
191
|
+
if (poIs(child, "w:r")) {
|
|
192
|
+
for (const rc of expandMcChildren(poChildren(child))) if (poIs(rc, "w:drawing")) {
|
|
193
|
+
if (!acc.drawing) acc.drawing = rc;
|
|
194
|
+
} else if (poIs(rc, "w:pict") || poIs(rc, "w:object")) {
|
|
195
|
+
if (!acc.vml && poFindDescendant(rc, "v:imagedata") !== void 0) acc.vml = rc;
|
|
196
|
+
} else if (poIs(rc, "w:t") && poText(rc).length > 0) acc.hasOther = true;
|
|
197
|
+
else if (poIs(rc, "w:tab") || poIs(rc, "w:br") || poIs(rc, "w:noBreakHyphen")) acc.hasOther = true;
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
if (poIs(child, "m:oMath") || poIs(child, "m:oMathPara")) {
|
|
201
|
+
acc.hasOther = true;
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
const tag = elementTag(child);
|
|
205
|
+
if (tag && COLLAPSE_TRANSPARENT_TAGS.has(tag)) scanForLoneDrawing(child, acc);
|
|
206
|
+
else if (tag && RUN_CONTAINER_TAGS.has(tag)) acc.hasOther = true;
|
|
186
207
|
}
|
|
187
|
-
|
|
208
|
+
}
|
|
209
|
+
function tryExtractDrawingFromParagraph(p, ctx) {
|
|
210
|
+
const scan = { hasOther: false };
|
|
211
|
+
scanForLoneDrawing(p, scan);
|
|
212
|
+
const { drawing, vml } = scan;
|
|
213
|
+
if (scan.hasOther || !drawing && !vml) return null;
|
|
214
|
+
const fromVml = drawing === void 0;
|
|
188
215
|
const parseBody = (children) => parseBodyElements(children, ctx);
|
|
189
|
-
const content = parseDrawing(drawing, ctx.resolveColor, parseBody);
|
|
216
|
+
const content = fromVml ? parseVmlPicture(vml) : parseDrawing(drawing, ctx.resolveColor, parseBody);
|
|
190
217
|
if (!content) return null;
|
|
218
|
+
if (fromVml && content.kind === "image" && ctx.resolveImage?.(content.imageId) === void 0) return null;
|
|
191
219
|
const pPrNode = poChildren(p).find((c) => poIs(c, "w:pPr"));
|
|
192
220
|
const paragraphProperties = pPrNode ? parseParagraphProperties(poElementToFlat(pPrNode)) : {};
|
|
193
221
|
if (content.kind === "image") {
|
|
@@ -431,6 +459,16 @@ function parseRun(r, ctx) {
|
|
|
431
459
|
height: content.height
|
|
432
460
|
};
|
|
433
461
|
}
|
|
462
|
+
} else if (poIs(child, "w:pict") || poIs(child, "w:object")) {
|
|
463
|
+
const content = parseVmlPicture(child);
|
|
464
|
+
if (content && content.kind === "image") {
|
|
465
|
+
const resource = ctx.resolveImage?.(content.imageId);
|
|
466
|
+
if (resource) inlineImage = {
|
|
467
|
+
resource,
|
|
468
|
+
width: content.width,
|
|
469
|
+
height: content.height
|
|
470
|
+
};
|
|
471
|
+
}
|
|
434
472
|
}
|
|
435
473
|
}
|
|
436
474
|
return {
|
|
@@ -6,8 +6,9 @@ import { resolveTableStyles } from "../core/style-cascade/table.js";
|
|
|
6
6
|
import "../core/style-cascade/index.js";
|
|
7
7
|
import { bytesInclude } from "../core/bytes.js";
|
|
8
8
|
import { DEFAULT_THEME_PALETTE, makeColorResolver } from "../core/drawingml/colors.js";
|
|
9
|
-
import { parseChart } from "../core/drawingml/chart-parser.js";
|
|
9
|
+
import { parseChart, withChartColorStyle } from "../core/drawingml/chart-parser.js";
|
|
10
10
|
import { parseTheme } from "../core/drawingml/theme-parser.js";
|
|
11
|
+
import { isOoxmlRel } from "../core/opc/relationship-types.js";
|
|
11
12
|
import { OpcPackage } from "../core/opc/package.js";
|
|
12
13
|
import { parseCoreProperties } from "../core/opc/core-properties.js";
|
|
13
14
|
import "../core/opc/index.js";
|
|
@@ -25,12 +26,7 @@ var NUMBERING_PART = "word/numbering.xml";
|
|
|
25
26
|
var SETTINGS_PART = "word/settings.xml";
|
|
26
27
|
var CORE_PROPS_PART = "docProps/core.xml";
|
|
27
28
|
var MAIN_DOCUMENT_PART = "word/document.xml";
|
|
28
|
-
var REL_IMAGE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
|
|
29
29
|
var REL_HYPERLINK = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink";
|
|
30
|
-
var REL_CHART = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart";
|
|
31
|
-
var REL_THEME = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme";
|
|
32
|
-
var REL_HEADER = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/header";
|
|
33
|
-
var REL_FOOTER = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer";
|
|
34
30
|
var THEME_PART = "word/theme/theme1.xml";
|
|
35
31
|
function readDocx(docx) {
|
|
36
32
|
const pkg = OpcPackage.open(docx);
|
|
@@ -132,7 +128,7 @@ function infoFromCore(core) {
|
|
|
132
128
|
function makeImageResolver(pkg, store, partName = MAIN_DOCUMENT_PART) {
|
|
133
129
|
const byRelId = /* @__PURE__ */ new Map();
|
|
134
130
|
for (const rel of pkg.getPartRelationships(partName)) {
|
|
135
|
-
if (rel.type
|
|
131
|
+
if (!isOoxmlRel(rel.type, "image")) continue;
|
|
136
132
|
const resolved = pkg.resolveRelatedPart(partName, rel);
|
|
137
133
|
if (resolved) byRelId.set(rel.id, resolved.data);
|
|
138
134
|
}
|
|
@@ -157,11 +153,11 @@ function makeHyperlinkResolver(pkg, partName = MAIN_DOCUMENT_PART) {
|
|
|
157
153
|
function loadCharts(pkg, resolveColor) {
|
|
158
154
|
const out = /* @__PURE__ */ new Map();
|
|
159
155
|
for (const rel of pkg.getPartRelationships(MAIN_DOCUMENT_PART)) {
|
|
160
|
-
if (rel.type
|
|
156
|
+
if (!isOoxmlRel(rel.type, "chart")) continue;
|
|
161
157
|
const resolved = pkg.resolveRelatedPart(MAIN_DOCUMENT_PART, rel);
|
|
162
158
|
if (!resolved) continue;
|
|
163
159
|
const chart = parseChart(resolved.data, resolveColor);
|
|
164
|
-
if (chart) out.set(rel.id, chart);
|
|
160
|
+
if (chart) out.set(rel.id, withChartColorStyle(chart, pkg, resolved.path, resolveColor));
|
|
165
161
|
}
|
|
166
162
|
return out;
|
|
167
163
|
}
|
|
@@ -174,7 +170,7 @@ function buildColorResolver(pkg) {
|
|
|
174
170
|
}
|
|
175
171
|
function loadTheme(pkg) {
|
|
176
172
|
for (const rel of pkg.getPartRelationships(MAIN_DOCUMENT_PART)) {
|
|
177
|
-
if (rel.type
|
|
173
|
+
if (!isOoxmlRel(rel.type, "theme")) continue;
|
|
178
174
|
const resolved = pkg.resolveRelatedPart(MAIN_DOCUMENT_PART, rel);
|
|
179
175
|
if (resolved) return resolved.data;
|
|
180
176
|
}
|
|
@@ -192,7 +188,7 @@ function loadHeadersFootersForSections(pkg, sections, ctx, store) {
|
|
|
192
188
|
const out = /* @__PURE__ */ new Map();
|
|
193
189
|
for (const rel of rels) {
|
|
194
190
|
if (!wanted.has(rel.id)) continue;
|
|
195
|
-
if (rel.type
|
|
191
|
+
if (!isOoxmlRel(rel.type, "header") && !isOoxmlRel(rel.type, "footer")) continue;
|
|
196
192
|
const resolved = pkg.resolveRelatedPart(MAIN_DOCUMENT_PART, rel);
|
|
197
193
|
if (!resolved) continue;
|
|
198
194
|
const hfCtx = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { fetchFontSet, resolveFamilyKey } from "../core/fonts/remote-fonts.js";
|
|
2
2
|
import { FontRegistry } from "../core/font/font-registry.js";
|
|
3
3
|
import { signPdf } from "../pdf/signature.js";
|
|
4
|
-
import { renderStyledPdf } from "../pdf/styled-page-renderer.js";
|
|
4
|
+
import { renderStyledPdf, renderStyledPdfEncrypted } from "../pdf/styled-page-renderer.js";
|
|
5
5
|
import "../pdf/index.js";
|
|
6
6
|
import { OpcPackage } from "../core/opc/package.js";
|
|
7
7
|
import "../core/opc/index.js";
|
|
@@ -16,6 +16,10 @@ var MAIN_DOCUMENT_PART = "word/document.xml";
|
|
|
16
16
|
* Internal since 1.0 — see the async variant above.
|
|
17
17
|
*/
|
|
18
18
|
function convertDocxToPdfSync(docx, options) {
|
|
19
|
+
const args = prepareDocxStyledRender(docx, options);
|
|
20
|
+
return renderStyledPdf(args.body, args.styled);
|
|
21
|
+
}
|
|
22
|
+
function prepareDocxStyledRender(docx, options) {
|
|
19
23
|
const fonts = options.fonts ?? (options.fontBytes ? { regular: options.fontBytes } : void 0);
|
|
20
24
|
if (!fonts) throw new Error("convertDocxToPdfSync requires options.fonts or options.fontBytes");
|
|
21
25
|
const registry = FontRegistry.fromBytes(fonts);
|
|
@@ -33,13 +37,16 @@ function convertDocxToPdfSync(docx, options) {
|
|
|
33
37
|
relationship: "Source",
|
|
34
38
|
description: "Source Word document"
|
|
35
39
|
});
|
|
36
|
-
return
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
return {
|
|
41
|
+
body: flow.body,
|
|
42
|
+
styled: {
|
|
43
|
+
registry,
|
|
44
|
+
...flowRenderOptions(flow),
|
|
45
|
+
...info ? { info } : {},
|
|
46
|
+
...attachments.length > 0 ? { attachments } : {},
|
|
47
|
+
...renderOptions
|
|
48
|
+
}
|
|
49
|
+
};
|
|
43
50
|
}
|
|
44
51
|
function detectDocxFamilyKeys(docx) {
|
|
45
52
|
const keys = new Set(["roboto"]);
|
|
@@ -76,12 +83,15 @@ async function convertDocxToPdf(docx, options = {}) {
|
|
|
76
83
|
return signature ? signPdf(pdf, signature) : pdf;
|
|
77
84
|
}
|
|
78
85
|
async function buildUnsignedDocxPdf(docx, options) {
|
|
79
|
-
|
|
80
|
-
const auto = await resolveDocxAutoFonts(docx, options);
|
|
81
|
-
return convertDocxToPdfSync(docx, {
|
|
86
|
+
const withFonts = options.fonts ?? options.fontBytes ? options : {
|
|
82
87
|
...options,
|
|
83
|
-
...
|
|
84
|
-
}
|
|
88
|
+
...await resolveDocxAutoFonts(docx, options)
|
|
89
|
+
};
|
|
90
|
+
if (withFonts.encrypt) {
|
|
91
|
+
const args = prepareDocxStyledRender(docx, withFonts);
|
|
92
|
+
return renderStyledPdfEncrypted(args.body, args.styled);
|
|
93
|
+
}
|
|
94
|
+
return convertDocxToPdfSync(docx, withFonts);
|
|
85
95
|
}
|
|
86
96
|
async function resolveDocxAutoFonts(docx, options = {}) {
|
|
87
97
|
const fetchOpt = options.fontFetch ? { fetch: options.fontFetch } : {};
|