pagyra-js 0.0.21 → 0.0.23
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 +283 -264
- package/dist/browser/pagyra.min.js +30 -30
- package/dist/browser/pagyra.min.js.map +4 -4
- package/dist/src/css/apply-declarations.js +2 -1
- package/dist/src/css/clip-path-types.d.ts +9 -1
- package/dist/src/css/compute-style/overrides.js +10 -1
- package/dist/src/css/parsers/clip-path-parser.js +51 -0
- package/dist/src/css/parsers/register-parsers.js +21 -0
- package/dist/src/css/properties/visual.d.ts +2 -0
- package/dist/src/css/style.d.ts +5 -0
- package/dist/src/css/style.js +3 -0
- package/dist/src/css/ua-defaults/element-defaults.js +13 -0
- package/dist/src/dom/node.d.ts +2 -0
- package/dist/src/dom/node.js +1 -0
- package/dist/src/fonts/woff2/decoder.d.ts +1 -9
- package/dist/src/fonts/woff2/decoder.js +6 -565
- package/dist/src/fonts/woff2/glyf-reconstructor.d.ts +54 -0
- package/dist/src/fonts/woff2/glyf-reconstructor.js +357 -0
- package/dist/src/fonts/woff2/hmtx-reconstructor.d.ts +5 -0
- package/dist/src/fonts/woff2/hmtx-reconstructor.js +42 -0
- package/dist/src/fonts/woff2/sfnt-builder.d.ts +7 -0
- package/dist/src/fonts/woff2/sfnt-builder.js +55 -0
- package/dist/src/fonts/woff2/utils.d.ts +12 -0
- package/dist/src/fonts/woff2/utils.js +111 -0
- package/dist/src/html-to-pdf/render-finalize.js +5 -1
- package/dist/src/layout/inline/run-placer.js +1 -1
- package/dist/src/layout/strategies/flex/alignment.d.ts +10 -0
- package/dist/src/layout/strategies/flex/alignment.js +91 -0
- package/dist/src/layout/strategies/flex/distributor.d.ts +5 -0
- package/dist/src/layout/strategies/flex/distributor.js +56 -0
- package/dist/src/layout/strategies/flex/line-builder.d.ts +5 -0
- package/dist/src/layout/strategies/flex/line-builder.js +55 -0
- package/dist/src/layout/strategies/flex/types.d.ts +27 -0
- package/dist/src/layout/strategies/flex/types.js +2 -0
- package/dist/src/layout/strategies/flex/utils.d.ts +12 -0
- package/dist/src/layout/strategies/flex/utils.js +113 -0
- package/dist/src/layout/strategies/flex.js +4 -308
- package/dist/src/layout/strategies/grid.js +0 -3
- package/dist/src/layout/strategies/table.js +85 -58
- package/dist/src/layout/utils/text-metrics.js +16 -8
- package/dist/src/pdf/font/embedder.js +3 -3
- package/dist/src/pdf/font/font-subset.js +1 -3
- package/dist/src/pdf/font/to-unicode.js +16 -16
- package/dist/src/pdf/layout-tree-builder.js +15 -9
- package/dist/src/pdf/renderer/box-painter.js +74 -9
- package/dist/src/pdf/renderers/text-renderer.d.ts +4 -2
- package/dist/src/pdf/renderers/text-renderer.js +52 -2
- package/dist/src/pdf/types.d.ts +16 -1
- package/dist/src/pdf/utils/clip-path-resolver.js +28 -12
- package/dist/src/pdf/utils/mask-resolver.d.ts +7 -0
- package/dist/src/pdf/utils/mask-resolver.js +25 -0
- package/dist/src/pdf/utils/node-text-run-factory.d.ts +2 -1
- package/dist/src/pdf/utils/node-text-run-factory.js +5 -26
- package/dist/src/pdf/utils/rounded-rect-to-path.d.ts +7 -0
- package/dist/src/pdf/utils/rounded-rect-to-path.js +86 -0
- package/dist/src/render/offset.d.ts +5 -0
- package/dist/src/render/offset.js +93 -9
- package/dist/src/text/line-breaker.js +31 -0
- package/dist/tests/css/clip-path-parser.spec.js +15 -8
- package/dist/tests/environment/path-resolution.spec.js +2 -1
- package/dist/tests/helpers/ai-layout-diagnostics.js +6 -6
- package/dist/tests/layout/container-query-units.spec.js +0 -7
- package/dist/tests/layout/inline-background-alignment.spec.js +6 -6
- package/dist/tests/layout/table-image-cell.spec.js +95 -0
- package/dist/tests/pdf/alignments.spec.js +12 -12
- package/dist/tests/pdf/clip-path.spec.js +3 -1
- package/dist/tests/pdf/form-text-encoding.spec.js +1 -1
- package/dist/tests/pdf/svg-stroke-dash.spec.js +8 -8
- package/dist/tests/pdf/text-transform-matrix.spec.js +1 -1
- package/dist/tests/pdf/xref-integrity.spec.js +1 -1
- package/dist/tests/verify-subset-multi.spec.js +14 -14
- package/dist/tests/verify-subset.spec.js +12 -12
- package/package.json +89 -71
- package/dist/src/image/js-png-backend.d.ts +0 -7
- package/dist/src/image/js-png-backend.js +0 -9
- package/dist/src/image/png-backend.d.ts +0 -5
- package/dist/src/image/png-wasm-loader.d.ts +0 -5
- package/dist/src/image/png-wasm-loader.js +0 -59
- package/dist/src/image/wasm/png_decoder_wasm.d.ts +0 -8
- package/dist/src/image/wasm/png_decoder_wasm.js +0 -24
- package/dist/src/image/wasm/png_decoder_wasm_bg.js +0 -16
- package/dist/src/image/wasm-png-backend.d.ts +0 -6
- package/dist/src/image/wasm-png-backend.js +0 -17
- package/dist/src/layout/table/cell_layout.d.ts +0 -2
- package/dist/src/layout/table/cell_layout.js +0 -26
- package/dist/tests/image/png-backend.spec.d.ts +0 -1
- package/dist/tests/image/png-backend.spec.js +0 -34
- package/dist/tests/pdf/font-subset-registry-key.spec.d.ts +0 -1
- package/dist/tests/pdf/font-subset-registry-key.spec.js +0 -66
- package/dist/tests/pdf/header-footer.spec.d.ts +0 -1
- package/dist/tests/pdf/header-footer.spec.js +0 -46
- /package/dist/{src/image/png-backend.js → tests/layout/table-image-cell.spec.d.ts} +0 -0
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
import { Buf, read255UShort } from "./buffer.js";
|
|
2
|
+
import { computeULongSum, round4, store16, store32 } from "./utils.js";
|
|
3
|
+
export const GLYF_FLAGS = {
|
|
4
|
+
ON_CURVE: 1 << 0,
|
|
5
|
+
X_SHORT: 1 << 1,
|
|
6
|
+
Y_SHORT: 1 << 2,
|
|
7
|
+
REPEAT: 1 << 3,
|
|
8
|
+
X_SAME: 1 << 4,
|
|
9
|
+
Y_SAME: 1 << 5,
|
|
10
|
+
OVERLAP_SIMPLE: 1 << 6
|
|
11
|
+
};
|
|
12
|
+
export const COMPOSITE_FLAGS = {
|
|
13
|
+
ARG_WORDS: 1 << 0,
|
|
14
|
+
WE_HAVE_A_SCALE: 1 << 3,
|
|
15
|
+
MORE_COMPONENTS: 1 << 5,
|
|
16
|
+
WE_HAVE_AN_XY_SCALE: 1 << 6,
|
|
17
|
+
WE_HAVE_A_TWO_BY_TWO: 1 << 7,
|
|
18
|
+
WE_HAVE_INSTRUCTIONS: 1 << 8
|
|
19
|
+
};
|
|
20
|
+
export function withSign(flag, base) {
|
|
21
|
+
return (flag & 1) ? base : -base;
|
|
22
|
+
}
|
|
23
|
+
export function tripletDecode(flagsIn, data, nPoints) {
|
|
24
|
+
let x = 0;
|
|
25
|
+
let y = 0;
|
|
26
|
+
let tripletIndex = 0;
|
|
27
|
+
const points = new Array(nPoints);
|
|
28
|
+
for (let i = 0; i < nPoints; i++) {
|
|
29
|
+
let flag = flagsIn[i];
|
|
30
|
+
const onCurve = (flag >> 7) === 0;
|
|
31
|
+
flag &= 0x7f;
|
|
32
|
+
let nDataBytes = 1;
|
|
33
|
+
if (flag >= 84 && flag < 120)
|
|
34
|
+
nDataBytes = 2;
|
|
35
|
+
else if (flag < 84)
|
|
36
|
+
nDataBytes = 1;
|
|
37
|
+
else if (flag < 124)
|
|
38
|
+
nDataBytes = 3;
|
|
39
|
+
else
|
|
40
|
+
nDataBytes = 4;
|
|
41
|
+
if (tripletIndex + nDataBytes > data.length) {
|
|
42
|
+
throw new Error("Invalid WOFF2 glyf triplet");
|
|
43
|
+
}
|
|
44
|
+
let dx;
|
|
45
|
+
let dy;
|
|
46
|
+
if (flag < 10) {
|
|
47
|
+
dx = 0;
|
|
48
|
+
dy = withSign(flag, ((flag & 14) << 7) + data[tripletIndex]);
|
|
49
|
+
}
|
|
50
|
+
else if (flag < 20) {
|
|
51
|
+
dx = withSign(flag, (((flag - 10) & 14) << 7) + data[tripletIndex]);
|
|
52
|
+
dy = 0;
|
|
53
|
+
}
|
|
54
|
+
else if (flag < 84) {
|
|
55
|
+
const b0 = flag - 20;
|
|
56
|
+
const b1 = data[tripletIndex];
|
|
57
|
+
dx = withSign(flag, 1 + (b0 & 0x30) + (b1 >> 4));
|
|
58
|
+
dy = withSign(flag >> 1, 1 + ((b0 & 0x0c) << 2) + (b1 & 0x0f));
|
|
59
|
+
}
|
|
60
|
+
else if (flag < 120) {
|
|
61
|
+
const b0 = flag - 84;
|
|
62
|
+
dx = withSign(flag, 1 + ((b0 / 12) << 8) + data[tripletIndex]);
|
|
63
|
+
dy = withSign(flag >> 1, 1 + (((b0 % 12) >> 2) << 8) + data[tripletIndex + 1]);
|
|
64
|
+
}
|
|
65
|
+
else if (flag < 124) {
|
|
66
|
+
const b2 = data[tripletIndex + 1];
|
|
67
|
+
dx = withSign(flag, (data[tripletIndex] << 4) + (b2 >> 4));
|
|
68
|
+
dy = withSign(flag >> 1, ((b2 & 0x0f) << 8) + data[tripletIndex + 2]);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
dx = withSign(flag, (data[tripletIndex] << 8) + data[tripletIndex + 1]);
|
|
72
|
+
dy = withSign(flag >> 1, (data[tripletIndex + 2] << 8) + data[tripletIndex + 3]);
|
|
73
|
+
}
|
|
74
|
+
tripletIndex += nDataBytes;
|
|
75
|
+
x += dx;
|
|
76
|
+
y += dy;
|
|
77
|
+
points[i] = { x, y, onCurve };
|
|
78
|
+
}
|
|
79
|
+
return { points, consumed: tripletIndex };
|
|
80
|
+
}
|
|
81
|
+
export function storePoints(points, nContours, instructionLength, hasOverlap) {
|
|
82
|
+
const nPoints = points.length;
|
|
83
|
+
const xBytes = [];
|
|
84
|
+
const yBytes = [];
|
|
85
|
+
let lastX = 0;
|
|
86
|
+
let lastY = 0;
|
|
87
|
+
let lastFlag = -1;
|
|
88
|
+
let repeatCount = 0;
|
|
89
|
+
const flagOffset = 10 + 2 * nContours + 2 + instructionLength;
|
|
90
|
+
const flagsOut = [];
|
|
91
|
+
for (let i = 0; i < nPoints; i++) {
|
|
92
|
+
const point = points[i];
|
|
93
|
+
let flag = point.onCurve ? GLYF_FLAGS.ON_CURVE : 0;
|
|
94
|
+
if (hasOverlap && i === 0) {
|
|
95
|
+
flag |= GLYF_FLAGS.OVERLAP_SIMPLE;
|
|
96
|
+
}
|
|
97
|
+
const dx = point.x - lastX;
|
|
98
|
+
const dy = point.y - lastY;
|
|
99
|
+
if (dx === 0)
|
|
100
|
+
flag |= GLYF_FLAGS.X_SAME;
|
|
101
|
+
else if (dx > -256 && dx < 256) {
|
|
102
|
+
flag |= GLYF_FLAGS.X_SHORT | (dx > 0 ? GLYF_FLAGS.X_SAME : 0);
|
|
103
|
+
xBytes.push(Math.abs(dx));
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
xBytes.push((dx >> 8) & 0xff, dx & 0xff);
|
|
107
|
+
}
|
|
108
|
+
if (dy === 0)
|
|
109
|
+
flag |= GLYF_FLAGS.Y_SAME;
|
|
110
|
+
else if (dy > -256 && dy < 256) {
|
|
111
|
+
flag |= GLYF_FLAGS.Y_SHORT | (dy > 0 ? GLYF_FLAGS.Y_SAME : 0);
|
|
112
|
+
yBytes.push(Math.abs(dy));
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
yBytes.push((dy >> 8) & 0xff, dy & 0xff);
|
|
116
|
+
}
|
|
117
|
+
if (flag === lastFlag && repeatCount !== 255) {
|
|
118
|
+
flagsOut[flagsOut.length - 1] |= GLYF_FLAGS.REPEAT;
|
|
119
|
+
repeatCount++;
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
if (repeatCount !== 0) {
|
|
123
|
+
flagsOut.push(repeatCount);
|
|
124
|
+
}
|
|
125
|
+
flagsOut.push(flag);
|
|
126
|
+
repeatCount = 0;
|
|
127
|
+
lastFlag = flag;
|
|
128
|
+
}
|
|
129
|
+
lastX = point.x;
|
|
130
|
+
lastY = point.y;
|
|
131
|
+
}
|
|
132
|
+
if (repeatCount !== 0) {
|
|
133
|
+
flagsOut.push(repeatCount);
|
|
134
|
+
}
|
|
135
|
+
const totalSize = flagOffset + flagsOut.length + xBytes.length + yBytes.length;
|
|
136
|
+
const out = new Uint8Array(totalSize);
|
|
137
|
+
let offset = flagOffset;
|
|
138
|
+
for (const f of flagsOut)
|
|
139
|
+
out[offset++] = f;
|
|
140
|
+
for (const b of xBytes)
|
|
141
|
+
out[offset++] = b;
|
|
142
|
+
for (const b of yBytes)
|
|
143
|
+
out[offset++] = b;
|
|
144
|
+
return out;
|
|
145
|
+
}
|
|
146
|
+
export function computeBBox(points) {
|
|
147
|
+
if (points.length === 0) {
|
|
148
|
+
return [0, 0, 0, 0];
|
|
149
|
+
}
|
|
150
|
+
let xMin = points[0].x;
|
|
151
|
+
let xMax = points[0].x;
|
|
152
|
+
let yMin = points[0].y;
|
|
153
|
+
let yMax = points[0].y;
|
|
154
|
+
for (let i = 1; i < points.length; i++) {
|
|
155
|
+
const p = points[i];
|
|
156
|
+
if (p.x < xMin)
|
|
157
|
+
xMin = p.x;
|
|
158
|
+
if (p.x > xMax)
|
|
159
|
+
xMax = p.x;
|
|
160
|
+
if (p.y < yMin)
|
|
161
|
+
yMin = p.y;
|
|
162
|
+
if (p.y > yMax)
|
|
163
|
+
yMax = p.y;
|
|
164
|
+
}
|
|
165
|
+
return [xMin, yMin, xMax, yMax];
|
|
166
|
+
}
|
|
167
|
+
export function sizeOfComposite(stream) {
|
|
168
|
+
const start = stream.offset;
|
|
169
|
+
let flags = COMPOSITE_FLAGS.MORE_COMPONENTS;
|
|
170
|
+
let haveInstructions = false;
|
|
171
|
+
while (flags & COMPOSITE_FLAGS.MORE_COMPONENTS) {
|
|
172
|
+
flags = stream.readU16();
|
|
173
|
+
haveInstructions || (haveInstructions = (flags & COMPOSITE_FLAGS.WE_HAVE_INSTRUCTIONS) !== 0);
|
|
174
|
+
let argSize = 2; // glyph index
|
|
175
|
+
if (flags & COMPOSITE_FLAGS.ARG_WORDS)
|
|
176
|
+
argSize += 4;
|
|
177
|
+
else
|
|
178
|
+
argSize += 2;
|
|
179
|
+
if (flags & COMPOSITE_FLAGS.WE_HAVE_A_SCALE)
|
|
180
|
+
argSize += 2;
|
|
181
|
+
else if (flags & COMPOSITE_FLAGS.WE_HAVE_AN_XY_SCALE)
|
|
182
|
+
argSize += 4;
|
|
183
|
+
else if (flags & COMPOSITE_FLAGS.WE_HAVE_A_TWO_BY_TWO)
|
|
184
|
+
argSize += 8;
|
|
185
|
+
stream.skip(argSize);
|
|
186
|
+
}
|
|
187
|
+
return { size: stream.offset - start, haveInstructions };
|
|
188
|
+
}
|
|
189
|
+
export function storeLoca(locaValues, indexFormat) {
|
|
190
|
+
const offsetSize = indexFormat ? 4 : 2;
|
|
191
|
+
const buffer = new Uint8Array(locaValues.length * offsetSize);
|
|
192
|
+
let off = 0;
|
|
193
|
+
for (const value of locaValues) {
|
|
194
|
+
if (indexFormat) {
|
|
195
|
+
off = store32(value, buffer, off);
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
off = store16(value >> 1, buffer, off);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return { data: buffer, checksum: computeULongSum(buffer) };
|
|
202
|
+
}
|
|
203
|
+
export function reconstructGlyfTable(transformed, locaDstLength) {
|
|
204
|
+
const stream = new Buf(transformed);
|
|
205
|
+
/* const version = */ stream.readU16();
|
|
206
|
+
const flags = stream.readU16();
|
|
207
|
+
const hasOverlapBitmap = (flags & 1) !== 0;
|
|
208
|
+
const numGlyphs = stream.readU16();
|
|
209
|
+
const indexFormat = stream.readU16();
|
|
210
|
+
const substreamSizes = [];
|
|
211
|
+
for (let i = 0; i < 7; i++) {
|
|
212
|
+
substreamSizes.push(stream.readU32());
|
|
213
|
+
}
|
|
214
|
+
let dataOffset = (2 + 7) * 4; // header (version/flags/numGlyphs/indexFormat + sizes)
|
|
215
|
+
const substreams = [];
|
|
216
|
+
for (const sz of substreamSizes) {
|
|
217
|
+
if (dataOffset + sz > transformed.length) {
|
|
218
|
+
throw new Error("Invalid WOFF2 glyf stream");
|
|
219
|
+
}
|
|
220
|
+
substreams.push(transformed.subarray(dataOffset, dataOffset + sz));
|
|
221
|
+
dataOffset += sz;
|
|
222
|
+
}
|
|
223
|
+
let overlapBitmap = null;
|
|
224
|
+
if (hasOverlapBitmap) {
|
|
225
|
+
const len = (numGlyphs + 7) >> 3;
|
|
226
|
+
if (dataOffset + len > transformed.length) {
|
|
227
|
+
throw new Error("Invalid WOFF2 overlap bitmap");
|
|
228
|
+
}
|
|
229
|
+
overlapBitmap = transformed.subarray(dataOffset, dataOffset + len);
|
|
230
|
+
}
|
|
231
|
+
const nContourStream = new Buf(substreams[0]);
|
|
232
|
+
const nPointsStream = new Buf(substreams[1]);
|
|
233
|
+
const flagStream = new Buf(substreams[2]);
|
|
234
|
+
const glyphStream = new Buf(substreams[3]);
|
|
235
|
+
const compositeStream = new Buf(substreams[4]);
|
|
236
|
+
const bboxStream = new Buf(substreams[5]);
|
|
237
|
+
const instructionStream = new Buf(substreams[6]);
|
|
238
|
+
const bboxBitmapLen = ((numGlyphs + 31) >> 5) << 2;
|
|
239
|
+
const bboxBitmap = bboxStream.readBytes(bboxBitmapLen);
|
|
240
|
+
const locaValues = new Array(numGlyphs + 1).fill(0);
|
|
241
|
+
const xMins = new Int16Array(numGlyphs);
|
|
242
|
+
const glyfChunks = [];
|
|
243
|
+
let glyfChecksum = 0 >>> 0;
|
|
244
|
+
let currentGlyfOffset = 0;
|
|
245
|
+
for (let i = 0; i < numGlyphs; i++) {
|
|
246
|
+
const nContours = nContourStream.readU16();
|
|
247
|
+
const haveBbox = (bboxBitmap[i >> 3] & (0x80 >> (i & 7))) !== 0;
|
|
248
|
+
let glyphBytes = null;
|
|
249
|
+
if (nContours === 0xffff) {
|
|
250
|
+
// Composite
|
|
251
|
+
const { size: compositeSize, haveInstructions } = sizeOfComposite(new Buf(compositeStream.peekRemaining()));
|
|
252
|
+
let instructionSize = 0;
|
|
253
|
+
if (haveInstructions) {
|
|
254
|
+
instructionSize = read255UShort(glyphStream);
|
|
255
|
+
}
|
|
256
|
+
const total = 12 + compositeSize + instructionSize; // nContours + bbox + composite + instructions
|
|
257
|
+
const out = new Uint8Array(total);
|
|
258
|
+
let off = 0;
|
|
259
|
+
off = store16(nContours, out, off);
|
|
260
|
+
if (!haveBbox) {
|
|
261
|
+
throw new Error("Invalid WOFF2 glyf: composite without bbox");
|
|
262
|
+
}
|
|
263
|
+
const bboxData = bboxStream.readBytes(8);
|
|
264
|
+
const xMinRaw = (bboxData[0] << 8) | bboxData[1];
|
|
265
|
+
xMins[i] = xMinRaw & 0x8000 ? xMinRaw - 0x10000 : xMinRaw;
|
|
266
|
+
out.set(bboxData, off);
|
|
267
|
+
off += 8;
|
|
268
|
+
out.set(compositeStream.readBytes(compositeSize), off);
|
|
269
|
+
off += compositeSize;
|
|
270
|
+
if (haveInstructions) {
|
|
271
|
+
off = store16(instructionSize, out, off);
|
|
272
|
+
out.set(instructionStream.readBytes(instructionSize), off);
|
|
273
|
+
}
|
|
274
|
+
glyphBytes = out;
|
|
275
|
+
}
|
|
276
|
+
else if (nContours > 0) {
|
|
277
|
+
const nPoints = [];
|
|
278
|
+
let totalPoints = 0;
|
|
279
|
+
for (let c = 0; c < nContours; c++) {
|
|
280
|
+
const pts = read255UShort(nPointsStream);
|
|
281
|
+
totalPoints += pts;
|
|
282
|
+
nPoints.push(pts);
|
|
283
|
+
}
|
|
284
|
+
const flagData = flagStream.readBytes(totalPoints);
|
|
285
|
+
const tripletsView = glyphStream.peekRemaining();
|
|
286
|
+
const { points, consumed } = tripletDecode(flagData, tripletsView, totalPoints);
|
|
287
|
+
glyphStream.offset += consumed;
|
|
288
|
+
const instructionSize = read255UShort(glyphStream);
|
|
289
|
+
const bbox = haveBbox ? bboxStream.readBytes(8) : null;
|
|
290
|
+
// const _baseSize = 12 + 2 * nContours + instructionSize;
|
|
291
|
+
const ptsBuf = storePoints(points, nContours, instructionSize, !!(overlapBitmap && (overlapBitmap[i >> 3] & (0x80 >> (i & 7)))));
|
|
292
|
+
const glyphBuf = new Uint8Array(ptsBuf.length);
|
|
293
|
+
glyphBuf.set(ptsBuf);
|
|
294
|
+
let off = 0;
|
|
295
|
+
off = store16(nContours, glyphBuf, off);
|
|
296
|
+
if (bbox) {
|
|
297
|
+
glyphBuf.set(bbox, off);
|
|
298
|
+
off += 8;
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
const [xMin, yMin, xMax, yMax] = computeBBox(points);
|
|
302
|
+
off = store16(xMin, glyphBuf, off);
|
|
303
|
+
off = store16(yMin, glyphBuf, off);
|
|
304
|
+
off = store16(xMax, glyphBuf, off);
|
|
305
|
+
off = store16(yMax, glyphBuf, off);
|
|
306
|
+
}
|
|
307
|
+
let endPoint = -1;
|
|
308
|
+
for (const count of nPoints) {
|
|
309
|
+
endPoint += count;
|
|
310
|
+
off = store16(endPoint, glyphBuf, off);
|
|
311
|
+
}
|
|
312
|
+
off = store16(instructionSize, glyphBuf, off);
|
|
313
|
+
const remaining = instructionStream.peekRemaining().length;
|
|
314
|
+
const safeSize = Math.min(instructionSize, remaining);
|
|
315
|
+
const instructions = instructionStream.readBytes(safeSize);
|
|
316
|
+
glyphBuf.set(instructions, off);
|
|
317
|
+
glyphBytes = glyphBuf;
|
|
318
|
+
const xMinRaw = (glyphBuf[2] << 8) | glyphBuf[3];
|
|
319
|
+
xMins[i] = xMinRaw & 0x8000 ? xMinRaw - 0x10000 : xMinRaw;
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
// Empty glyph
|
|
323
|
+
if (haveBbox) {
|
|
324
|
+
throw new Error("Invalid WOFF2 glyf: empty glyph with bbox");
|
|
325
|
+
}
|
|
326
|
+
glyphBytes = new Uint8Array(0);
|
|
327
|
+
}
|
|
328
|
+
locaValues[i] = currentGlyfOffset;
|
|
329
|
+
glyfChunks.push(glyphBytes);
|
|
330
|
+
currentGlyfOffset += round4(glyphBytes.length);
|
|
331
|
+
glyfChecksum = (glyfChecksum + computeULongSum(glyphBytes)) >>> 0;
|
|
332
|
+
}
|
|
333
|
+
locaValues[numGlyphs] = currentGlyfOffset;
|
|
334
|
+
if (locaDstLength !== (indexFormat ? 4 : 2) * (numGlyphs + 1)) {
|
|
335
|
+
throw new Error("Invalid WOFF2 loca length");
|
|
336
|
+
}
|
|
337
|
+
// Build glyf table with per-glyph padding
|
|
338
|
+
const glyfSize = locaValues[numGlyphs];
|
|
339
|
+
const glyfData = new Uint8Array(glyfSize);
|
|
340
|
+
let glyfOffset = 0;
|
|
341
|
+
for (const chunk of glyfChunks) {
|
|
342
|
+
glyfData.set(chunk, glyfOffset);
|
|
343
|
+
glyfOffset += chunk.length;
|
|
344
|
+
const padded = round4(glyfOffset) - glyfOffset;
|
|
345
|
+
glyfOffset += padded;
|
|
346
|
+
}
|
|
347
|
+
const loca = storeLoca(locaValues, indexFormat);
|
|
348
|
+
return {
|
|
349
|
+
glyfData,
|
|
350
|
+
locaData: loca.data,
|
|
351
|
+
glyfChecksum,
|
|
352
|
+
locaChecksum: loca.checksum,
|
|
353
|
+
numGlyphs,
|
|
354
|
+
indexFormat,
|
|
355
|
+
xMins
|
|
356
|
+
};
|
|
357
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Buf } from "./buffer.js";
|
|
2
|
+
import { computeULongSum, store16 } from "./utils.js";
|
|
3
|
+
export function readNumHMetrics(hheaTable) {
|
|
4
|
+
const buf = new Buf(hheaTable);
|
|
5
|
+
buf.skip(34);
|
|
6
|
+
return buf.readU16();
|
|
7
|
+
}
|
|
8
|
+
export function reconstructTransformedHmtx(transformed, numGlyphs, numHMetrics, xMins) {
|
|
9
|
+
const buf = new Buf(transformed);
|
|
10
|
+
const flags = buf.readU8();
|
|
11
|
+
const hasPropLSB = (flags & 1) === 0;
|
|
12
|
+
const hasMonoLSB = (flags & 2) === 0;
|
|
13
|
+
if ((flags & 0xfc) !== 0) {
|
|
14
|
+
throw new Error("Invalid hmtx flags");
|
|
15
|
+
}
|
|
16
|
+
if (hasPropLSB && hasMonoLSB) {
|
|
17
|
+
throw new Error("Invalid hmtx transform state");
|
|
18
|
+
}
|
|
19
|
+
if (numHMetrics < 1 || numHMetrics > numGlyphs) {
|
|
20
|
+
throw new Error("Invalid hmtx metrics count");
|
|
21
|
+
}
|
|
22
|
+
const advanceWidths = [];
|
|
23
|
+
const lsbs = [];
|
|
24
|
+
for (let i = 0; i < numHMetrics; i++) {
|
|
25
|
+
advanceWidths.push(buf.readU16());
|
|
26
|
+
}
|
|
27
|
+
for (let i = 0; i < numHMetrics; i++) {
|
|
28
|
+
lsbs.push(hasPropLSB ? buf.readS16() : xMins[i]);
|
|
29
|
+
}
|
|
30
|
+
for (let i = numHMetrics; i < numGlyphs; i++) {
|
|
31
|
+
lsbs.push(hasMonoLSB ? buf.readS16() : xMins[i]);
|
|
32
|
+
}
|
|
33
|
+
const out = new Uint8Array(2 * numGlyphs + 2 * numHMetrics);
|
|
34
|
+
let off = 0;
|
|
35
|
+
for (let i = 0; i < numGlyphs; i++) {
|
|
36
|
+
if (i < numHMetrics) {
|
|
37
|
+
off = store16(advanceWidths[i], out, off);
|
|
38
|
+
}
|
|
39
|
+
off = store16(lsbs[i], out, off);
|
|
40
|
+
}
|
|
41
|
+
return { data: out, checksum: computeULongSum(out) };
|
|
42
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const SFNT_HEADER_SIZE = 12;
|
|
2
|
+
export declare const SFNT_ENTRY_SIZE = 16;
|
|
3
|
+
export declare const CHECKSUM_ADJUSTMENT_OFFSET = 8;
|
|
4
|
+
export declare function buildSfnt(flavor: number, tableData: Map<number, Uint8Array>): {
|
|
5
|
+
ttf: Uint8Array;
|
|
6
|
+
tables: Record<string, Uint8Array>;
|
|
7
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { computeULongSum, round4, store16, store32, tagToString, TAG_HEAD, } from "./utils.js";
|
|
2
|
+
export const SFNT_HEADER_SIZE = 12;
|
|
3
|
+
export const SFNT_ENTRY_SIZE = 16;
|
|
4
|
+
export const CHECKSUM_ADJUSTMENT_OFFSET = 8;
|
|
5
|
+
export function buildSfnt(flavor, tableData) {
|
|
6
|
+
const entries = Array.from(tableData.entries()).sort((a, b) => a[0] - b[0]);
|
|
7
|
+
const numTables = entries.length;
|
|
8
|
+
let offset = SFNT_HEADER_SIZE + SFNT_ENTRY_SIZE * numTables;
|
|
9
|
+
const records = [];
|
|
10
|
+
for (const [tag, data] of entries) {
|
|
11
|
+
const paddedLen = round4(data.length);
|
|
12
|
+
records.push({
|
|
13
|
+
tag,
|
|
14
|
+
checksum: computeULongSum(data),
|
|
15
|
+
offset,
|
|
16
|
+
length: data.length,
|
|
17
|
+
data
|
|
18
|
+
});
|
|
19
|
+
offset += paddedLen;
|
|
20
|
+
}
|
|
21
|
+
const ttf = new Uint8Array(offset);
|
|
22
|
+
let off = 0;
|
|
23
|
+
off = store32(flavor >>> 0, ttf, off);
|
|
24
|
+
off = store16(numTables, ttf, off);
|
|
25
|
+
let maxPow2 = 0;
|
|
26
|
+
while ((1 << (maxPow2 + 1)) <= numTables)
|
|
27
|
+
maxPow2++;
|
|
28
|
+
const searchRange = (1 << maxPow2) * 16;
|
|
29
|
+
off = store16(searchRange, ttf, off);
|
|
30
|
+
off = store16(maxPow2, ttf, off);
|
|
31
|
+
off = store16(numTables * 16 - searchRange, ttf, off);
|
|
32
|
+
// table records
|
|
33
|
+
for (const rec of records) {
|
|
34
|
+
off = store32(rec.tag, ttf, off);
|
|
35
|
+
off = store32(rec.checksum, ttf, off);
|
|
36
|
+
off = store32(rec.offset, ttf, off);
|
|
37
|
+
off = store32(rec.length, ttf, off);
|
|
38
|
+
}
|
|
39
|
+
for (const rec of records) {
|
|
40
|
+
ttf.set(rec.data, rec.offset);
|
|
41
|
+
// zero padding already present
|
|
42
|
+
}
|
|
43
|
+
// checkSumAdjustment for 'head'
|
|
44
|
+
const headRecord = records.find((r) => r.tag === TAG_HEAD);
|
|
45
|
+
if (headRecord) {
|
|
46
|
+
const checksum = computeULongSum(ttf);
|
|
47
|
+
const adjustment = (0xb1b0afba - checksum) >>> 0;
|
|
48
|
+
store32(adjustment, ttf, headRecord.offset + CHECKSUM_ADJUSTMENT_OFFSET);
|
|
49
|
+
}
|
|
50
|
+
const tables = {};
|
|
51
|
+
for (const [tag, data] of entries) {
|
|
52
|
+
tables[tagToString(tag)] = data;
|
|
53
|
+
}
|
|
54
|
+
return { ttf, tables };
|
|
55
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare const TAG: (text: string) => number;
|
|
2
|
+
export declare const TAG_GLYF: number;
|
|
3
|
+
export declare const TAG_LOCA: number;
|
|
4
|
+
export declare const TAG_HMTX: number;
|
|
5
|
+
export declare const TAG_HHEA: number;
|
|
6
|
+
export declare const TAG_HEAD: number;
|
|
7
|
+
export declare const KNOWN_TAGS: number[];
|
|
8
|
+
export declare function tagToString(tag: number): string;
|
|
9
|
+
export declare const round4: (v: number) => number;
|
|
10
|
+
export declare function computeULongSum(data: Uint8Array): number;
|
|
11
|
+
export declare function store16(value: number, out: Uint8Array, offset: number): number;
|
|
12
|
+
export declare function store32(value: number, out: Uint8Array, offset: number): number;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
export const TAG = (text) => (text.charCodeAt(0) << 24) |
|
|
2
|
+
(text.charCodeAt(1) << 16) |
|
|
3
|
+
(text.charCodeAt(2) << 8) |
|
|
4
|
+
text.charCodeAt(3);
|
|
5
|
+
export const TAG_GLYF = TAG("glyf");
|
|
6
|
+
export const TAG_LOCA = TAG("loca");
|
|
7
|
+
export const TAG_HMTX = TAG("hmtx");
|
|
8
|
+
export const TAG_HHEA = TAG("hhea");
|
|
9
|
+
export const TAG_HEAD = TAG("head");
|
|
10
|
+
// Known tag lookup as defined in table_tags.cc
|
|
11
|
+
export const KNOWN_TAGS = [
|
|
12
|
+
TAG("cmap"),
|
|
13
|
+
TAG("head"),
|
|
14
|
+
TAG("hhea"),
|
|
15
|
+
TAG("hmtx"),
|
|
16
|
+
TAG("maxp"),
|
|
17
|
+
TAG("name"),
|
|
18
|
+
TAG("OS/2"),
|
|
19
|
+
TAG("post"),
|
|
20
|
+
TAG("cvt "),
|
|
21
|
+
TAG("fpgm"),
|
|
22
|
+
TAG_GLYF,
|
|
23
|
+
TAG_LOCA,
|
|
24
|
+
TAG("prep"),
|
|
25
|
+
TAG("CFF "),
|
|
26
|
+
TAG("VORG"),
|
|
27
|
+
TAG("EBDT"),
|
|
28
|
+
TAG("EBLC"),
|
|
29
|
+
TAG("gasp"),
|
|
30
|
+
TAG("hdmx"),
|
|
31
|
+
TAG("kern"),
|
|
32
|
+
TAG("LTSH"),
|
|
33
|
+
TAG("PCLT"),
|
|
34
|
+
TAG("VDMX"),
|
|
35
|
+
TAG("vhea"),
|
|
36
|
+
TAG("vmtx"),
|
|
37
|
+
TAG("BASE"),
|
|
38
|
+
TAG("GDEF"),
|
|
39
|
+
TAG("GPOS"),
|
|
40
|
+
TAG("GSUB"),
|
|
41
|
+
TAG("EBSC"),
|
|
42
|
+
TAG("JSTF"),
|
|
43
|
+
TAG("MATH"),
|
|
44
|
+
TAG("CBDT"),
|
|
45
|
+
TAG("CBLC"),
|
|
46
|
+
TAG("COLR"),
|
|
47
|
+
TAG("CPAL"),
|
|
48
|
+
TAG("SVG "),
|
|
49
|
+
TAG("sbix"),
|
|
50
|
+
TAG("acnt"),
|
|
51
|
+
TAG("avar"),
|
|
52
|
+
TAG("bdat"),
|
|
53
|
+
TAG("bloc"),
|
|
54
|
+
TAG("bsln"),
|
|
55
|
+
TAG("cvar"),
|
|
56
|
+
TAG("fdsc"),
|
|
57
|
+
TAG("feat"),
|
|
58
|
+
TAG("fmtx"),
|
|
59
|
+
TAG("fvar"),
|
|
60
|
+
TAG("gvar"),
|
|
61
|
+
TAG("hsty"),
|
|
62
|
+
TAG("just"),
|
|
63
|
+
TAG("lcar"),
|
|
64
|
+
TAG("mort"),
|
|
65
|
+
TAG("morx"),
|
|
66
|
+
TAG("opbd"),
|
|
67
|
+
TAG("prop"),
|
|
68
|
+
TAG("trak"),
|
|
69
|
+
TAG("Zapf"),
|
|
70
|
+
TAG("Silf"),
|
|
71
|
+
TAG("Glat"),
|
|
72
|
+
TAG("Gloc"),
|
|
73
|
+
TAG("Feat"),
|
|
74
|
+
TAG("Sill")
|
|
75
|
+
];
|
|
76
|
+
export function tagToString(tag) {
|
|
77
|
+
return String.fromCharCode((tag >> 24) & 0xff, (tag >> 16) & 0xff, (tag >> 8) & 0xff, tag & 0xff);
|
|
78
|
+
}
|
|
79
|
+
export const round4 = (v) => (v + 3) & ~3;
|
|
80
|
+
export function computeULongSum(data) {
|
|
81
|
+
let checksum = 0 >>> 0;
|
|
82
|
+
const aligned = data.length & ~3;
|
|
83
|
+
for (let i = 0; i < aligned; i += 4) {
|
|
84
|
+
const value = (data[i] << 24) |
|
|
85
|
+
(data[i + 1] << 16) |
|
|
86
|
+
(data[i + 2] << 8) |
|
|
87
|
+
data[i + 3];
|
|
88
|
+
checksum = (checksum + value) >>> 0;
|
|
89
|
+
}
|
|
90
|
+
if (aligned !== data.length) {
|
|
91
|
+
let v = 0;
|
|
92
|
+
for (let i = aligned; i < data.length; i++) {
|
|
93
|
+
v |= data[i] << (24 - 8 * (i & 3));
|
|
94
|
+
}
|
|
95
|
+
checksum = (checksum + v) >>> 0;
|
|
96
|
+
}
|
|
97
|
+
return checksum >>> 0;
|
|
98
|
+
}
|
|
99
|
+
export function store16(value, out, offset) {
|
|
100
|
+
const v = value & 0xffff;
|
|
101
|
+
out[offset] = (v >> 8) & 0xff;
|
|
102
|
+
out[offset + 1] = v & 0xff;
|
|
103
|
+
return offset + 2;
|
|
104
|
+
}
|
|
105
|
+
export function store32(value, out, offset) {
|
|
106
|
+
out[offset] = (value >>> 24) & 0xff;
|
|
107
|
+
out[offset + 1] = (value >>> 16) & 0xff;
|
|
108
|
+
out[offset + 2] = (value >>> 8) & 0xff;
|
|
109
|
+
out[offset + 3] = value & 0xff;
|
|
110
|
+
return offset + 4;
|
|
111
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { FontEmbedder } from "../pdf/font/embedder.js";
|
|
2
2
|
import { PdfDocument } from "../pdf/primitives/pdf-document.js";
|
|
3
|
-
import { applyPageVerticalMarginsWithHf, offsetRenderTree } from "../render/offset.js";
|
|
3
|
+
import { applyBreakInsideAvoid, applyPageVerticalMarginsWithHf, offsetRenderTree } from "../render/offset.js";
|
|
4
4
|
import { applyTextLayoutAdjustments } from "../pdf/utils/text-layout-adjuster.js";
|
|
5
5
|
import { buildRenderTree } from "../pdf/layout-tree-builder.js";
|
|
6
6
|
export async function initializeFontEmbedder(fontConfig) {
|
|
@@ -17,6 +17,10 @@ export function finalizeRenderTreePositioning(options) {
|
|
|
17
17
|
applyTextLayoutAdjustments(renderTree.root);
|
|
18
18
|
const headerHeightPx = resolvedHeaderFooter?.maxHeaderHeightPx ?? 0;
|
|
19
19
|
const footerHeightPx = resolvedHeaderFooter?.maxFooterHeightPx ?? 0;
|
|
20
|
+
const usablePageHeight = pageHeight - marginsPx.top - marginsPx.bottom - headerHeightPx - footerHeightPx;
|
|
21
|
+
if (usablePageHeight > 0) {
|
|
22
|
+
applyBreakInsideAvoid(renderTree.root, usablePageHeight);
|
|
23
|
+
}
|
|
20
24
|
applyPageVerticalMarginsWithHf(renderTree.root, {
|
|
21
25
|
pageHeight,
|
|
22
26
|
margins: marginsPx,
|
|
@@ -18,7 +18,7 @@ export class RunPlacer {
|
|
|
18
18
|
if (parts.length === 0) {
|
|
19
19
|
return;
|
|
20
20
|
}
|
|
21
|
-
const { lineTop,
|
|
21
|
+
const { lineTop, lineStartX, lineIndex, availableWidth, offsetShift, isLastLine, contentX, inlineOffsetStart } = lineContext;
|
|
22
22
|
const lineWidth = parts.reduce((max, part) => Math.max(max, part.offset + part.item.width), 0);
|
|
23
23
|
const currentAvailableWidth = Math.max(availableWidth, 0);
|
|
24
24
|
const spaceCount = parts.reduce((count, part) => {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { AlignItems, JustifyContent } from "../../../css/enums.js";
|
|
2
|
+
import type { AlignSelfValue } from "../../../css/style.js";
|
|
3
|
+
import type { AlignContentResolution, FlexLine } from "./types.js";
|
|
4
|
+
export declare function resolveAlignContentLayout(lines: FlexLine[], alignContent: string, containerCrossSize: number, crossAxisGap: number): AlignContentResolution;
|
|
5
|
+
export declare function resolveItemAlignment(alignSelf: AlignSelfValue | undefined, containerAlign: AlignItems): AlignItems;
|
|
6
|
+
export declare function computeCrossOffset(alignment: AlignItems, containerSize: number, itemSize: number, marginStart: number, marginEnd: number): number;
|
|
7
|
+
export declare function resolveJustifySpacing(justify: JustifyContent, freeSpace: number, itemCount: number): {
|
|
8
|
+
offset: number;
|
|
9
|
+
gap: number;
|
|
10
|
+
};
|