reamkit 1.0.0 → 1.0.2

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.
Files changed (58) hide show
  1. package/README.md +4 -3
  2. package/dist/esm/core/bidi/index.d.ts +2 -0
  3. package/dist/esm/core/bidi/index.js +1 -0
  4. package/dist/esm/core/bidi/segments.d.ts +6 -0
  5. package/dist/esm/core/bidi/segments.js +41 -0
  6. package/dist/esm/core/converter/facade.js +1 -1
  7. package/dist/esm/core/converter/project.js +3 -2
  8. package/dist/esm/core/converter/ream.js +2 -1
  9. package/dist/esm/core/font/index.d.ts +2 -0
  10. package/dist/esm/core/font/measure.d.ts +7 -0
  11. package/dist/esm/core/font/measure.js +30 -0
  12. package/dist/esm/core/images.d.ts +17 -0
  13. package/dist/esm/core/images.js +308 -0
  14. package/dist/esm/core/line-breaker/index.d.ts +1 -1
  15. package/dist/esm/core/line-breaker/knuth-plass.d.ts +1 -0
  16. package/dist/esm/core/line-breaker/knuth-plass.js +2 -2
  17. package/dist/esm/core/numbering/apply.d.ts +3 -0
  18. package/dist/esm/core/numbering/apply.js +62 -0
  19. package/dist/esm/core/numbering/index.d.ts +1 -0
  20. package/dist/esm/core/opc/package.d.ts +2 -1
  21. package/dist/esm/core/opc/package.js +6 -2
  22. package/dist/esm/core/style-cascade/index.d.ts +1 -1
  23. package/dist/esm/core/style-cascade/resolver.d.ts +3 -1
  24. package/dist/esm/core/style-cascade/resolver.js +75 -2
  25. package/dist/esm/core/style-cascade/types.d.ts +2 -1
  26. package/dist/esm/core/vector.d.ts +45 -0
  27. package/dist/esm/core/vector.js +48 -0
  28. package/dist/esm/excel/print-model.js +21 -3
  29. package/dist/esm/excel/xlsx-reader.js +2 -2
  30. package/dist/esm/index.d.ts +3 -3
  31. package/dist/esm/index.js +2 -1
  32. package/dist/esm/{pdf → layout}/arc-to-bezier.d.ts +1 -1
  33. package/dist/esm/{pdf → layout}/arc-to-bezier.js +2 -2
  34. package/dist/esm/{pdf → layout}/math-layout.d.ts +1 -1
  35. package/dist/esm/{pdf → layout}/math-layout.js +1 -1
  36. package/dist/esm/layout/page-doc.d.ts +133 -0
  37. package/dist/esm/layout/page-doc.js +38 -0
  38. package/dist/esm/{pdf → layout}/preset-geometry.d.ts +1 -1
  39. package/dist/esm/{pdf → layout}/preset-geometry.js +2 -2
  40. package/dist/esm/layout/styled-layout.d.ts +84 -0
  41. package/dist/esm/layout/styled-layout.js +1729 -0
  42. package/dist/esm/pdf/cid-font.d.ts +0 -6
  43. package/dist/esm/pdf/cid-font.js +2 -28
  44. package/dist/esm/pdf/image-xobject.d.ts +3 -17
  45. package/dist/esm/pdf/image-xobject.js +2 -306
  46. package/dist/esm/pdf/index.js +1 -0
  47. package/dist/esm/pdf/styled-page-emitter.d.ts +8 -2
  48. package/dist/esm/pdf/styled-page-emitter.js +29 -14
  49. package/dist/esm/pdf/styled-page-renderer.d.ts +4 -217
  50. package/dist/esm/pdf/styled-page-renderer.js +3 -1824
  51. package/dist/esm/pdf/vector-graphics.d.ts +3 -45
  52. package/dist/esm/pdf/vector-graphics.js +2 -46
  53. package/dist/esm/svg/svg-writer.d.ts +1 -1
  54. package/dist/esm/svg/svg-writer.js +14 -15
  55. package/dist/esm/word/docx-reader.js +4 -3
  56. package/package.json +2 -2
  57. /package/dist/esm/{pdf → layout}/chart-geometry.d.ts +0 -0
  58. /package/dist/esm/{pdf → layout}/chart-geometry.js +0 -0
package/README.md CHANGED
@@ -147,9 +147,10 @@ automatically from the document's `docProps/core.xml`), `attachments`
147
147
 
148
148
  ### Lower-level APIs
149
149
 
