reamkit 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/dist/esm/core/bidi/index.d.ts +2 -0
- package/dist/esm/core/bidi/index.js +1 -0
- package/dist/esm/core/bidi/segments.d.ts +6 -0
- package/dist/esm/core/bidi/segments.js +41 -0
- package/dist/esm/core/converter/facade.js +1 -1
- package/dist/esm/core/converter/project.js +3 -2
- package/dist/esm/core/converter/ream.js +2 -1
- package/dist/esm/core/font/index.d.ts +2 -0
- package/dist/esm/core/font/measure.d.ts +7 -0
- package/dist/esm/core/font/measure.js +30 -0
- package/dist/esm/core/images.d.ts +17 -0
- package/dist/esm/core/images.js +308 -0
- package/dist/esm/core/line-breaker/index.d.ts +1 -1
- package/dist/esm/core/line-breaker/knuth-plass.d.ts +1 -0
- package/dist/esm/core/line-breaker/knuth-plass.js +2 -2
- package/dist/esm/core/numbering/apply.d.ts +3 -0
- package/dist/esm/core/numbering/apply.js +62 -0
- package/dist/esm/core/numbering/index.d.ts +1 -0
- package/dist/esm/core/opc/package.d.ts +2 -1
- package/dist/esm/core/opc/package.js +6 -2
- package/dist/esm/core/style-cascade/index.d.ts +1 -1
- package/dist/esm/core/style-cascade/resolver.d.ts +3 -1
- package/dist/esm/core/style-cascade/resolver.js +69 -2
- package/dist/esm/core/style-cascade/types.d.ts +2 -1
- package/dist/esm/core/vector.d.ts +45 -0
- package/dist/esm/core/vector.js +48 -0
- package/dist/esm/excel/xlsx-reader.js +2 -2
- package/dist/esm/index.d.ts +3 -3
- package/dist/esm/index.js +2 -1
- package/dist/esm/{pdf → layout}/arc-to-bezier.d.ts +1 -1
- package/dist/esm/{pdf → layout}/arc-to-bezier.js +2 -2
- package/dist/esm/{pdf → layout}/math-layout.d.ts +1 -1
- package/dist/esm/{pdf → layout}/math-layout.js +1 -1
- package/dist/esm/layout/page-doc.d.ts +133 -0
- package/dist/esm/layout/page-doc.js +38 -0
- package/dist/esm/{pdf → layout}/preset-geometry.d.ts +1 -1
- package/dist/esm/{pdf → layout}/preset-geometry.js +2 -2
- package/dist/esm/layout/styled-layout.d.ts +84 -0
- package/dist/esm/layout/styled-layout.js +1729 -0
- package/dist/esm/pdf/cid-font.d.ts +0 -6
- package/dist/esm/pdf/cid-font.js +2 -28
- package/dist/esm/pdf/image-xobject.d.ts +3 -17
- package/dist/esm/pdf/image-xobject.js +2 -306
- package/dist/esm/pdf/index.js +1 -0
- package/dist/esm/pdf/styled-page-emitter.d.ts +8 -2
- package/dist/esm/pdf/styled-page-emitter.js +29 -14
- package/dist/esm/pdf/styled-page-renderer.d.ts +4 -217
- package/dist/esm/pdf/styled-page-renderer.js +3 -1824
- package/dist/esm/pdf/vector-graphics.d.ts +3 -45
- package/dist/esm/pdf/vector-graphics.js +2 -46
- package/dist/esm/svg/svg-writer.d.ts +1 -1
- package/dist/esm/svg/svg-writer.js +14 -15
- package/dist/esm/word/docx-reader.js +4 -3
- package/package.json +1 -1
- /package/dist/esm/{pdf → layout}/chart-geometry.d.ts +0 -0
- /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`, `
|
|
151
|
-
|
|
152
|
-
|
|
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';
|
|
@@ -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 "../../
|
|
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:
|
|
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 {
|
|
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';
|
|
@@ -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 >
|
|
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 };
|
|
@@ -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):
|
|
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
|
-
|
|
63
|
-
|
|
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): Array<BodyElement>;
|
|
7
|
+
export declare function resolveHeadersFootersStyles(hf: ReadonlyMap<string, ReadonlyArray<BodyElement>>, sheet: StyleSheet): ReadonlyMap<string, ReadonlyArray<BodyElement>>;
|