reamkit 1.18.0 → 1.20.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/dist/esm/core/converter/ream.d.ts +4 -2
- package/dist/esm/core/converter/ream.js +8 -14
- package/dist/esm/core/crypto/offcrypto.d.ts +16 -0
- package/dist/esm/core/crypto/offcrypto.js +222 -0
- package/dist/esm/{pdf-reader/crypto.d.ts → core/crypto/primitives.d.ts} +10 -0
- package/dist/esm/{pdf-reader/crypto.js → core/crypto/primitives.js} +176 -45
- package/dist/esm/core/document-model/types.d.ts +6 -0
- package/dist/esm/core/drawingml/colors.js +13 -6
- package/dist/esm/core/po-helpers.d.ts +9 -0
- package/dist/esm/core/po-helpers.js +9 -1
- package/dist/esm/layout/styled-layout.js +163 -27
- package/dist/esm/pdf-reader/decrypt.js +1 -1
- package/dist/esm/word/drawing-parser.js +5 -2
- package/dist/esm/word/paragraph-properties.js +2 -2
- package/dist/esm/word/run-properties.js +2 -2
- package/dist/esm/word/xml-helpers.d.ts +12 -0
- package/dist/esm/word/xml-helpers.js +23 -3
- package/package.json +1 -1
|
@@ -14,8 +14,10 @@ export interface ReamParseOptions {
|
|
|
14
14
|
/** Reader registry override; defaults to the built-in docx + xlsx readers. */
|
|
15
15
|
readonly readers?: ReadonlyArray<DocumentReader<SourceDoc>>;
|
|
16
16
|
/**
|
|
17
|
-
* The
|
|
18
|
-
*
|
|
17
|
+
* The password an encrypted source is opened with: a PDF's user password
|
|
18
|
+
* (ISO 32000 §7.6) or an OOXML package's (ECMA-376 §2.3, MS-OFFCRYPTO).
|
|
19
|
+
* Defaults to the empty string, which opens a PDF's common permissions-only
|
|
20
|
+
* encryption (EP14); an encrypted OOXML package always names a password.
|
|
19
21
|
*/
|
|
20
22
|
readonly password?: string;
|
|
21
23
|
}
|
|
@@ -13,8 +13,8 @@ import { writeXlsx } from "../../excel/xlsx-writer.js";
|
|
|
13
13
|
import { writeHtml } from "../../html/html-writer.js";
|
|
14
14
|
import { writeSvg } from "../../svg/svg-writer.js";
|
|
15
15
|
import { resolveDocxAutoFonts } from "../../word/docx-to-pdf.js";
|
|
16
|
-
import { isCfb, openCfb } from "../ole/cfb.js";
|
|
17
16
|
import { DEFAULT_READERS, resolveFontsViaChain, toFlowDoc } from "./facade.js";
|
|
17
|
+
import { decryptPackage, isEncryptedPackage } from "../crypto/offcrypto.js";
|
|
18
18
|
//#region src/core/converter/ream.ts
|
|
19
19
|
/**
|
|
20
20
|
* The point size Excel's column-width unit is quoted at — its default theme
|
|
@@ -77,12 +77,14 @@ var Ream = class Ream {
|
|
|
77
77
|
*/
|
|
78
78
|
static parse(bytes, options = {}) {
|
|
79
79
|
const readers = options.readers ?? DEFAULT_READERS;
|
|
80
|
-
|
|
81
|
-
if (
|
|
82
|
-
if (
|
|
83
|
-
|
|
80
|
+
let source = bytes;
|
|
81
|
+
if (isEncryptedPackage(bytes)) {
|
|
82
|
+
if (options.password === void 0) throw new Error("Password-protected OOXML document (ECMA-376 §2.3 EncryptedPackage) — pass the password as `password`");
|
|
83
|
+
source = decryptPackage(bytes, options.password);
|
|
84
84
|
}
|
|
85
|
-
const
|
|
85
|
+
const reader = readers.find((r) => r.sniff(source));
|
|
86
|
+
if (!reader) throw new Error(`Unrecognized document format (readers: ${readers.map((r) => r.id).join(", ")})`);
|
|
87
|
+
const { doc, losses } = reader.read(source, { password: options.password });
|
|
86
88
|
const sheet = doc.kind === "sheet" ? doc : void 0;
|
|
87
89
|
return new Ream(toFlowDoc(doc), sheet, losses, bytes, reader.id);
|
|
88
90
|
}
|
|
@@ -232,13 +234,5 @@ var Ream = class Ream {
|
|
|
232
234
|
if (options.strict && losses.length > 0) throw new ConversionLossError(losses[0]);
|
|
233
235
|
}
|
|
234
236
|
};
|
|
235
|
-
function isEncryptedOoxml(bytes) {
|
|
236
|
-
if (!isCfb(bytes)) return false;
|
|
237
|
-
try {
|
|
238
|
-
return openCfb(bytes).hasStream("EncryptedPackage");
|
|
239
|
-
} catch {
|
|
240
|
-
return false;
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
237
|
//#endregion
|
|
244
238
|
export { Ream };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Thrown when the bytes are encrypted and the password does not open them. */
|
|
2
|
+
export declare class WrongPasswordError extends Error {
|
|
3
|
+
constructor(message?: string);
|
|
4
|
+
}
|
|
5
|
+
/** Whether the bytes are an OOXML package encrypted per ECMA-376 §2.3. */
|
|
6
|
+
export declare function isEncryptedPackage(bytes: Uint8Array): boolean;
|
|
7
|
+
/**
|
|
8
|
+
* Decrypt an encrypted OOXML package into the plain OPC zip inside it.
|
|
9
|
+
*
|
|
10
|
+
* @param bytes The compound file (`EncryptionInfo` + `EncryptedPackage`).
|
|
11
|
+
* @param password The password to open it with.
|
|
12
|
+
* @returns The decrypted package bytes — an ordinary `.docx`/`.xlsx` zip.
|
|
13
|
+
* @throws WrongPasswordError when the password fails the document's own
|
|
14
|
+
* verifier, and Error when the encryption is one this reader does not know.
|
|
15
|
+
*/
|
|
16
|
+
export declare function decryptPackage(bytes: Uint8Array, password: string): Uint8Array;
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { openCfb } from "../ole/cfb.js";
|
|
2
|
+
import { aesCbcDecrypt, aesEcbDecrypt, sha1, sha256, sha384, sha512 } from "./primitives.js";
|
|
3
|
+
//#region src/core/crypto/offcrypto.ts
|
|
4
|
+
/** Thrown when the bytes are encrypted and the password does not open them. */
|
|
5
|
+
var WrongPasswordError = class extends Error {
|
|
6
|
+
constructor(message = "Wrong password for this document") {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = "WrongPasswordError";
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
/** Whether the bytes are an OOXML package encrypted per ECMA-376 §2.3. */
|
|
12
|
+
function isEncryptedPackage(bytes) {
|
|
13
|
+
try {
|
|
14
|
+
const cfb = openCfb(bytes);
|
|
15
|
+
return cfb.hasStream("EncryptedPackage") && cfb.hasStream("EncryptionInfo");
|
|
16
|
+
} catch {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Decrypt an encrypted OOXML package into the plain OPC zip inside it.
|
|
22
|
+
*
|
|
23
|
+
* @param bytes The compound file (`EncryptionInfo` + `EncryptedPackage`).
|
|
24
|
+
* @param password The password to open it with.
|
|
25
|
+
* @returns The decrypted package bytes — an ordinary `.docx`/`.xlsx` zip.
|
|
26
|
+
* @throws WrongPasswordError when the password fails the document's own
|
|
27
|
+
* verifier, and Error when the encryption is one this reader does not know.
|
|
28
|
+
*/
|
|
29
|
+
function decryptPackage(bytes, password) {
|
|
30
|
+
const cfb = openCfb(bytes);
|
|
31
|
+
const info = cfb.readStream("EncryptionInfo");
|
|
32
|
+
const payload = cfb.readStream("EncryptedPackage");
|
|
33
|
+
if (!info || !payload) throw new Error("Not an encrypted OOXML package");
|
|
34
|
+
const v = new DataView(info.buffer, info.byteOffset, info.byteLength);
|
|
35
|
+
const major = v.getUint16(0, true);
|
|
36
|
+
const minor = v.getUint16(2, true);
|
|
37
|
+
if (major === 4 && minor === 4) return agile(info, payload, password);
|
|
38
|
+
if ((major === 3 || major === 4) && minor === 2) return standard(info, payload, password);
|
|
39
|
+
throw new Error(`Unsupported OOXML encryption (version ${String(major)}.${String(minor)})`);
|
|
40
|
+
}
|
|
41
|
+
function packageSize(payload) {
|
|
42
|
+
const v = new DataView(payload.buffer, payload.byteOffset, payload.byteLength);
|
|
43
|
+
const lo = v.getUint32(0, true);
|
|
44
|
+
return v.getUint32(4, true) * 4294967296 + lo;
|
|
45
|
+
}
|
|
46
|
+
function standard(info, payload, password) {
|
|
47
|
+
const v = new DataView(info.buffer, info.byteOffset, info.byteLength);
|
|
48
|
+
const headerSize = v.getUint32(8, true);
|
|
49
|
+
const algId = v.getUint32(20, true);
|
|
50
|
+
const keyBits = v.getUint32(28, true);
|
|
51
|
+
let at = 12 + headerSize;
|
|
52
|
+
const saltSize = v.getUint32(at, true);
|
|
53
|
+
at += 4;
|
|
54
|
+
const salt = info.subarray(at, at + saltSize);
|
|
55
|
+
at += saltSize;
|
|
56
|
+
const encryptedVerifier = info.subarray(at, at + 16);
|
|
57
|
+
at += 16;
|
|
58
|
+
const verifierHashSize = v.getUint32(at, true);
|
|
59
|
+
at += 4;
|
|
60
|
+
const encryptedVerifierHash = info.subarray(at, at + (verifierHashSize <= 20 ? 32 : 32));
|
|
61
|
+
if (algId !== 0 && (algId < 26126 || algId > 26128)) throw new Error(`Unsupported OOXML cipher (algId 0x${algId.toString(16)})`);
|
|
62
|
+
const key = standardKey(password, salt, Math.max(16, Math.floor((keyBits || 128) / 8)));
|
|
63
|
+
const verifier = aesEcbDecrypt(key, encryptedVerifier);
|
|
64
|
+
const expected = aesEcbDecrypt(key, encryptedVerifierHash).subarray(0, 20);
|
|
65
|
+
if (!sameBytes(sha1(verifier), expected)) throw new WrongPasswordError();
|
|
66
|
+
const plain = aesEcbDecrypt(key, payload.subarray(8));
|
|
67
|
+
return plain.subarray(0, Math.min(packageSize(payload), plain.length));
|
|
68
|
+
}
|
|
69
|
+
function standardKey(password, salt, keyLength) {
|
|
70
|
+
let h = sha1(concat(salt, utf16le(password)));
|
|
71
|
+
const counter = new Uint8Array(4);
|
|
72
|
+
for (let i = 0; i < 5e4; i++) {
|
|
73
|
+
new DataView(counter.buffer).setUint32(0, i, true);
|
|
74
|
+
h = sha1(concat(counter, h));
|
|
75
|
+
}
|
|
76
|
+
h = sha1(concat(h, new Uint8Array(4)));
|
|
77
|
+
return concat(sha1(padded(h, 54)), sha1(padded(h, 92))).subarray(0, keyLength);
|
|
78
|
+
}
|
|
79
|
+
function padded(h, byte) {
|
|
80
|
+
const buf = new Uint8Array(64).fill(byte);
|
|
81
|
+
for (let i = 0; i < h.length && i < 64; i++) buf[i] = buf[i] ^ h[i];
|
|
82
|
+
return buf;
|
|
83
|
+
}
|
|
84
|
+
var BLOCK_VERIFIER_INPUT = Uint8Array.of(254, 167, 210, 118, 59, 75, 158, 121);
|
|
85
|
+
var BLOCK_VERIFIER_VALUE = Uint8Array.of(215, 170, 15, 109, 48, 97, 52, 78);
|
|
86
|
+
var BLOCK_KEY_VALUE = Uint8Array.of(20, 110, 11, 231, 171, 172, 208, 214);
|
|
87
|
+
var SEGMENT = 4096;
|
|
88
|
+
function agile(info, payload, password) {
|
|
89
|
+
const xml = new TextDecoder().decode(info.subarray(8));
|
|
90
|
+
const keyData = agileKeyData(attrsOf(xml, "keyData"));
|
|
91
|
+
const encAttrs = passwordKeyEncryptor(xml);
|
|
92
|
+
const enc = agileKeyData(encAttrs);
|
|
93
|
+
const spinCount = Number(encAttrs["spinCount"] ?? "0");
|
|
94
|
+
const encryptedKeyValue = b64(encAttrs["encryptedKeyValue"] ?? "");
|
|
95
|
+
const encryptedVerifierHashInput = b64(encAttrs["encryptedVerifierHashInput"] ?? "");
|
|
96
|
+
const encryptedVerifierHashValue = b64(encAttrs["encryptedVerifierHashValue"] ?? "");
|
|
97
|
+
const spun = agileSpin(password, enc, spinCount);
|
|
98
|
+
const keyFor = (block) => agileBlockKey(spun, enc, block);
|
|
99
|
+
const verifier = decryptAgile(keyFor(BLOCK_VERIFIER_INPUT), enc.saltValue, encryptedVerifierHashInput, enc.cbc);
|
|
100
|
+
const expected = decryptAgile(keyFor(BLOCK_VERIFIER_VALUE), enc.saltValue, encryptedVerifierHashValue, enc.cbc);
|
|
101
|
+
const actual = enc.hash(verifier.subarray(0, enc.saltValue.length));
|
|
102
|
+
if (!sameBytes(actual, expected.subarray(0, actual.length))) throw new WrongPasswordError();
|
|
103
|
+
const packageKey = decryptAgile(keyFor(BLOCK_KEY_VALUE), enc.saltValue, encryptedKeyValue, enc.cbc).subarray(0, keyData.keyBits / 8);
|
|
104
|
+
const cipher = payload.subarray(8);
|
|
105
|
+
const out = new Uint8Array(cipher.length);
|
|
106
|
+
const counter = new Uint8Array(4);
|
|
107
|
+
for (let i = 0; i * SEGMENT < cipher.length; i++) {
|
|
108
|
+
new DataView(counter.buffer).setUint32(0, i, true);
|
|
109
|
+
const iv = keyData.hash(concat(keyData.saltValue, counter)).subarray(0, keyData.blockSize || 16);
|
|
110
|
+
const block = cipher.subarray(i * SEGMENT, Math.min((i + 1) * SEGMENT, cipher.length));
|
|
111
|
+
out.set(decryptAgile(packageKey, iv, block, keyData.cbc), i * SEGMENT);
|
|
112
|
+
}
|
|
113
|
+
return out.subarray(0, Math.min(packageSize(payload), out.length));
|
|
114
|
+
}
|
|
115
|
+
function agileSpin(password, enc, spinCount) {
|
|
116
|
+
let h = enc.hash(concat(enc.saltValue, utf16le(password)));
|
|
117
|
+
const counter = new Uint8Array(4);
|
|
118
|
+
for (let i = 0; i < spinCount; i++) {
|
|
119
|
+
new DataView(counter.buffer).setUint32(0, i, true);
|
|
120
|
+
h = enc.hash(concat(counter, h));
|
|
121
|
+
}
|
|
122
|
+
return h;
|
|
123
|
+
}
|
|
124
|
+
function agileBlockKey(spun, enc, block) {
|
|
125
|
+
const keyLength = enc.keyBits / 8;
|
|
126
|
+
const derived = enc.hash(concat(spun, block));
|
|
127
|
+
if (derived.length >= keyLength) return derived.subarray(0, keyLength);
|
|
128
|
+
const out = new Uint8Array(keyLength).fill(54);
|
|
129
|
+
out.set(derived);
|
|
130
|
+
return out;
|
|
131
|
+
}
|
|
132
|
+
function decryptAgile(key, iv, data, cbc) {
|
|
133
|
+
if (!cbc) return aesEcbDecrypt(key, data);
|
|
134
|
+
return aesCbcDecrypt(key, iv.length >= 16 ? iv.subarray(0, 16) : concat(iv, new Uint8Array(16 - iv.length)), data, false);
|
|
135
|
+
}
|
|
136
|
+
var HASHES = new Map([
|
|
137
|
+
["sha1", sha1],
|
|
138
|
+
["sha-1", sha1],
|
|
139
|
+
["sha256", sha256],
|
|
140
|
+
["sha-256", sha256],
|
|
141
|
+
["sha384", sha384],
|
|
142
|
+
["sha-384", sha384],
|
|
143
|
+
["sha512", sha512],
|
|
144
|
+
["sha-512", sha512]
|
|
145
|
+
]);
|
|
146
|
+
function agileKeyData(attrs) {
|
|
147
|
+
const name = (attrs["hashAlgorithm"] ?? "sha512").toLowerCase();
|
|
148
|
+
const hash = HASHES.get(name);
|
|
149
|
+
if (!hash) throw new Error(`Unsupported OOXML hash (${name})`);
|
|
150
|
+
const cipher = (attrs["cipherAlgorithm"] ?? "AES").toUpperCase();
|
|
151
|
+
if (!cipher.startsWith("AES")) throw new Error(`Unsupported OOXML cipher (${cipher})`);
|
|
152
|
+
return {
|
|
153
|
+
saltValue: b64(attrs["saltValue"] ?? ""),
|
|
154
|
+
blockSize: Number(attrs["blockSize"] ?? "16"),
|
|
155
|
+
keyBits: Number(attrs["keyBits"] ?? "256"),
|
|
156
|
+
hash,
|
|
157
|
+
cbc: (attrs["cipherChaining"] ?? "ChainingModeCBC") !== "ChainingModeECB"
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function passwordKeyEncryptor(xml) {
|
|
161
|
+
const re = /<[A-Za-z0-9]*:?encryptedKey\b([^>]*)\/?>/gu;
|
|
162
|
+
let m = re.exec(xml);
|
|
163
|
+
while (m) {
|
|
164
|
+
const attrs = parseAttrs(m[1] ?? "");
|
|
165
|
+
if (attrs["spinCount"] !== void 0) return attrs;
|
|
166
|
+
m = re.exec(xml);
|
|
167
|
+
}
|
|
168
|
+
throw new Error("No password key encryptor in the encryption descriptor");
|
|
169
|
+
}
|
|
170
|
+
function attrsOf(xml, tag) {
|
|
171
|
+
const m = new RegExp(`<[A-Za-z0-9]*:?${tag}\\b([^>]*)/?>`, "u").exec(xml);
|
|
172
|
+
return m ? parseAttrs(m[1] ?? "") : {};
|
|
173
|
+
}
|
|
174
|
+
function parseAttrs(raw) {
|
|
175
|
+
const out = {};
|
|
176
|
+
const re = /([A-Za-z0-9_:]+)\s*=\s*"([^"]*)"/gu;
|
|
177
|
+
let m = re.exec(raw);
|
|
178
|
+
while (m) {
|
|
179
|
+
out[m[1].replace(/^.*:/u, "")] = m[2];
|
|
180
|
+
m = re.exec(raw);
|
|
181
|
+
}
|
|
182
|
+
return out;
|
|
183
|
+
}
|
|
184
|
+
function utf16le(s) {
|
|
185
|
+
const out = new Uint8Array(s.length * 2);
|
|
186
|
+
const v = new DataView(out.buffer);
|
|
187
|
+
for (let i = 0; i < s.length; i++) v.setUint16(i * 2, s.charCodeAt(i), true);
|
|
188
|
+
return out;
|
|
189
|
+
}
|
|
190
|
+
function concat(a, b) {
|
|
191
|
+
const out = new Uint8Array(a.length + b.length);
|
|
192
|
+
out.set(a);
|
|
193
|
+
out.set(b, a.length);
|
|
194
|
+
return out;
|
|
195
|
+
}
|
|
196
|
+
function sameBytes(a, b) {
|
|
197
|
+
if (a.length !== b.length) return false;
|
|
198
|
+
let diff = 0;
|
|
199
|
+
for (let i = 0; i < a.length; i++) diff |= a[i] ^ b[i];
|
|
200
|
+
return diff === 0;
|
|
201
|
+
}
|
|
202
|
+
var B64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
203
|
+
function b64(s) {
|
|
204
|
+
const clean = s.replace(/[^A-Za-z0-9+/]/gu, "");
|
|
205
|
+
const out = new Uint8Array(Math.floor(clean.length * 3 / 4));
|
|
206
|
+
let acc = 0;
|
|
207
|
+
let bits = 0;
|
|
208
|
+
let at = 0;
|
|
209
|
+
for (const ch of clean) {
|
|
210
|
+
const v = B64.indexOf(ch);
|
|
211
|
+
if (v < 0) continue;
|
|
212
|
+
acc = acc << 6 | v;
|
|
213
|
+
bits += 6;
|
|
214
|
+
if (bits >= 8) {
|
|
215
|
+
bits -= 8;
|
|
216
|
+
out[at++] = acc >> bits & 255;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return out.subarray(0, at);
|
|
220
|
+
}
|
|
221
|
+
//#endregion
|
|
222
|
+
export { decryptPackage, isEncryptedPackage };
|
|
@@ -5,8 +5,18 @@
|
|
|
5
5
|
export declare function rc4(key: Uint8Array, data: Uint8Array): Uint8Array;
|
|
6
6
|
/** MD5 digest (RFC 1321) → 16 bytes; the key-derivation hash for V1/V2/V4. */
|
|
7
7
|
export declare function md5(input: Uint8Array): Uint8Array;
|
|
8
|
+
/**
|
|
9
|
+
* SHA-1. Used by MS-OFFCRYPTO's standard encryption for its key derivation and
|
|
10
|
+
* password verifier, and by agile encryption when the descriptor names it.
|
|
11
|
+
*/
|
|
12
|
+
export declare function sha1(input: Uint8Array): Uint8Array;
|
|
8
13
|
export declare function sha256(input: Uint8Array): Uint8Array;
|
|
9
14
|
export declare function sha512(input: Uint8Array): Uint8Array;
|
|
10
15
|
export declare function sha384(input: Uint8Array): Uint8Array;
|
|
11
16
|
export declare function aesCbcDecrypt(key: Uint8Array, iv: Uint8Array, data: Uint8Array, stripPad: boolean): Uint8Array;
|
|
17
|
+
/**
|
|
18
|
+
* AES-ECB decrypt — every block on its own key, no chaining. MS-OFFCRYPTO's
|
|
19
|
+
* standard encryption uses it for both the verifier and the package.
|
|
20
|
+
*/
|
|
21
|
+
export declare function aesEcbDecrypt(key: Uint8Array, data: Uint8Array): Uint8Array;
|
|
12
22
|
export declare function aesCbcEncrypt(key: Uint8Array, iv: Uint8Array, data: Uint8Array): Uint8Array;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region src/
|
|
1
|
+
//#region src/core/crypto/primitives.ts
|
|
2
2
|
/**
|
|
3
3
|
* RC4 stream cipher (§7.6.2). Symmetric, so the same call both encrypts and
|
|
4
4
|
* decrypts; used by the legacy V1/V2/V4 Standard security handlers.
|
|
@@ -217,6 +217,58 @@ var SHA256_K = [
|
|
|
217
217
|
3204031479,
|
|
218
218
|
3329325298
|
|
219
219
|
];
|
|
220
|
+
/**
|
|
221
|
+
* SHA-1. Used by MS-OFFCRYPTO's standard encryption for its key derivation and
|
|
222
|
+
* password verifier, and by agile encryption when the descriptor names it.
|
|
223
|
+
*/
|
|
224
|
+
function sha1(input) {
|
|
225
|
+
const ml = input.length * 8;
|
|
226
|
+
const withPad = new Uint8Array((input.length + 8 >> 6 << 6) + 64);
|
|
227
|
+
withPad.set(input);
|
|
228
|
+
withPad[input.length] = 128;
|
|
229
|
+
const view = new DataView(withPad.buffer);
|
|
230
|
+
view.setUint32(withPad.length - 4, ml >>> 0, false);
|
|
231
|
+
view.setUint32(withPad.length - 8, Math.floor(ml / 4294967296), false);
|
|
232
|
+
let h0 = 1732584193;
|
|
233
|
+
let h1 = 4023233417;
|
|
234
|
+
let h2 = 2562383102;
|
|
235
|
+
let h3 = 271733878;
|
|
236
|
+
let h4 = 3285377520;
|
|
237
|
+
const w = new Uint32Array(80);
|
|
238
|
+
for (let off = 0; off < withPad.length; off += 64) {
|
|
239
|
+
for (let i = 0; i < 16; i++) w[i] = view.getUint32(off + i * 4, false);
|
|
240
|
+
for (let i = 16; i < 80; i++) {
|
|
241
|
+
const x = w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16];
|
|
242
|
+
w[i] = x << 1 | x >>> 31;
|
|
243
|
+
}
|
|
244
|
+
let a = h0;
|
|
245
|
+
let b = h1;
|
|
246
|
+
let c = h2;
|
|
247
|
+
let d = h3;
|
|
248
|
+
let e = h4;
|
|
249
|
+
for (let i = 0; i < 80; i++) {
|
|
250
|
+
const [f, k] = i < 20 ? [b & c | ~b & d, 1518500249] : i < 40 ? [b ^ c ^ d, 1859775393] : i < 60 ? [b & c | b & d | c & d, 2400959708] : [b ^ c ^ d, 3395469782];
|
|
251
|
+
const t = (a << 5 | a >>> 27) + f + e + k + w[i] >>> 0;
|
|
252
|
+
e = d;
|
|
253
|
+
d = c;
|
|
254
|
+
c = (b << 30 | b >>> 2) >>> 0;
|
|
255
|
+
b = a;
|
|
256
|
+
a = t;
|
|
257
|
+
}
|
|
258
|
+
h0 = h0 + a >>> 0;
|
|
259
|
+
h1 = h1 + b >>> 0;
|
|
260
|
+
h2 = h2 + c >>> 0;
|
|
261
|
+
h3 = h3 + d >>> 0;
|
|
262
|
+
h4 = h4 + e >>> 0;
|
|
263
|
+
}
|
|
264
|
+
const out = new Uint8Array(20);
|
|
265
|
+
new DataView(out.buffer).setUint32(0, h0, false);
|
|
266
|
+
new DataView(out.buffer).setUint32(4, h1, false);
|
|
267
|
+
new DataView(out.buffer).setUint32(8, h2, false);
|
|
268
|
+
new DataView(out.buffer).setUint32(12, h3, false);
|
|
269
|
+
new DataView(out.buffer).setUint32(16, h4, false);
|
|
270
|
+
return out;
|
|
271
|
+
}
|
|
220
272
|
function sha256(input) {
|
|
221
273
|
const h = [
|
|
222
274
|
1779033703,
|
|
@@ -275,7 +327,6 @@ function sha256(input) {
|
|
|
275
327
|
for (let i = 0; i < 8; i++) new DataView(out.buffer).setUint32(i * 4, h[i] >>> 0);
|
|
276
328
|
return out;
|
|
277
329
|
}
|
|
278
|
-
var MASK64 = (1n << 64n) - 1n;
|
|
279
330
|
var SHA512_K = [
|
|
280
331
|
4794697086780616226n,
|
|
281
332
|
8158064640168781261n,
|
|
@@ -358,62 +409,130 @@ var SHA512_K = [
|
|
|
358
409
|
6902733635092675308n,
|
|
359
410
|
7801388544844847127n
|
|
360
411
|
];
|
|
412
|
+
var SHA512_KH = new Int32Array(80);
|
|
413
|
+
var SHA512_KL = new Int32Array(80);
|
|
414
|
+
for (let i = 0; i < 80; i++) {
|
|
415
|
+
SHA512_KH[i] = Number(SHA512_K[i] >> 32n & 4294967295n) | 0;
|
|
416
|
+
SHA512_KL[i] = Number(SHA512_K[i] & 4294967295n) | 0;
|
|
417
|
+
}
|
|
361
418
|
function sha512Core(iv, input, outLen) {
|
|
362
|
-
const
|
|
363
|
-
const
|
|
419
|
+
const hh = new Int32Array(8);
|
|
420
|
+
const hl = new Int32Array(8);
|
|
421
|
+
for (let i = 0; i < 8; i++) {
|
|
422
|
+
hh[i] = Number(iv[i] >> 32n & 4294967295n) | 0;
|
|
423
|
+
hl[i] = Number(iv[i] & 4294967295n) | 0;
|
|
424
|
+
}
|
|
364
425
|
const len = input.length;
|
|
365
426
|
const blocks = (len + 16 >> 7) + 1;
|
|
366
427
|
const msg = new Uint8Array(blocks * 128);
|
|
367
428
|
msg.set(input);
|
|
368
429
|
msg[len] = 128;
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
const
|
|
430
|
+
const view = new DataView(msg.buffer);
|
|
431
|
+
const bits = len * 8;
|
|
432
|
+
view.setUint32(msg.length - 4, bits >>> 0, false);
|
|
433
|
+
view.setUint32(msg.length - 8, Math.floor(bits / 4294967296), false);
|
|
434
|
+
const wh = new Int32Array(80);
|
|
435
|
+
const wl = new Int32Array(80);
|
|
375
436
|
for (let off = 0; off < msg.length; off += 128) {
|
|
376
437
|
for (let i = 0; i < 16; i++) {
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
w[i] = v;
|
|
438
|
+
wh[i] = view.getInt32(off + i * 8, false);
|
|
439
|
+
wl[i] = view.getInt32(off + i * 8 + 4, false);
|
|
380
440
|
}
|
|
381
441
|
for (let i = 16; i < 80; i++) {
|
|
382
|
-
const
|
|
383
|
-
const
|
|
384
|
-
|
|
442
|
+
const x15h = wh[i - 15];
|
|
443
|
+
const x15l = wl[i - 15];
|
|
444
|
+
const s0h = (x15h >>> 1 | x15l << 31) ^ (x15h >>> 8 | x15l << 24) ^ x15h >>> 7;
|
|
445
|
+
const s0l = (x15l >>> 1 | x15h << 31) ^ (x15l >>> 8 | x15h << 24) ^ (x15l >>> 7 | x15h << 25);
|
|
446
|
+
const x2h = wh[i - 2];
|
|
447
|
+
const x2l = wl[i - 2];
|
|
448
|
+
const s1h = (x2h >>> 19 | x2l << 13) ^ (x2l >>> 29 | x2h << 3) ^ x2h >>> 6;
|
|
449
|
+
const s1l = (x2l >>> 19 | x2h << 13) ^ (x2h >>> 29 | x2l << 3) ^ (x2l >>> 6 | x2h << 26);
|
|
450
|
+
let lo = (wl[i - 16] >>> 0) + (s0l >>> 0);
|
|
451
|
+
let hi = (wh[i - 16] >>> 0) + (s0h >>> 0) + (lo > 4294967295 ? 1 : 0);
|
|
452
|
+
lo = (lo >>> 0) + (wl[i - 7] >>> 0);
|
|
453
|
+
hi = hi + (wh[i - 7] >>> 0) + (lo > 4294967295 ? 1 : 0);
|
|
454
|
+
lo = (lo >>> 0) + (s1l >>> 0);
|
|
455
|
+
hi = hi + (s1h >>> 0) + (lo > 4294967295 ? 1 : 0);
|
|
456
|
+
wh[i] = hi | 0;
|
|
457
|
+
wl[i] = lo | 0;
|
|
385
458
|
}
|
|
386
|
-
let
|
|
459
|
+
let ah = hh[0];
|
|
460
|
+
let al = hl[0];
|
|
461
|
+
let bh = hh[1];
|
|
462
|
+
let bl = hl[1];
|
|
463
|
+
let ch = hh[2];
|
|
464
|
+
let cl = hl[2];
|
|
465
|
+
let dh = hh[3];
|
|
466
|
+
let dl = hl[3];
|
|
467
|
+
let eh = hh[4];
|
|
468
|
+
let el = hl[4];
|
|
469
|
+
let fh = hh[5];
|
|
470
|
+
let fl = hl[5];
|
|
471
|
+
let gh = hh[6];
|
|
472
|
+
let gl = hl[6];
|
|
473
|
+
let xh = hh[7];
|
|
474
|
+
let xl = hl[7];
|
|
387
475
|
for (let i = 0; i < 80; i++) {
|
|
388
|
-
const
|
|
389
|
-
const
|
|
390
|
-
const
|
|
391
|
-
const
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
476
|
+
const S1h = (eh >>> 14 | el << 18) ^ (eh >>> 18 | el << 14) ^ (el >>> 9 | eh << 23);
|
|
477
|
+
const S1l = (el >>> 14 | eh << 18) ^ (el >>> 18 | eh << 14) ^ (eh >>> 9 | el << 23);
|
|
478
|
+
const chh = eh & fh ^ ~eh & gh;
|
|
479
|
+
const chl = el & fl ^ ~el & gl;
|
|
480
|
+
const S0h = (ah >>> 28 | al << 4) ^ (al >>> 2 | ah << 30) ^ (al >>> 7 | ah << 25);
|
|
481
|
+
const S0l = (al >>> 28 | ah << 4) ^ (ah >>> 2 | al << 30) ^ (ah >>> 7 | al << 25);
|
|
482
|
+
const majh = ah & bh ^ ah & ch ^ bh & ch;
|
|
483
|
+
const majl = al & bl ^ al & cl ^ bl & cl;
|
|
484
|
+
let lo = (xl >>> 0) + (S1l >>> 0);
|
|
485
|
+
let hi = (xh >>> 0) + (S1h >>> 0) + (lo > 4294967295 ? 1 : 0);
|
|
486
|
+
lo = (lo >>> 0) + (chl >>> 0);
|
|
487
|
+
hi = hi + (chh >>> 0) + (lo > 4294967295 ? 1 : 0);
|
|
488
|
+
lo = (lo >>> 0) + (SHA512_KL[i] >>> 0);
|
|
489
|
+
hi = hi + (SHA512_KH[i] >>> 0) + (lo > 4294967295 ? 1 : 0);
|
|
490
|
+
lo = (lo >>> 0) + (wl[i] >>> 0);
|
|
491
|
+
hi = hi + (wh[i] >>> 0) + (lo > 4294967295 ? 1 : 0);
|
|
492
|
+
const t1h = hi | 0;
|
|
493
|
+
const t1l = lo | 0;
|
|
494
|
+
const lo2 = (S0l >>> 0) + (majl >>> 0);
|
|
495
|
+
const t2h = (S0h >>> 0) + (majh >>> 0) + (lo2 > 4294967295 ? 1 : 0) | 0;
|
|
496
|
+
const t2l = lo2 | 0;
|
|
497
|
+
xh = gh;
|
|
498
|
+
xl = gl;
|
|
499
|
+
gh = fh;
|
|
500
|
+
gl = fl;
|
|
501
|
+
fh = eh;
|
|
502
|
+
fl = el;
|
|
503
|
+
lo = (dl >>> 0) + (t1l >>> 0);
|
|
504
|
+
eh = (dh >>> 0) + (t1h >>> 0) + (lo > 4294967295 ? 1 : 0) | 0;
|
|
505
|
+
el = lo | 0;
|
|
506
|
+
dh = ch;
|
|
507
|
+
dl = cl;
|
|
508
|
+
ch = bh;
|
|
509
|
+
cl = bl;
|
|
510
|
+
bh = ah;
|
|
511
|
+
bl = al;
|
|
512
|
+
lo = (t1l >>> 0) + (t2l >>> 0);
|
|
513
|
+
ah = (t1h >>> 0) + (t2h >>> 0) + (lo > 4294967295 ? 1 : 0) | 0;
|
|
514
|
+
al = lo | 0;
|
|
400
515
|
}
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
516
|
+
const add = (i, addH, addL) => {
|
|
517
|
+
const lo = (hl[i] >>> 0) + (addL >>> 0);
|
|
518
|
+
hh[i] = (hh[i] >>> 0) + (addH >>> 0) + (lo > 4294967295 ? 1 : 0) | 0;
|
|
519
|
+
hl[i] = lo | 0;
|
|
520
|
+
};
|
|
521
|
+
add(0, ah, al);
|
|
522
|
+
add(1, bh, bl);
|
|
523
|
+
add(2, ch, cl);
|
|
524
|
+
add(3, dh, dl);
|
|
525
|
+
add(4, eh, el);
|
|
526
|
+
add(5, fh, fl);
|
|
527
|
+
add(6, gh, gl);
|
|
528
|
+
add(7, xh, xl);
|
|
529
|
+
}
|
|
530
|
+
const out = new Uint8Array(64);
|
|
531
|
+
const ov = new DataView(out.buffer);
|
|
532
|
+
for (let i = 0; i < 8; i++) {
|
|
533
|
+
ov.setInt32(i * 8, hh[i], false);
|
|
534
|
+
ov.setInt32(i * 8 + 4, hl[i], false);
|
|
409
535
|
}
|
|
410
|
-
const out = new Uint8Array(h.length * 8);
|
|
411
|
-
h.forEach((v, i) => {
|
|
412
|
-
for (let b = 7; b >= 0; b--) {
|
|
413
|
-
out[i * 8 + b] = Number(v & 255n);
|
|
414
|
-
v >>= 8n;
|
|
415
|
-
}
|
|
416
|
-
});
|
|
417
536
|
return out.slice(0, outLen);
|
|
418
537
|
}
|
|
419
538
|
function sha512(input) {
|
|
@@ -559,6 +678,18 @@ function aesCbcDecrypt(key, iv, data, stripPad) {
|
|
|
559
678
|
const pad = out[out.length - 1];
|
|
560
679
|
return pad >= 1 && pad <= 16 && pad <= out.length ? out.subarray(0, out.length - pad) : out;
|
|
561
680
|
}
|
|
681
|
+
/**
|
|
682
|
+
* AES-ECB decrypt — every block on its own key, no chaining. MS-OFFCRYPTO's
|
|
683
|
+
* standard encryption uses it for both the verifier and the package.
|
|
684
|
+
*/
|
|
685
|
+
function aesEcbDecrypt(key, data) {
|
|
686
|
+
const w = expandKey(key);
|
|
687
|
+
const nr = key.length / 4 + 6;
|
|
688
|
+
const blocks = Math.floor(data.length / 16);
|
|
689
|
+
const out = new Uint8Array(blocks * 16);
|
|
690
|
+
for (let i = 0; i < blocks; i++) decryptBlock(data.subarray(i * 16, i * 16 + 16), w, nr, out, i * 16);
|
|
691
|
+
return out;
|
|
692
|
+
}
|
|
562
693
|
function aesCbcEncrypt(key, iv, data) {
|
|
563
694
|
const w = expandKey(key);
|
|
564
695
|
const nr = key.length / 4 + 6;
|
|
@@ -611,4 +742,4 @@ function mixColumns(s) {
|
|
|
611
742
|
}
|
|
612
743
|
}
|
|
613
744
|
//#endregion
|
|
614
|
-
export { aesCbcDecrypt, aesCbcEncrypt, md5, rc4, sha256, sha384, sha512 };
|
|
745
|
+
export { aesCbcDecrypt, aesCbcEncrypt, aesEcbDecrypt, md5, rc4, sha1, sha256, sha384, sha512 };
|
|
@@ -992,6 +992,12 @@ export interface ShapeFill {
|
|
|
992
992
|
readonly kind: ShapeFillKind;
|
|
993
993
|
readonly colorHex?: string;
|
|
994
994
|
readonly gradient?: ShapeGradient;
|
|
995
|
+
/**
|
|
996
|
+
* §14.1.2.5 `@type="tile"` — the picture REPEATS at its own size rather than
|
|
997
|
+
* stretching over the box. A parchment behind a page, a texture behind a
|
|
998
|
+
* text box: stretched, such a fill is a blur where it should be a pattern.
|
|
999
|
+
*/
|
|
1000
|
+
readonly tiled?: boolean;
|
|
995
1001
|
/**
|
|
996
1002
|
* §20.1.8.14 `a:blipFill` — the picture painted across the shape's box. A
|
|
997
1003
|
* DrawingML picture IS a shape with one of these, which is how a `pic:pic`
|
|
@@ -69,6 +69,13 @@ function hslToRgb(h, s, l) {
|
|
|
69
69
|
b + m
|
|
70
70
|
];
|
|
71
71
|
}
|
|
72
|
+
function toLinear(c) {
|
|
73
|
+
return c <= .04045 ? c / 12.92 : Math.pow((c + .055) / 1.055, 2.4);
|
|
74
|
+
}
|
|
75
|
+
function toSrgb(c) {
|
|
76
|
+
const v = clamp01(c);
|
|
77
|
+
return v <= .0031308 ? v * 12.92 : 1.055 * Math.pow(v, 1 / 2.4) - .055;
|
|
78
|
+
}
|
|
72
79
|
/**
|
|
73
80
|
* Apply DrawingML colour transforms ({@link ColorMod}s) to a 6-hex value,
|
|
74
81
|
* returning a 6-hex value. `shade`/`tint` scale in RGB; `lumMod`/`lumOff` adjust
|
|
@@ -85,13 +92,13 @@ function applyColorMods(hex, mods) {
|
|
|
85
92
|
let g = (n >> 8 & 255) / 255;
|
|
86
93
|
let b = (n & 255) / 255;
|
|
87
94
|
for (const m of mods) if (m.kind === "shade") {
|
|
88
|
-
r
|
|
89
|
-
g
|
|
90
|
-
b
|
|
95
|
+
r = toSrgb(toLinear(r) * m.val);
|
|
96
|
+
g = toSrgb(toLinear(g) * m.val);
|
|
97
|
+
b = toSrgb(toLinear(b) * m.val);
|
|
91
98
|
} else if (m.kind === "tint") {
|
|
92
|
-
r = r * m.val + (1 - m.val);
|
|
93
|
-
g = g * m.val + (1 - m.val);
|
|
94
|
-
b = b * m.val + (1 - m.val);
|
|
99
|
+
r = toSrgb(toLinear(r) * m.val + (1 - m.val));
|
|
100
|
+
g = toSrgb(toLinear(g) * m.val + (1 - m.val));
|
|
101
|
+
b = toSrgb(toLinear(b) * m.val + (1 - m.val));
|
|
95
102
|
} else if (m.kind === "lumMod" || m.kind === "lumOff") {
|
|
96
103
|
const [h, s, l] = rgbToHsl(r, g, b);
|
|
97
104
|
const l2 = m.kind === "lumMod" ? l * m.val : clamp01(l + m.val);
|
|
@@ -34,6 +34,15 @@ export declare function poAttrLocal(node: PoNode | undefined, name: string): str
|
|
|
34
34
|
export declare function poIsLocal(node: PoNode | undefined, name: string): boolean;
|
|
35
35
|
/** {@link poAttr} parsed as a finite number, or undefined when absent/non-numeric. */
|
|
36
36
|
export declare function poIntAttr(node: PoNode | undefined, name: string): number | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* §22.9.2.15 ST_UniversalMeasure — a number with a UNIT (`"28.35pt"`, `"1cm"`)
|
|
39
|
+
* as twips. Producers write whole page geometries, tab stops and indents this
|
|
40
|
+
* way; read as a plain number they are nothing at all.
|
|
41
|
+
*
|
|
42
|
+
* @param raw The attribute value.
|
|
43
|
+
* @returns The measure in twips, or undefined when it names no unit.
|
|
44
|
+
*/
|
|
45
|
+
export declare function universalMeasureTwips(raw: string): number | undefined;
|
|
37
46
|
/** Shorthand for the `val` attribute (`@w:val`/…). */
|
|
38
47
|
export declare function poVal(node: PoNode | undefined): string | undefined;
|
|
39
48
|
/**
|
|
@@ -76,6 +76,14 @@ var MEASURE_TWIPS = new Map([
|
|
|
76
76
|
["cm", 1440 / 2.54],
|
|
77
77
|
["mm", 144 / 2.54]
|
|
78
78
|
]);
|
|
79
|
+
/**
|
|
80
|
+
* §22.9.2.15 ST_UniversalMeasure — a number with a UNIT (`"28.35pt"`, `"1cm"`)
|
|
81
|
+
* as twips. Producers write whole page geometries, tab stops and indents this
|
|
82
|
+
* way; read as a plain number they are nothing at all.
|
|
83
|
+
*
|
|
84
|
+
* @param raw The attribute value.
|
|
85
|
+
* @returns The measure in twips, or undefined when it names no unit.
|
|
86
|
+
*/
|
|
79
87
|
function universalMeasureTwips(raw) {
|
|
80
88
|
const m = /^\s*(-?[0-9]+(?:\.[0-9]+)?)(pt|pc|pi|in|cm|mm)\s*$/iu.exec(raw);
|
|
81
89
|
if (!m) return void 0;
|
|
@@ -134,4 +142,4 @@ function poFindDescendant(node, tag) {
|
|
|
134
142
|
}
|
|
135
143
|
}
|
|
136
144
|
//#endregion
|
|
137
|
-
export { poAttr, poAttrLocal, poChildren, poChildrenWith, poFindByPath, poFindDescendant, poFirstChild, poIntAttr, poIs, poIsLocal, poTag, poText, poToggle, poVal };
|
|
145
|
+
export { poAttr, poAttrLocal, poChildren, poChildrenWith, poFindByPath, poFindDescendant, poFirstChild, poIntAttr, poIs, poIsLocal, poTag, poText, poToggle, poVal, universalMeasureTwips };
|
|
@@ -432,7 +432,7 @@ function layoutStyledDocument(body, options) {
|
|
|
432
432
|
const commentNotes = options.commentAnnotations && options.comments ? buildCommentNotes(noteAssigned.comments, options.comments) : void 0;
|
|
433
433
|
const reflowParagraph = (paragraph, width, widths) => layoutParagraphBlock(paragraph, options, fontResources, imageResources, width, widths);
|
|
434
434
|
return {
|
|
435
|
-
pages: paginateSections(blocks, sectionCtxs, structBuilder, options.language ?? "en-US", notePlan, bookmarks, reflowParagraph, options
|
|
435
|
+
pages: paginateSections(blocks, sectionCtxs, structBuilder, options.language ?? "en-US", notePlan, bookmarks, reflowParagraph, backgroundImageOf(options, imageResources)),
|
|
436
436
|
resources: options.resources ?? new ResourceStore(),
|
|
437
437
|
fontResources,
|
|
438
438
|
imageResources,
|
|
@@ -609,7 +609,7 @@ function balancedColumnHeight(bandHeightPt, colCount, roomPt) {
|
|
|
609
609
|
/** Total laid-out height of a run of blocks, paragraph spacing included. */
|
|
610
610
|
function blocksHeight(blocks) {
|
|
611
611
|
return blocks.reduce((sum, b) => {
|
|
612
|
-
if (b.kind !== "paragraph" &&
|
|
612
|
+
if (b.kind !== "paragraph" && isOutOfFlowFloat(b.float)) return sum;
|
|
613
613
|
return sum + (b.kind === "paragraph" ? b.spacingBeforePt + b.heightPt + b.spacingAfterPt : b.heightPt);
|
|
614
614
|
}, 0);
|
|
615
615
|
}
|
|
@@ -1024,7 +1024,12 @@ function layoutShapeBlock(shape, options, fontResources, imageResources, content
|
|
|
1024
1024
|
heightPt = maxHeight;
|
|
1025
1025
|
}
|
|
1026
1026
|
const fillGradient = shape.fill.kind === "gradient" ? shape.fill.gradient : void 0;
|
|
1027
|
-
const
|
|
1027
|
+
const fillImage = shape.fill.kind === "picture" && shape.fill.imageResource !== void 0 ? imageResources?.get(shape.fill.imageResource) : void 0;
|
|
1028
|
+
const fillImageResourceName = fillImage?.resourceName;
|
|
1029
|
+
const fillImageTile = shape.fill.kind === "picture" && shape.fill.tiled === true && fillImage?.prepared ? {
|
|
1030
|
+
widthPt: fillImage.prepared.widthPx * 72 / 96,
|
|
1031
|
+
heightPt: fillImage.prepared.heightPx * 72 / 96
|
|
1032
|
+
} : void 0;
|
|
1028
1033
|
const fillColorHex = shape.fill.kind === "solid" ? shape.fill.colorHex : fillGradient ? gradientToSolid(fillGradient) : void 0;
|
|
1029
1034
|
const stroke = buildStroke(shape.line);
|
|
1030
1035
|
const t = shape.transform;
|
|
@@ -1178,6 +1183,7 @@ function layoutShapeBlock(shape, options, fontResources, imageResources, content
|
|
|
1178
1183
|
paths,
|
|
1179
1184
|
...markerPaths.length > 0 ? { markerPaths } : {},
|
|
1180
1185
|
...fillImageResourceName ? { fillImageResourceName } : {},
|
|
1186
|
+
...fillImageTile ? { fillImageTile } : {},
|
|
1181
1187
|
...fillImageResourceName && shape.fill.imageCrop ? { fillImageCrop: shape.fill.imageCrop } : {},
|
|
1182
1188
|
...fillColorHex ? { fillColorHex } : {},
|
|
1183
1189
|
...fillGradient ? { fillGradient } : {},
|
|
@@ -1612,6 +1618,21 @@ function makeChartLabelLine(text, font, sizePt, colorHex) {
|
|
|
1612
1618
|
isLastInParagraph: true
|
|
1613
1619
|
};
|
|
1614
1620
|
}
|
|
1621
|
+
/**
|
|
1622
|
+
* §17.2.1 — the page background's picture, with the size a TILE of it is drawn
|
|
1623
|
+
* at: the picture's own pixels read at 96 dpi, which is the resolution Office
|
|
1624
|
+
* measures a picture in.
|
|
1625
|
+
*/
|
|
1626
|
+
function backgroundImageOf(options, imageResources) {
|
|
1627
|
+
const id = options.pageBackgroundFill?.imageResource;
|
|
1628
|
+
const res = id === void 0 ? void 0 : imageResources.get(id);
|
|
1629
|
+
if (!res?.resourceName || !res.prepared) return void 0;
|
|
1630
|
+
return {
|
|
1631
|
+
name: res.resourceName,
|
|
1632
|
+
widthPt: res.prepared.widthPx * 72 / 96,
|
|
1633
|
+
heightPt: res.prepared.heightPx * 72 / 96
|
|
1634
|
+
};
|
|
1635
|
+
}
|
|
1615
1636
|
function collectImageResources(body, headersFooters, options) {
|
|
1616
1637
|
const out = /* @__PURE__ */ new Map();
|
|
1617
1638
|
if (!options.resources || options.resources.size === 0) return out;
|
|
@@ -1794,20 +1815,38 @@ function emitCellFloat(cf, ctx, frame) {
|
|
|
1794
1815
|
if (float.wrap !== "none") ctx.exclude(float, x, topYUp, cf.block.widthPt, cf.block.heightPt);
|
|
1795
1816
|
}
|
|
1796
1817
|
function emitShapeItems(sh, x, bottomYUp, sink, pageHeight, figId) {
|
|
1797
|
-
if (sh.fillImageResourceName)
|
|
1798
|
-
|
|
1799
|
-
x: pt(x),
|
|
1800
|
-
y: pt(pageHeight - (bottomYUp + sh.heightPt)),
|
|
1801
|
-
width: pt(sh.widthPt),
|
|
1802
|
-
height: pt(sh.heightPt),
|
|
1803
|
-
imageResourceName: sh.fillImageResourceName,
|
|
1804
|
-
...sh.fillImageCrop ? { crop: sh.fillImageCrop } : {},
|
|
1805
|
-
clip: {
|
|
1818
|
+
if (sh.fillImageResourceName) {
|
|
1819
|
+
const clip = {
|
|
1806
1820
|
paths: sh.paths,
|
|
1807
1821
|
transform: flipTransform(buildShapeTransform(x, bottomYUp, sh.widthPt, sh.heightPt, sh.rotation60k, sh.flipH, sh.flipV), pageHeight)
|
|
1808
|
-
}
|
|
1809
|
-
|
|
1810
|
-
|
|
1822
|
+
};
|
|
1823
|
+
const fig = figId !== void 0 ? { structId: figId } : {};
|
|
1824
|
+
const tile = sh.fillImageTile;
|
|
1825
|
+
if (tile) for (const item of PageAssembler.tileItems(sh.fillImageResourceName, tile, {
|
|
1826
|
+
x,
|
|
1827
|
+
y: pageHeight - (bottomYUp + sh.heightPt),
|
|
1828
|
+
width: sh.widthPt,
|
|
1829
|
+
height: sh.heightPt
|
|
1830
|
+
})) {
|
|
1831
|
+
if (item.type !== "image") continue;
|
|
1832
|
+
sink.push({
|
|
1833
|
+
...item,
|
|
1834
|
+
clip,
|
|
1835
|
+
...fig
|
|
1836
|
+
});
|
|
1837
|
+
}
|
|
1838
|
+
else sink.push({
|
|
1839
|
+
type: "image",
|
|
1840
|
+
x: pt(x),
|
|
1841
|
+
y: pt(pageHeight - (bottomYUp + sh.heightPt)),
|
|
1842
|
+
width: pt(sh.widthPt),
|
|
1843
|
+
height: pt(sh.heightPt),
|
|
1844
|
+
imageResourceName: sh.fillImageResourceName,
|
|
1845
|
+
...sh.fillImageCrop ? { crop: sh.fillImageCrop } : {},
|
|
1846
|
+
clip,
|
|
1847
|
+
...fig
|
|
1848
|
+
});
|
|
1849
|
+
}
|
|
1811
1850
|
if (sh.markerPaths && sh.stroke) sink.push({
|
|
1812
1851
|
type: "shape",
|
|
1813
1852
|
shape: {
|
|
@@ -1906,6 +1945,15 @@ function drawBlocksSequentially(blocks, startX, startY, pageHeight, contentWidth
|
|
|
1906
1945
|
const ordered = blocks.some(isBehind) ? [...blocks.filter(isBehind), ...blocks.filter((b) => !isBehind(b))] : blocks;
|
|
1907
1946
|
for (const block of ordered) {
|
|
1908
1947
|
if (block.kind === "table") {
|
|
1948
|
+
const anchored = isOutOfFlowFloat(block.float) ? bandAnchor(block.float, block.totalWidthPt, contentWidth, startX) : void 0;
|
|
1949
|
+
if (anchored !== void 0 && block.float) {
|
|
1950
|
+
let rowY = bandAnchorTop(block.float, cursorY, pageHeight, block.heightPt, bandMargins);
|
|
1951
|
+
for (const row of block.rows) {
|
|
1952
|
+
emitRowChunk(out, row, startX + anchored, rowY, pageHeight, block.colCount);
|
|
1953
|
+
rowY -= row.heightPt;
|
|
1954
|
+
}
|
|
1955
|
+
continue;
|
|
1956
|
+
}
|
|
1909
1957
|
const tableX = startX + block.xOffsetPt;
|
|
1910
1958
|
for (const row of block.rows) {
|
|
1911
1959
|
emitRowChunk(out, row, tableX, cursorY, pageHeight, block.colCount);
|
|
@@ -3211,9 +3259,67 @@ function alignmentOffset(alignment, lineWidth, available) {
|
|
|
3211
3259
|
if (alignment === "center") return Math.max(0, (available - lineWidth) / 2);
|
|
3212
3260
|
return 0;
|
|
3213
3261
|
}
|
|
3262
|
+
/**
|
|
3263
|
+
* §17.4.85 `w:vMerge` — a vertical merge is ONE cell, so its continuation rows
|
|
3264
|
+
* are painted with the START cell's shading. Word writes the fill on the cell
|
|
3265
|
+
* that opens the merge and nothing on the rest; painting only what is written,
|
|
3266
|
+
* tdf123104.docx's three-row banner came out a staircase of yellow with white
|
|
3267
|
+
* notches where every merged cell continued.
|
|
3268
|
+
*/
|
|
3269
|
+
function withMergedShading(table, roles) {
|
|
3270
|
+
const columnOf = table.rows.map((row) => {
|
|
3271
|
+
const at = [];
|
|
3272
|
+
let col = row.properties.gridBefore ?? 0;
|
|
3273
|
+
for (const cell of row.cells) {
|
|
3274
|
+
at.push(col);
|
|
3275
|
+
col += Math.max(1, cell.properties.colSpan ?? 1);
|
|
3276
|
+
}
|
|
3277
|
+
return at;
|
|
3278
|
+
});
|
|
3279
|
+
let changed = false;
|
|
3280
|
+
const rows = table.rows.map((row, r) => {
|
|
3281
|
+
const cells = row.cells.map((cell, c) => {
|
|
3282
|
+
const role = roles[r]?.[c] ?? "standalone";
|
|
3283
|
+
if (role !== "middle" && role !== "end") return cell;
|
|
3284
|
+
if (cell.properties.shading?.colorHex) return cell;
|
|
3285
|
+
const col = columnOf[r]?.[c];
|
|
3286
|
+
if (col === void 0) return cell;
|
|
3287
|
+
for (let up = r - 1; up >= 0; up--) {
|
|
3288
|
+
const idx = columnOf[up]?.indexOf(col) ?? -1;
|
|
3289
|
+
if (idx < 0) break;
|
|
3290
|
+
const above = table.rows[up]?.cells[idx];
|
|
3291
|
+
const aboveRole = roles[up]?.[idx] ?? "standalone";
|
|
3292
|
+
if (!above) break;
|
|
3293
|
+
if (aboveRole === "start") {
|
|
3294
|
+
const shading = above.properties.shading;
|
|
3295
|
+
if (!shading?.colorHex) break;
|
|
3296
|
+
changed = true;
|
|
3297
|
+
return {
|
|
3298
|
+
...cell,
|
|
3299
|
+
properties: {
|
|
3300
|
+
...cell.properties,
|
|
3301
|
+
shading
|
|
3302
|
+
}
|
|
3303
|
+
};
|
|
3304
|
+
}
|
|
3305
|
+
if (aboveRole !== "middle") break;
|
|
3306
|
+
}
|
|
3307
|
+
return cell;
|
|
3308
|
+
});
|
|
3309
|
+
return {
|
|
3310
|
+
...row,
|
|
3311
|
+
cells
|
|
3312
|
+
};
|
|
3313
|
+
});
|
|
3314
|
+
return changed ? {
|
|
3315
|
+
...table,
|
|
3316
|
+
rows
|
|
3317
|
+
} : table;
|
|
3318
|
+
}
|
|
3214
3319
|
function layoutTableBlock(table, options, fontResources, imageResources, contentWidth) {
|
|
3215
3320
|
const columnWidthsPt = computeColumnWidths(table, options, fontResources, contentWidth);
|
|
3216
3321
|
const mergeRoles = table.rows.map((r) => r.cells.map((c) => c.properties.merge ?? "standalone"));
|
|
3322
|
+
table = withMergedShading(table, mergeRoles);
|
|
3217
3323
|
const rows = [];
|
|
3218
3324
|
const colCount = columnWidthsPt.length;
|
|
3219
3325
|
let aboveBordersByCol = [];
|
|
@@ -3792,12 +3898,12 @@ var PageAssembler = class PageAssembler {
|
|
|
3792
3898
|
* `undefined` when there are none.
|
|
3793
3899
|
* @param bookmarkPositions Out-param: bookmark name → its GoTo destination.
|
|
3794
3900
|
*/
|
|
3795
|
-
constructor(sectionCtxs, builder, notes, bookmarkPositions,
|
|
3901
|
+
constructor(sectionCtxs, builder, notes, bookmarkPositions, backgroundImage) {
|
|
3796
3902
|
this.sectionCtxs = sectionCtxs;
|
|
3797
3903
|
this.builder = builder;
|
|
3798
3904
|
this.notes = notes;
|
|
3799
3905
|
this.bookmarkPositions = bookmarkPositions;
|
|
3800
|
-
this.
|
|
3906
|
+
this.backgroundImage = backgroundImage;
|
|
3801
3907
|
this.ctx = sectionCtxs[0];
|
|
3802
3908
|
this.pageStartCtx = this.ctx;
|
|
3803
3909
|
this.pageNumber = this.ctx.properties.pageNumberStart ?? 1;
|
|
@@ -4305,19 +4411,49 @@ var PageAssembler = class PageAssembler {
|
|
|
4305
4411
|
*
|
|
4306
4412
|
* @returns The background items for this page (at most one).
|
|
4307
4413
|
*/
|
|
4414
|
+
/**
|
|
4415
|
+
* §14.1.2.5 `@type="tile"` — the picture repeated over a box at its own size,
|
|
4416
|
+
* as image items. A tile is a texture, so its natural size is what makes it
|
|
4417
|
+
* one: stretched over the page it is a blur, and its 128 pixels are 96 points
|
|
4418
|
+
* at the resolution Office measures a picture in.
|
|
4419
|
+
*/
|
|
4420
|
+
static tileItems = (resourceName, natural, box) => {
|
|
4421
|
+
const tw = Math.max(1, natural.widthPt);
|
|
4422
|
+
const th = Math.max(1, natural.heightPt);
|
|
4423
|
+
const cols = Math.min(200, Math.ceil(box.width / tw));
|
|
4424
|
+
const rows = Math.min(200, Math.ceil(box.height / th));
|
|
4425
|
+
const out = [];
|
|
4426
|
+
for (let r = 0; r < rows; r++) for (let c = 0; c < cols; c++) out.push({
|
|
4427
|
+
type: "image",
|
|
4428
|
+
x: pt(box.x + c * tw),
|
|
4429
|
+
y: pt(box.y + r * th),
|
|
4430
|
+
width: pt(tw),
|
|
4431
|
+
height: pt(th),
|
|
4432
|
+
imageResourceName: resourceName
|
|
4433
|
+
});
|
|
4434
|
+
return out;
|
|
4435
|
+
};
|
|
4308
4436
|
pageBackgroundItems = () => {
|
|
4309
4437
|
const options = this.ctx.options;
|
|
4310
4438
|
const w = this.ctx.pageWidth;
|
|
4311
4439
|
const h = this.ctx.pageHeight;
|
|
4312
4440
|
const fill = options?.pageBackgroundFill;
|
|
4313
|
-
if (fill?.kind === "picture" && this.
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4441
|
+
if (fill?.kind === "picture" && this.backgroundImage !== void 0) {
|
|
4442
|
+
const bg = this.backgroundImage;
|
|
4443
|
+
return fill.tiled ? PageAssembler.tileItems(bg.name, bg, {
|
|
4444
|
+
x: 0,
|
|
4445
|
+
y: 0,
|
|
4446
|
+
width: w,
|
|
4447
|
+
height: h
|
|
4448
|
+
}) : [{
|
|
4449
|
+
type: "image",
|
|
4450
|
+
x: pt(0),
|
|
4451
|
+
y: pt(0),
|
|
4452
|
+
width: pt(w),
|
|
4453
|
+
height: pt(h),
|
|
4454
|
+
imageResourceName: bg.name
|
|
4455
|
+
}];
|
|
4456
|
+
}
|
|
4321
4457
|
if (fill?.kind === "gradient" && fill.gradient) return [{
|
|
4322
4458
|
type: "shape",
|
|
4323
4459
|
shape: {
|
|
@@ -4401,9 +4537,9 @@ var PageAssembler = class PageAssembler {
|
|
|
4401
4537
|
this.balanceHeightPt = 0;
|
|
4402
4538
|
};
|
|
4403
4539
|
};
|
|
4404
|
-
function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US", notes, bookmarkPositions, reflowParagraph,
|
|
4540
|
+
function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US", notes, bookmarkPositions, reflowParagraph, backgroundImage) {
|
|
4405
4541
|
if (sectionCtxs.length === 0) return [];
|
|
4406
|
-
const asm = new PageAssembler(sectionCtxs, builder, notes, bookmarkPositions,
|
|
4542
|
+
const asm = new PageAssembler(sectionCtxs, builder, notes, bookmarkPositions, backgroundImage);
|
|
4407
4543
|
const balanceIfEndsContinuous = (fromBlock) => {
|
|
4408
4544
|
const here = asm.ctx;
|
|
4409
4545
|
const next = sectionCtxs[asm.secIdx + 1];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PdfHexString, PdfName, PdfStream } from "../pdf/objects.js";
|
|
2
|
-
import { aesCbcDecrypt, aesCbcEncrypt, md5, rc4, sha256, sha384, sha512 } from "
|
|
2
|
+
import { aesCbcDecrypt, aesCbcEncrypt, md5, rc4, sha256, sha384, sha512 } from "../core/crypto/primitives.js";
|
|
3
3
|
//#region src/pdf-reader/decrypt.ts
|
|
4
4
|
var PAD = Uint8Array.from([
|
|
5
5
|
40,
|
|
@@ -838,11 +838,13 @@ function vmlFill(shape, shapeType, resolveImage) {
|
|
|
838
838
|
const fillEl = poChildren(shape).find((c) => poIs(c, "v:fill"));
|
|
839
839
|
if (fillEl && (poAttr(fillEl, "type") === "none" || poAttr(fillEl, "on") === "f")) return { kind: "none" };
|
|
840
840
|
const base = vmlColor(poAttr(shape, "fillcolor")) ?? (fillEl ? vmlColor(poAttr(fillEl, "color")) : void 0) ?? vmlColor(shapeType ? poAttr(shapeType, "fillcolor") : void 0) ?? "FFFFFF";
|
|
841
|
-
const
|
|
841
|
+
const fillType = fillEl ? poAttr(fillEl, "type") : void 0;
|
|
842
|
+
const fillRelId = fillType === "frame" || fillType === "tile" ? poAttr(fillEl, "r:id") : void 0;
|
|
842
843
|
const picture = fillRelId !== void 0 ? resolveImage?.(fillRelId) : void 0;
|
|
843
844
|
if (picture !== void 0) return {
|
|
844
845
|
kind: "picture",
|
|
845
|
-
imageResource: picture
|
|
846
|
+
imageResource: picture,
|
|
847
|
+
...fillType === "tile" ? { tiled: true } : {}
|
|
846
848
|
};
|
|
847
849
|
const gradient = fillEl ? vmlGradient(fillEl, base) : void 0;
|
|
848
850
|
if (gradient) return {
|
|
@@ -1802,6 +1804,7 @@ function fillFromNode(child, resolveColor, resolveImage) {
|
|
|
1802
1804
|
return {
|
|
1803
1805
|
kind: "picture",
|
|
1804
1806
|
imageResource: resource,
|
|
1807
|
+
...poChildren(child).some((c) => poIs(c, "a:tile")) ? { tiled: true } : {},
|
|
1805
1808
|
...crop ? { imageCrop: crop } : {}
|
|
1806
1809
|
};
|
|
1807
1810
|
}
|
|
@@ -131,8 +131,8 @@ function parseParagraphProperties(pPr, autoSpacingPt = 0) {
|
|
|
131
131
|
}
|
|
132
132
|
if ("w:ind" in el) {
|
|
133
133
|
const node = el["w:ind"];
|
|
134
|
-
const left = parseIntAttr(node, "left");
|
|
135
|
-
const right = parseIntAttr(node, "right");
|
|
134
|
+
const left = parseIntAttr(node, "left") ?? parseIntAttr(node, "start");
|
|
135
|
+
const right = parseIntAttr(node, "right") ?? parseIntAttr(node, "end");
|
|
136
136
|
const firstLine = parseIntAttr(node, "firstLine");
|
|
137
137
|
const hanging = parseIntAttr(node, "hanging");
|
|
138
138
|
if (left !== void 0) out.indentLeft = twipsToPt(left);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { halfPtToPt, twipsToPt } from "../core/ir/units.js";
|
|
2
|
-
import { asElement, getAttr, getVal,
|
|
2
|
+
import { asElement, getAttr, getVal, parseHalfPointAttr, parseToggle } from "./xml-helpers.js";
|
|
3
3
|
import { shadingFillHex } from "./shading.js";
|
|
4
4
|
//#region src/word/run-properties.ts
|
|
5
5
|
var UNDERLINE_STYLES = new Set([
|
|
@@ -81,7 +81,7 @@ function parseRunProperties(rPr) {
|
|
|
81
81
|
if (c && /^[0-9A-Fa-f]{6}$/u.test(c)) out.underlineColorHex = c.toUpperCase();
|
|
82
82
|
}
|
|
83
83
|
if ("w:sz" in el) {
|
|
84
|
-
const v =
|
|
84
|
+
const v = parseHalfPointAttr(el["w:sz"], "val");
|
|
85
85
|
if (v !== void 0) out.fontSizePt = halfPtToPt(v);
|
|
86
86
|
}
|
|
87
87
|
if ("w:color" in el) {
|
|
@@ -36,6 +36,18 @@ export declare function getVal(node: unknown): string | undefined;
|
|
|
36
36
|
* @param name The local attribute name.
|
|
37
37
|
* @returns The parsed number, or `undefined` when absent or not finite.
|
|
38
38
|
*/
|
|
39
|
+
/**
|
|
40
|
+
* §17.3.2.38 ST_HpsMeasure — a size in HALF-POINTS, or the same universal
|
|
41
|
+
* measure with a unit on it ("20pt"). The two units differ by a factor of ten
|
|
42
|
+
* from the twips a length is written in, so a size is read here rather than
|
|
43
|
+
* through {@link parseIntAttr}: tdf108408.docx writes `w:sz w:val="20pt"` and
|
|
44
|
+
* read as twips it set its sample text 200 points tall.
|
|
45
|
+
*
|
|
46
|
+
* @param node The element carrying the attribute.
|
|
47
|
+
* @param name The attribute name.
|
|
48
|
+
* @returns The size in half-points, or undefined.
|
|
49
|
+
*/
|
|
50
|
+
export declare function parseHalfPointAttr(node: unknown, name: string): number | undefined;
|
|
39
51
|
export declare function parseIntAttr(node: unknown, name: string): number | undefined;
|
|
40
52
|
/**
|
|
41
53
|
* Parse a toggle (boolean) property per ECMA-376 Part 1 §17.17.4:
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { universalMeasureTwips } from "../core/po-helpers.js";
|
|
1
2
|
//#region src/word/xml-helpers.ts
|
|
2
3
|
/**
|
|
3
4
|
* Narrow an arbitrary parsed value to an {@link XmlElement}.
|
|
@@ -47,12 +48,31 @@ function getVal(node) {
|
|
|
47
48
|
* @param name The local attribute name.
|
|
48
49
|
* @returns The parsed number, or `undefined` when absent or not finite.
|
|
49
50
|
*/
|
|
51
|
+
/**
|
|
52
|
+
* §17.3.2.38 ST_HpsMeasure — a size in HALF-POINTS, or the same universal
|
|
53
|
+
* measure with a unit on it ("20pt"). The two units differ by a factor of ten
|
|
54
|
+
* from the twips a length is written in, so a size is read here rather than
|
|
55
|
+
* through {@link parseIntAttr}: tdf108408.docx writes `w:sz w:val="20pt"` and
|
|
56
|
+
* read as twips it set its sample text 200 points tall.
|
|
57
|
+
*
|
|
58
|
+
* @param node The element carrying the attribute.
|
|
59
|
+
* @param name The attribute name.
|
|
60
|
+
* @returns The size in half-points, or undefined.
|
|
61
|
+
*/
|
|
62
|
+
function parseHalfPointAttr(node, name) {
|
|
63
|
+
const v = getAttr(node, name);
|
|
64
|
+
if (v === void 0) return void 0;
|
|
65
|
+
const n = Number(v);
|
|
66
|
+
if (Number.isFinite(n)) return n;
|
|
67
|
+
const twips = universalMeasureTwips(v);
|
|
68
|
+
return twips === void 0 ? void 0 : twips / 10;
|
|
69
|
+
}
|
|
50
70
|
function parseIntAttr(node, name) {
|
|
51
71
|
const v = getAttr(node, name);
|
|
52
72
|
if (v === void 0) return void 0;
|
|
53
73
|
const n = Number(v);
|
|
54
|
-
if (
|
|
55
|
-
return
|
|
74
|
+
if (Number.isFinite(n)) return n;
|
|
75
|
+
return universalMeasureTwips(v);
|
|
56
76
|
}
|
|
57
77
|
/**
|
|
58
78
|
* Parse a toggle (boolean) property per ECMA-376 Part 1 §17.17.4:
|
|
@@ -71,4 +91,4 @@ function parseToggle(node) {
|
|
|
71
91
|
return !(v === "false" || v === "0" || v === "off");
|
|
72
92
|
}
|
|
73
93
|
//#endregion
|
|
74
|
-
export { asArray, asElement, getAttr, getVal, parseIntAttr, parseToggle };
|
|
94
|
+
export { asArray, asElement, getAttr, getVal, parseHalfPointAttr, parseIntAttr, parseToggle };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reamkit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.20.0",
|
|
4
4
|
"description": "Ream — convert DOCX, XLSX, PPTX and PDF to PDF, SVG, HTML, DOCX and XLSX, built from scratch on the ECMA-376 and ISO 32000 specifications. Parse once, convert anywhere.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Alex Krassavin <info@reamkit.dev>",
|