150
- - `docxReader` / `xlsxReader`, `layoutStyledDocument`, `svgWriter` — the
151
- `@experimental` reader/writer interfaces of the interlayer, for building
152
- custom pipelines (and keeping unused formats out of your bundle).
150
+ - `docxReader` / `xlsxReader`, `svgWriter` — the `@experimental` reader/writer
151
+ interfaces of the interlayer, for building custom pipelines (and keeping
152
+ unused formats out of your bundle); `layoutStyledDocument` produces the
153
+ frozen page model (`PageItem` pages in a top-left `Pt` frame) they consume.
153
154
  - `renderStyledPdf` drives the layout engine directly; the typed document
154
155
  model is on the `reamkit/document-model` subpath.
155
156
 
@@ -12,3 +12,5 @@ export declare function analyzeString(text: string, dir?: Direction): StringBidi
12
12
  export declare function hasBidiCharacters(text: string): boolean;
13
13
  export declare function reverseByCodePoint(text: string): string;
14
14
  export declare function reorderTokens(tokenLevels: ReadonlyArray<number>): Array<number>;
15
+ export { segmentLevels } from './segments.js';
16
+ export type { BidiSegment } from './segments.js';
@@ -1,5 +1,6 @@
1
1
  import "./char-types.js";
2
2
  import { computeBidi } from "./algorithm.js";
3
+ import "./segments.js";
3
4
  //#region src/core/bidi/index.ts
