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.
Files changed (92) hide show
  1. package/README.md +283 -264
  2. package/dist/browser/pagyra.min.js +30 -30
  3. package/dist/browser/pagyra.min.js.map +4 -4
  4. package/dist/src/css/apply-declarations.js +2 -1
  5. package/dist/src/css/clip-path-types.d.ts +9 -1
  6. package/dist/src/css/compute-style/overrides.js +10 -1
  7. package/dist/src/css/parsers/clip-path-parser.js +51 -0
  8. package/dist/src/css/parsers/register-parsers.js +21 -0
  9. package/dist/src/css/properties/visual.d.ts +2 -0
  10. package/dist/src/css/style.d.ts +5 -0
  11. package/dist/src/css/style.js +3 -0
  12. package/dist/src/css/ua-defaults/element-defaults.js +13 -0
  13. package/dist/src/dom/node.d.ts +2 -0
  14. package/dist/src/dom/node.js +1 -0
  15. package/dist/src/fonts/woff2/decoder.d.ts +1 -9
  16. package/dist/src/fonts/woff2/decoder.js +6 -565
  17. package/dist/src/fonts/woff2/glyf-reconstructor.d.ts +54 -0
  18. package/dist/src/fonts/woff2/glyf-reconstructor.js +357 -0
  19. package/dist/src/fonts/woff2/hmtx-reconstructor.d.ts +5 -0
  20. package/dist/src/fonts/woff2/hmtx-reconstructor.js +42 -0
  21. package/dist/src/fonts/woff2/sfnt-builder.d.ts +7 -0
  22. package/dist/src/fonts/woff2/sfnt-builder.js +55 -0
  23. package/dist/src/fonts/woff2/utils.d.ts +12 -0
  24. package/dist/src/fonts/woff2/utils.js +111 -0
  25. package/dist/src/html-to-pdf/render-finalize.js +5 -1
  26. package/dist/src/layout/inline/run-placer.js +1 -1
  27. package/dist/src/layout/strategies/flex/alignment.d.ts +10 -0
  28. package/dist/src/layout/strategies/flex/alignment.js +91 -0
  29. package/dist/src/layout/strategies/flex/distributor.d.ts +5 -0
  30. package/dist/src/layout/strategies/flex/distributor.js +56 -0
  31. package/dist/src/layout/strategies/flex/line-builder.d.ts +5 -0
  32. package/dist/src/layout/strategies/flex/line-builder.js +55 -0
  33. package/dist/src/layout/strategies/flex/types.d.ts +27 -0
  34. package/dist/src/layout/strategies/flex/types.js +2 -0
  35. package/dist/src/layout/strategies/flex/utils.d.ts +12 -0
  36. package/dist/src/layout/strategies/flex/utils.js +113 -0
  37. package/dist/src/layout/strategies/flex.js +4 -308
  38. package/dist/src/layout/strategies/grid.js +0 -3
  39. package/dist/src/layout/strategies/table.js +85 -58
  40. package/dist/src/layout/utils/text-metrics.js +16 -8
  41. package/dist/src/pdf/font/embedder.js +3 -3
  42. package/dist/src/pdf/font/font-subset.js +1 -3
  43. package/dist/src/pdf/font/to-unicode.js +16 -16
  44. package/dist/src/pdf/layout-tree-builder.js +15 -9
  45. package/dist/src/pdf/renderer/box-painter.js +74 -9
  46. package/dist/src/pdf/renderers/text-renderer.d.ts +4 -2
  47. package/dist/src/pdf/renderers/text-renderer.js +52 -2
  48. package/dist/src/pdf/types.d.ts +16 -1
  49. package/dist/src/pdf/utils/clip-path-resolver.js +28 -12
  50. package/dist/src/pdf/utils/mask-resolver.d.ts +7 -0
  51. package/dist/src/pdf/utils/mask-resolver.js +25 -0
  52. package/dist/src/pdf/utils/node-text-run-factory.d.ts +2 -1
  53. package/dist/src/pdf/utils/node-text-run-factory.js +5 -26
  54. package/dist/src/pdf/utils/rounded-rect-to-path.d.ts +7 -0
  55. package/dist/src/pdf/utils/rounded-rect-to-path.js +86 -0
  56. package/dist/src/render/offset.d.ts +5 -0
  57. package/dist/src/render/offset.js +93 -9
  58. package/dist/src/text/line-breaker.js +31 -0
  59. package/dist/tests/css/clip-path-parser.spec.js +15 -8
  60. package/dist/tests/environment/path-resolution.spec.js +2 -1
  61. package/dist/tests/helpers/ai-layout-diagnostics.js +6 -6
  62. package/dist/tests/layout/container-query-units.spec.js +0 -7
  63. package/dist/tests/layout/inline-background-alignment.spec.js +6 -6
  64. package/dist/tests/layout/table-image-cell.spec.js +95 -0
  65. package/dist/tests/pdf/alignments.spec.js +12 -12
  66. package/dist/tests/pdf/clip-path.spec.js +3 -1
  67. package/dist/tests/pdf/form-text-encoding.spec.js +1 -1
  68. package/dist/tests/pdf/svg-stroke-dash.spec.js +8 -8
  69. package/dist/tests/pdf/text-transform-matrix.spec.js +1 -1
  70. package/dist/tests/pdf/xref-integrity.spec.js +1 -1
  71. package/dist/tests/verify-subset-multi.spec.js +14 -14
  72. package/dist/tests/verify-subset.spec.js +12 -12
  73. package/package.json +89 -71
  74. package/dist/src/image/js-png-backend.d.ts +0 -7
  75. package/dist/src/image/js-png-backend.js +0 -9
  76. package/dist/src/image/png-backend.d.ts +0 -5
  77. package/dist/src/image/png-wasm-loader.d.ts +0 -5
  78. package/dist/src/image/png-wasm-loader.js +0 -59
  79. package/dist/src/image/wasm/png_decoder_wasm.d.ts +0 -8
  80. package/dist/src/image/wasm/png_decoder_wasm.js +0 -24
  81. package/dist/src/image/wasm/png_decoder_wasm_bg.js +0 -16
  82. package/dist/src/image/wasm-png-backend.d.ts +0 -6
  83. package/dist/src/image/wasm-png-backend.js +0 -17
  84. package/dist/src/layout/table/cell_layout.d.ts +0 -2
  85. package/dist/src/layout/table/cell_layout.js +0 -26
  86. package/dist/tests/image/png-backend.spec.d.ts +0 -1
  87. package/dist/tests/image/png-backend.spec.js +0 -34
  88. package/dist/tests/pdf/font-subset-registry-key.spec.d.ts +0 -1
  89. package/dist/tests/pdf/font-subset-registry-key.spec.js +0 -66
  90. package/dist/tests/pdf/header-footer.spec.d.ts +0 -1
  91. package/dist/tests/pdf/header-footer.spec.js +0 -46
  92. /package/dist/{src/image/png-backend.js → tests/layout/table-image-cell.spec.d.ts} +0 -0
