pdf-codec 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -0
- package/dist/afm-widths-BoeTOK2r.cjs +826 -0
- package/dist/afm-widths-Dxucrg7D.js +779 -0
- package/dist/afm-widths-DyDq56Ph.d.cts +19 -0
- package/dist/afm-widths-DyDq56Ph.d.ts +19 -0
- package/dist/afm-widths.cjs +4 -0
- package/dist/afm-widths.d.cts +2 -0
- package/dist/afm-widths.d.ts +2 -0
- package/dist/afm-widths.js +2 -0
- package/dist/assets/stix-two-math-font.cjs +5 -0
- package/dist/assets/stix-two-math-font.d.cts +4 -0
- package/dist/assets/stix-two-math-font.d.ts +4 -0
- package/dist/assets/stix-two-math-font.js +4 -0
- package/dist/bytes/crc32.cjs +21 -0
- package/dist/bytes/crc32.d.cts +4 -0
- package/dist/bytes/crc32.d.ts +4 -0
- package/dist/bytes/crc32.js +20 -0
- package/dist/bytes/flate.cjs +53 -0
- package/dist/bytes/flate.d.cts +12 -0
- package/dist/bytes/flate.d.ts +12 -0
- package/dist/bytes/flate.js +49 -0
- package/dist/bytes/reader.cjs +60 -0
- package/dist/bytes/reader.d.cts +20 -0
- package/dist/bytes/reader.d.ts +20 -0
- package/dist/bytes/reader.js +58 -0
- package/dist/bytes/writer.cjs +37 -0
- package/dist/bytes/writer.d.cts +13 -0
- package/dist/bytes/writer.d.ts +13 -0
- package/dist/bytes/writer.js +35 -0
- package/dist/cmap.cjs +110 -0
- package/dist/cmap.d.cts +8 -0
- package/dist/cmap.d.ts +8 -0
- package/dist/cmap.js +109 -0
- package/dist/codec.cjs +34 -0
- package/dist/codec.d.cts +167 -0
- package/dist/codec.d.ts +167 -0
- package/dist/codec.js +32 -0
- package/dist/content-read.cjs +126 -0
- package/dist/content-read.d.cts +21 -0
- package/dist/content-read.d.ts +21 -0
- package/dist/content-read.js +125 -0
- package/dist/content-write.cjs +138 -0
- package/dist/content-write.d.cts +24 -0
- package/dist/content-write.d.ts +24 -0
- package/dist/content-write.js +137 -0
- package/dist/diagnostics.cjs +21 -0
- package/dist/diagnostics.d.cts +19 -0
- package/dist/diagnostics.d.ts +19 -0
- package/dist/diagnostics.js +18 -0
- package/dist/document.cjs +162 -0
- package/dist/document.d.cts +12 -0
- package/dist/document.d.ts +12 -0
- package/dist/document.js +161 -0
- package/dist/encoding.cjs +5 -0
- package/dist/encoding.d.cts +6 -0
- package/dist/encoding.d.ts +6 -0
- package/dist/encoding.js +2 -0
- package/dist/filters.cjs +223 -0
- package/dist/filters.d.cts +14 -0
- package/dist/filters.d.ts +14 -0
- package/dist/filters.js +218 -0
- package/dist/font-read.cjs +199 -0
- package/dist/font-read.d.cts +23 -0
- package/dist/font-read.d.ts +23 -0
- package/dist/font-read.js +198 -0
- package/dist/font-style.cjs +35 -0
- package/dist/font-style.d.cts +14 -0
- package/dist/font-style.d.ts +14 -0
- package/dist/font-style.js +34 -0
- package/dist/fonts.cjs +124 -0
- package/dist/fonts.d.cts +15 -0
- package/dist/fonts.d.ts +15 -0
- package/dist/fonts.js +122 -0
- package/dist/formula.cjs +0 -0
- package/dist/formula.d.cts +10 -0
- package/dist/formula.d.ts +10 -0
- package/dist/formula.js +0 -0
- package/dist/image/jpeg-info.cjs +71 -0
- package/dist/image/jpeg-info.d.cts +12 -0
- package/dist/image/jpeg-info.d.ts +12 -0
- package/dist/image/jpeg-info.js +70 -0
- package/dist/image/png-decode.cjs +181 -0
- package/dist/image/png-decode.d.cts +14 -0
- package/dist/image/png-decode.d.ts +14 -0
- package/dist/image/png-decode.js +180 -0
- package/dist/image/png-encode.cjs +64 -0
- package/dist/image/png-encode.d.cts +8 -0
- package/dist/image/png-encode.d.ts +8 -0
- package/dist/image/png-encode.js +63 -0
- package/dist/image/png-filter.cjs +95 -0
- package/dist/image/png-filter.d.cts +6 -0
- package/dist/image/png-filter.d.ts +6 -0
- package/dist/image/png-filter.js +93 -0
- package/dist/images-read.cjs +250 -0
- package/dist/images-read.d.cts +13 -0
- package/dist/images-read.d.ts +13 -0
- package/dist/images-read.js +249 -0
- package/dist/index.cjs +45 -5912
- package/dist/index.d.cts +21 -483
- package/dist/index.d.ts +21 -483
- package/dist/index.js +18 -5885
- package/dist/interpret.cjs +587 -0
- package/dist/interpret.d.cts +84 -0
- package/dist/interpret.d.ts +84 -0
- package/dist/interpret.js +586 -0
- package/dist/lexer.cjs +180 -0
- package/dist/lexer.d.cts +29 -0
- package/dist/lexer.d.ts +29 -0
- package/dist/lexer.js +179 -0
- package/dist/math-cmap.cjs +81 -0
- package/dist/math-cmap.d.cts +6 -0
- package/dist/math-cmap.d.ts +6 -0
- package/dist/math-cmap.js +80 -0
- package/dist/math-content-write.cjs +83 -0
- package/dist/math-content-write.d.cts +11 -0
- package/dist/math-content-write.d.ts +11 -0
- package/dist/math-content-write.js +81 -0
- package/dist/math-font-write.cjs +100 -0
- package/dist/math-font-write.d.cts +19 -0
- package/dist/math-font-write.d.ts +19 -0
- package/dist/math-font-write.js +99 -0
- package/dist/math-font.cjs +103 -0
- package/dist/math-font.d.cts +25 -0
- package/dist/math-font.d.ts +25 -0
- package/dist/math-font.js +102 -0
- package/dist/math-hmtx.cjs +17 -0
- package/dist/math-hmtx.d.cts +8 -0
- package/dist/math-hmtx.d.ts +8 -0
- package/dist/math-hmtx.js +16 -0
- package/dist/math-table.cjs +117 -0
- package/dist/math-table.d.cts +45 -0
- package/dist/math-table.d.ts +45 -0
- package/dist/math-table.js +116 -0
- package/dist/math-types-Ba8qAf1p.d.cts +82 -0
- package/dist/math-types-Ba8qAf1p.d.ts +82 -0
- package/dist/math-types.cjs +0 -0
- package/dist/math-types.d.cts +2 -0
- package/dist/math-types.d.ts +2 -0
- package/dist/math-types.js +0 -0
- package/dist/matrix-B3c2_a2f.d.cts +18 -0
- package/dist/matrix-B3c2_a2f.d.ts +18 -0
- package/dist/matrix.cjs +93 -0
- package/dist/matrix.d.cts +2 -0
- package/dist/matrix.d.ts +2 -0
- package/dist/matrix.js +83 -0
- package/dist/measure.cjs +54 -0
- package/dist/measure.d.cts +20 -0
- package/dist/measure.d.ts +20 -0
- package/dist/measure.js +53 -0
- package/dist/objects.cjs +100 -0
- package/dist/objects.d.cts +58 -0
- package/dist/objects.d.ts +58 -0
- package/dist/objects.js +84 -0
- package/dist/parse.cjs +230 -0
- package/dist/parse.d.cts +13 -0
- package/dist/parse.d.ts +13 -0
- package/dist/parse.js +228 -0
- package/dist/predictors.cjs +64 -0
- package/dist/predictors.d.cts +13 -0
- package/dist/predictors.d.ts +13 -0
- package/dist/predictors.js +62 -0
- package/dist/read.cjs +418 -0
- package/dist/read.d.cts +21 -0
- package/dist/read.d.ts +21 -0
- package/dist/read.js +413 -0
- package/dist/serialize.cjs +69 -0
- package/dist/serialize.d.cts +8 -0
- package/dist/serialize.d.ts +8 -0
- package/dist/serialize.js +66 -0
- package/dist/sfnt.cjs +52 -0
- package/dist/sfnt.d.cts +17 -0
- package/dist/sfnt.d.ts +17 -0
- package/dist/sfnt.js +46 -0
- package/dist/text-layout.cjs +224 -0
- package/dist/text-layout.d.cts +37 -0
- package/dist/text-layout.d.ts +37 -0
- package/dist/text-layout.js +222 -0
- package/dist/util/abort.cjs +7 -0
- package/dist/util/abort.d.cts +4 -0
- package/dist/util/abort.d.ts +4 -0
- package/dist/util/abort.js +6 -0
- package/dist/util/base64.cjs +48 -0
- package/dist/util/base64.d.cts +5 -0
- package/dist/util/base64.d.ts +5 -0
- package/dist/util/base64.js +46 -0
- package/dist/winansi.cjs +5 -0
- package/dist/winansi.d.cts +20 -0
- package/dist/winansi.d.ts +20 -0
- package/dist/winansi.js +2 -0
- package/dist/write.cjs +421 -0
- package/dist/write.d.cts +16 -0
- package/dist/write.d.ts +16 -0
- package/dist/write.js +419 -0
- package/dist/xref.cjs +325 -0
- package/dist/xref.d.cts +21 -0
- package/dist/xref.d.ts +21 -0
- package/dist/xref.js +324 -0
- package/package.json +9 -1
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_objects = require("./objects.cjs");
|
|
3
|
+
const require_filters = require("./filters.cjs");
|
|
4
|
+
const require_image_png_encode = require("./image/png-encode.cjs");
|
|
5
|
+
const require_image_jpeg_info = require("./image/jpeg-info.cjs");
|
|
6
|
+
//#region src/images-read.ts
|
|
7
|
+
function componentsOf(cs) {
|
|
8
|
+
if (cs.kind === "gray") return 1;
|
|
9
|
+
if (cs.kind === "cmyk") return 4;
|
|
10
|
+
return 3;
|
|
11
|
+
}
|
|
12
|
+
function resolveColorSpace(csObj, resolver, sink) {
|
|
13
|
+
const resolved = resolver.resolve(csObj);
|
|
14
|
+
const directName = resolved?.kind === "name" ? resolved.name : void 0;
|
|
15
|
+
if (directName !== void 0) {
|
|
16
|
+
if (directName === "DeviceGray" || directName === "CalGray" || directName === "G") return { kind: "gray" };
|
|
17
|
+
if (directName === "DeviceRGB" || directName === "CalRGB" || directName === "RGB") return { kind: "rgb" };
|
|
18
|
+
if (directName === "DeviceCMYK" || directName === "CMYK") return { kind: "cmyk" };
|
|
19
|
+
return {
|
|
20
|
+
kind: "unsupported",
|
|
21
|
+
name: directName
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
const arr = resolved?.kind === "array" ? resolved.items : void 0;
|
|
25
|
+
if (arr === void 0) return {
|
|
26
|
+
kind: "unsupported",
|
|
27
|
+
name: "(missing or invalid /ColorSpace)"
|
|
28
|
+
};
|
|
29
|
+
const family = require_objects.asName(arr[0]);
|
|
30
|
+
if (family === "ICCBased") {
|
|
31
|
+
const streamObj = resolver.resolve(arr[1]);
|
|
32
|
+
const n = streamObj?.kind === "stream" ? require_objects.asNumber(require_objects.dictGet(streamObj.dict, "N")) : void 0;
|
|
33
|
+
if (n === 1) return { kind: "gray" };
|
|
34
|
+
if (n === 4) return { kind: "cmyk" };
|
|
35
|
+
return { kind: "rgb" };
|
|
36
|
+
}
|
|
37
|
+
if (family === "Indexed") {
|
|
38
|
+
const base = resolveColorSpace(arr[1], resolver, sink);
|
|
39
|
+
const lookupObj = resolver.resolve(arr[3]);
|
|
40
|
+
let lookup;
|
|
41
|
+
if (lookupObj?.kind === "stream") lookup = require_filters.decodeStream(lookupObj.raw, lookupObj.dict, sink).bytes;
|
|
42
|
+
else if (lookupObj?.kind === "string") lookup = lookupObj.bytes;
|
|
43
|
+
else lookup = /* @__PURE__ */ new Uint8Array(0);
|
|
44
|
+
return {
|
|
45
|
+
kind: "indexed",
|
|
46
|
+
base,
|
|
47
|
+
lookup
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
if (family === "CalGray") return { kind: "gray" };
|
|
51
|
+
if (family === "CalRGB") return { kind: "rgb" };
|
|
52
|
+
return {
|
|
53
|
+
kind: "unsupported",
|
|
54
|
+
name: family ?? "(unrecognised array colour space)"
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function unpackSamples(data, width, height, componentsPerPixel, bitsPerComponent) {
|
|
58
|
+
if (bitsPerComponent === 8) return Array.from(data.subarray(0, width * height * componentsPerPixel));
|
|
59
|
+
const samplesPerRow = width * componentsPerPixel;
|
|
60
|
+
const bytesPerRow = Math.ceil(samplesPerRow * bitsPerComponent / 8);
|
|
61
|
+
const out = [];
|
|
62
|
+
for (let row = 0; row < height; row++) {
|
|
63
|
+
const rowStart = row * bytesPerRow;
|
|
64
|
+
let bitPos = 0;
|
|
65
|
+
for (let s = 0; s < samplesPerRow; s++) {
|
|
66
|
+
let value = 0;
|
|
67
|
+
for (let b = 0; b < bitsPerComponent; b++) {
|
|
68
|
+
const byteIndex = rowStart + Math.floor(bitPos / 8);
|
|
69
|
+
const bitIndex = 7 - bitPos % 8;
|
|
70
|
+
const bit = (data[byteIndex] ?? 0) >> bitIndex & 1;
|
|
71
|
+
value = value << 1 | bit;
|
|
72
|
+
bitPos++;
|
|
73
|
+
}
|
|
74
|
+
out.push(value);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return out;
|
|
78
|
+
}
|
|
79
|
+
function scaleToByte(value, maxValue, inverted) {
|
|
80
|
+
const v = inverted ? maxValue - value : value;
|
|
81
|
+
return maxValue === 255 ? v : Math.round(v * 255 / maxValue);
|
|
82
|
+
}
|
|
83
|
+
function cmykToRgbByte(c, m, y, k) {
|
|
84
|
+
return {
|
|
85
|
+
r: Math.round(255 * (1 - c) * (1 - k)),
|
|
86
|
+
g: Math.round(255 * (1 - m) * (1 - k)),
|
|
87
|
+
b: Math.round(255 * (1 - y) * (1 - k))
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function sampleFromPalette(base, lookup, offset) {
|
|
91
|
+
if (base.kind === "gray") {
|
|
92
|
+
const g = lookup[offset] ?? 0;
|
|
93
|
+
return {
|
|
94
|
+
r: g,
|
|
95
|
+
g,
|
|
96
|
+
b: g
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
if (base.kind === "cmyk") return cmykToRgbByte((lookup[offset] ?? 0) / 255, (lookup[offset + 1] ?? 0) / 255, (lookup[offset + 2] ?? 0) / 255, (lookup[offset + 3] ?? 0) / 255);
|
|
100
|
+
return {
|
|
101
|
+
r: lookup[offset] ?? 0,
|
|
102
|
+
g: lookup[offset + 1] ?? 0,
|
|
103
|
+
b: lookup[offset + 2] ?? 0
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
function buildRawImage(data, width, height, bitsPerComponent, colorSpace, inverted) {
|
|
107
|
+
const maxValue = (1 << bitsPerComponent) - 1;
|
|
108
|
+
if (colorSpace.kind === "indexed") {
|
|
109
|
+
const indices = unpackSamples(data, width, height, 1, bitsPerComponent);
|
|
110
|
+
const baseComponents = componentsOf(colorSpace.base);
|
|
111
|
+
const out = new Uint8Array(width * height * (colorSpace.base.kind === "gray" ? 1 : 3));
|
|
112
|
+
let outIdx = 0;
|
|
113
|
+
for (const index of indices) {
|
|
114
|
+
const rgb = sampleFromPalette(colorSpace.base, colorSpace.lookup, index * baseComponents);
|
|
115
|
+
if (colorSpace.base.kind === "gray") out[outIdx++] = rgb.r;
|
|
116
|
+
else {
|
|
117
|
+
out[outIdx++] = rgb.r;
|
|
118
|
+
out[outIdx++] = rgb.g;
|
|
119
|
+
out[outIdx++] = rgb.b;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
width,
|
|
124
|
+
height,
|
|
125
|
+
channels: colorSpace.base.kind === "gray" ? 1 : 3,
|
|
126
|
+
data: out
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
if (colorSpace.kind === "gray") {
|
|
130
|
+
const samples = unpackSamples(data, width, height, 1, bitsPerComponent);
|
|
131
|
+
return {
|
|
132
|
+
width,
|
|
133
|
+
height,
|
|
134
|
+
channels: 1,
|
|
135
|
+
data: Uint8Array.from(samples, (v) => scaleToByte(v, maxValue, inverted))
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
if (colorSpace.kind === "rgb") {
|
|
139
|
+
const samples = unpackSamples(data, width, height, 3, bitsPerComponent);
|
|
140
|
+
return {
|
|
141
|
+
width,
|
|
142
|
+
height,
|
|
143
|
+
channels: 3,
|
|
144
|
+
data: Uint8Array.from(samples, (v) => scaleToByte(v, maxValue, inverted))
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
const samples = unpackSamples(data, width, height, 4, bitsPerComponent);
|
|
148
|
+
const out = new Uint8Array(width * height * 3);
|
|
149
|
+
for (let px = 0; px < width * height; px++) {
|
|
150
|
+
const rgb = cmykToRgbByte(scaleToByte(samples[px * 4] ?? 0, maxValue, inverted) / 255, scaleToByte(samples[px * 4 + 1] ?? 0, maxValue, inverted) / 255, scaleToByte(samples[px * 4 + 2] ?? 0, maxValue, inverted) / 255, scaleToByte(samples[px * 4 + 3] ?? 0, maxValue, inverted) / 255);
|
|
151
|
+
out[px * 3] = rgb.r;
|
|
152
|
+
out[px * 3 + 1] = rgb.g;
|
|
153
|
+
out[px * 3 + 2] = rgb.b;
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
width,
|
|
157
|
+
height,
|
|
158
|
+
channels: 3,
|
|
159
|
+
data: out
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
function readSoftMaskAlpha(dict, width, height, resolver, sink) {
|
|
163
|
+
const smaskObj = resolver.resolve(require_objects.dictGet(dict, "SMask"));
|
|
164
|
+
if (smaskObj?.kind !== "stream") return;
|
|
165
|
+
const decoded = require_filters.decodeStream(smaskObj.raw, smaskObj.dict, sink);
|
|
166
|
+
if (decoded.remainingFilter !== void 0) return;
|
|
167
|
+
const smaskWidth = require_objects.asNumber(require_objects.dictGet(smaskObj.dict, "Width")) ?? width;
|
|
168
|
+
const smaskHeight = require_objects.asNumber(require_objects.dictGet(smaskObj.dict, "Height")) ?? height;
|
|
169
|
+
const smaskBpc = require_objects.asNumber(require_objects.dictGet(smaskObj.dict, "BitsPerComponent")) ?? 8;
|
|
170
|
+
if (smaskWidth !== width || smaskHeight !== height || smaskBpc !== 8) return;
|
|
171
|
+
return decoded.bytes.subarray(0, width * height);
|
|
172
|
+
}
|
|
173
|
+
function isTrue(obj) {
|
|
174
|
+
return obj?.kind === "bool" && obj.value;
|
|
175
|
+
}
|
|
176
|
+
function readImageXObject(dict, raw, resolver, sink) {
|
|
177
|
+
if (isTrue(require_objects.dictGet(dict, "ImageMask") ?? require_objects.dictGet(dict, "IM"))) {
|
|
178
|
+
sink({
|
|
179
|
+
code: "image/mask-unsupported",
|
|
180
|
+
severity: "info",
|
|
181
|
+
message: "an /ImageMask stencil paints with the current fill colour rather than standing alone as an image; skipping"
|
|
182
|
+
});
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
const decoded = require_filters.decodeStream(raw, dict, sink);
|
|
186
|
+
if (decoded.remainingFilter === "DCTDecode") {
|
|
187
|
+
let info;
|
|
188
|
+
try {
|
|
189
|
+
info = require_image_jpeg_info.readJpegInfo(decoded.bytes);
|
|
190
|
+
} catch {
|
|
191
|
+
sink({
|
|
192
|
+
code: "image/undecodable",
|
|
193
|
+
severity: "warning",
|
|
194
|
+
message: "DCTDecode image bytes did not look like a valid JPEG (no SOF marker found); skipping this image"
|
|
195
|
+
});
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
format: "jpeg",
|
|
200
|
+
bytes: decoded.bytes,
|
|
201
|
+
widthPx: info.width,
|
|
202
|
+
heightPx: info.height
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
if (decoded.remainingFilter !== void 0) return;
|
|
206
|
+
const width = require_objects.asNumber(require_objects.dictGet(dict, "Width") ?? require_objects.dictGet(dict, "W"));
|
|
207
|
+
const height = require_objects.asNumber(require_objects.dictGet(dict, "Height") ?? require_objects.dictGet(dict, "H"));
|
|
208
|
+
if (width === void 0 || height === void 0 || width <= 0 || height <= 0) {
|
|
209
|
+
sink({
|
|
210
|
+
code: "image/undecodable",
|
|
211
|
+
severity: "warning",
|
|
212
|
+
message: "image XObject is missing a valid /Width or /Height; skipping"
|
|
213
|
+
});
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
const bitsPerComponent = require_objects.asNumber(require_objects.dictGet(dict, "BitsPerComponent") ?? require_objects.dictGet(dict, "BPC")) ?? 8;
|
|
217
|
+
if (bitsPerComponent !== 1 && bitsPerComponent !== 2 && bitsPerComponent !== 4 && bitsPerComponent !== 8) {
|
|
218
|
+
sink({
|
|
219
|
+
code: "image/unsupported-bit-depth",
|
|
220
|
+
severity: "warning",
|
|
221
|
+
message: `image has an unsupported /BitsPerComponent (${String(bitsPerComponent)}); skipping`
|
|
222
|
+
});
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
const colorSpace = resolveColorSpace(require_objects.dictGet(dict, "ColorSpace") ?? require_objects.dictGet(dict, "CS"), resolver, sink);
|
|
226
|
+
if (colorSpace.kind === "unsupported") {
|
|
227
|
+
sink({
|
|
228
|
+
code: "image/unsupported-colorspace",
|
|
229
|
+
severity: "warning",
|
|
230
|
+
message: `image has an unsupported colour space (${colorSpace.name}); skipping`
|
|
231
|
+
});
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
const decodeArr = require_objects.asArray(require_objects.dictGet(dict, "Decode"));
|
|
235
|
+
const inverted = decodeArr !== void 0 && require_objects.asNumber(decodeArr[0]) === 1 && require_objects.asNumber(decodeArr[1]) === 0;
|
|
236
|
+
const rawImage = buildRawImage(decoded.bytes, width, height, bitsPerComponent, colorSpace, inverted);
|
|
237
|
+
const alpha = readSoftMaskAlpha(dict, width, height, resolver, sink);
|
|
238
|
+
const withAlpha = alpha !== void 0 ? {
|
|
239
|
+
...rawImage,
|
|
240
|
+
alpha
|
|
241
|
+
} : rawImage;
|
|
242
|
+
return {
|
|
243
|
+
format: "png",
|
|
244
|
+
bytes: require_image_png_encode.encodePng(withAlpha),
|
|
245
|
+
widthPx: width,
|
|
246
|
+
heightPx: height
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
//#endregion
|
|
250
|
+
exports.readImageXObject = readImageXObject;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { PdfDiagnosticSink } from "./diagnostics.cjs";
|
|
2
|
+
import { PdfDict } from "./objects.cjs";
|
|
3
|
+
import { PdfObjectResolver } from "./interpret.cjs";
|
|
4
|
+
//#region src/images-read.d.ts
|
|
5
|
+
interface ExtractedPdfImage {
|
|
6
|
+
readonly format: 'png' | 'jpeg';
|
|
7
|
+
readonly bytes: Uint8Array<ArrayBuffer>;
|
|
8
|
+
readonly widthPx: number;
|
|
9
|
+
readonly heightPx: number;
|
|
10
|
+
}
|
|
11
|
+
declare function readImageXObject(dict: PdfDict, raw: Uint8Array<ArrayBuffer>, resolver: PdfObjectResolver, sink: PdfDiagnosticSink): ExtractedPdfImage | undefined;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { ExtractedPdfImage, readImageXObject };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { PdfDiagnosticSink } from "./diagnostics.js";
|
|
2
|
+
import { PdfDict } from "./objects.js";
|
|
3
|
+
import { PdfObjectResolver } from "./interpret.js";
|
|
4
|
+
//#region src/images-read.d.ts
|
|
5
|
+
interface ExtractedPdfImage {
|
|
6
|
+
readonly format: 'png' | 'jpeg';
|
|
7
|
+
readonly bytes: Uint8Array<ArrayBuffer>;
|
|
8
|
+
readonly widthPx: number;
|
|
9
|
+
readonly heightPx: number;
|
|
10
|
+
}
|
|
11
|
+
declare function readImageXObject(dict: PdfDict, raw: Uint8Array<ArrayBuffer>, resolver: PdfObjectResolver, sink: PdfDiagnosticSink): ExtractedPdfImage | undefined;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { ExtractedPdfImage, readImageXObject };
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { asArray, asName, asNumber, dictGet } from "./objects.js";
|
|
2
|
+
import { decodeStream } from "./filters.js";
|
|
3
|
+
import { encodePng } from "./image/png-encode.js";
|
|
4
|
+
import { readJpegInfo } from "./image/jpeg-info.js";
|
|
5
|
+
//#region src/images-read.ts
|
|
6
|
+
function componentsOf(cs) {
|
|
7
|
+
if (cs.kind === "gray") return 1;
|
|
8
|
+
if (cs.kind === "cmyk") return 4;
|
|
9
|
+
return 3;
|
|
10
|
+
}
|
|
11
|
+
function resolveColorSpace(csObj, resolver, sink) {
|
|
12
|
+
const resolved = resolver.resolve(csObj);
|
|
13
|
+
const directName = resolved?.kind === "name" ? resolved.name : void 0;
|
|
14
|
+
if (directName !== void 0) {
|
|
15
|
+
if (directName === "DeviceGray" || directName === "CalGray" || directName === "G") return { kind: "gray" };
|
|
16
|
+
if (directName === "DeviceRGB" || directName === "CalRGB" || directName === "RGB") return { kind: "rgb" };
|
|
17
|
+
if (directName === "DeviceCMYK" || directName === "CMYK") return { kind: "cmyk" };
|
|
18
|
+
return {
|
|
19
|
+
kind: "unsupported",
|
|
20
|
+
name: directName
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
const arr = resolved?.kind === "array" ? resolved.items : void 0;
|
|
24
|
+
if (arr === void 0) return {
|
|
25
|
+
kind: "unsupported",
|
|
26
|
+
name: "(missing or invalid /ColorSpace)"
|
|
27
|
+
};
|
|
28
|
+
const family = asName(arr[0]);
|
|
29
|
+
if (family === "ICCBased") {
|
|
30
|
+
const streamObj = resolver.resolve(arr[1]);
|
|
31
|
+
const n = streamObj?.kind === "stream" ? asNumber(dictGet(streamObj.dict, "N")) : void 0;
|
|
32
|
+
if (n === 1) return { kind: "gray" };
|
|
33
|
+
if (n === 4) return { kind: "cmyk" };
|
|
34
|
+
return { kind: "rgb" };
|
|
35
|
+
}
|
|
36
|
+
if (family === "Indexed") {
|
|
37
|
+
const base = resolveColorSpace(arr[1], resolver, sink);
|
|
38
|
+
const lookupObj = resolver.resolve(arr[3]);
|
|
39
|
+
let lookup;
|
|
40
|
+
if (lookupObj?.kind === "stream") lookup = decodeStream(lookupObj.raw, lookupObj.dict, sink).bytes;
|
|
41
|
+
else if (lookupObj?.kind === "string") lookup = lookupObj.bytes;
|
|
42
|
+
else lookup = /* @__PURE__ */ new Uint8Array(0);
|
|
43
|
+
return {
|
|
44
|
+
kind: "indexed",
|
|
45
|
+
base,
|
|
46
|
+
lookup
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
if (family === "CalGray") return { kind: "gray" };
|
|
50
|
+
if (family === "CalRGB") return { kind: "rgb" };
|
|
51
|
+
return {
|
|
52
|
+
kind: "unsupported",
|
|
53
|
+
name: family ?? "(unrecognised array colour space)"
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function unpackSamples(data, width, height, componentsPerPixel, bitsPerComponent) {
|
|
57
|
+
if (bitsPerComponent === 8) return Array.from(data.subarray(0, width * height * componentsPerPixel));
|
|
58
|
+
const samplesPerRow = width * componentsPerPixel;
|
|
59
|
+
const bytesPerRow = Math.ceil(samplesPerRow * bitsPerComponent / 8);
|
|
60
|
+
const out = [];
|
|
61
|
+
for (let row = 0; row < height; row++) {
|
|
62
|
+
const rowStart = row * bytesPerRow;
|
|
63
|
+
let bitPos = 0;
|
|
64
|
+
for (let s = 0; s < samplesPerRow; s++) {
|
|
65
|
+
let value = 0;
|
|
66
|
+
for (let b = 0; b < bitsPerComponent; b++) {
|
|
67
|
+
const byteIndex = rowStart + Math.floor(bitPos / 8);
|
|
68
|
+
const bitIndex = 7 - bitPos % 8;
|
|
69
|
+
const bit = (data[byteIndex] ?? 0) >> bitIndex & 1;
|
|
70
|
+
value = value << 1 | bit;
|
|
71
|
+
bitPos++;
|
|
72
|
+
}
|
|
73
|
+
out.push(value);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
function scaleToByte(value, maxValue, inverted) {
|
|
79
|
+
const v = inverted ? maxValue - value : value;
|
|
80
|
+
return maxValue === 255 ? v : Math.round(v * 255 / maxValue);
|
|
81
|
+
}
|
|
82
|
+
function cmykToRgbByte(c, m, y, k) {
|
|
83
|
+
return {
|
|
84
|
+
r: Math.round(255 * (1 - c) * (1 - k)),
|
|
85
|
+
g: Math.round(255 * (1 - m) * (1 - k)),
|
|
86
|
+
b: Math.round(255 * (1 - y) * (1 - k))
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function sampleFromPalette(base, lookup, offset) {
|
|
90
|
+
if (base.kind === "gray") {
|
|
91
|
+
const g = lookup[offset] ?? 0;
|
|
92
|
+
return {
|
|
93
|
+
r: g,
|
|
94
|
+
g,
|
|
95
|
+
b: g
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
if (base.kind === "cmyk") return cmykToRgbByte((lookup[offset] ?? 0) / 255, (lookup[offset + 1] ?? 0) / 255, (lookup[offset + 2] ?? 0) / 255, (lookup[offset + 3] ?? 0) / 255);
|
|
99
|
+
return {
|
|
100
|
+
r: lookup[offset] ?? 0,
|
|
101
|
+
g: lookup[offset + 1] ?? 0,
|
|
102
|
+
b: lookup[offset + 2] ?? 0
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function buildRawImage(data, width, height, bitsPerComponent, colorSpace, inverted) {
|
|
106
|
+
const maxValue = (1 << bitsPerComponent) - 1;
|
|
107
|
+
if (colorSpace.kind === "indexed") {
|
|
108
|
+
const indices = unpackSamples(data, width, height, 1, bitsPerComponent);
|
|
109
|
+
const baseComponents = componentsOf(colorSpace.base);
|
|
110
|
+
const out = new Uint8Array(width * height * (colorSpace.base.kind === "gray" ? 1 : 3));
|
|
111
|
+
let outIdx = 0;
|
|
112
|
+
for (const index of indices) {
|
|
113
|
+
const rgb = sampleFromPalette(colorSpace.base, colorSpace.lookup, index * baseComponents);
|
|
114
|
+
if (colorSpace.base.kind === "gray") out[outIdx++] = rgb.r;
|
|
115
|
+
else {
|
|
116
|
+
out[outIdx++] = rgb.r;
|
|
117
|
+
out[outIdx++] = rgb.g;
|
|
118
|
+
out[outIdx++] = rgb.b;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
width,
|
|
123
|
+
height,
|
|
124
|
+
channels: colorSpace.base.kind === "gray" ? 1 : 3,
|
|
125
|
+
data: out
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
if (colorSpace.kind === "gray") {
|
|
129
|
+
const samples = unpackSamples(data, width, height, 1, bitsPerComponent);
|
|
130
|
+
return {
|
|
131
|
+
width,
|
|
132
|
+
height,
|
|
133
|
+
channels: 1,
|
|
134
|
+
data: Uint8Array.from(samples, (v) => scaleToByte(v, maxValue, inverted))
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
if (colorSpace.kind === "rgb") {
|
|
138
|
+
const samples = unpackSamples(data, width, height, 3, bitsPerComponent);
|
|
139
|
+
return {
|
|
140
|
+
width,
|
|
141
|
+
height,
|
|
142
|
+
channels: 3,
|
|
143
|
+
data: Uint8Array.from(samples, (v) => scaleToByte(v, maxValue, inverted))
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
const samples = unpackSamples(data, width, height, 4, bitsPerComponent);
|
|
147
|
+
const out = new Uint8Array(width * height * 3);
|
|
148
|
+
for (let px = 0; px < width * height; px++) {
|
|
149
|
+
const rgb = cmykToRgbByte(scaleToByte(samples[px * 4] ?? 0, maxValue, inverted) / 255, scaleToByte(samples[px * 4 + 1] ?? 0, maxValue, inverted) / 255, scaleToByte(samples[px * 4 + 2] ?? 0, maxValue, inverted) / 255, scaleToByte(samples[px * 4 + 3] ?? 0, maxValue, inverted) / 255);
|
|
150
|
+
out[px * 3] = rgb.r;
|
|
151
|
+
out[px * 3 + 1] = rgb.g;
|
|
152
|
+
out[px * 3 + 2] = rgb.b;
|
|
153
|
+
}
|
|
154
|
+
return {
|
|
155
|
+
width,
|
|
156
|
+
height,
|
|
157
|
+
channels: 3,
|
|
158
|
+
data: out
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
function readSoftMaskAlpha(dict, width, height, resolver, sink) {
|
|
162
|
+
const smaskObj = resolver.resolve(dictGet(dict, "SMask"));
|
|
163
|
+
if (smaskObj?.kind !== "stream") return;
|
|
164
|
+
const decoded = decodeStream(smaskObj.raw, smaskObj.dict, sink);
|
|
165
|
+
if (decoded.remainingFilter !== void 0) return;
|
|
166
|
+
const smaskWidth = asNumber(dictGet(smaskObj.dict, "Width")) ?? width;
|
|
167
|
+
const smaskHeight = asNumber(dictGet(smaskObj.dict, "Height")) ?? height;
|
|
168
|
+
const smaskBpc = asNumber(dictGet(smaskObj.dict, "BitsPerComponent")) ?? 8;
|
|
169
|
+
if (smaskWidth !== width || smaskHeight !== height || smaskBpc !== 8) return;
|
|
170
|
+
return decoded.bytes.subarray(0, width * height);
|
|
171
|
+
}
|
|
172
|
+
function isTrue(obj) {
|
|
173
|
+
return obj?.kind === "bool" && obj.value;
|
|
174
|
+
}
|
|
175
|
+
function readImageXObject(dict, raw, resolver, sink) {
|
|
176
|
+
if (isTrue(dictGet(dict, "ImageMask") ?? dictGet(dict, "IM"))) {
|
|
177
|
+
sink({
|
|
178
|
+
code: "image/mask-unsupported",
|
|
179
|
+
severity: "info",
|
|
180
|
+
message: "an /ImageMask stencil paints with the current fill colour rather than standing alone as an image; skipping"
|
|
181
|
+
});
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
const decoded = decodeStream(raw, dict, sink);
|
|
185
|
+
if (decoded.remainingFilter === "DCTDecode") {
|
|
186
|
+
let info;
|
|
187
|
+
try {
|
|
188
|
+
info = readJpegInfo(decoded.bytes);
|
|
189
|
+
} catch {
|
|
190
|
+
sink({
|
|
191
|
+
code: "image/undecodable",
|
|
192
|
+
severity: "warning",
|
|
193
|
+
message: "DCTDecode image bytes did not look like a valid JPEG (no SOF marker found); skipping this image"
|
|
194
|
+
});
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
return {
|
|
198
|
+
format: "jpeg",
|
|
199
|
+
bytes: decoded.bytes,
|
|
200
|
+
widthPx: info.width,
|
|
201
|
+
heightPx: info.height
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
if (decoded.remainingFilter !== void 0) return;
|
|
205
|
+
const width = asNumber(dictGet(dict, "Width") ?? dictGet(dict, "W"));
|
|
206
|
+
const height = asNumber(dictGet(dict, "Height") ?? dictGet(dict, "H"));
|
|
207
|
+
if (width === void 0 || height === void 0 || width <= 0 || height <= 0) {
|
|
208
|
+
sink({
|
|
209
|
+
code: "image/undecodable",
|
|
210
|
+
severity: "warning",
|
|
211
|
+
message: "image XObject is missing a valid /Width or /Height; skipping"
|
|
212
|
+
});
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
const bitsPerComponent = asNumber(dictGet(dict, "BitsPerComponent") ?? dictGet(dict, "BPC")) ?? 8;
|
|
216
|
+
if (bitsPerComponent !== 1 && bitsPerComponent !== 2 && bitsPerComponent !== 4 && bitsPerComponent !== 8) {
|
|
217
|
+
sink({
|
|
218
|
+
code: "image/unsupported-bit-depth",
|
|
219
|
+
severity: "warning",
|
|
220
|
+
message: `image has an unsupported /BitsPerComponent (${String(bitsPerComponent)}); skipping`
|
|
221
|
+
});
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
const colorSpace = resolveColorSpace(dictGet(dict, "ColorSpace") ?? dictGet(dict, "CS"), resolver, sink);
|
|
225
|
+
if (colorSpace.kind === "unsupported") {
|
|
226
|
+
sink({
|
|
227
|
+
code: "image/unsupported-colorspace",
|
|
228
|
+
severity: "warning",
|
|
229
|
+
message: `image has an unsupported colour space (${colorSpace.name}); skipping`
|
|
230
|
+
});
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
const decodeArr = asArray(dictGet(dict, "Decode"));
|
|
234
|
+
const inverted = decodeArr !== void 0 && asNumber(decodeArr[0]) === 1 && asNumber(decodeArr[1]) === 0;
|
|
235
|
+
const rawImage = buildRawImage(decoded.bytes, width, height, bitsPerComponent, colorSpace, inverted);
|
|
236
|
+
const alpha = readSoftMaskAlpha(dict, width, height, resolver, sink);
|
|
237
|
+
const withAlpha = alpha !== void 0 ? {
|
|
238
|
+
...rawImage,
|
|
239
|
+
alpha
|
|
240
|
+
} : rawImage;
|
|
241
|
+
return {
|
|
242
|
+
format: "png",
|
|
243
|
+
bytes: encodePng(withAlpha),
|
|
244
|
+
widthPx: width,
|
|
245
|
+
heightPx: height
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
//#endregion
|
|
249
|
+
export { readImageXObject };
|