4
5
  function analyzeString(text, dir = "auto") {
5
6
  const codePoints = [];
@@ -0,0 +1,6 @@
1
+ export interface BidiSegment {
2
+ readonly text: string;
3
+ readonly object?: boolean;
4
+ readonly rtl?: boolean;
5
+ }
6
+ export declare function segmentLevels(segments: ReadonlyArray<BidiSegment>, baseDir: 'ltr' | 'rtl'): Array<number>;
@@ -0,0 +1,41 @@
1
+ import { computeBidi } from "./algorithm.js";
2
+ //#region src/core/bidi/segments.ts
3
+ var OBJECT_REPLACEMENT = 65532;
4
+ var RLE = 8235;
5
+ var PDF_FMT = 8236;
6
+ var LRE = 8234;
7
+ function segmentLevels(segments, baseDir) {
8
+ const bidiCps = [];
9
+ const realIndexOfBidi = [];
10
+ let realCount = 0;
11
+ for (const seg of segments) {
12
+ const isObject = seg.object === true;
13
+ const wrap = seg.rtl ? RLE : baseDir === "rtl" && !isObject ? LRE : 0;
14
+ if (wrap) {
15
+ bidiCps.push(wrap);
16
+ realIndexOfBidi.push(-1);
17
+ }
18
+ if (isObject) {
19
+ bidiCps.push(OBJECT_REPLACEMENT);
20
+ realIndexOfBidi.push(realCount);
21
+ realCount++;
22
+ } else for (const ch of seg.text) {
23
+ bidiCps.push(ch.codePointAt(0));
24
+ realIndexOfBidi.push(realCount);
25
+ realCount++;
26
+ }
27
+ if (wrap) {
28
+ bidiCps.push(PDF_FMT);
29
+ realIndexOfBidi.push(-1);
30
+ }
31
+ }
32
+ const { levels } = computeBidi(bidiCps, baseDir);
33
+ const realLevels = new Array(realCount).fill(baseDir === "rtl" ? 1 : 0);
34
+ for (let i = 0; i < bidiCps.length; i++) {
35
+ const ri = realIndexOfBidi[i];
36
+ if (ri >= 0) realLevels[ri] = levels[i];
37
+ }
38
+ return realLevels;
39
+ }
40
+ //#endregion
41
+ export { segmentLevels };
@@ -1,7 +1,7 @@
1
1
  import { FontRegistry } from "../font/font-registry.js";
2
2
  import { FEATURES } from "../ir/features.js";
3
3
  import { ConversionLossError } from "../ir/loss.js";
4
- import { layoutStyledDocument } from "../../pdf/styled-page-renderer.js";
4
+ import { layoutStyledDocument } from "../../layout/styled-layout.js";
5
5
  import { docxReader } from "../../word/docx-reader.js";
6
6
  import { xlsxReader } from "../../excel/xlsx-reader.js";
7
7
  import { chainProviders } from "../fonts/provider.js";
@@ -1,8 +1,9 @@
1
+ import { EMPTY_STYLE_SHEET } from "../style-cascade/resolver.js";
2
+ import "../style-cascade/index.js";
1
3
  //#region src/core/converter/project.ts
2
4
  function flowRenderOptions(flow) {
3
5
  return {
4
- styles: flow.styles,
5
- ...flow.numbering ? { numbering: flow.numbering } : {},
6
+ styles: EMPTY_STYLE_SHEET,
6
7
  ...flow.sections.length > 0 ? { sections: flow.sections } : {},
7
8
  ...flow.section ? { section: flow.section } : {},
8
9
  ...flow.headersFooters ? { headersFooters: flow.headersFooters } : {},
@@ -1,8 +1,9 @@
1
1
  import { fetchFontSet } from "../fonts/remote-fonts.js";
2
2
  import { FontRegistry } from "../font/font-registry.js";
3
3
  import { ConversionLossError } from "../ir/loss.js";
4
+ import { layoutStyledDocument } from "../../layout/styled-layout.js";
4
5
  import { signPdf } from "../../pdf/signature.js";
5
- import { layoutStyledDocument, renderStyledPdf } from "../../pdf/styled-page-renderer.js";
6
+ import { renderStyledPdf } from "../../pdf/styled-page-renderer.js";
6
7
  import "../../pdf/index.js";
7
8
  import { flowRenderOptions } from "./project.js";
8
9
  import { writeSvg } from "../../svg/svg-writer.js";
@@ -6,3 +6,5 @@ export type { FontVariant, FontBytesByVariant } from './font-registry.js';
6
6
  export { pickVariant } from './font-registry.js';
7
7
  export { parseGposKerning, parseGsubLigatures, shapeText } from './opentype-layout.js';
8
8
  export type { KerningMap, LigatureMap, ShapedRun } from './opentype-layout.js';
9
+ export { createFontMeasure } from './measure.js';
10
+ export type { FontMeasure } from './measure.js';
@@ -0,0 +1,7 @@
1
+ import { ParsedTtf } from './ttf-parser.js';
2
+ export interface FontMeasure {
3
+ readonly pdfWidthForGid: (gid: number) => number;
4
+ readonly textWidthPt: (text: string, fontSize: number) => number;
5
+ readonly encodeTextAsCidHex: (text: string) => string;
6
+ }
7
+ export declare function createFontMeasure(parsed: ParsedTtf): FontMeasure;
@@ -0,0 +1,30 @@
1
+ import { shapeText } from "./opentype-layout.js";
2
+ //#region src/core/font/measure.ts
3
+ function createFontMeasure(parsed) {
4
+ const scale = 1e3 / parsed.unitsPerEm;
5
+ const widths = new Array(parsed.numGlyphs);
6
+ for (let i = 0; i < parsed.numGlyphs; i++) widths[i] = Math.round((parsed.advanceWidths[i] ?? 0) * scale);
7
+ const pdfWidthForGid = (gid) => {
8
+ if (gid < 0 || gid >= parsed.numGlyphs) return 1e3;
9
+ return widths[gid];
10
+ };
11
+ const textWidthPt = (text, fontSize) => {
12
+ const shaped = shapeText(text, parsed.glyphForCodepoint, parsed.advanceWidths, parsed.ligatures, parsed.kerning, parsed.joiningForms);
13
+ let totalEm = 0;
14
+ for (const a of shaped.advances) totalEm += a;
15
+ return totalEm * fontSize / parsed.unitsPerEm;
16
+ };
17
+ const encodeTextAsCidHex = (text) => {
18
+ const shaped = shapeText(text, parsed.glyphForCodepoint, parsed.advanceWidths, parsed.ligatures, parsed.kerning, parsed.joiningForms);
19
+ let out = "";
20
+ for (const gid of shaped.gids) out += gid.toString(16).padStart(4, "0").toUpperCase();
21
+ return out;
22
+ };
23
+ return {
24
+ pdfWidthForGid,
25
+ textWidthPt,
26
+ encodeTextAsCidHex
27
+ };
28
+ }
29
+ //#endregion
30
+ export { createFontMeasure };
@@ -0,0 +1,17 @@
1
+ export type ImageFormat = 'jpeg' | 'png' | 'jpeg2000';
2
+ export declare function detectImageFormat(bytes: Uint8Array): ImageFormat | null;
3
+ export interface EmbedImageOptions {
4
+ readonly flattenAlpha?: boolean;
5
+ }
6
+ export interface PreparedImage {
7
+ readonly format: ImageFormat;
8
+ readonly mimeType: 'image/jp2' | 'image/jpeg' | 'image/png';
9
+ readonly widthPx: number;
10
+ readonly heightPx: number;
11
+ readonly colorSpace?: 'DeviceGray' | 'DeviceRGB';
12
+ readonly bitsPerComponent?: number;
13
+ readonly filter: 'DCTDecode' | 'FlateDecode' | 'JPXDecode';
14
+ readonly data: Uint8Array;
15
+ readonly smaskData?: Uint8Array;
16
+ }
17
+ export declare function prepareImage(bytes: Uint8Array, options?: EmbedImageOptions): PreparedImage;
@@ -0,0 +1,308 @@
1
+ import { unzlibSync, zlibSync } from "fflate";
2
+ //#region src/core/images.ts
3
+ function detectImageFormat(bytes) {
4
+ if (bytes.length >= 3 && bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255) return "jpeg";
5
+ if (bytes.length >= 8 && bytes[0] === 137 && bytes[1] === 80 && bytes[2] === 78 && bytes[3] === 71 && bytes[4] === 13 && bytes[5] === 10 && bytes[6] === 26 && bytes[7] === 10) return "png";
6
+ if (bytes.length >= 12 && bytes[0] === 0 && bytes[1] === 0 && bytes[2] === 0 && bytes[3] === 12 && bytes[4] === 106 && bytes[5] === 80 && bytes[6] === 32 && bytes[7] === 32 && bytes[8] === 13 && bytes[9] === 10 && bytes[10] === 135 && bytes[11] === 10) return "jpeg2000";
7
+ if (bytes.length >= 4 && bytes[0] === 255 && bytes[1] === 79 && bytes[2] === 255 && bytes[3] === 81) return "jpeg2000";
8
+ return null;
9
+ }
10
+ function prepareImage(bytes, options = {}) {
11
+ const format = detectImageFormat(bytes);
12
+ if (format === "jpeg") return prepareJpeg(bytes);
13
+ if (format === "png") return preparePng(bytes, options);
14
+ if (format === "jpeg2000") return prepareJpeg2000(bytes);
15
+ throw new Error("Unsupported image format");
16
+ }
17
+ function prepareJpeg2000(bytes) {
18
+ const { width, height } = readJpeg2000Info(bytes);
19
+ return {
20
+ format: "jpeg2000",
21
+ mimeType: "image/jp2",
22
+ widthPx: width,
23
+ heightPx: height,
24
+ filter: "JPXDecode",
25
+ data: bytes
26
+ };
27
+ }
28
+ function readU32(b, o) {
29
+ return b[o] * 16777216 + (b[o + 1] << 16) + (b[o + 2] << 8) + b[o + 3];
30
+ }
31
+ var boxType = (b, o) => String.fromCharCode(b[o], b[o + 1], b[o + 2], b[o + 3]);
32
+ function readJpeg2000Info(bytes) {
33
+ if (bytes[0] === 255 && bytes[1] === 79) return readSiz(bytes, 2);
34
+ let p = 0;
35
+ while (p + 8 <= bytes.length) {
36
+ let len = readU32(bytes, p);
37
+ const type = boxType(bytes, p + 4);
38
+ let contentStart = p + 8;
39
+ if (len === 1) {
40
+ len = readU32(bytes, p + 12);
41
+ contentStart = p + 16;
42
+ }
43
+ const end = len === 0 ? bytes.length : p + len;
44
+ if (type === "jp2h") {
45
+ let q = contentStart;
46
+ while (q + 8 <= end) {
47
+ const clen = readU32(bytes, q);
48
+ if (boxType(bytes, q + 4) === "ihdr") return {
49
+ height: readU32(bytes, q + 8),
50
+ width: readU32(bytes, q + 12)
51
+ };
52
+ if (clen === 0) break;
53
+ q += clen;
54
+ }
55
+ }
56
+ if (len === 0) break;
57
+ p = end;
58
+ }
59
+ for (let i = 0; i + 22 < bytes.length; i++) if (bytes[i] === 255 && bytes[i + 1] === 81) return readSiz(bytes, i);
60
+ throw new Error("JPEG 2000: no ihdr/SIZ found");
61
+ }
62
+ function readSiz(bytes, sizOffset) {
63
+ const xsiz = readU32(bytes, sizOffset + 6);
64
+ const ysiz = readU32(bytes, sizOffset + 10);
65
+ const xo = readU32(bytes, sizOffset + 14);
66
+ const yo = readU32(bytes, sizOffset + 18);
67
+ const width = xsiz - xo;
68
+ const height = ysiz - yo;
69
+ if (width <= 0 || height <= 0) throw new Error("JPEG 2000: invalid SIZ dimensions");
70
+ return {
71
+ width,
72
+ height
73
+ };
74
+ }
75
+ function prepareJpeg(bytes) {
76
+ const info = readJpegInfo(bytes);
77
+ return {
78
+ format: "jpeg",
79
+ mimeType: "image/jpeg",
80
+ widthPx: info.width,
81
+ heightPx: info.height,
82
+ colorSpace: info.numComponents === 1 ? "DeviceGray" : "DeviceRGB",
83
+ bitsPerComponent: info.precision,
84
+ filter: "DCTDecode",
85
+ data: bytes
86
+ };
87
+ }
88
+ function readJpegInfo(bytes) {
89
+ if (bytes.length < 4 || bytes[0] !== 255 || bytes[1] !== 216) throw new Error("Not a JPEG (missing SOI)");
90
+ let i = 2;
91
+ while (i + 9 < bytes.length) {
92
+ if (bytes[i] !== 255) {
93
+ i++;
94
+ continue;
95
+ }
96
+ let marker = bytes[i + 1];
97
+ while (marker === 255) {
98
+ i++;
99
+ marker = bytes[i + 1];
100
+ }
101
+ if (marker === 1 || marker >= 208 && marker <= 217) {
102
+ i += 2;
103
+ continue;
104
+ }
105
+ if (marker >= 192 && marker <= 207 && marker !== 196 && marker !== 200 && marker !== 204) return {
106
+ precision: bytes[i + 4],
107
+ height: bytes[i + 5] << 8 | bytes[i + 6],
108
+ width: bytes[i + 7] << 8 | bytes[i + 8],
109
+ numComponents: bytes[i + 9]
110
+ };
111
+ const length = bytes[i + 2] << 8 | bytes[i + 3];
112
+ i += 2 + length;
113
+ }
114
+ throw new Error("JPEG SOFn marker not found");
115
+ }
116
+ function preparePng(bytes, options = {}) {
117
+ let decoded = decodePng(bytes);
118
+ if (options.flattenAlpha && decoded.smaskRaw) decoded = flattenAlphaOnWhite(decoded);
119
+ return {
120
+ format: "png",
121
+ mimeType: "image/png",
122
+ widthPx: decoded.width,
123
+ heightPx: decoded.height,
124
+ colorSpace: decoded.colorSpace,
125
+ bitsPerComponent: decoded.bitsPerComponent,
126
+ filter: "FlateDecode",
127
+ data: zlibSync(decoded.raw),
128
+ ...decoded.smaskRaw ? { smaskData: zlibSync(decoded.smaskRaw) } : {}
129
+ };
130
+ }
131
+ function decodePng(bytes) {
132
+ let width = 0;
133
+ let height = 0;
134
+ let bitDepth = 0;
135
+ let colorType = 0;
136
+ let interlaceMethod = 0;
137
+ const idatChunks = [];
138
+ let pos = 8;
139
+ while (pos + 12 <= bytes.length) {
140
+ const len = readU32BE(bytes, pos);
141
+ const type = chunkType(bytes, pos + 4);
142
+ const dataOff = pos + 8;
143
+ if (type === "IHDR") {
144
+ width = readU32BE(bytes, dataOff);
145
+ height = readU32BE(bytes, dataOff + 4);
146
+ bitDepth = bytes[dataOff + 8];
147
+ colorType = bytes[dataOff + 9];
148
+ interlaceMethod = bytes[dataOff + 12];
149
+ } else if (type === "IDAT") idatChunks.push(bytes.subarray(dataOff, dataOff + len));
150
+ else if (type === "IEND") break;
151
+ pos = dataOff + len + 4;
152
+ }
153
+ if (width === 0 || height === 0) throw new Error("PNG: missing IHDR");
154
+ if (idatChunks.length === 0) throw new Error("PNG: no IDAT");
155
+ if (interlaceMethod !== 0) throw new Error("PNG: interlaced not supported");
156
+ if (bitDepth !== 8) throw new Error(`PNG: bit depth ${bitDepth} not supported`);
157
+ const inflated = unzlibSync(concatBytes(idatChunks));
158
+ const channels = pngChannels(colorType);
159
+ if (channels === 0) throw new Error(`PNG color type ${colorType} not supported`);
160
+ const bpp = channels;
161
+ const scanlineBytes = width * bpp;
162
+ const expected = height * (1 + scanlineBytes);
163
+ if (inflated.length !== expected) throw new Error(`PNG: inflated size ${inflated.length} != expected ${expected} (interlaced or malformed)`);
164
+ const raw = new Uint8Array(height * scanlineBytes);
165
+ let prev = null;
166
+ for (let y = 0; y < height; y++) {
167
+ const inOff = y * (1 + scanlineBytes);
168
+ const filterType = inflated[inOff];
169
+ const decoded = new Uint8Array(scanlineBytes);
170
+ for (let x = 0; x < scanlineBytes; x++) {
171
+ const filt = inflated[inOff + 1 + x];
172
+ const a = x < bpp ? 0 : decoded[x - bpp];
173
+ const b = prev ? prev[x] : 0;
174
+ const c = !prev || x < bpp ? 0 : prev[x - bpp];
175
+ let v;
176
+ switch (filterType) {
177
+ case 0:
178
+ v = filt;
179
+ break;
180
+ case 1:
181
+ v = filt + a;
182
+ break;
183
+ case 2:
184
+ v = filt + b;
185
+ break;
186
+ case 3:
187
+ v = filt + Math.floor((a + b) / 2);
188
+ break;
189
+ case 4:
190
+ v = filt + paethPredictor(a, b, c);
191
+ break;
192
+ default: throw new Error(`PNG: unknown filter type ${filterType}`);
193
+ }
194
+ decoded[x] = v & 255;
195
+ }
196
+ raw.set(decoded, y * scanlineBytes);
197
+ prev = decoded;
198
+ }
199
+ return splitChannels(width, height, colorType, raw);
200
+ }
201
+ function flattenAlphaOnWhite(decoded) {
202
+ const alpha = decoded.smaskRaw;
203
+ if (!alpha) return decoded;
204
+ const channels = decoded.colorSpace === "DeviceRGB" ? 3 : 1;
205
+ const pixelCount = decoded.width * decoded.height;
206
+ const out = new Uint8Array(pixelCount * channels);
207
+ for (let i = 0; i < pixelCount; i++) {
208
+ const a = alpha[i] / 255;
209
+ for (let c = 0; c < channels; c++) {
210
+ const src = decoded.raw[i * channels + c];
211
+ out[i * channels + c] = Math.round(src * a + 255 * (1 - a));
212
+ }
213
+ }
214
+ return {
215
+ width: decoded.width,
216
+ height: decoded.height,
217
+ raw: out,
218
+ colorSpace: decoded.colorSpace,
219
+ bitsPerComponent: decoded.bitsPerComponent
220
+ };
221
+ }
222
+ function splitChannels(width, height, colorType, raw) {
223
+ const pixelCount = width * height;
224
+ if (colorType === 0) return {
225
+ width,
226
+ height,
227
+ raw,
228
+ colorSpace: "DeviceGray",
229
+ bitsPerComponent: 8
230
+ };
231
+ if (colorType === 2) return {
232
+ width,
233
+ height,
234
+ raw,
235
+ colorSpace: "DeviceRGB",
236
+ bitsPerComponent: 8
237
+ };
238
+ if (colorType === 4) {
239
+ const gray = new Uint8Array(pixelCount);
240
+ const alpha = new Uint8Array(pixelCount);
241
+ for (let i = 0; i < pixelCount; i++) {
242
+ gray[i] = raw[i * 2];
243
+ alpha[i] = raw[i * 2 + 1];
244
+ }
245
+ return {
246
+ width,
247
+ height,
248
+ raw: gray,
249
+ colorSpace: "DeviceGray",
250
+ bitsPerComponent: 8,
251
+ smaskRaw: alpha
252
+ };
253
+ }
254
+ if (colorType === 6) {
255
+ const rgb = new Uint8Array(pixelCount * 3);
256
+ const alpha = new Uint8Array(pixelCount);
257
+ for (let i = 0; i < pixelCount; i++) {
258
+ rgb[i * 3] = raw[i * 4];
259
+ rgb[i * 3 + 1] = raw[i * 4 + 1];
260
+ rgb[i * 3 + 2] = raw[i * 4 + 2];
261
+ alpha[i] = raw[i * 4 + 3];
262
+ }
263
+ return {
264
+ width,
265
+ height,
266
+ raw: rgb,
267
+ colorSpace: "DeviceRGB",
268
+ bitsPerComponent: 8,
269
+ smaskRaw: alpha
270
+ };
271
+ }
272
+ throw new Error(`PNG color type ${colorType} not supported (palette/indexed?)`);
273
+ }
274
+ function pngChannels(colorType) {
275
+ if (colorType === 0) return 1;
276
+ if (colorType === 2) return 3;
277
+ if (colorType === 4) return 2;
278
+ if (colorType === 6) return 4;
279
+ return 0;
280
+ }
281
+ function paethPredictor(a, b, c) {
282
+ const p = a + b - c;
283
+ const pa = Math.abs(p - a);
284
+ const pb = Math.abs(p - b);
285
+ const pc = Math.abs(p - c);
286
+ if (pa <= pb && pa <= pc) return a;
287
+ if (pb <= pc) return b;
288
+ return c;
289
+ }
290
+ function readU32BE(bytes, offset) {
291
+ return bytes[offset] * 16777216 + (bytes[offset + 1] << 16 | bytes[offset + 2] << 8 | bytes[offset + 3]);
292
+ }
293
+ function chunkType(bytes, offset) {
294
+ return String.fromCharCode(bytes[offset], bytes[offset + 1], bytes[offset + 2], bytes[offset + 3]);
295
+ }
296
+ function concatBytes(parts) {
297
+ let total = 0;
298
+ for (const p of parts) total += p.length;
299
+ const out = new Uint8Array(total);
300
+ let off = 0;
301
+ for (const p of parts) {
302
+ out.set(p, off);
303
+ off += p.length;
304
+ }
305
+ return out;
306
+ }
307
+ //#endregion
308
+ export { detectImageFormat, prepareImage };
@@ -1,2 +1,2 @@
1
- export { breakLines } from './knuth-plass.js';
1
+ export { FORCED_BREAK, breakLines } from './knuth-plass.js';
2
2
  export type { Item, BoxItem, GlueItem, PenaltyItem, BreakResult, } from './knuth-plass.js';
@@ -19,6 +19,7 @@ export interface BreakResult {
19
19
  readonly breaks: ReadonlyArray<number>;
20
20
  readonly ratios: ReadonlyArray<number>;
21
21
  }
22
+ export declare const FORCED_BREAK = -10000;
22
23
  /**
23
24
  * Break a paragraph into lines using the Knuth-Plass total-fit algorithm.
24
25
  *
@@ -91,7 +91,7 @@ function breakLines(items, lineWidths) {
91
91
  const badness = 100 * Math.pow(Math.abs(r), 3);
92
92
  let d;
93
93
  if (penaltyAtBreak >= 0 && penaltyAtBreak < FORBIDDEN_BREAK) d = Math.pow(LINE_PENALTY + badness + penaltyAtBreak, 2);
94
- else if (penaltyAtBreak > FORCED_BREAK && penaltyAtBreak < 0) d = Math.pow(LINE_PENALTY + badness, 2) - penaltyAtBreak * penaltyAtBreak;
94
+ else if (penaltyAtBreak > -1e4 && penaltyAtBreak < 0) d = Math.pow(LINE_PENALTY + badness, 2) - penaltyAtBreak * penaltyAtBreak;
95
95
  else d = Math.pow(LINE_PENALTY + badness, 2);
96
96
  return d;
97
97
  };
@@ -169,4 +169,4 @@ function breakLines(items, lineWidths) {
169
169
  };
170
170
  }
171
171
  //#endregion
172
- export { breakLines };
172
+ export { FORCED_BREAK, breakLines };
@@ -0,0 +1,3 @@
1
+ import { BodyElement, Numbering } from '../document-model/index.js';
2
+ export declare function applyNumbering(body: ReadonlyArray<BodyElement>, numbering: Numbering | undefined): Array<BodyElement>;
3
+ export declare function applyNumberingToHeadersFooters(hf: ReadonlyMap<string, ReadonlyArray<BodyElement>> | undefined, numbering: Numbering | undefined): ReadonlyMap<string, ReadonlyArray<BodyElement>>;
@@ -0,0 +1,62 @@
1
+ import { NumberingState } from "./state.js";
2
+ //#region src/core/numbering/apply.ts
3
+ function applyNumbering(body, numbering) {
4
+ if (!numbering || numbering.abstractNums.size === 0) return body.map((b) => b);
5
+ const state = new NumberingState();
6
+ const transformParagraph = (p) => {
7
+ const ref = p.properties.numbering;
8
+ if (!ref) return p;
9
+ const marker = state.resolveMarker(numbering, ref);
10
+ if (marker === null) return p;
11
+ const instance = numbering.numInstances.get(ref.numId);
12
+ const level = (instance ? numbering.abstractNums.get(instance.abstractNumId) : void 0)?.levels.get(ref.ilvl);
13
+ const markerRun = {
14
+ text: `${marker}\t`,
15
+ properties: level?.runProperties ?? {}
16
+ };
17
+ return {
18
+ properties: mergeIndentFromLevel(p.properties, level?.paragraphProperties),
19
+ runs: [markerRun, ...p.runs]
20
+ };
21
+ };
22
+ const visit = (el) => {
23
+ if (el.kind === "paragraph") return {
24
+ kind: "paragraph",
25
+ paragraph: transformParagraph(el.paragraph)
26
+ };
27
+ if (el.kind === "image") return el;
28
+ if (el.kind === "shape") return el;
29
+ if (el.kind === "chart") return el;
30
+ return {
31
+ kind: "table",
32
+ table: {
33
+ ...el.table,
34
+ rows: el.table.rows.map((row) => ({
35
+ ...row,
36
+ cells: row.cells.map((cell) => ({
37
+ ...cell,
38
+ content: cell.content.map(visit)
39
+ }))
40
+ }))
41
+ }
42
+ };
43
+ };
44
+ return body.map(visit);
45
+ }
46
+ function mergeIndentFromLevel(paragraphProps, levelProps) {
47
+ if (!levelProps) return paragraphProps;
48
+ const out = { ...paragraphProps };
49
+ if (out.indentLeft === void 0 && levelProps.indentLeft !== void 0) out.indentLeft = levelProps.indentLeft;
50
+ if (out.indentRight === void 0 && levelProps.indentRight !== void 0) out.indentRight = levelProps.indentRight;
51
+ if (out.indentFirstLine === void 0 && levelProps.indentFirstLine !== void 0) out.indentFirstLine = levelProps.indentFirstLine;
52
+ return out;
53
+ }
54
+ function applyNumberingToHeadersFooters(hf, numbering) {
55
+ if (!hf || hf.size === 0) return /* @__PURE__ */ new Map();
56
+ if (!numbering || numbering.abstractNums.size === 0) return hf;
57
+ const out = /* @__PURE__ */ new Map();
58
+ for (const [key, value] of hf) out.set(key, applyNumbering(value, numbering));
59
+ return out;
60
+ }
61
+ //#endregion
62
+ export { applyNumbering, applyNumberingToHeadersFooters };
@@ -1 +1,2 @@
1
1
  export { NumberingState, formatLevelMarker } from './state.js';
2
+ export { applyNumbering, applyNumberingToHeadersFooters } from './apply.js';
@@ -9,11 +9,12 @@ export declare class OpcPackage {
9
9
  private readonly parts;
10
10
  private readonly rootRelationships;
11
11
  private constructor();
12
+ private readonly relsCache;
12
13
  static open(buffer: Uint8Array, options?: OpcOpenOptions): OpcPackage;
13
14
  getPart(path: string): Uint8Array | undefined;
14
15
  requirePart(path: string): Uint8Array;
15
16
  listParts(): Array<string>;
16
- getPartRelationships(partPath: string): Array<Relationship>;
17
+ getPartRelationships(partPath: string): ReadonlyArray<Relationship>;
17
18
  resolveRelatedPart(sourcePartPath: string, relationship: Relationship): {
18
19
  readonly path: string;
19
20
  readonly data: Uint8Array;
@@ -9,6 +9,7 @@ var OpcPackage = class OpcPackage {
9
9
  this.parts = parts;
10
10
  this.rootRelationships = rootRelationships;
11
11
  }
12
+ relsCache = /* @__PURE__ */ new Map();
12
13
  static open(buffer, options = {}) {
13
14
  const maxArchive = options.maxArchiveBytes ?? 128 * MIB;
14
15
  const maxEntry = options.maxEntryBytes ?? 256 * MIB;
@@ -54,13 +55,16 @@ var OpcPackage = class OpcPackage {
54
55
  }
55
56
  getPartRelationships(partPath) {
56
57
  const normalized = normalizePath(partPath);
58
+ const cached = this.relsCache.get(normalized);
59
+ if (cached) return cached;
57
60
  const slash = normalized.lastIndexOf("/");
58
61
  const dir = slash >= 0 ? normalized.substring(0, slash) : "";
59
62
  const base = slash >= 0 ? normalized.substring(slash + 1) : normalized;
60
63
  const relsPath = dir.length > 0 ? `${dir}/_rels/${base}.rels` : `_rels/${base}.rels`;
61
64
  const data = this.parts.get(relsPath);
62
- if (!data) return [];
63
- return parseRelationships(data);
65
+ const rels = data ? parseRelationships(data) : [];
66
+ this.relsCache.set(normalized, rels);
67
+ return rels;
64
68
  }
65
69
  resolveRelatedPart(sourcePartPath, relationship) {
66
70
  if (relationship.targetMode !== "Internal") return void 0;
@@ -1,3 +1,3 @@
1
- export { EMPTY_STYLE_SHEET, resolveParagraphProperties, resolveRunProperties, } from './resolver.js';
1
+ export { EMPTY_STYLE_SHEET, resolveBodyStyles, resolveHeadersFootersStyles, resolveParagraphProperties, resolveRunProperties, } from './resolver.js';
2
2
  export type { ResolvedRunProperties, ResolvedParagraphProperties, } from './types.js';
3
3
  export { DEFAULT_RESOLVED_RUN, DEFAULT_RESOLVED_PARAGRAPH } from './types.js';
@@ -1,5 +1,7 @@
1
- import { ParagraphProperties, RunProperties, StyleSheet } from '../document-model/index.js';
1
+ import { BodyElement, ParagraphProperties, RunProperties, StyleSheet } from '../document-model/index.js';
2
2
  import { ResolvedParagraphProperties, ResolvedRunProperties } from './types.js';
3
3
  export declare const EMPTY_STYLE_SHEET: StyleSheet;
4
4
  export declare function resolveRunProperties(runDirect: RunProperties, paragraphDirect: ParagraphProperties, sheet: StyleSheet): ResolvedRunProperties;
5
5
  export declare function resolveParagraphProperties(paragraphDirect: ParagraphProperties, sheet: StyleSheet): ResolvedParagraphProperties;
6
+ export declare function resolveBodyStyles(body: ReadonlyArray<BodyElement>, sheet: StyleSheet): ReadonlyArray<BodyElement>;
7
+ export declare function resolveHeadersFootersStyles(hf: ReadonlyMap<string, ReadonlyArray<BodyElement>>, sheet: StyleSheet): ReadonlyMap<string, ReadonlyArray<BodyElement>>;