@@ -1,131 +1,20 @@
1
1
  import { decompressWoff2 } from "../../compression/decompress.js";
2
- import { Buf, readBase128, read255UShort } from "./buffer.js";
3
- // --- Constants & helpers ----------------------------------------------------
2
+ import { Buf, readBase128 } from "./buffer.js";
3
+ import { TAG, TAG_GLYF, TAG_LOCA, TAG_HMTX, TAG_HHEA, TAG_HEAD, KNOWN_TAGS, tagToString, } from "./utils.js";
4
+ import { buildSfnt, CHECKSUM_ADJUSTMENT_OFFSET } from "./sfnt-builder.js";
5
+ import { reconstructGlyfTable } from "./glyf-reconstructor.js";
6
+ import { readNumHMetrics, reconstructTransformedHmtx } from "./hmtx-reconstructor.js";
7
+ // --- Constants --------------------------------------------------------------
4
8
  const WOFF2_SIGNATURE = 0x774f4632; // "wOF2"
5
9
  const WOFF2_HEADER_SIZE = 48;
6
10
  const WOFF2_FLAG_TRANSFORM = 1 << 8;
7
- const SFNT_HEADER_SIZE = 12;
8
- const SFNT_ENTRY_SIZE = 16;
9
- const CHECKSUM_ADJUSTMENT_OFFSET = 8;
10
11
  const MAX_PLAUSIBLE_COMPRESSION_RATIO = 100;
