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
|
@@ -12,10 +12,4 @@ export interface EmbeddedFont {
|
|
|
12
12
|
readonly textWidthPt: (text: string, fontSize: number) => number;
|
|
13
13
|
readonly encodeTextAsCidHex: (text: string) => string;
|
|
14
14
|
}
|
|
15
|
-
export interface FontMeasure {
|
|
16
|
-
readonly pdfWidthForGid: (gid: number) => number;
|
|
17
|
-
readonly textWidthPt: (text: string, fontSize: number) => number;
|
|
18
|
-
readonly encodeTextAsCidHex: (text: string) => string;
|
|
19
|
-
}
|
|
20
|
-
export declare function createFontMeasure(parsed: ParsedTtf): FontMeasure;
|
|
21
15
|
export declare function embedTtfFont(doc: PdfDocument, parsed: ParsedTtf, options?: EmbedTtfOptions): EmbeddedFont;
|
package/dist/esm/pdf/cid-font.js
CHANGED
|
@@ -1,34 +1,8 @@
|
|
|
1
|
-
import { shapeText } from "../core/font/opentype-layout.js";
|
|
2
1
|
import { glyphClosure, subsetTtf } from "../core/font/ttf-subset.js";
|
|
2
|
+
import { createFontMeasure } from "../core/font/measure.js";
|
|
3
3
|
import { dict, name, ref, stream } from "./objects.js";
|
|
4
4
|
//#region src/pdf/cid-font.ts
|
|
5
5
|
var encoder = new TextEncoder();
|
|
6
|
-
function createFontMeasure(parsed) {
|
|
7
|
-
const scale = 1e3 / parsed.unitsPerEm;
|
|
8
|
-
const widths = new Array(parsed.numGlyphs);
|
|
9
|
-
for (let i = 0; i < parsed.numGlyphs; i++) widths[i] = Math.round((parsed.advanceWidths[i] ?? 0) * scale);
|
|
10
|
-
const pdfWidthForGid = (gid) => {
|
|
11
|
-
if (gid < 0 || gid >= parsed.numGlyphs) return 1e3;
|
|
12
|
-
return widths[gid];
|
|
13
|
-
};
|
|
14
|
-
const textWidthPt = (text, fontSize) => {
|
|
15
|
-
const shaped = shapeText(text, parsed.glyphForCodepoint, parsed.advanceWidths, parsed.ligatures, parsed.kerning, parsed.joiningForms);
|
|
16
|
-
let totalEm = 0;
|
|
17
|
-
for (const a of shaped.advances) totalEm += a;
|
|
18
|
-
return totalEm * fontSize / parsed.unitsPerEm;
|
|
19
|
-
};
|
|
20
|
-
const encodeTextAsCidHex = (text) => {
|
|
21
|
-
const shaped = shapeText(text, parsed.glyphForCodepoint, parsed.advanceWidths, parsed.ligatures, parsed.kerning, parsed.joiningForms);
|
|
22
|
-
let out = "";
|
|
23
|
-
for (const gid of shaped.gids) out += gid.toString(16).padStart(4, "0").toUpperCase();
|
|
24
|
-
return out;
|
|
25
|
-
};
|
|
26
|
-
return {
|
|
27
|
-
pdfWidthForGid,
|
|
28
|
-
textWidthPt,
|
|
29
|
-
encodeTextAsCidHex
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
6
|
function embedTtfFont(doc, parsed, options = {}) {
|
|
33
7
|
const scale = 1e3 / parsed.unitsPerEm;
|
|
34
8
|
const toPdfUnits = (v) => Math.round(v * scale);
|
|
@@ -182,4 +156,4 @@ function buildToUnicodeCMap(parsed) {
|
|
|
182
156
|
return encoder.encode(lines.join("\n"));
|
|
183
157
|
}
|
|
184
158
|
//#endregion
|
|
185
|
-
export {
|
|
159
|
+
export { embedTtfFont };
|
|
@@ -1,26 +1,12 @@
|
|
|
1
1
|
import { PdfRef } from './objects.js';
|
|
2
2
|
import { PdfDocument } from './writer.js';
|
|
3
|
-
|
|
3
|
+
import { EmbedImageOptions, PreparedImage } from '../core/images.js';
|
|
4
|
+
export { detectImageFormat, prepareImage } from '../core/images.js';
|
|
5
|
+
export type { EmbedImageOptions, ImageFormat, PreparedImage } from '../core/images.js';
|
|
4
6
|
export interface EmbeddedImage {
|
|
5
7
|
readonly ref: PdfRef;
|
|
6
8
|
readonly widthPx: number;
|
|
7
9
|
readonly heightPx: number;
|
|
8
10
|
}
|
|
9
|
-
export declare function detectImageFormat(bytes: Uint8Array): ImageFormat | null;
|
|
10
|
-
export interface EmbedImageOptions {
|
|
11
|
-
readonly flattenAlpha?: boolean;
|
|
12
|
-
}
|
|
13
|
-
export interface PreparedImage {
|
|
14
|
-
readonly format: ImageFormat;
|
|
15
|
-
readonly mimeType: 'image/jp2' | 'image/jpeg' | 'image/png';
|
|
16
|
-
readonly widthPx: number;
|
|
17
|
-
readonly heightPx: number;
|
|
18
|
-
readonly colorSpace?: 'DeviceGray' | 'DeviceRGB';
|
|
19
|
-
readonly bitsPerComponent?: number;
|
|
20
|
-
readonly filter: 'DCTDecode' | 'FlateDecode' | 'JPXDecode';
|
|
21
|
-
readonly data: Uint8Array;
|
|
22
|
-
readonly smaskData?: Uint8Array;
|
|
23
|
-
}
|
|
24
|
-
export declare function prepareImage(bytes: Uint8Array, options?: EmbedImageOptions): PreparedImage;
|
|
25
11
|
export declare function addImage(doc: PdfDocument, prepared: PreparedImage): EmbeddedImage;
|
|
26
12
|
export declare function embedImage(doc: PdfDocument, bytes: Uint8Array, options?: EmbedImageOptions): EmbeddedImage;
|
|
@@ -1,20 +1,6 @@
|
|
|
1
1
|
import { name, stream } from "./objects.js";
|
|
2
|
-
import
|
|
2
|
+
import "../core/images.js";
|
|
3
3
|
//#region src/pdf/image-xobject.ts
|
|
4
|
-
function detectImageFormat(bytes) {
|
|
5
|
-
if (bytes.length >= 3 && bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255) return "jpeg";
|
|
6
|
-
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";
|
|
7
|
-
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";
|
|
8
|
-
if (bytes.length >= 4 && bytes[0] === 255 && bytes[1] === 79 && bytes[2] === 255 && bytes[3] === 81) return "jpeg2000";
|
|
9
|
-
return null;
|
|
10
|
-
}
|
|
11
|
-
function prepareImage(bytes, options = {}) {
|
|
12
|
-
const format = detectImageFormat(bytes);
|
|
13
|
-
if (format === "jpeg") return prepareJpeg(bytes);
|
|
14
|
-
if (format === "png") return preparePng(bytes, options);
|
|
15
|
-
if (format === "jpeg2000") return prepareJpeg2000(bytes);
|
|
16
|
-
throw new Error("Unsupported image format");
|
|
17
|
-
}
|
|
18
4
|
function addImage(doc, prepared) {
|
|
19
5
|
let smaskRef;
|
|
20
6
|
if (prepared.smaskData) smaskRef = doc.add(stream({
|
|
@@ -42,295 +28,5 @@ function addImage(doc, prepared) {
|
|
|
42
28
|
heightPx: prepared.heightPx
|
|
43
29
|
};
|
|
44
30
|
}
|
|
45
|
-
function prepareJpeg2000(bytes) {
|
|
46
|
-
const { width, height } = readJpeg2000Info(bytes);
|
|
47
|
-
return {
|
|
48
|
-
format: "jpeg2000",
|
|
49
|
-
mimeType: "image/jp2",
|
|
50
|
-
widthPx: width,
|
|
51
|
-
heightPx: height,
|
|
52
|
-
filter: "JPXDecode",
|
|
53
|
-
data: bytes
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
function readU32(b, o) {
|
|
57
|
-
return b[o] * 16777216 + (b[o + 1] << 16) + (b[o + 2] << 8) + b[o + 3];
|
|
58
|
-
}
|
|
59
|
-
var boxType = (b, o) => String.fromCharCode(b[o], b[o + 1], b[o + 2], b[o + 3]);
|
|
60
|
-
function readJpeg2000Info(bytes) {
|
|
61
|
-
if (bytes[0] === 255 && bytes[1] === 79) return readSiz(bytes, 2);
|
|
62
|
-
let p = 0;
|
|
63
|
-
while (p + 8 <= bytes.length) {
|
|
64
|
-
let len = readU32(bytes, p);
|
|
65
|
-
const type = boxType(bytes, p + 4);
|
|
66
|
-
let contentStart = p + 8;
|
|
67
|
-
if (len === 1) {
|
|
68
|
-
len = readU32(bytes, p + 12);
|
|
69
|
-
contentStart = p + 16;
|
|
70
|
-
}
|
|
71
|
-
const end = len === 0 ? bytes.length : p + len;
|
|
72
|
-
if (type === "jp2h") {
|
|
73
|
-
let q = contentStart;
|
|
74
|
-
while (q + 8 <= end) {
|
|
75
|
-
const clen = readU32(bytes, q);
|
|
76
|
-
if (boxType(bytes, q + 4) === "ihdr") return {
|
|
77
|
-
height: readU32(bytes, q + 8),
|
|
78
|
-
width: readU32(bytes, q + 12)
|
|
79
|
-
};
|
|
80
|
-
if (clen === 0) break;
|
|
81
|
-
q += clen;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
if (len === 0) break;
|
|
85
|
-
p = end;
|
|
86
|
-
}
|
|
87
|
-
for (let i = 0; i + 22 < bytes.length; i++) if (bytes[i] === 255 && bytes[i + 1] === 81) return readSiz(bytes, i);
|
|
88
|
-
throw new Error("JPEG 2000: no ihdr/SIZ found");
|
|
89
|
-
}
|
|
90
|
-
function readSiz(bytes, sizOffset) {
|
|
91
|
-
const xsiz = readU32(bytes, sizOffset + 6);
|
|
92
|
-
const ysiz = readU32(bytes, sizOffset + 10);
|
|
93
|
-
const xo = readU32(bytes, sizOffset + 14);
|
|
94
|
-
const yo = readU32(bytes, sizOffset + 18);
|
|
95
|
-
const width = xsiz - xo;
|
|
96
|
-
const height = ysiz - yo;
|
|
97
|
-
if (width <= 0 || height <= 0) throw new Error("JPEG 2000: invalid SIZ dimensions");
|
|
98
|
-
return {
|
|
99
|
-
width,
|
|
100
|
-
height
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
function prepareJpeg(bytes) {
|
|
104
|
-
const info = readJpegInfo(bytes);
|
|
105
|
-
return {
|
|
106
|
-
format: "jpeg",
|
|
107
|
-
mimeType: "image/jpeg",
|
|
108
|
-
widthPx: info.width,
|
|
109
|
-
heightPx: info.height,
|
|
110
|
-
colorSpace: info.numComponents === 1 ? "DeviceGray" : "DeviceRGB",
|
|
111
|
-
bitsPerComponent: info.precision,
|
|
112
|
-
filter: "DCTDecode",
|
|
113
|
-
data: bytes
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
function readJpegInfo(bytes) {
|
|
117
|
-
if (bytes.length < 4 || bytes[0] !== 255 || bytes[1] !== 216) throw new Error("Not a JPEG (missing SOI)");
|
|
118
|
-
let i = 2;
|
|
119
|
-
while (i + 9 < bytes.length) {
|
|
120
|
-
if (bytes[i] !== 255) {
|
|
121
|
-
i++;
|
|
122
|
-
continue;
|
|
123
|
-
}
|
|
124
|
-
let marker = bytes[i + 1];
|
|
125
|
-
while (marker === 255) {
|
|
126
|
-
i++;
|
|
127
|
-
marker = bytes[i + 1];
|
|
128
|
-
}
|
|
129
|
-
if (marker === 1 || marker >= 208 && marker <= 217) {
|
|
130
|
-
i += 2;
|
|
131
|
-
continue;
|
|
132
|
-
}
|
|
133
|
-
if (marker >= 192 && marker <= 207 && marker !== 196 && marker !== 200 && marker !== 204) return {
|
|
134
|
-
precision: bytes[i + 4],
|
|
135
|
-
height: bytes[i + 5] << 8 | bytes[i + 6],
|
|
136
|
-
width: bytes[i + 7] << 8 | bytes[i + 8],
|
|
137
|
-
numComponents: bytes[i + 9]
|
|
138
|
-
};
|
|
139
|
-
const length = bytes[i + 2] << 8 | bytes[i + 3];
|
|
140
|
-
i += 2 + length;
|
|
141
|
-
}
|
|
142
|
-
throw new Error("JPEG SOFn marker not found");
|
|
143
|
-
}
|
|
144
|
-
function preparePng(bytes, options = {}) {
|
|
145
|
-
let decoded = decodePng(bytes);
|
|
146
|
-
if (options.flattenAlpha && decoded.smaskRaw) decoded = flattenAlphaOnWhite(decoded);
|
|
147
|
-
return {
|
|
148
|
-
format: "png",
|
|
149
|
-
mimeType: "image/png",
|
|
150
|
-
widthPx: decoded.width,
|
|
151
|
-
heightPx: decoded.height,
|
|
152
|
-
colorSpace: decoded.colorSpace,
|
|
153
|
-
bitsPerComponent: decoded.bitsPerComponent,
|
|
154
|
-
filter: "FlateDecode",
|
|
155
|
-
data: zlibSync(decoded.raw),
|
|
156
|
-
...decoded.smaskRaw ? { smaskData: zlibSync(decoded.smaskRaw) } : {}
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
function decodePng(bytes) {
|
|
160
|
-
let width = 0;
|
|
161
|
-
let height = 0;
|
|
162
|
-
let bitDepth = 0;
|
|
163
|
-
let colorType = 0;
|
|
164
|
-
let interlaceMethod = 0;
|
|
165
|
-
const idatChunks = [];
|
|
166
|
-
let pos = 8;
|
|
167
|
-
while (pos + 12 <= bytes.length) {
|
|
168
|
-
const len = readU32BE(bytes, pos);
|
|
169
|
-
const type = chunkType(bytes, pos + 4);
|
|
170
|
-
const dataOff = pos + 8;
|
|
171
|
-
if (type === "IHDR") {
|
|
172
|
-
width = readU32BE(bytes, dataOff);
|
|
173
|
-
height = readU32BE(bytes, dataOff + 4);
|
|
174
|
-
bitDepth = bytes[dataOff + 8];
|
|
175
|
-
colorType = bytes[dataOff + 9];
|
|
176
|
-
interlaceMethod = bytes[dataOff + 12];
|
|
177
|
-
} else if (type === "IDAT") idatChunks.push(bytes.subarray(dataOff, dataOff + len));
|
|
178
|
-
else if (type === "IEND") break;
|
|
179
|
-
pos = dataOff + len + 4;
|
|
180
|
-
}
|
|
181
|
-
if (width === 0 || height === 0) throw new Error("PNG: missing IHDR");
|
|
182
|
-
if (idatChunks.length === 0) throw new Error("PNG: no IDAT");
|
|
183
|
-
if (interlaceMethod !== 0) throw new Error("PNG: interlaced not supported");
|
|
184
|
-
if (bitDepth !== 8) throw new Error(`PNG: bit depth ${bitDepth} not supported`);
|
|
185
|
-
const inflated = unzlibSync(concatBytes(idatChunks));
|
|
186
|
-
const channels = pngChannels(colorType);
|
|
187
|
-
if (channels === 0) throw new Error(`PNG color type ${colorType} not supported`);
|
|
188
|
-
const bpp = channels;
|
|
189
|
-
const scanlineBytes = width * bpp;
|
|
190
|
-
const expected = height * (1 + scanlineBytes);
|
|
191
|
-
if (inflated.length !== expected) throw new Error(`PNG: inflated size ${inflated.length} != expected ${expected} (interlaced or malformed)`);
|
|
192
|
-
const raw = new Uint8Array(height * scanlineBytes);
|
|
193
|
-
let prev = null;
|
|
194
|
-
for (let y = 0; y < height; y++) {
|
|
195
|
-
const inOff = y * (1 + scanlineBytes);
|
|
196
|
-
const filterType = inflated[inOff];
|
|
197
|
-
const decoded = new Uint8Array(scanlineBytes);
|
|
198
|
-
for (let x = 0; x < scanlineBytes; x++) {
|
|
199
|
-
const filt = inflated[inOff + 1 + x];
|
|
200
|
-
const a = x < bpp ? 0 : decoded[x - bpp];
|
|
201
|
-
const b = prev ? prev[x] : 0;
|
|
202
|
-
const c = !prev || x < bpp ? 0 : prev[x - bpp];
|
|
203
|
-
let v;
|
|
204
|
-
switch (filterType) {
|
|
205
|
-
case 0:
|
|
206
|
-
v = filt;
|
|
207
|
-
break;
|
|
208
|
-
case 1:
|
|
209
|
-
v = filt + a;
|
|
210
|
-
break;
|
|
211
|
-
case 2:
|
|
212
|
-
v = filt + b;
|
|
213
|
-
break;
|
|
214
|
-
case 3:
|
|
215
|
-
v = filt + Math.floor((a + b) / 2);
|
|
216
|
-
break;
|
|
217
|
-
case 4:
|
|
218
|
-
v = filt + paethPredictor(a, b, c);
|
|
219
|
-
break;
|
|
220
|
-
default: throw new Error(`PNG: unknown filter type ${filterType}`);
|
|
221
|
-
}
|
|
222
|
-
decoded[x] = v & 255;
|
|
223
|
-
}
|
|
224
|
-
raw.set(decoded, y * scanlineBytes);
|
|
225
|
-
prev = decoded;
|
|
226
|
-
}
|
|
227
|
-
return splitChannels(width, height, colorType, raw);
|
|
228
|
-
}
|
|
229
|
-
function flattenAlphaOnWhite(decoded) {
|
|
230
|
-
const alpha = decoded.smaskRaw;
|
|
231
|
-
if (!alpha) return decoded;
|
|
232
|
-
const channels = decoded.colorSpace === "DeviceRGB" ? 3 : 1;
|
|
233
|
-
const pixelCount = decoded.width * decoded.height;
|
|
234
|
-
const out = new Uint8Array(pixelCount * channels);
|
|
235
|
-
for (let i = 0; i < pixelCount; i++) {
|
|
236
|
-
const a = alpha[i] / 255;
|
|
237
|
-
for (let c = 0; c < channels; c++) {
|
|
238
|
-
const src = decoded.raw[i * channels + c];
|
|
239
|
-
out[i * channels + c] = Math.round(src * a + 255 * (1 - a));
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
return {
|
|
243
|
-
width: decoded.width,
|
|
244
|
-
height: decoded.height,
|
|
245
|
-
raw: out,
|
|
246
|
-
colorSpace: decoded.colorSpace,
|
|
247
|
-
bitsPerComponent: decoded.bitsPerComponent
|
|
248
|
-
};
|
|
249
|
-
}
|
|
250
|
-
function splitChannels(width, height, colorType, raw) {
|
|
251
|
-
const pixelCount = width * height;
|
|
252
|
-
if (colorType === 0) return {
|
|
253
|
-
width,
|
|
254
|
-
height,
|
|
255
|
-
raw,
|
|
256
|
-
colorSpace: "DeviceGray",
|
|
257
|
-
bitsPerComponent: 8
|
|
258
|
-
};
|
|
259
|
-
if (colorType === 2) return {
|
|
260
|
-
width,
|
|
261
|
-
height,
|
|
262
|
-
raw,
|
|
263
|
-
colorSpace: "DeviceRGB",
|
|
264
|
-
bitsPerComponent: 8
|
|
265
|
-
};
|
|
266
|
-
if (colorType === 4) {
|
|
267
|
-
const gray = new Uint8Array(pixelCount);
|
|
268
|
-
const alpha = new Uint8Array(pixelCount);
|
|
269
|
-
for (let i = 0; i < pixelCount; i++) {
|
|
270
|
-
gray[i] = raw[i * 2];
|
|
271
|
-
alpha[i] = raw[i * 2 + 1];
|
|
272
|
-
}
|
|
273
|
-
return {
|
|
274
|
-
width,
|
|
275
|
-
height,
|
|
276
|
-
raw: gray,
|
|
277
|
-
colorSpace: "DeviceGray",
|
|
278
|
-
bitsPerComponent: 8,
|
|
279
|
-
smaskRaw: alpha
|
|
280
|
-
};
|
|
281
|
-
}
|
|
282
|
-
if (colorType === 6) {
|
|
283
|
-
const rgb = new Uint8Array(pixelCount * 3);
|
|
284
|
-
const alpha = new Uint8Array(pixelCount);
|
|
285
|
-
for (let i = 0; i < pixelCount; i++) {
|
|
286
|
-
rgb[i * 3] = raw[i * 4];
|
|
287
|
-
rgb[i * 3 + 1] = raw[i * 4 + 1];
|
|
288
|
-
rgb[i * 3 + 2] = raw[i * 4 + 2];
|
|
289
|
-
alpha[i] = raw[i * 4 + 3];
|
|
290
|
-
}
|
|
291
|
-
return {
|
|
292
|
-
width,
|
|
293
|
-
height,
|
|
294
|
-
raw: rgb,
|
|
295
|
-
colorSpace: "DeviceRGB",
|
|
296
|
-
bitsPerComponent: 8,
|
|
297
|
-
smaskRaw: alpha
|
|
298
|
-
};
|
|
299
|
-
}
|
|
300
|
-
throw new Error(`PNG color type ${colorType} not supported (palette/indexed?)`);
|
|
301
|
-
}
|
|
302
|
-
function pngChannels(colorType) {
|
|
303
|
-
if (colorType === 0) return 1;
|
|
304
|
-
if (colorType === 2) return 3;
|
|
305
|
-
if (colorType === 4) return 2;
|
|
306
|
-
if (colorType === 6) return 4;
|
|
307
|
-
return 0;
|
|
308
|
-
}
|
|
309
|
-
function paethPredictor(a, b, c) {
|
|
310
|
-
const p = a + b - c;
|
|
311
|
-
const pa = Math.abs(p - a);
|
|
312
|
-
const pb = Math.abs(p - b);
|
|
313
|
-
const pc = Math.abs(p - c);
|
|
314
|
-
if (pa <= pb && pa <= pc) return a;
|
|
315
|
-
if (pb <= pc) return b;
|
|
316
|
-
return c;
|
|
317
|
-
}
|
|
318
|
-
function readU32BE(bytes, offset) {
|
|
319
|
-
return bytes[offset] * 16777216 + (bytes[offset + 1] << 16 | bytes[offset + 2] << 8 | bytes[offset + 3]);
|
|
320
|
-
}
|
|
321
|
-
function chunkType(bytes, offset) {
|
|
322
|
-
return String.fromCharCode(bytes[offset], bytes[offset + 1], bytes[offset + 2], bytes[offset + 3]);
|
|
323
|
-
}
|
|
324
|
-
function concatBytes(parts) {
|
|
325
|
-
let total = 0;
|
|
326
|
-
for (const p of parts) total += p.length;
|
|
327
|
-
const out = new Uint8Array(total);
|
|
328
|
-
let off = 0;
|
|
329
|
-
for (const p of parts) {
|
|
330
|
-
out.set(p, off);
|
|
331
|
-
off += p.length;
|
|
332
|
-
}
|
|
333
|
-
return out;
|
|
334
|
-
}
|
|
335
31
|
//#endregion
|
|
336
|
-
export { addImage
|
|
32
|
+
export { addImage };
|
package/dist/esm/pdf/index.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { LaidOutPdfDocument, StyledRenderOptions } from '../layout/styled-layout.js';
|
|
2
2
|
import { PdfDocument } from './writer.js';
|
|
3
3
|
type EmitOptions = Pick<StyledRenderOptions, 'attachments' | 'info' | 'language' | 'signaturePlaceholder'>;
|
|
4
|
-
export declare function emitStyledPdf(laid:
|
|
4
|
+
export declare function emitStyledPdf(laid: LaidOutPdfDocument, options: EmitOptions, doc: PdfDocument): Uint8Array;
|
|
5
|
+
export interface PageTagging {
|
|
6
|
+
next: number;
|
|
7
|
+
assigned: boolean;
|
|
8
|
+
record: (structId: number, mcid: number) => void;
|
|
9
|
+
tagFor: (structId: number) => string;
|
|
10
|
+
}
|
|
5
11
|
export {};
|
|
@@ -2,19 +2,20 @@ import { dict, name, ref, stream, unicodeString } from "./objects.js";
|
|
|
2
2
|
import { embedTtfFont } from "./cid-font.js";
|
|
3
3
|
import { reorderVisual } from "../core/bidi/algorithm.js";
|
|
4
4
|
import { reverseByCodePoint } from "../core/bidi/index.js";
|
|
5
|
+
import "../layout/styled-layout.js";
|
|
6
|
+
import { paintPlan } from "../layout/page-doc.js";
|
|
5
7
|
import { emitVectorShape } from "./vector-graphics.js";
|
|
6
|
-
import { addImage } from "./image-xobject.js";
|
|
7
8
|
import { embedAssociatedFile } from "./embedded-file.js";
|
|
8
9
|
import { buildSrgbIccProfile } from "./icc-profile.js";
|
|
10
|
+
import { addImage } from "./image-xobject.js";
|
|
9
11
|
import { addSignaturePlaceholder } from "./signature.js";
|
|
10
12
|
import { buildXmpPacket } from "./xmp.js";
|
|
11
|
-
import { paintPlan } from "./styled-page-renderer.js";
|
|
12
13
|
//#region src/pdf/styled-page-emitter.ts
|
|
13
14
|
var encoder = new TextEncoder();
|
|
14
15
|
function emitStyledPdf(laid, options, doc) {
|
|
15
|
-
const { pages: renderedPages
|
|
16
|
-
const { sectionCtxs, pdfaProfile, tagged } = laid;
|
|
17
|
-
const embeddedFonts = embedFontResources(doc, laid);
|
|
16
|
+
const { pages: renderedPages } = laid;
|
|
17
|
+
const { structBuilder, sectionCtxs, pdfaProfile, tagged } = laid.pdf;
|
|
18
|
+
const embeddedFonts = embedFontResources(doc, laid, pdfaProfile);
|
|
18
19
|
const embeddedImages = embedImageResources(doc, laid);
|
|
19
20
|
const pagesDict = dict({
|
|
20
21
|
Type: name("Pages"),
|
|
@@ -53,7 +54,7 @@ function emitStyledPdf(laid, options, doc) {
|
|
|
53
54
|
tagFor: (structId) => builder.node(structId).type
|
|
54
55
|
};
|
|
55
56
|
}
|
|
56
|
-
const contentBytes = emitPageContent(page
|
|
57
|
+
const contentBytes = emitPageContent(page, pageTagging);
|
|
57
58
|
const contentsRef = doc.add(stream({}, contentBytes));
|
|
58
59
|
const pageEntries = {
|
|
59
60
|
Type: name("Page"),
|
|
@@ -212,8 +213,8 @@ function embedImageResources(doc, laid) {
|
|
|
212
213
|
for (const [resourceId, res] of laid.imageResources) out.set(resourceId, addImage(doc, res.prepared).ref);
|
|
213
214
|
return out;
|
|
214
215
|
}
|
|
215
|
-
function embedFontResources(doc, laid) {
|
|
216
|
-
const cidSet =
|
|
216
|
+
function embedFontResources(doc, laid, pdfaProfile) {
|
|
217
|
+
const cidSet = pdfaProfile?.part === 1;
|
|
217
218
|
const out = /* @__PURE__ */ new Map();
|
|
218
219
|
for (const [variant, res] of laid.fontResources) out.set(variant, embedTtfFont(doc, res.parsed, {
|
|
219
220
|
usedGids: res.gids,
|
|
@@ -267,8 +268,10 @@ function emitTaggedRuns(out, cmds, tagging, body) {
|
|
|
267
268
|
}
|
|
268
269
|
close();
|
|
269
270
|
}
|
|
270
|
-
function emitPageContent(
|
|
271
|
+
function emitPageContent(page, tagging) {
|
|
272
|
+
const commands = page.commands;
|
|
271
273
|
if (commands.length === 0) return new Uint8Array(0);
|
|
274
|
+
const H = page.height;
|
|
272
275
|
const out = [];
|
|
273
276
|
const plan = paintPlan(commands);
|
|
274
277
|
const fills = plan.fills;
|
|
@@ -283,7 +286,7 @@ function emitPageContent(commands, tagging) {
|
|
|
283
286
|
out.push(`${formatNumber(r)} ${formatNumber(g)} ${formatNumber(b)} rg`);
|
|
284
287
|
lastColor = color;
|
|
285
288
|
}
|
|
286
|
-
out.push(`${formatNumber(f.x)} ${formatNumber(f.y)} ${formatNumber(f.width)} ${formatNumber(f.height)} re`);
|
|
289
|
+
out.push(`${formatNumber(f.x)} ${formatNumber(H - f.y - f.height)} ${formatNumber(f.width)} ${formatNumber(f.height)} re`);
|
|
287
290
|
out.push("f");
|
|
288
291
|
}
|
|
289
292
|
out.push("Q");
|
|
@@ -291,7 +294,7 @@ function emitPageContent(commands, tagging) {
|
|
|
291
294
|
}
|
|
292
295
|
emitTaggedRuns(out, plan.images.filter((c) => c.imageResourceName !== ""), tagging, (img) => {
|
|
293
296
|
out.push("q");
|
|
294
|
-
out.push(`${formatNumber(img.width)} 0 0 ${formatNumber(img.height)} ${formatNumber(img.x)} ${formatNumber(img.y)} cm`);
|
|
297
|
+
out.push(`${formatNumber(img.width)} 0 0 ${formatNumber(img.height)} ${formatNumber(img.x)} ${formatNumber(H - img.y - img.height)} cm`);
|
|
295
298
|
out.push(`/${img.imageResourceName} Do`);
|
|
296
299
|
out.push("Q");
|
|
297
300
|
});
|
|
@@ -314,7 +317,7 @@ function emitPageContent(commands, tagging) {
|
|
|
314
317
|
lastColor = color;
|
|
315
318
|
}
|
|
316
319
|
const x = b.x;
|
|
317
|
-
const y = b.y;
|
|
320
|
+
const y = H - b.y - b.height;
|
|
318
321
|
const w = b.width;
|
|
319
322
|
const h = b.height;
|
|
320
323
|
switch (b.side) {
|
|
@@ -342,7 +345,19 @@ function emitPageContent(commands, tagging) {
|
|
|
342
345
|
}
|
|
343
346
|
const shapes = plan.shapes;
|
|
344
347
|
emitTaggedRuns(out, shapes, tagging, (sh) => {
|
|
345
|
-
|
|
348
|
+
const t = sh.shape.transform;
|
|
349
|
+
const shape = {
|
|
350
|
+
...sh.shape,
|
|
351
|
+
transform: [
|
|
352
|
+
t[0],
|
|
353
|
+
-t[1],
|
|
354
|
+
t[2],
|
|
355
|
+
-t[3],
|
|
356
|
+
t[4],
|
|
357
|
+
H - t[5]
|
|
358
|
+
]
|
|
359
|
+
};
|
|
360
|
+
for (const op of emitVectorShape(shape)) out.push(op);
|
|
346
361
|
});
|
|
347
362
|
const lines = plan.lines;
|
|
348
363
|
if (lines.length > 0) {
|
|
@@ -437,7 +452,7 @@ function emitPageContent(commands, tagging) {
|
|
|
437
452
|
const emitOneLine = (cmd) => {
|
|
438
453
|
const line = cmd.line;
|
|
439
454
|
const originX = cmd.originX;
|
|
440
|
-
const baselineY = cmd.baselineY;
|
|
455
|
+
const baselineY = H - cmd.baselineY;
|
|
441
456
|
const extraPerSpace = computeJustifyExtra(line);
|
|
442
457
|
const hasImageToken = line.tokens.some((t) => t.kind === "image");
|
|
443
458
|
const hasMathToken = line.tokens.some((t) => t.kind === "math");
|