pdf-codec 1.5.0 → 1.7.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 +100 -9
- package/dist/assets/caladea-bold.cjs +5 -0
- package/dist/assets/caladea-bold.d.cts +4 -0
- package/dist/assets/caladea-bold.d.ts +4 -0
- package/dist/assets/caladea-bold.js +4 -0
- package/dist/assets/caladea-bolditalic.cjs +5 -0
- package/dist/assets/caladea-bolditalic.d.cts +4 -0
- package/dist/assets/caladea-bolditalic.d.ts +4 -0
- package/dist/assets/caladea-bolditalic.js +4 -0
- package/dist/assets/caladea-italic.cjs +5 -0
- package/dist/assets/caladea-italic.d.cts +4 -0
- package/dist/assets/caladea-italic.d.ts +4 -0
- package/dist/assets/caladea-italic.js +4 -0
- package/dist/assets/caladea-regular.cjs +5 -0
- package/dist/assets/caladea-regular.d.cts +4 -0
- package/dist/assets/caladea-regular.d.ts +4 -0
- package/dist/assets/caladea-regular.js +4 -0
- package/dist/assets/carlito-bold.cjs +5 -0
- package/dist/assets/carlito-bold.d.cts +4 -0
- package/dist/assets/carlito-bold.d.ts +4 -0
- package/dist/assets/carlito-bold.js +4 -0
- package/dist/assets/carlito-bolditalic.cjs +5 -0
- package/dist/assets/carlito-bolditalic.d.cts +4 -0
- package/dist/assets/carlito-bolditalic.d.ts +4 -0
- package/dist/assets/carlito-bolditalic.js +4 -0
- package/dist/assets/carlito-italic.cjs +5 -0
- package/dist/assets/carlito-italic.d.cts +4 -0
- package/dist/assets/carlito-italic.d.ts +4 -0
- package/dist/assets/carlito-italic.js +4 -0
- package/dist/assets/carlito-regular.cjs +5 -0
- package/dist/assets/carlito-regular.d.cts +4 -0
- package/dist/assets/carlito-regular.d.ts +4 -0
- package/dist/assets/carlito-regular.js +4 -0
- package/dist/cff-bounds.cjs +432 -0
- package/dist/cff-bounds.d.cts +9 -0
- package/dist/cff-bounds.d.ts +9 -0
- package/dist/cff-bounds.js +431 -0
- package/dist/cff-probe.cjs +35 -0
- package/dist/cff-probe.d.cts +10 -0
- package/dist/cff-probe.d.ts +10 -0
- package/dist/cff-probe.js +34 -0
- package/dist/cff.cjs +158 -0
- package/dist/cff.d.cts +17 -0
- package/dist/cff.d.ts +17 -0
- package/dist/cff.js +150 -0
- package/dist/cmap-table.cjs +108 -0
- package/dist/cmap-table.d.cts +6 -0
- package/dist/cmap-table.d.ts +6 -0
- package/dist/cmap-table.js +107 -0
- package/dist/content-write.cjs +34 -14
- package/dist/content-write.d.cts +9 -2
- package/dist/content-write.d.ts +9 -2
- package/dist/content-write.js +34 -14
- package/dist/embedded-font-CLedn-mT.d.cts +42 -0
- package/dist/embedded-font-CbqkKgBl.d.ts +42 -0
- package/dist/embedded-font-write.cjs +92 -0
- package/dist/embedded-font-write.d.cts +22 -0
- package/dist/embedded-font-write.d.ts +22 -0
- package/dist/embedded-font-write.js +90 -0
- package/dist/embedded-font.cjs +142 -0
- package/dist/embedded-font.d.cts +2 -0
- package/dist/embedded-font.d.ts +2 -0
- package/dist/embedded-font.js +138 -0
- package/dist/filters.cjs +45 -2
- package/dist/filters.d.cts +4 -3
- package/dist/filters.d.ts +4 -3
- package/dist/filters.js +45 -2
- package/dist/font-registry-Cn7P9lAh.d.ts +38 -0
- package/dist/font-registry-yby02snZ.d.cts +38 -0
- package/dist/font-registry.cjs +152 -0
- package/dist/font-registry.d.cts +2 -0
- package/dist/font-registry.d.ts +2 -0
- package/dist/font-registry.js +150 -0
- package/dist/font-substitutes.cjs +13 -0
- package/dist/font-substitutes.d.cts +5 -0
- package/dist/font-substitutes.d.ts +5 -0
- package/dist/font-substitutes.js +12 -0
- package/dist/font-tables.cjs +127 -0
- package/dist/font-tables.d.cts +43 -0
- package/dist/font-tables.d.ts +43 -0
- package/dist/font-tables.js +122 -0
- package/dist/fonts.cjs +1 -0
- package/dist/fonts.d.cts +2 -1
- package/dist/fonts.d.ts +2 -1
- package/dist/fonts.js +1 -1
- package/dist/formula.d.cts +1 -1
- package/dist/formula.d.ts +1 -1
- package/dist/glyf.cjs +183 -0
- package/dist/glyf.d.cts +33 -0
- package/dist/glyf.d.ts +33 -0
- package/dist/glyf.js +181 -0
- package/dist/glyph-bounds-BV_Z40KS.d.cts +10 -0
- package/dist/glyph-bounds-BV_Z40KS.d.ts +10 -0
- package/dist/glyph-bounds.cjs +12 -0
- package/dist/glyph-bounds.d.cts +2 -0
- package/dist/glyph-bounds.d.ts +2 -0
- package/dist/glyph-bounds.js +11 -0
- package/dist/{math-hmtx.cjs → hmtx-table.cjs} +3 -3
- package/dist/{math-hmtx.d.ts → hmtx-table.d.cts} +2 -2
- package/dist/{math-hmtx.d.cts → hmtx-table.d.ts} +2 -2
- package/dist/{math-hmtx.js → hmtx-table.js} +3 -3
- package/dist/image/jbig2-arith.cjs +432 -0
- package/dist/image/jbig2-arith.d.cts +23 -0
- package/dist/image/jbig2-arith.d.ts +23 -0
- package/dist/image/jbig2-arith.js +426 -0
- package/dist/image/jbig2-bitmap.cjs +80 -0
- package/dist/image/jbig2-bitmap.d.cts +20 -0
- package/dist/image/jbig2-bitmap.d.ts +20 -0
- package/dist/image/jbig2-bitmap.js +72 -0
- package/dist/image/jbig2-errors.cjs +17 -0
- package/dist/image/jbig2-errors.d.cts +2 -0
- package/dist/image/jbig2-errors.d.ts +2 -0
- package/dist/image/jbig2-errors.js +15 -0
- package/dist/image/jbig2-generic.cjs +242 -0
- package/dist/image/jbig2-generic.d.cts +27 -0
- package/dist/image/jbig2-generic.d.ts +27 -0
- package/dist/image/jbig2-generic.js +237 -0
- package/dist/image/jbig2-text.cjs +175 -0
- package/dist/image/jbig2-text.d.cts +58 -0
- package/dist/image/jbig2-text.d.ts +58 -0
- package/dist/image/jbig2-text.js +170 -0
- package/dist/image/jbig2.cjs +333 -0
- package/dist/image/jbig2.d.cts +15 -0
- package/dist/image/jbig2.d.ts +15 -0
- package/dist/image/jbig2.js +332 -0
- package/dist/image/jp2-boxes.cjs +148 -0
- package/dist/image/jp2-boxes.d.cts +26 -0
- package/dist/image/jp2-boxes.d.ts +26 -0
- package/dist/image/jp2-boxes.js +146 -0
- package/dist/image/jpeg2000-codestream.cjs +309 -0
- package/dist/image/jpeg2000-codestream.d.cts +78 -0
- package/dist/image/jpeg2000-codestream.d.ts +78 -0
- package/dist/image/jpeg2000-codestream.js +308 -0
- package/dist/image/jpeg2000-dwt.cjs +148 -0
- package/dist/image/jpeg2000-dwt.d.cts +34 -0
- package/dist/image/jpeg2000-dwt.d.ts +34 -0
- package/dist/image/jpeg2000-dwt.js +145 -0
- package/dist/image/jpeg2000-errors.cjs +17 -0
- package/dist/image/jpeg2000-errors.d.cts +9 -0
- package/dist/image/jpeg2000-errors.d.ts +9 -0
- package/dist/image/jpeg2000-errors.js +15 -0
- package/dist/image/jpeg2000-t1.cjs +254 -0
- package/dist/image/jpeg2000-t1.d.cts +18 -0
- package/dist/image/jpeg2000-t1.d.ts +18 -0
- package/dist/image/jpeg2000-t1.js +253 -0
- package/dist/image/jpeg2000-t2.cjs +316 -0
- package/dist/image/jpeg2000-t2.d.cts +79 -0
- package/dist/image/jpeg2000-t2.d.ts +79 -0
- package/dist/image/jpeg2000-t2.js +311 -0
- package/dist/image/jpeg2000-tagtree.cjs +105 -0
- package/dist/image/jpeg2000-tagtree.d.cts +29 -0
- package/dist/image/jpeg2000-tagtree.d.ts +29 -0
- package/dist/image/jpeg2000-tagtree.js +103 -0
- package/dist/image/jpeg2000.cjs +322 -0
- package/dist/image/jpeg2000.d.cts +54 -0
- package/dist/image/jpeg2000.d.ts +54 -0
- package/dist/image/jpeg2000.js +320 -0
- package/dist/images-read.cjs +78 -1
- package/dist/images-read.js +78 -1
- package/dist/index.cjs +22 -0
- package/dist/index.d.cts +14 -3
- package/dist/index.d.ts +14 -3
- package/dist/index.js +9 -2
- package/dist/jbig2-errors-Bj6MPxrx.d.cts +9 -0
- package/dist/jbig2-errors-Bj6MPxrx.d.ts +9 -0
- package/dist/math-font-write.cjs +2 -31
- package/dist/math-font-write.js +1 -30
- package/dist/math-font.cjs +50 -23
- package/dist/math-font.d.cts +8 -1
- package/dist/math-font.d.ts +8 -1
- package/dist/math-font.js +48 -21
- package/dist/math-stretch-Bwk_bxh1.d.ts +24 -0
- package/dist/math-stretch-DXWjs8gL.d.cts +24 -0
- package/dist/math-stretch.cjs +96 -0
- package/dist/math-stretch.d.cts +2 -0
- package/dist/math-stretch.d.ts +2 -0
- package/dist/math-stretch.js +94 -0
- package/dist/math-table-DhPrNoPa.d.ts +70 -0
- package/dist/math-table-gFNB8DmQ.d.cts +70 -0
- package/dist/math-table.cjs +69 -1
- package/dist/math-table.d.cts +2 -45
- package/dist/math-table.d.ts +2 -45
- package/dist/math-table.js +69 -1
- package/dist/{math-types-Ba8qAf1p.d.cts → math-types-YSQUTviw.d.cts} +2 -0
- package/dist/{math-types-Ba8qAf1p.d.ts → math-types-YSQUTviw.d.ts} +2 -0
- package/dist/math-types.d.cts +1 -1
- package/dist/math-types.d.ts +1 -1
- package/dist/measure.cjs +65 -14
- package/dist/measure.d.cts +8 -1
- package/dist/measure.d.ts +8 -1
- package/dist/measure.js +64 -15
- package/dist/sfnt-V08bb5ut.d.cts +21 -0
- package/dist/sfnt-V08bb5ut.d.ts +21 -0
- package/dist/sfnt-subset.cjs +250 -0
- package/dist/sfnt-subset.d.cts +11 -0
- package/dist/sfnt-subset.d.ts +11 -0
- package/dist/sfnt-subset.js +249 -0
- package/dist/sfnt.cjs +50 -4
- package/dist/sfnt.d.cts +2 -17
- package/dist/sfnt.d.ts +2 -17
- package/dist/sfnt.js +47 -5
- package/dist/tounicode.cjs +34 -0
- package/dist/tounicode.d.cts +5 -0
- package/dist/tounicode.d.ts +5 -0
- package/dist/tounicode.js +33 -0
- package/dist/write.cjs +80 -10
- package/dist/write.d.cts +6 -0
- package/dist/write.d.ts +6 -0
- package/dist/write.js +81 -11
- package/package.json +1 -1
- package/dist/math-cmap.cjs +0 -81
- package/dist/math-cmap.d.cts +0 -6
- package/dist/math-cmap.d.ts +0 -6
- package/dist/math-cmap.js +0 -80
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import { decodeCcittFax } from "./ccitt.js";
|
|
2
|
+
import { MqDecoder, createArithContexts } from "./jbig2-arith.js";
|
|
3
|
+
import { Jbig2ParseError, Jbig2UnsupportedError } from "./jbig2-errors.js";
|
|
4
|
+
import { combinationOperatorFromCode, combineBitmap, createBitmap, getPixel, packBitmapRows, unpackBitmapRows } from "./jbig2-bitmap.js";
|
|
5
|
+
import { GENERIC_CONTEXT_BITS, NOMINAL_REFINEMENT_AT, REFINEMENT_CONTEXT_BITS, decodeGenericRegion, decodeRefinementRegion } from "./jbig2-generic.js";
|
|
6
|
+
import { createTextArithContexts, decodeSymbolDictionary, decodeTextRegion, referenceCornerFromCode, symbolCodeLength } from "./jbig2-text.js";
|
|
7
|
+
//#region src/image/jbig2.ts
|
|
8
|
+
const SEGMENT_SYMBOL_DICTIONARY = 0;
|
|
9
|
+
const SEGMENT_INTERMEDIATE_TEXT_REGION = 4;
|
|
10
|
+
const SEGMENT_IMMEDIATE_TEXT_REGION = 6;
|
|
11
|
+
const SEGMENT_IMMEDIATE_LOSSLESS_TEXT_REGION = 7;
|
|
12
|
+
const SEGMENT_PATTERN_DICTIONARY = 16;
|
|
13
|
+
const SEGMENT_INTERMEDIATE_HALFTONE_REGION = 20;
|
|
14
|
+
const SEGMENT_IMMEDIATE_HALFTONE_REGION = 22;
|
|
15
|
+
const SEGMENT_IMMEDIATE_LOSSLESS_HALFTONE_REGION = 23;
|
|
16
|
+
const SEGMENT_INTERMEDIATE_GENERIC_REGION = 36;
|
|
17
|
+
const SEGMENT_IMMEDIATE_GENERIC_REGION = 38;
|
|
18
|
+
const SEGMENT_IMMEDIATE_LOSSLESS_GENERIC_REGION = 39;
|
|
19
|
+
const SEGMENT_INTERMEDIATE_REFINEMENT_REGION = 40;
|
|
20
|
+
const SEGMENT_IMMEDIATE_REFINEMENT_REGION = 42;
|
|
21
|
+
const SEGMENT_IMMEDIATE_LOSSLESS_REFINEMENT_REGION = 43;
|
|
22
|
+
const SEGMENT_PAGE_INFORMATION = 48;
|
|
23
|
+
const SEGMENT_END_OF_PAGE = 49;
|
|
24
|
+
const SEGMENT_END_OF_STRIPE = 50;
|
|
25
|
+
const SEGMENT_END_OF_FILE = 51;
|
|
26
|
+
const SEGMENT_PROFILES = 52;
|
|
27
|
+
const SEGMENT_TABLES = 53;
|
|
28
|
+
const SEGMENT_EXTENSION = 62;
|
|
29
|
+
const UNKNOWN_DATA_LENGTH = 4294967295;
|
|
30
|
+
const UNKNOWN_PAGE_HEIGHT = 4294967295;
|
|
31
|
+
var ByteCursor = class {
|
|
32
|
+
data;
|
|
33
|
+
position;
|
|
34
|
+
constructor(data, start = 0) {
|
|
35
|
+
this.data = data;
|
|
36
|
+
this.position = start;
|
|
37
|
+
}
|
|
38
|
+
get remaining() {
|
|
39
|
+
return this.data.length - this.position;
|
|
40
|
+
}
|
|
41
|
+
uint8() {
|
|
42
|
+
const value = this.data[this.position];
|
|
43
|
+
if (value === void 0) throw new Jbig2ParseError("JBIG2 stream ended in the middle of a segment header");
|
|
44
|
+
this.position++;
|
|
45
|
+
return value;
|
|
46
|
+
}
|
|
47
|
+
uint16() {
|
|
48
|
+
return this.uint8() << 8 | this.uint8();
|
|
49
|
+
}
|
|
50
|
+
uint32() {
|
|
51
|
+
return (this.uint8() << 24 | this.uint8() << 16 | this.uint8() << 8 | this.uint8()) >>> 0;
|
|
52
|
+
}
|
|
53
|
+
int8() {
|
|
54
|
+
const value = this.uint8();
|
|
55
|
+
return value > 127 ? value - 256 : value;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
function readSegmentHeader(cursor, dataLength) {
|
|
59
|
+
const number = cursor.uint32();
|
|
60
|
+
const flags = cursor.uint8();
|
|
61
|
+
const type = flags & 63;
|
|
62
|
+
const pageAssociationIsLong = (flags & 64) !== 0;
|
|
63
|
+
let referredCount = cursor.uint8() >> 5;
|
|
64
|
+
if (referredCount === 7) {
|
|
65
|
+
cursor.position -= 1;
|
|
66
|
+
referredCount = cursor.uint32() & 536870911;
|
|
67
|
+
cursor.position += Math.ceil((referredCount + 1) / 8);
|
|
68
|
+
}
|
|
69
|
+
const referredSize = number <= 256 ? 1 : number <= 65536 ? 2 : 4;
|
|
70
|
+
const referredTo = [];
|
|
71
|
+
for (let i = 0; i < referredCount; i++) referredTo.push(referredSize === 1 ? cursor.uint8() : referredSize === 2 ? cursor.uint16() : cursor.uint32());
|
|
72
|
+
const pageAssociation = pageAssociationIsLong ? cursor.uint32() : cursor.uint8();
|
|
73
|
+
const declaredLength = cursor.uint32();
|
|
74
|
+
if (declaredLength === UNKNOWN_DATA_LENGTH) throw new Jbig2UnsupportedError("JBIG2 segment declares an unknown data length (T.88 7.2.7), which this decoder does not scan for a terminator");
|
|
75
|
+
const dataStart = cursor.position;
|
|
76
|
+
const dataEnd = dataStart + declaredLength;
|
|
77
|
+
if (dataEnd > dataLength) throw new Jbig2ParseError(`JBIG2 segment ${String(number)} declares ${String(declaredLength)} bytes of data but only ${String(dataLength - dataStart)} remain`);
|
|
78
|
+
return {
|
|
79
|
+
number,
|
|
80
|
+
type,
|
|
81
|
+
referredTo,
|
|
82
|
+
pageAssociation,
|
|
83
|
+
dataStart,
|
|
84
|
+
dataEnd
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function readRegionInfo(cursor) {
|
|
88
|
+
const width = cursor.uint32();
|
|
89
|
+
const height = cursor.uint32();
|
|
90
|
+
const x = cursor.uint32();
|
|
91
|
+
const y = cursor.uint32();
|
|
92
|
+
const flags = cursor.uint8();
|
|
93
|
+
const combinationOperator = combinationOperatorFromCode(flags & 7);
|
|
94
|
+
if (combinationOperator === void 0) throw new Jbig2ParseError(`JBIG2 region declares external combination operator ${String(flags & 7)}, outside the 0-4 range T.88 Table 12 defines`);
|
|
95
|
+
return {
|
|
96
|
+
width,
|
|
97
|
+
height,
|
|
98
|
+
x,
|
|
99
|
+
y,
|
|
100
|
+
combinationOperator
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function readAtPixels(cursor, count) {
|
|
104
|
+
const at = [];
|
|
105
|
+
for (let i = 0; i < count; i++) at.push({
|
|
106
|
+
x: cursor.int8(),
|
|
107
|
+
y: cursor.int8()
|
|
108
|
+
});
|
|
109
|
+
return at;
|
|
110
|
+
}
|
|
111
|
+
var Jbig2Decoder = class {
|
|
112
|
+
options;
|
|
113
|
+
page;
|
|
114
|
+
exportedSymbols = /* @__PURE__ */ new Map();
|
|
115
|
+
constructor(options) {
|
|
116
|
+
this.options = options;
|
|
117
|
+
}
|
|
118
|
+
warn(message) {
|
|
119
|
+
this.options.onWarning?.(message);
|
|
120
|
+
}
|
|
121
|
+
run(data) {
|
|
122
|
+
const cursor = new ByteCursor(data);
|
|
123
|
+
while (cursor.remaining > 0) {
|
|
124
|
+
const header = readSegmentHeader(cursor, data.length);
|
|
125
|
+
this.handleSegment(header, data);
|
|
126
|
+
cursor.position = header.dataEnd;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
handleSegment(header, data) {
|
|
130
|
+
const cursor = new ByteCursor(data, header.dataStart);
|
|
131
|
+
switch (header.type) {
|
|
132
|
+
case SEGMENT_PAGE_INFORMATION:
|
|
133
|
+
this.readPageInformation(cursor);
|
|
134
|
+
return;
|
|
135
|
+
case SEGMENT_SYMBOL_DICTIONARY:
|
|
136
|
+
this.exportedSymbols.set(header.number, this.readSymbolDictionary(header, cursor));
|
|
137
|
+
return;
|
|
138
|
+
case SEGMENT_IMMEDIATE_GENERIC_REGION:
|
|
139
|
+
case SEGMENT_IMMEDIATE_LOSSLESS_GENERIC_REGION:
|
|
140
|
+
this.readGenericRegion(cursor, header.dataEnd);
|
|
141
|
+
return;
|
|
142
|
+
case SEGMENT_IMMEDIATE_TEXT_REGION:
|
|
143
|
+
case SEGMENT_IMMEDIATE_LOSSLESS_TEXT_REGION:
|
|
144
|
+
this.readTextRegion(header, cursor);
|
|
145
|
+
return;
|
|
146
|
+
case SEGMENT_IMMEDIATE_REFINEMENT_REGION:
|
|
147
|
+
case SEGMENT_IMMEDIATE_LOSSLESS_REFINEMENT_REGION:
|
|
148
|
+
this.readRefinementRegion(cursor, header.dataEnd);
|
|
149
|
+
return;
|
|
150
|
+
case SEGMENT_END_OF_PAGE:
|
|
151
|
+
case SEGMENT_END_OF_STRIPE:
|
|
152
|
+
case SEGMENT_END_OF_FILE:
|
|
153
|
+
case SEGMENT_PROFILES:
|
|
154
|
+
case SEGMENT_EXTENSION: return;
|
|
155
|
+
case SEGMENT_INTERMEDIATE_GENERIC_REGION:
|
|
156
|
+
case SEGMENT_INTERMEDIATE_TEXT_REGION:
|
|
157
|
+
case SEGMENT_INTERMEDIATE_REFINEMENT_REGION:
|
|
158
|
+
case SEGMENT_INTERMEDIATE_HALFTONE_REGION: throw new Jbig2UnsupportedError(`JBIG2 segment ${String(header.number)} is an intermediate region (type ${String(header.type)}), which is retained in an auxiliary buffer rather than composed onto the page; auxiliary buffers are not implemented`);
|
|
159
|
+
case SEGMENT_PATTERN_DICTIONARY:
|
|
160
|
+
case SEGMENT_IMMEDIATE_HALFTONE_REGION:
|
|
161
|
+
case SEGMENT_IMMEDIATE_LOSSLESS_HALFTONE_REGION: throw new Jbig2UnsupportedError(`JBIG2 segment ${String(header.number)} is a halftone region or pattern dictionary (type ${String(header.type)}), which is not implemented`);
|
|
162
|
+
case SEGMENT_TABLES: throw new Jbig2UnsupportedError(`JBIG2 segment ${String(header.number)} is a custom Huffman table, which only the Huffman-coded region forms use; those are not implemented`);
|
|
163
|
+
default: throw new Jbig2UnsupportedError(`JBIG2 segment ${String(header.number)} has unrecognised type ${String(header.type)}`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
readPageInformation(cursor) {
|
|
167
|
+
const declaredWidth = cursor.uint32();
|
|
168
|
+
const declaredHeight = cursor.uint32();
|
|
169
|
+
cursor.uint32();
|
|
170
|
+
cursor.uint32();
|
|
171
|
+
const flags = cursor.uint8();
|
|
172
|
+
cursor.uint16();
|
|
173
|
+
const width = this.options.width ?? declaredWidth;
|
|
174
|
+
const declaredOrHintedHeight = declaredHeight === UNKNOWN_PAGE_HEIGHT ? this.options.height : this.options.height ?? declaredHeight;
|
|
175
|
+
if (declaredOrHintedHeight === void 0) throw new Jbig2UnsupportedError("JBIG2 page information declares an unknown height and the caller supplied none; a striped page with no known height is not resolvable from the page segments alone");
|
|
176
|
+
const defaultPixel = flags >> 2 & 1;
|
|
177
|
+
const defaultCombinationOperator = combinationOperatorFromCode(flags >> 3 & 3);
|
|
178
|
+
if (defaultCombinationOperator === void 0) throw new Jbig2ParseError("JBIG2 page information declares an unrecognised default combination operator");
|
|
179
|
+
this.page = {
|
|
180
|
+
bitmap: createBitmap(width, declaredOrHintedHeight, defaultPixel),
|
|
181
|
+
defaultCombinationOperator,
|
|
182
|
+
combinationOperatorOverridden: (flags & 64) !== 0
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
requirePage() {
|
|
186
|
+
if (this.page === void 0) throw new Jbig2ParseError("JBIG2 stream composed a region before any page information segment declared the page");
|
|
187
|
+
return this.page;
|
|
188
|
+
}
|
|
189
|
+
compose(region, bitmap) {
|
|
190
|
+
const page = this.requirePage();
|
|
191
|
+
combineBitmap(page.bitmap, bitmap, region.x, region.y, page.combinationOperatorOverridden ? region.combinationOperator : page.defaultCombinationOperator);
|
|
192
|
+
}
|
|
193
|
+
readGenericRegion(cursor, dataEnd) {
|
|
194
|
+
const region = readRegionInfo(cursor);
|
|
195
|
+
const flags = cursor.uint8();
|
|
196
|
+
const mmr = (flags & 1) !== 0;
|
|
197
|
+
const template = flags >> 1 & 3;
|
|
198
|
+
const tpgdon = (flags & 8) !== 0;
|
|
199
|
+
if ((flags & 16) !== 0) throw new Jbig2UnsupportedError("JBIG2 generic region sets EXTTEMPLATE, the twelve-adaptive-pixel template of T.88 Amendment 2, which is not implemented");
|
|
200
|
+
const at = mmr ? [] : readAtPixels(cursor, template === 0 ? 4 : 1);
|
|
201
|
+
const payload = cursor.position;
|
|
202
|
+
if (mmr) {
|
|
203
|
+
const fax = decodeCcittFax(cursor.data.subarray(payload, dataEnd), {
|
|
204
|
+
k: -1,
|
|
205
|
+
columns: region.width,
|
|
206
|
+
rows: region.height,
|
|
207
|
+
blackIs1: true,
|
|
208
|
+
onWarning: (message) => this.warn(message)
|
|
209
|
+
});
|
|
210
|
+
this.compose(region, unpackBitmapRows(fax.bytes, region.width, region.height));
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
const mq = new MqDecoder(cursor.data, payload, dataEnd);
|
|
214
|
+
const contexts = createArithContexts(GENERIC_CONTEXT_BITS[template] ?? 16);
|
|
215
|
+
this.compose(region, decodeGenericRegion(region.width, region.height, {
|
|
216
|
+
template,
|
|
217
|
+
tpgdon,
|
|
218
|
+
at
|
|
219
|
+
}, mq, contexts));
|
|
220
|
+
}
|
|
221
|
+
readRefinementRegion(cursor, dataEnd) {
|
|
222
|
+
const region = readRegionInfo(cursor);
|
|
223
|
+
const flags = cursor.uint8();
|
|
224
|
+
const template = flags & 1;
|
|
225
|
+
const tpgron = (flags & 2) !== 0;
|
|
226
|
+
const at = template === 0 ? readAtPixels(cursor, 2) : [...NOMINAL_REFINEMENT_AT];
|
|
227
|
+
const page = this.requirePage();
|
|
228
|
+
const reference = createBitmap(region.width, region.height);
|
|
229
|
+
for (let y = 0; y < region.height; y++) for (let x = 0; x < region.width; x++) reference.data[y * region.width + x] = getPixel(page.bitmap, region.x + x, region.y + y);
|
|
230
|
+
const mq = new MqDecoder(cursor.data, cursor.position, dataEnd);
|
|
231
|
+
const contexts = createArithContexts(REFINEMENT_CONTEXT_BITS[template] ?? 13);
|
|
232
|
+
const refined = decodeRefinementRegion(region.width, region.height, {
|
|
233
|
+
template,
|
|
234
|
+
tpgron,
|
|
235
|
+
at,
|
|
236
|
+
reference,
|
|
237
|
+
dx: 0,
|
|
238
|
+
dy: 0
|
|
239
|
+
}, mq, contexts);
|
|
240
|
+
combineBitmap(page.bitmap, refined, region.x, region.y, region.combinationOperator);
|
|
241
|
+
}
|
|
242
|
+
readSymbolDictionary(header, cursor) {
|
|
243
|
+
const flags = cursor.uint16();
|
|
244
|
+
if ((flags & 1) !== 0) throw new Jbig2UnsupportedError("JBIG2 symbol dictionary is Huffman-coded (SDHUFF = 1); only the arithmetic form is implemented");
|
|
245
|
+
if ((flags & 256) !== 0 || (flags & 512) !== 0) throw new Jbig2UnsupportedError("JBIG2 symbol dictionary uses or retains a shared bitmap coding context (T.88 7.4.3.1.1 bits 8-9), which is not implemented");
|
|
246
|
+
const refinementAggregate = (flags & 2) !== 0;
|
|
247
|
+
const template = flags >> 10 & 3;
|
|
248
|
+
const refinementTemplate = flags >> 12 & 1;
|
|
249
|
+
const at = readAtPixels(cursor, template === 0 ? 4 : 1);
|
|
250
|
+
const refinementAt = refinementAggregate && refinementTemplate === 0 ? readAtPixels(cursor, 2) : [...NOMINAL_REFINEMENT_AT];
|
|
251
|
+
const exportedSymbolCount = cursor.uint32();
|
|
252
|
+
const newSymbolCount = cursor.uint32();
|
|
253
|
+
const inputSymbols = this.gatherSymbols(header.referredTo);
|
|
254
|
+
const symbolIdBits = symbolCodeLength(inputSymbols.length + newSymbolCount);
|
|
255
|
+
const mq = new MqDecoder(cursor.data, cursor.position, header.dataEnd);
|
|
256
|
+
const contexts = createTextArithContexts(template, refinementTemplate, symbolIdBits);
|
|
257
|
+
return decodeSymbolDictionary({
|
|
258
|
+
template,
|
|
259
|
+
at,
|
|
260
|
+
refinementAggregate,
|
|
261
|
+
refinementTemplate,
|
|
262
|
+
refinementAt,
|
|
263
|
+
newSymbolCount,
|
|
264
|
+
exportedSymbolCount,
|
|
265
|
+
inputSymbols
|
|
266
|
+
}, mq, contexts, symbolIdBits);
|
|
267
|
+
}
|
|
268
|
+
readTextRegion(header, cursor) {
|
|
269
|
+
const region = readRegionInfo(cursor);
|
|
270
|
+
const flags = cursor.uint16();
|
|
271
|
+
if ((flags & 1) !== 0) throw new Jbig2UnsupportedError("JBIG2 text region is Huffman-coded (SBHUFF = 1); only the arithmetic form is implemented");
|
|
272
|
+
const refine = (flags & 2) !== 0;
|
|
273
|
+
const stripSize = 1 << (flags >> 2 & 3);
|
|
274
|
+
const referenceCorner = referenceCornerFromCode(flags >> 4 & 3);
|
|
275
|
+
if (referenceCorner === void 0) throw new Jbig2ParseError("JBIG2 text region declares an unrecognised REFCORNER");
|
|
276
|
+
const transposed = (flags & 64) !== 0;
|
|
277
|
+
const combinationOperator = combinationOperatorFromCode(flags >> 7 & 3);
|
|
278
|
+
if (combinationOperator === void 0) throw new Jbig2ParseError("JBIG2 text region declares an unrecognised SBCOMBOP");
|
|
279
|
+
const defaultPixel = flags >> 9 & 1;
|
|
280
|
+
const dsOffset = flags << 17 >> 27 | 0;
|
|
281
|
+
const refinementTemplate = flags >> 15 & 1;
|
|
282
|
+
const refinementAt = refine && refinementTemplate === 0 ? readAtPixels(cursor, 2) : [...NOMINAL_REFINEMENT_AT];
|
|
283
|
+
const instanceCount = cursor.uint32();
|
|
284
|
+
const symbols = this.gatherSymbols(header.referredTo);
|
|
285
|
+
if (symbols.length === 0) throw new Jbig2ParseError("JBIG2 text region refers to no symbol dictionary, so it has no symbols to place");
|
|
286
|
+
const symbolIdBits = symbolCodeLength(symbols.length);
|
|
287
|
+
const mq = new MqDecoder(cursor.data, cursor.position, header.dataEnd);
|
|
288
|
+
const contexts = createTextArithContexts(0, refinementTemplate, symbolIdBits);
|
|
289
|
+
const params = {
|
|
290
|
+
width: region.width,
|
|
291
|
+
height: region.height,
|
|
292
|
+
instanceCount,
|
|
293
|
+
stripSize,
|
|
294
|
+
symbols,
|
|
295
|
+
defaultPixel,
|
|
296
|
+
combinationOperator,
|
|
297
|
+
transposed,
|
|
298
|
+
referenceCorner,
|
|
299
|
+
dsOffset,
|
|
300
|
+
refine,
|
|
301
|
+
refinementTemplate,
|
|
302
|
+
refinementAt
|
|
303
|
+
};
|
|
304
|
+
this.compose(region, decodeTextRegion(params, mq, contexts, symbolIdBits));
|
|
305
|
+
}
|
|
306
|
+
gatherSymbols(referredTo) {
|
|
307
|
+
const symbols = [];
|
|
308
|
+
for (const segmentNumber of referredTo) {
|
|
309
|
+
const exported = this.exportedSymbols.get(segmentNumber);
|
|
310
|
+
if (exported !== void 0) symbols.push(...exported);
|
|
311
|
+
}
|
|
312
|
+
return symbols;
|
|
313
|
+
}
|
|
314
|
+
finish() {
|
|
315
|
+
const page = this.requirePage();
|
|
316
|
+
const width = this.options.width ?? page.bitmap.width;
|
|
317
|
+
const height = this.options.height ?? page.bitmap.height;
|
|
318
|
+
return {
|
|
319
|
+
bytes: packBitmapRows(page.bitmap, width, height),
|
|
320
|
+
width,
|
|
321
|
+
height
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
function decodeJbig2Embedded(data, options = {}) {
|
|
326
|
+
const decoder = new Jbig2Decoder(options);
|
|
327
|
+
if (options.globals !== void 0) decoder.run(options.globals);
|
|
328
|
+
decoder.run(data);
|
|
329
|
+
return decoder.finish();
|
|
330
|
+
}
|
|
331
|
+
//#endregion
|
|
332
|
+
export { decodeJbig2Embedded };
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_image_jpeg2000_errors = require("./jpeg2000-errors.cjs");
|
|
3
|
+
//#region src/image/jp2-boxes.ts
|
|
4
|
+
const BOX_HEADER_BYTES = 8;
|
|
5
|
+
const BOX_LENGTH_EXTENDED = 1;
|
|
6
|
+
const BOX_LENGTH_TO_END = 0;
|
|
7
|
+
const BOX_SIGNATURE = 1783636e3;
|
|
8
|
+
const BOX_JP2_HEADER = 1785737832;
|
|
9
|
+
const BOX_IMAGE_HEADER = 1768449138;
|
|
10
|
+
const BOX_COLOUR_SPECIFICATION = 1668246642;
|
|
11
|
+
const BOX_PALETTE = 1885564018;
|
|
12
|
+
const BOX_COMPONENT_MAPPING = 1668112752;
|
|
13
|
+
const BOX_CHANNEL_DEFINITION = 1667523942;
|
|
14
|
+
const BOX_CONTIGUOUS_CODESTREAM = 1785737827;
|
|
15
|
+
const ENUMERATED_COLOUR_SPACES = /* @__PURE__ */ new Map([
|
|
16
|
+
[12, "cmyk"],
|
|
17
|
+
[14, "cielab"],
|
|
18
|
+
[16, "srgb"],
|
|
19
|
+
[17, "greyscale"],
|
|
20
|
+
[18, "sycc"],
|
|
21
|
+
[20, "e-srgb"],
|
|
22
|
+
[24, "rommrgb"]
|
|
23
|
+
]);
|
|
24
|
+
function looksLikeBareCodestream(data) {
|
|
25
|
+
return data.length >= 4 && data[0] === 255 && data[1] === 79 && data[2] === 255 && data[3] === 81;
|
|
26
|
+
}
|
|
27
|
+
function readUint32(data, offset) {
|
|
28
|
+
const b0 = data[offset];
|
|
29
|
+
const b1 = data[offset + 1];
|
|
30
|
+
const b2 = data[offset + 2];
|
|
31
|
+
const b3 = data[offset + 3];
|
|
32
|
+
if (b0 === void 0 || b1 === void 0 || b2 === void 0 || b3 === void 0) throw new require_image_jpeg2000_errors.Jpeg2000ParseError("JP2 box structure ended in the middle of a 32-bit field");
|
|
33
|
+
return b0 * 16777216 + (b1 << 16) + (b2 << 8) + b3;
|
|
34
|
+
}
|
|
35
|
+
function readBox(data, offset, limit) {
|
|
36
|
+
if (offset + BOX_HEADER_BYTES > limit) return;
|
|
37
|
+
const declaredLength = readUint32(data, offset);
|
|
38
|
+
const type = readUint32(data, offset + 4);
|
|
39
|
+
let payloadStart = offset + BOX_HEADER_BYTES;
|
|
40
|
+
let boxEnd;
|
|
41
|
+
if (declaredLength === BOX_LENGTH_EXTENDED) {
|
|
42
|
+
const high = readUint32(data, payloadStart);
|
|
43
|
+
const low = readUint32(data, payloadStart + 4);
|
|
44
|
+
payloadStart += 8;
|
|
45
|
+
boxEnd = high === 0 ? offset + low : limit;
|
|
46
|
+
} else if (declaredLength === BOX_LENGTH_TO_END) boxEnd = limit;
|
|
47
|
+
else boxEnd = offset + declaredLength;
|
|
48
|
+
if (boxEnd < payloadStart) throw new require_image_jpeg2000_errors.Jpeg2000ParseError(`a JP2 box declares a length (${String(declaredLength)}) shorter than its own header`);
|
|
49
|
+
return {
|
|
50
|
+
type,
|
|
51
|
+
payloadStart,
|
|
52
|
+
payloadEnd: Math.min(boxEnd, limit),
|
|
53
|
+
nextBoxStart: Math.min(boxEnd, limit)
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function readImageHeader(data, start, end) {
|
|
57
|
+
if (end - start < 14) throw new require_image_jpeg2000_errors.Jpeg2000ParseError("the JP2 image header box is shorter than the 14 bytes ISO/IEC 15444-1 I.5.3.1 defines");
|
|
58
|
+
const height = readUint32(data, start);
|
|
59
|
+
const width = readUint32(data, start + 4);
|
|
60
|
+
const componentCount = (data[start + 8] ?? 0) << 8 | (data[start + 9] ?? 0);
|
|
61
|
+
const bpc = data[start + 10] ?? 0;
|
|
62
|
+
if (bpc === 255) return {
|
|
63
|
+
width,
|
|
64
|
+
height,
|
|
65
|
+
componentCount
|
|
66
|
+
};
|
|
67
|
+
return {
|
|
68
|
+
width,
|
|
69
|
+
height,
|
|
70
|
+
componentCount,
|
|
71
|
+
bitDepth: (bpc & 127) + 1,
|
|
72
|
+
signed: (bpc & 128) !== 0
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function readChannelDefinitions(data, start, end) {
|
|
76
|
+
if (end - start < 2) return [];
|
|
77
|
+
const count = (data[start] ?? 0) << 8 | (data[start + 1] ?? 0);
|
|
78
|
+
const definitions = [];
|
|
79
|
+
for (let i = 0; i < count; i++) {
|
|
80
|
+
const entry = start + 2 + i * 6;
|
|
81
|
+
if (entry + 6 > end) break;
|
|
82
|
+
definitions.push({
|
|
83
|
+
channel: (data[entry] ?? 0) << 8 | (data[entry + 1] ?? 0),
|
|
84
|
+
type: (data[entry + 2] ?? 0) << 8 | (data[entry + 3] ?? 0),
|
|
85
|
+
association: (data[entry + 4] ?? 0) << 8 | (data[entry + 5] ?? 0)
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
return definitions;
|
|
89
|
+
}
|
|
90
|
+
function readJp2HeaderBox(data, start, end, into) {
|
|
91
|
+
let offset = start;
|
|
92
|
+
for (;;) {
|
|
93
|
+
const box = readBox(data, offset, end);
|
|
94
|
+
if (box === void 0 || box.nextBoxStart <= offset) return;
|
|
95
|
+
if (box.type === BOX_IMAGE_HEADER) into.imageHeader = readImageHeader(data, box.payloadStart, box.payloadEnd);
|
|
96
|
+
else if (box.type === BOX_COLOUR_SPECIFICATION && into.colourSpace === void 0 && into.iccProfile === void 0) readColourSpecification(data, box.payloadStart, box.payloadEnd, into);
|
|
97
|
+
else if (box.type === BOX_PALETTE || box.type === BOX_COMPONENT_MAPPING) into.hasPalette = true;
|
|
98
|
+
else if (box.type === BOX_CHANNEL_DEFINITION) into.channelDefinitions = readChannelDefinitions(data, box.payloadStart, box.payloadEnd);
|
|
99
|
+
offset = box.nextBoxStart;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function readColourSpecification(data, start, end, into) {
|
|
103
|
+
if (end - start < 3) return;
|
|
104
|
+
const method = data[start] ?? 0;
|
|
105
|
+
if (method === 1) {
|
|
106
|
+
if (end - start >= 7) into.colourSpace = ENUMERATED_COLOUR_SPACES.get(readUint32(data, start + 3));
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (method === 2 && end - start > 3) into.iccProfile = data.subarray(start + 3, end);
|
|
110
|
+
}
|
|
111
|
+
function parseJp2Container(data) {
|
|
112
|
+
if (looksLikeBareCodestream(data)) return {
|
|
113
|
+
codestream: data,
|
|
114
|
+
hasBoxes: false,
|
|
115
|
+
channelDefinitions: []
|
|
116
|
+
};
|
|
117
|
+
const contents = {
|
|
118
|
+
hasPalette: false,
|
|
119
|
+
channelDefinitions: []
|
|
120
|
+
};
|
|
121
|
+
let codestream;
|
|
122
|
+
let offset = 0;
|
|
123
|
+
let sawSignature = false;
|
|
124
|
+
for (;;) {
|
|
125
|
+
const box = readBox(data, offset, data.length);
|
|
126
|
+
if (box === void 0 || box.nextBoxStart <= offset) break;
|
|
127
|
+
if (box.type === BOX_SIGNATURE) sawSignature = true;
|
|
128
|
+
else if (box.type === BOX_JP2_HEADER) readJp2HeaderBox(data, box.payloadStart, box.payloadEnd, contents);
|
|
129
|
+
else if (box.type === BOX_CONTIGUOUS_CODESTREAM && codestream === void 0) codestream = data.subarray(box.payloadStart, box.payloadEnd);
|
|
130
|
+
offset = box.nextBoxStart;
|
|
131
|
+
}
|
|
132
|
+
if (codestream === void 0) {
|
|
133
|
+
if (!sawSignature && data[0] !== 0) throw new require_image_jpeg2000_errors.Jpeg2000ParseError("the data is neither a bare JPEG 2000 codestream nor a JP2 file (no SOC marker and no JP2 signature box)");
|
|
134
|
+
throw new require_image_jpeg2000_errors.Jpeg2000ParseError("the JP2 file carries no contiguous codestream (jp2c) box");
|
|
135
|
+
}
|
|
136
|
+
if (contents.hasPalette) throw new require_image_jpeg2000_errors.Jpeg2000UnsupportedError("the JP2 file carries a palette (pclr/cmap) box, which this decoder does not apply");
|
|
137
|
+
return {
|
|
138
|
+
codestream,
|
|
139
|
+
hasBoxes: true,
|
|
140
|
+
channelDefinitions: contents.channelDefinitions,
|
|
141
|
+
...contents.imageHeader !== void 0 ? { imageHeader: contents.imageHeader } : {},
|
|
142
|
+
...contents.colourSpace !== void 0 ? { colourSpace: contents.colourSpace } : {},
|
|
143
|
+
...contents.iccProfile !== void 0 ? { iccProfile: contents.iccProfile } : {}
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
//#endregion
|
|
147
|
+
exports.looksLikeBareCodestream = looksLikeBareCodestream;
|
|
148
|
+
exports.parseJp2Container = parseJp2Container;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
//#region src/image/jp2-boxes.d.ts
|
|
2
|
+
type Jp2ColourSpace = 'greyscale' | 'srgb' | 'sycc' | 'cmyk' | 'e-srgb' | 'rommrgb' | 'cielab';
|
|
3
|
+
interface Jp2ImageHeader {
|
|
4
|
+
readonly width: number;
|
|
5
|
+
readonly height: number;
|
|
6
|
+
readonly componentCount: number;
|
|
7
|
+
readonly bitDepth?: number;
|
|
8
|
+
readonly signed?: boolean;
|
|
9
|
+
}
|
|
10
|
+
interface Jp2Container {
|
|
11
|
+
readonly codestream: Uint8Array<ArrayBuffer>;
|
|
12
|
+
readonly hasBoxes: boolean;
|
|
13
|
+
readonly imageHeader?: Jp2ImageHeader;
|
|
14
|
+
readonly colourSpace?: Jp2ColourSpace;
|
|
15
|
+
readonly iccProfile?: Uint8Array<ArrayBuffer>;
|
|
16
|
+
readonly channelDefinitions: readonly Jp2ChannelDefinition[];
|
|
17
|
+
}
|
|
18
|
+
interface Jp2ChannelDefinition {
|
|
19
|
+
readonly channel: number;
|
|
20
|
+
readonly type: number;
|
|
21
|
+
readonly association: number;
|
|
22
|
+
}
|
|
23
|
+
declare function looksLikeBareCodestream(data: Uint8Array<ArrayBuffer>): boolean;
|
|
24
|
+
declare function parseJp2Container(data: Uint8Array<ArrayBuffer>): Jp2Container;
|
|
25
|
+
//#endregion
|
|
26
|
+
export { Jp2ChannelDefinition, Jp2ColourSpace, Jp2Container, Jp2ImageHeader, looksLikeBareCodestream, parseJp2Container };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
//#region src/image/jp2-boxes.d.ts
|
|
2
|
+
type Jp2ColourSpace = 'greyscale' | 'srgb' | 'sycc' | 'cmyk' | 'e-srgb' | 'rommrgb' | 'cielab';
|
|
3
|
+
interface Jp2ImageHeader {
|
|
4
|
+
readonly width: number;
|
|
5
|
+
readonly height: number;
|
|
6
|
+
readonly componentCount: number;
|
|
7
|
+
readonly bitDepth?: number;
|
|
8
|
+
readonly signed?: boolean;
|
|
9
|
+
}
|
|
10
|
+
interface Jp2Container {
|
|
11
|
+
readonly codestream: Uint8Array<ArrayBuffer>;
|
|
12
|
+
readonly hasBoxes: boolean;
|
|
13
|
+
readonly imageHeader?: Jp2ImageHeader;
|
|
14
|
+
readonly colourSpace?: Jp2ColourSpace;
|
|
15
|
+
readonly iccProfile?: Uint8Array<ArrayBuffer>;
|
|
16
|
+
readonly channelDefinitions: readonly Jp2ChannelDefinition[];
|
|
17
|
+
}
|
|
18
|
+
interface Jp2ChannelDefinition {
|
|
19
|
+
readonly channel: number;
|
|
20
|
+
readonly type: number;
|
|
21
|
+
readonly association: number;
|
|
22
|
+
}
|
|
23
|
+
declare function looksLikeBareCodestream(data: Uint8Array<ArrayBuffer>): boolean;
|
|
24
|
+
declare function parseJp2Container(data: Uint8Array<ArrayBuffer>): Jp2Container;
|
|
25
|
+
//#endregion
|
|
26
|
+
export { Jp2ChannelDefinition, Jp2ColourSpace, Jp2Container, Jp2ImageHeader, looksLikeBareCodestream, parseJp2Container };
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { Jpeg2000ParseError, Jpeg2000UnsupportedError } from "./jpeg2000-errors.js";
|
|
2
|
+
//#region src/image/jp2-boxes.ts
|
|
3
|
+
const BOX_HEADER_BYTES = 8;
|
|
4
|
+
const BOX_LENGTH_EXTENDED = 1;
|
|
5
|
+
const BOX_LENGTH_TO_END = 0;
|
|
6
|
+
const BOX_SIGNATURE = 1783636e3;
|
|
7
|
+
const BOX_JP2_HEADER = 1785737832;
|
|
8
|
+
const BOX_IMAGE_HEADER = 1768449138;
|
|
9
|
+
const BOX_COLOUR_SPECIFICATION = 1668246642;
|
|
10
|
+
const BOX_PALETTE = 1885564018;
|
|
11
|
+
const BOX_COMPONENT_MAPPING = 1668112752;
|
|
12
|
+
const BOX_CHANNEL_DEFINITION = 1667523942;
|
|
13
|
+
const BOX_CONTIGUOUS_CODESTREAM = 1785737827;
|
|
14
|
+
const ENUMERATED_COLOUR_SPACES = /* @__PURE__ */ new Map([
|
|
15
|
+
[12, "cmyk"],
|
|
16
|
+
[14, "cielab"],
|
|
17
|
+
[16, "srgb"],
|
|
18
|
+
[17, "greyscale"],
|
|
19
|
+
[18, "sycc"],
|
|
20
|
+
[20, "e-srgb"],
|
|
21
|
+
[24, "rommrgb"]
|
|
22
|
+
]);
|
|
23
|
+
function looksLikeBareCodestream(data) {
|
|
24
|
+
return data.length >= 4 && data[0] === 255 && data[1] === 79 && data[2] === 255 && data[3] === 81;
|
|
25
|
+
}
|
|
26
|
+
function readUint32(data, offset) {
|
|
27
|
+
const b0 = data[offset];
|
|
28
|
+
const b1 = data[offset + 1];
|
|
29
|
+
const b2 = data[offset + 2];
|
|
30
|
+
const b3 = data[offset + 3];
|
|
31
|
+
if (b0 === void 0 || b1 === void 0 || b2 === void 0 || b3 === void 0) throw new Jpeg2000ParseError("JP2 box structure ended in the middle of a 32-bit field");
|
|
32
|
+
return b0 * 16777216 + (b1 << 16) + (b2 << 8) + b3;
|
|
33
|
+
}
|
|
34
|
+
function readBox(data, offset, limit) {
|
|
35
|
+
if (offset + BOX_HEADER_BYTES > limit) return;
|
|
36
|
+
const declaredLength = readUint32(data, offset);
|
|
37
|
+
const type = readUint32(data, offset + 4);
|
|
38
|
+
let payloadStart = offset + BOX_HEADER_BYTES;
|
|
39
|
+
let boxEnd;
|
|
40
|
+
if (declaredLength === BOX_LENGTH_EXTENDED) {
|
|
41
|
+
const high = readUint32(data, payloadStart);
|
|
42
|
+
const low = readUint32(data, payloadStart + 4);
|
|
43
|
+
payloadStart += 8;
|
|
44
|
+
boxEnd = high === 0 ? offset + low : limit;
|
|
45
|
+
} else if (declaredLength === BOX_LENGTH_TO_END) boxEnd = limit;
|
|
46
|
+
else boxEnd = offset + declaredLength;
|
|
47
|
+
if (boxEnd < payloadStart) throw new Jpeg2000ParseError(`a JP2 box declares a length (${String(declaredLength)}) shorter than its own header`);
|
|
48
|
+
return {
|
|
49
|
+
type,
|
|
50
|
+
payloadStart,
|
|
51
|
+
payloadEnd: Math.min(boxEnd, limit),
|
|
52
|
+
nextBoxStart: Math.min(boxEnd, limit)
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function readImageHeader(data, start, end) {
|
|
56
|
+
if (end - start < 14) throw new Jpeg2000ParseError("the JP2 image header box is shorter than the 14 bytes ISO/IEC 15444-1 I.5.3.1 defines");
|
|
57
|
+
const height = readUint32(data, start);
|
|
58
|
+
const width = readUint32(data, start + 4);
|
|
59
|
+
const componentCount = (data[start + 8] ?? 0) << 8 | (data[start + 9] ?? 0);
|
|
60
|
+
const bpc = data[start + 10] ?? 0;
|
|
61
|
+
if (bpc === 255) return {
|
|
62
|
+
width,
|
|
63
|
+
height,
|
|
64
|
+
componentCount
|
|
65
|
+
};
|
|
66
|
+
return {
|
|
67
|
+
width,
|
|
68
|
+
height,
|
|
69
|
+
componentCount,
|
|
70
|
+
bitDepth: (bpc & 127) + 1,
|
|
71
|
+
signed: (bpc & 128) !== 0
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function readChannelDefinitions(data, start, end) {
|
|
75
|
+
if (end - start < 2) return [];
|
|
76
|
+
const count = (data[start] ?? 0) << 8 | (data[start + 1] ?? 0);
|
|
77
|
+
const definitions = [];
|
|
78
|
+
for (let i = 0; i < count; i++) {
|
|
79
|
+
const entry = start + 2 + i * 6;
|
|
80
|
+
if (entry + 6 > end) break;
|
|
81
|
+
definitions.push({
|
|
82
|
+
channel: (data[entry] ?? 0) << 8 | (data[entry + 1] ?? 0),
|
|
83
|
+
type: (data[entry + 2] ?? 0) << 8 | (data[entry + 3] ?? 0),
|
|
84
|
+
association: (data[entry + 4] ?? 0) << 8 | (data[entry + 5] ?? 0)
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
return definitions;
|
|
88
|
+
}
|
|
89
|
+
function readJp2HeaderBox(data, start, end, into) {
|
|
90
|
+
let offset = start;
|
|
91
|
+
for (;;) {
|
|
92
|
+
const box = readBox(data, offset, end);
|
|
93
|
+
if (box === void 0 || box.nextBoxStart <= offset) return;
|
|
94
|
+
if (box.type === BOX_IMAGE_HEADER) into.imageHeader = readImageHeader(data, box.payloadStart, box.payloadEnd);
|
|
95
|
+
else if (box.type === BOX_COLOUR_SPECIFICATION && into.colourSpace === void 0 && into.iccProfile === void 0) readColourSpecification(data, box.payloadStart, box.payloadEnd, into);
|
|
96
|
+
else if (box.type === BOX_PALETTE || box.type === BOX_COMPONENT_MAPPING) into.hasPalette = true;
|
|
97
|
+
else if (box.type === BOX_CHANNEL_DEFINITION) into.channelDefinitions = readChannelDefinitions(data, box.payloadStart, box.payloadEnd);
|
|
98
|
+
offset = box.nextBoxStart;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function readColourSpecification(data, start, end, into) {
|
|
102
|
+
if (end - start < 3) return;
|
|
103
|
+
const method = data[start] ?? 0;
|
|
104
|
+
if (method === 1) {
|
|
105
|
+
if (end - start >= 7) into.colourSpace = ENUMERATED_COLOUR_SPACES.get(readUint32(data, start + 3));
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (method === 2 && end - start > 3) into.iccProfile = data.subarray(start + 3, end);
|
|
109
|
+
}
|
|
110
|
+
function parseJp2Container(data) {
|
|
111
|
+
if (looksLikeBareCodestream(data)) return {
|
|
112
|
+
codestream: data,
|
|
113
|
+
hasBoxes: false,
|
|
114
|
+
channelDefinitions: []
|
|
115
|
+
};
|
|
116
|
+
const contents = {
|
|
117
|
+
hasPalette: false,
|
|
118
|
+
channelDefinitions: []
|
|
119
|
+
};
|
|
120
|
+
let codestream;
|
|
121
|
+
let offset = 0;
|
|
122
|
+
let sawSignature = false;
|
|
123
|
+
for (;;) {
|
|
124
|
+
const box = readBox(data, offset, data.length);
|
|
125
|
+
if (box === void 0 || box.nextBoxStart <= offset) break;
|
|
126
|
+
if (box.type === BOX_SIGNATURE) sawSignature = true;
|
|
127
|
+
else if (box.type === BOX_JP2_HEADER) readJp2HeaderBox(data, box.payloadStart, box.payloadEnd, contents);
|
|
128
|
+
else if (box.type === BOX_CONTIGUOUS_CODESTREAM && codestream === void 0) codestream = data.subarray(box.payloadStart, box.payloadEnd);
|
|
129
|
+
offset = box.nextBoxStart;
|
|
130
|
+
}
|
|
131
|
+
if (codestream === void 0) {
|
|
132
|
+
if (!sawSignature && data[0] !== 0) throw new Jpeg2000ParseError("the data is neither a bare JPEG 2000 codestream nor a JP2 file (no SOC marker and no JP2 signature box)");
|
|
133
|
+
throw new Jpeg2000ParseError("the JP2 file carries no contiguous codestream (jp2c) box");
|
|
134
|
+
}
|
|
135
|
+
if (contents.hasPalette) throw new Jpeg2000UnsupportedError("the JP2 file carries a palette (pclr/cmap) box, which this decoder does not apply");
|
|
136
|
+
return {
|
|
137
|
+
codestream,
|
|
138
|
+
hasBoxes: true,
|
|
139
|
+
channelDefinitions: contents.channelDefinitions,
|
|
140
|
+
...contents.imageHeader !== void 0 ? { imageHeader: contents.imageHeader } : {},
|
|
141
|
+
...contents.colourSpace !== void 0 ? { colourSpace: contents.colourSpace } : {},
|
|
142
|
+
...contents.iccProfile !== void 0 ? { iccProfile: contents.iccProfile } : {}
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
//#endregion
|
|
146
|
+
export { looksLikeBareCodestream, parseJp2Container };
|