11
- const TAG = (text) => (text.charCodeAt(0) << 24) |
12
- (text.charCodeAt(1) << 16) |
13
- (text.charCodeAt(2) << 8) |
14
- text.charCodeAt(3);
15
- const TAG_GLYF = TAG("glyf");
16
- const TAG_LOCA = TAG("loca");
17
- const TAG_HMTX = TAG("hmtx");
18
- const TAG_HHEA = TAG("hhea");
19
- const TAG_HEAD = TAG("head");
20
- // Known tag lookup as defined in table_tags.cc
21
- const KNOWN_TAGS = [
22
- TAG("cmap"),
23
- TAG("head"),
24
- TAG("hhea"),
25
- TAG("hmtx"),
26
- TAG("maxp"),
27
- TAG("name"),
28
- TAG("OS/2"),
29
- TAG("post"),
30
- TAG("cvt "),
31
- TAG("fpgm"),
32
- TAG_GLYF,
33
- TAG_LOCA,
34
- TAG("prep"),
35
- TAG("CFF "),
36
- TAG("VORG"),
37
- TAG("EBDT"),
38
- TAG("EBLC"),
39
- TAG("gasp"),
40
- TAG("hdmx"),
41
- TAG("kern"),
42
- TAG("LTSH"),
43
- TAG("PCLT"),
44
- TAG("VDMX"),
45
- TAG("vhea"),
46
- TAG("vmtx"),
47
- TAG("BASE"),
48
- TAG("GDEF"),
49
- TAG("GPOS"),
50
- TAG("GSUB"),
51
- TAG("EBSC"),
52
- TAG("JSTF"),
53
- TAG("MATH"),
54
- TAG("CBDT"),
55
- TAG("CBLC"),
56
- TAG("COLR"),
57
- TAG("CPAL"),
58
- TAG("SVG "),
59
- TAG("sbix"),
60
- TAG("acnt"),
61
- TAG("avar"),
62
- TAG("bdat"),
63
- TAG("bloc"),
64
- TAG("bsln"),
65
- TAG("cvar"),
66
- TAG("fdsc"),
67
- TAG("feat"),
68
- TAG("fmtx"),
69
- TAG("fvar"),
70
- TAG("gvar"),
71
- TAG("hsty"),
72
- TAG("just"),
73
- TAG("lcar"),
74
- TAG("mort"),
75
- TAG("morx"),
76
- TAG("opbd"),
77
- TAG("prop"),
78
- TAG("trak"),
79
- TAG("Zapf"),
80
- TAG("Silf"),
81
- TAG("Glat"),
82
- TAG("Gloc"),
83
- TAG("Feat"),
84
- TAG("Sill")
85
- ];
86
- function tagToString(tag) {
87
- return String.fromCharCode((tag >> 24) & 0xff, (tag >> 16) & 0xff, (tag >> 8) & 0xff, tag & 0xff);
88
- }
89
- const round4 = (v) => (v + 3) & ~3;
90
12
  function resolveDependencies(deps) {
91
13
  return {
92
14
  decompress: deps?.decompress ?? decompressWoff2,
93
15
  transformers: deps?.transformers ?? createDefaultTransformers(),
94
16
  };
95
17
  }
96
- function computeULongSum(data) {
97
- let checksum = 0 >>> 0;
98
- const aligned = data.length & ~3;
99
- for (let i = 0; i < aligned; i += 4) {
100
- const value = (data[i] << 24) |
101
- (data[i + 1] << 16) |
102
- (data[i + 2] << 8) |
103
- data[i + 3];
104
- checksum = (checksum + value) >>> 0;
105
- }
106
- if (aligned !== data.length) {
107
- let v = 0;
108
- for (let i = aligned; i < data.length; i++) {
109
- v |= data[i] << (24 - 8 * (i & 3));
110
- }
111
- checksum = (checksum + v) >>> 0;
112
- }
113
- return checksum >>> 0;
114
- }
115
- // --- Data writers -----------------------------------------------------------
116
- function store16(value, out, offset) {
117
- const v = value & 0xffff;
118
- out[offset] = (v >> 8) & 0xff;
119
- out[offset + 1] = v & 0xff;
120
- return offset + 2;
121
- }
122
- function store32(value, out, offset) {
123
- out[offset] = (value >>> 24) & 0xff;
124
- out[offset + 1] = (value >>> 16) & 0xff;
125
- out[offset + 2] = (value >>> 8) & 0xff;
126
- out[offset + 3] = value & 0xff;
127
- return offset + 4;
128
- }
129
18
  // --- Table directory parsing ------------------------------------------------
130
19
  function readTableDirectory(buf, numTables) {
131
20
  const tables = [];
@@ -226,403 +115,6 @@ function parseHeader(fontData) {
226
115
  tables
227
116
  };
228
117
  }
229
- // --- Glyf reconstruction helpers -------------------------------------------
230
- const GLYF_FLAGS = {
231
- ON_CURVE: 1 << 0,
232
- X_SHORT: 1 << 1,
233
- Y_SHORT: 1 << 2,
234
- REPEAT: 1 << 3,
235
- X_SAME: 1 << 4,
236
- Y_SAME: 1 << 5,
237
- OVERLAP_SIMPLE: 1 << 6
238
- };
239
- const COMPOSITE_FLAGS = {
240
- ARG_WORDS: 1 << 0,
241
- WE_HAVE_A_SCALE: 1 << 3,
242
- MORE_COMPONENTS: 1 << 5,
243
- WE_HAVE_AN_XY_SCALE: 1 << 6,
244
- WE_HAVE_A_TWO_BY_TWO: 1 << 7,
245
- WE_HAVE_INSTRUCTIONS: 1 << 8
246
- };
247
- function withSign(flag, base) {
248
- return (flag & 1) ? base : -base;
249
- }
250
- function tripletDecode(flagsIn, data, nPoints) {
251
- let x = 0;
252
- let y = 0;
253
- let tripletIndex = 0;
254
- const points = new Array(nPoints);
255
- for (let i = 0; i < nPoints; i++) {
256
- let flag = flagsIn[i];
257
- const onCurve = (flag >> 7) === 0;
258
- flag &= 0x7f;
259
- let nDataBytes = 1;
260
- if (flag >= 84 && flag < 120)
261
- nDataBytes = 2;
262
- else if (flag < 84)
263
- nDataBytes = 1;
264
- else if (flag < 124)
265
- nDataBytes = 3;
266
- else
267
- nDataBytes = 4;
268
- if (tripletIndex + nDataBytes > data.length) {
269
- throw new Error("Invalid WOFF2 glyf triplet");
270
- }
271
- let dx;
272
- let dy;
273
- if (flag < 10) {
274
- dx = 0;
275
- dy = withSign(flag, ((flag & 14) << 7) + data[tripletIndex]);
276
- }
277
- else if (flag < 20) {
278
- dx = withSign(flag, (((flag - 10) & 14) << 7) + data[tripletIndex]);
279
- dy = 0;
280
- }
281
- else if (flag < 84) {
282
- const b0 = flag - 20;
283
- const b1 = data[tripletIndex];
284
- dx = withSign(flag, 1 + (b0 & 0x30) + (b1 >> 4));
285
- dy = withSign(flag >> 1, 1 + ((b0 & 0x0c) << 2) + (b1 & 0x0f));
286
- }
287
- else if (flag < 120) {
288
- const b0 = flag - 84;
289
- dx = withSign(flag, 1 + ((b0 / 12) << 8) + data[tripletIndex]);
290
- dy = withSign(flag >> 1, 1 + (((b0 % 12) >> 2) << 8) + data[tripletIndex + 1]);
291
- }
292
- else if (flag < 124) {
293
- const b2 = data[tripletIndex + 1];
294
- dx = withSign(flag, (data[tripletIndex] << 4) + (b2 >> 4));
295
- dy = withSign(flag >> 1, ((b2 & 0x0f) << 8) + data[tripletIndex + 2]);
296
- }
297
- else {
298
- dx = withSign(flag, (data[tripletIndex] << 8) + data[tripletIndex + 1]);
299
- dy = withSign(flag >> 1, (data[tripletIndex + 2] << 8) + data[tripletIndex + 3]);
300
- }
301
- tripletIndex += nDataBytes;
302
- x += dx;
303
- y += dy;
304
- points[i] = { x, y, onCurve };
305
- }
306
- return { points, consumed: tripletIndex };
307
- }
308
- function storePoints(points, nContours, instructionLength, hasOverlap) {
309
- const nPoints = points.length;
310
- const xBytes = [];
311
- const yBytes = [];
312
- let lastX = 0;
313
- let lastY = 0;
314
- let lastFlag = -1;
315
- let repeatCount = 0;
316
- const flagOffset = 10 + 2 * nContours + 2 + instructionLength;
317
- const flagsOut = [];
318
- for (let i = 0; i < nPoints; i++) {
319
- const point = points[i];
320
- let flag = point.onCurve ? GLYF_FLAGS.ON_CURVE : 0;
321
- if (hasOverlap && i === 0) {
322
- flag |= GLYF_FLAGS.OVERLAP_SIMPLE;
323
- }
324
- const dx = point.x - lastX;
325
- const dy = point.y - lastY;
326
- if (dx === 0)
327
- flag |= GLYF_FLAGS.X_SAME;
328
- else if (dx > -256 && dx < 256) {
329
- flag |= GLYF_FLAGS.X_SHORT | (dx > 0 ? GLYF_FLAGS.X_SAME : 0);
330
- xBytes.push(Math.abs(dx));
331
- }
332
- else {
333
- xBytes.push((dx >> 8) & 0xff, dx & 0xff);
334
- }
335
- if (dy === 0)
336
- flag |= GLYF_FLAGS.Y_SAME;
337
- else if (dy > -256 && dy < 256) {
338
- flag |= GLYF_FLAGS.Y_SHORT | (dy > 0 ? GLYF_FLAGS.Y_SAME : 0);
339
- yBytes.push(Math.abs(dy));
340
- }
341
- else {
342
- yBytes.push((dy >> 8) & 0xff, dy & 0xff);
343
- }
344
- if (flag === lastFlag && repeatCount !== 255) {
345
- flagsOut[flagsOut.length - 1] |= GLYF_FLAGS.REPEAT;
346
- repeatCount++;
347
- }
348
- else {
349
- if (repeatCount !== 0) {
350
- flagsOut.push(repeatCount);
351
- }
352
- flagsOut.push(flag);
353
- repeatCount = 0;
354
- lastFlag = flag;
355
- }
356
- lastX = point.x;
357
- lastY = point.y;
358
- }
359
- if (repeatCount !== 0) {
360
- flagsOut.push(repeatCount);
361
- }
362
- const totalSize = flagOffset + flagsOut.length + xBytes.length + yBytes.length;
363
- const out = new Uint8Array(totalSize);
364
- let offset = flagOffset;
365
- for (const f of flagsOut)
366
- out[offset++] = f;
367
- for (const b of xBytes)
368
- out[offset++] = b;
369
- for (const b of yBytes)
370
- out[offset++] = b;
371
- return out;
372
- }
373
- function computeBBox(points) {
374
- if (points.length === 0) {
375
- return [0, 0, 0, 0];
376
- }
377
- let xMin = points[0].x;
378
- let xMax = points[0].x;
379
- let yMin = points[0].y;
380
- let yMax = points[0].y;
381
- for (let i = 1; i < points.length; i++) {
382
- const p = points[i];
383
- if (p.x < xMin)
384
- xMin = p.x;
385
- if (p.x > xMax)
386
- xMax = p.x;
387
- if (p.y < yMin)
388
- yMin = p.y;
389
- if (p.y > yMax)
390
- yMax = p.y;
391
- }
392
- return [xMin, yMin, xMax, yMax];
393
- }
394
- function sizeOfComposite(stream) {
395
- const start = stream.offset;
396
- let flags = COMPOSITE_FLAGS.MORE_COMPONENTS;
397
- let haveInstructions = false;
398
- while (flags & COMPOSITE_FLAGS.MORE_COMPONENTS) {
399
- flags = stream.readU16();
400
- haveInstructions || (haveInstructions = (flags & COMPOSITE_FLAGS.WE_HAVE_INSTRUCTIONS) !== 0);
401
- let argSize = 2; // glyph index
402
- if (flags & COMPOSITE_FLAGS.ARG_WORDS)
403
- argSize += 4;
404
- else
405
- argSize += 2;
406
- if (flags & COMPOSITE_FLAGS.WE_HAVE_A_SCALE)
407
- argSize += 2;
408
- else if (flags & COMPOSITE_FLAGS.WE_HAVE_AN_XY_SCALE)
409
- argSize += 4;
410
- else if (flags & COMPOSITE_FLAGS.WE_HAVE_A_TWO_BY_TWO)
411
- argSize += 8;
412
- stream.skip(argSize);
413
- }
414
- return { size: stream.offset - start, haveInstructions };
415
- }
416
- function storeLoca(locaValues, indexFormat) {
417
- const offsetSize = indexFormat ? 4 : 2;
418
- const buffer = new Uint8Array(locaValues.length * offsetSize);
419
- let off = 0;
420
- for (const value of locaValues) {
421
- if (indexFormat) {
422
- off = store32(value, buffer, off);
423
- }
424
- else {
425
- off = store16(value >> 1, buffer, off);
426
- }
427
- }
428
- return { data: buffer, checksum: computeULongSum(buffer) };
429
- }
430
- function reconstructGlyfTable(transformed, locaDstLength) {
431
- const stream = new Buf(transformed);
432
- /* const version = */ stream.readU16();
433
- const flags = stream.readU16();
434
- const hasOverlapBitmap = (flags & 1) !== 0;
435
- const numGlyphs = stream.readU16();
436
- const indexFormat = stream.readU16();
437
- const substreamSizes = [];
438
- for (let i = 0; i < 7; i++) {
439
- substreamSizes.push(stream.readU32());
440
- }
441
- let dataOffset = (2 + 7) * 4; // header (version/flags/numGlyphs/indexFormat + sizes)
442
- const substreams = [];
443
- for (const sz of substreamSizes) {
444
- if (dataOffset + sz > transformed.length) {
445
- throw new Error("Invalid WOFF2 glyf stream");
446
- }
447
- substreams.push(transformed.subarray(dataOffset, dataOffset + sz));
448
- dataOffset += sz;
449
- }
450
- let overlapBitmap = null;
451
- if (hasOverlapBitmap) {
452
- const len = (numGlyphs + 7) >> 3;
453
- if (dataOffset + len > transformed.length) {
454
- throw new Error("Invalid WOFF2 overlap bitmap");
455
- }
456
- overlapBitmap = transformed.subarray(dataOffset, dataOffset + len);
457
- }
458
- const nContourStream = new Buf(substreams[0]);
459
- const nPointsStream = new Buf(substreams[1]);
460
- const flagStream = new Buf(substreams[2]);
461
- const glyphStream = new Buf(substreams[3]);
462
- const compositeStream = new Buf(substreams[4]);
463
- const bboxStream = new Buf(substreams[5]);
464
- const instructionStream = new Buf(substreams[6]);
465
- const bboxBitmapLen = ((numGlyphs + 31) >> 5) << 2;
466
- const bboxBitmap = bboxStream.readBytes(bboxBitmapLen);
467
- const locaValues = new Array(numGlyphs + 1).fill(0);
468
- const xMins = new Int16Array(numGlyphs);
469
- const glyfChunks = [];
470
- let glyfChecksum = 0 >>> 0;
471
- let currentGlyfOffset = 0;
472
- for (let i = 0; i < numGlyphs; i++) {
473
- const nContours = nContourStream.readU16();
474
- const haveBbox = (bboxBitmap[i >> 3] & (0x80 >> (i & 7))) !== 0;
475
- let glyphBytes = null;
476
- if (nContours === 0xffff) {
477
- // Composite
478
- const { size: compositeSize, haveInstructions } = sizeOfComposite(new Buf(compositeStream.peekRemaining()));
479
- let instructionSize = 0;
480
- if (haveInstructions) {
481
- instructionSize = read255UShort(glyphStream);
482
- }
483
- const total = 12 + compositeSize + instructionSize; // nContours + bbox + composite + instructions
484
- const out = new Uint8Array(total);
485
- let off = 0;
486
- off = store16(nContours, out, off);
487
- if (!haveBbox) {
488
- throw new Error("Invalid WOFF2 glyf: composite without bbox");
489
- }
490
- const bboxData = bboxStream.readBytes(8);
491
- const xMinRaw = (bboxData[0] << 8) | bboxData[1];
492
- xMins[i] = xMinRaw & 0x8000 ? xMinRaw - 0x10000 : xMinRaw;
493
- out.set(bboxData, off);
494
- off += 8;
495
- out.set(compositeStream.readBytes(compositeSize), off);
496
- off += compositeSize;
497
- if (haveInstructions) {
498
- off = store16(instructionSize, out, off);
499
- out.set(instructionStream.readBytes(instructionSize), off);
500
- }
501
- glyphBytes = out;
502
- }
503
- else if (nContours > 0) {
504
- const nPoints = [];
505
- let totalPoints = 0;
506
- for (let c = 0; c < nContours; c++) {
507
- const pts = read255UShort(nPointsStream);
508
- totalPoints += pts;
509
- nPoints.push(pts);
510
- }
511
- const flagData = flagStream.readBytes(totalPoints);
512
- const tripletsView = glyphStream.peekRemaining();
513
- const { points, consumed } = tripletDecode(flagData, tripletsView, totalPoints);
514
- glyphStream.offset += consumed;
515
- const instructionSize = read255UShort(glyphStream);
516
- const bbox = haveBbox ? bboxStream.readBytes(8) : null;
517
- // const _baseSize = 12 + 2 * nContours + instructionSize;
518
- const ptsBuf = storePoints(points, nContours, instructionSize, !!(overlapBitmap && (overlapBitmap[i >> 3] & (0x80 >> (i & 7)))));
519
- const glyphBuf = new Uint8Array(ptsBuf.length);
520
- glyphBuf.set(ptsBuf);
521
- let off = 0;
522
- off = store16(nContours, glyphBuf, off);
523
- if (bbox) {
524
- glyphBuf.set(bbox, off);
525
- off += 8;
526
- }
527
- else {
528
- const [xMin, yMin, xMax, yMax] = computeBBox(points);
529
- off = store16(xMin, glyphBuf, off);
530
- off = store16(yMin, glyphBuf, off);
531
- off = store16(xMax, glyphBuf, off);
532
- off = store16(yMax, glyphBuf, off);
533
- }
534
- let endPoint = -1;
535
- for (const count of nPoints) {
536
- endPoint += count;
537
- off = store16(endPoint, glyphBuf, off);
538
- }
539
- off = store16(instructionSize, glyphBuf, off);
540
- const remaining = instructionStream.peekRemaining().length;
541
- const safeSize = Math.min(instructionSize, remaining);
542
- const instructions = instructionStream.readBytes(safeSize);
543
- glyphBuf.set(instructions, off);
544
- glyphBytes = glyphBuf;
545
- const xMinRaw = (glyphBuf[2] << 8) | glyphBuf[3];
546
- xMins[i] = xMinRaw & 0x8000 ? xMinRaw - 0x10000 : xMinRaw;
547
- }
548
- else {
549
- // Empty glyph
550
- if (haveBbox) {
551
- throw new Error("Invalid WOFF2 glyf: empty glyph with bbox");
552
- }
553
- glyphBytes = new Uint8Array(0);
554
- }
555
- locaValues[i] = currentGlyfOffset;
556
- glyfChunks.push(glyphBytes);
557
- currentGlyfOffset += round4(glyphBytes.length);
558
- glyfChecksum = (glyfChecksum + computeULongSum(glyphBytes)) >>> 0;
559
- }
560
- locaValues[numGlyphs] = currentGlyfOffset;
561
- if (locaDstLength !== (indexFormat ? 4 : 2) * (numGlyphs + 1)) {
562
- throw new Error("Invalid WOFF2 loca length");
563
- }
564
- // Build glyf table with per-glyph padding
565
- const glyfSize = locaValues[numGlyphs];
566
- const glyfData = new Uint8Array(glyfSize);
567
- let glyfOffset = 0;
568
- for (const chunk of glyfChunks) {
569
- glyfData.set(chunk, glyfOffset);
570
- glyfOffset += chunk.length;
571
- const padded = round4(glyfOffset) - glyfOffset;
572
- glyfOffset += padded;
573
- }
574
- const loca = storeLoca(locaValues, indexFormat);
575
- return {
576
- glyfData,
577
- locaData: loca.data,
578
- glyfChecksum,
579
- locaChecksum: loca.checksum,
580
- numGlyphs,
581
- indexFormat,
582
- xMins
583
- };
584
- }
585
- // --- HMTX reconstruction ----------------------------------------------------
586
- function readNumHMetrics(hheaTable) {
587
- const buf = new Buf(hheaTable);
588
- buf.skip(34);
589
- return buf.readU16();
590
- }
591
- function reconstructTransformedHmtx(transformed, numGlyphs, numHMetrics, xMins) {
592
- const buf = new Buf(transformed);
593
- const flags = buf.readU8();
594
- const hasPropLSB = (flags & 1) === 0;
595
- const hasMonoLSB = (flags & 2) === 0;
596
- if ((flags & 0xfc) !== 0) {
597
- throw new Error("Invalid hmtx flags");
598
- }
599
- if (hasPropLSB && hasMonoLSB) {
600
- throw new Error("Invalid hmtx transform state");
601
- }
602
- if (numHMetrics < 1 || numHMetrics > numGlyphs) {
603
- throw new Error("Invalid hmtx metrics count");
604
- }
605
- const advanceWidths = [];
606
- const lsbs = [];
607
- for (let i = 0; i < numHMetrics; i++) {
608
- advanceWidths.push(buf.readU16());
609
- }
610
- for (let i = 0; i < numHMetrics; i++) {
611
- lsbs.push(hasPropLSB ? buf.readS16() : xMins[i]);
612
- }
613
- for (let i = numHMetrics; i < numGlyphs; i++) {
614
- lsbs.push(hasMonoLSB ? buf.readS16() : xMins[i]);
615
- }
616
- const out = new Uint8Array(2 * numGlyphs + 2 * numHMetrics);
617
- let off = 0;
618
- for (let i = 0; i < numGlyphs; i++) {
619
- if (i < numHMetrics) {
620
- off = store16(advanceWidths[i], out, off);
621
- }
622
- off = store16(lsbs[i], out, off);
623
- }
624
- return { data: out, checksum: computeULongSum(out) };
625
- }
626
118
  // --- Transform registry ----------------------------------------------------
627
119
  function transformGlyfTable(_table, srcData, context) {
628
120
  const locaTable = context.tablesByTag.get(TAG_LOCA);
@@ -655,57 +147,6 @@ function createDefaultTransformers() {
655
147
  ]);
656
148
  }
657
149
  // --- Font rebuild -----------------------------------------------------------
658
- function buildSfnt(flavor, tableData) {
659
- const entries = Array.from(tableData.entries()).sort((a, b) => a[0] - b[0]);
660
- const numTables = entries.length;
661
- let offset = SFNT_HEADER_SIZE + SFNT_ENTRY_SIZE * numTables;
662
- const records = [];
663
- for (const [tag, data] of entries) {
664
- const paddedLen = round4(data.length);
665
- records.push({
666
- tag,
667
- checksum: computeULongSum(data),
668
- offset,
669
- length: data.length,
670
- data
671
- });
672
- offset += paddedLen;
673
- }
674
- const ttf = new Uint8Array(offset);
675
- let off = 0;
676
- off = store32(flavor >>> 0, ttf, off);
677
- off = store16(numTables, ttf, off);
678
- let maxPow2 = 0;
679
- while ((1 << (maxPow2 + 1)) <= numTables)
680
- maxPow2++;
681
- const searchRange = (1 << maxPow2) * 16;
682
- off = store16(searchRange, ttf, off);
683
- off = store16(maxPow2, ttf, off);
684
- off = store16(numTables * 16 - searchRange, ttf, off);
685
- // table records
686
- for (const rec of records) {
687
- off = store32(rec.tag, ttf, off);
688
- off = store32(rec.checksum, ttf, off);
689
- off = store32(rec.offset, ttf, off);
690
- off = store32(rec.length, ttf, off);
691
- }
692
- for (const rec of records) {
693
- ttf.set(rec.data, rec.offset);
694
- // zero padding already present
695
- }
696
- // checkSumAdjustment for 'head'
697
- const headRecord = records.find((r) => r.tag === TAG_HEAD);
698
- if (headRecord) {
699
- const checksum = computeULongSum(ttf);
700
- const adjustment = (0xb1b0afba - checksum) >>> 0;
701
- store32(adjustment, ttf, headRecord.offset + CHECKSUM_ADJUSTMENT_OFFSET);
702
- }
703
- const tables = {};
704
- for (const [tag, data] of entries) {
705
- tables[tagToString(tag)] = data;
706
- }
707
- return { ttf, tables };
708
- }
709
150
  function rebuildFont(header, transformed, deps) {
710
151
  const tableMap = new Map();
711
152
  const tablesByTag = new Map(header.tables.map((t) => [t.tag, t]));
@@ -0,0 +1,54 @@
1
+ import { Buf } from "./buffer.js";
2
+ export declare const GLYF_FLAGS: {
3
+ ON_CURVE: number;
4
+ X_SHORT: number;
5
+ Y_SHORT: number;
6
+ REPEAT: number;
7
+ X_SAME: number;
8
+ Y_SAME: number;
9
+ OVERLAP_SIMPLE: number;
10
+ };
11
+ export declare const COMPOSITE_FLAGS: {
12
+ ARG_WORDS: number;
13
+ WE_HAVE_A_SCALE: number;
14
+ MORE_COMPONENTS: number;
15
+ WE_HAVE_AN_XY_SCALE: number;
16
+ WE_HAVE_A_TWO_BY_TWO: number;
17
+ WE_HAVE_INSTRUCTIONS: number;
18
+ };
19
+ export interface GlyfReconstruction {
20
+ glyfData: Uint8Array;
21
+ locaData: Uint8Array;
22
+ glyfChecksum: number;
23
+ locaChecksum: number;
24
+ numGlyphs: number;
25
+ indexFormat: number;
26
+ xMins: Int16Array;
27
+ }
28
+ export declare function withSign(flag: number, base: number): number;
29
+ export declare function tripletDecode(flagsIn: Uint8Array, data: Uint8Array, nPoints: number): {
30
+ points: {
31
+ x: number;
32
+ y: number;
33
+ onCurve: boolean;
34
+ }[];
35
+ consumed: number;
36
+ };
37
+ export declare function storePoints(points: {
38
+ x: number;
39
+ y: number;
40
+ onCurve: boolean;
41
+ }[], nContours: number, instructionLength: number, hasOverlap: boolean): Uint8Array;
42
+ export declare function computeBBox(points: {
43
+ x: number;
44
+ y: number;
45
+ }[]): [number, number, number, number];
46
+ export declare function sizeOfComposite(stream: Buf): {
47
+ size: number;
48
+ haveInstructions: boolean;
49
+ };
50
+ export declare function storeLoca(locaValues: number[], indexFormat: number): {
51
+ data: Uint8Array;
52
+ checksum: number;
53
+ };
54
+ export declare function reconstructGlyfTable(transformed: Uint8Array, locaDstLength: number): GlyfReconstruction;