web-doc 0.8.0 → 0.10.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.
Files changed (38) hide show
  1. package/THIRD_PARTY_NOTICES.md +14 -0
  2. package/dist/adapters/office.js +2 -1
  3. package/dist/contracts.d.ts +9 -0
  4. package/dist/edit/ai/tools.js +3 -1
  5. package/dist/edit/docx/elements.js +19 -1
  6. package/dist/edit/docx/types.d.ts +8 -0
  7. package/dist/edit/pdf/engine/cff.d.ts +28 -0
  8. package/dist/edit/pdf/engine/cff.js +416 -0
  9. package/dist/edit/pdf/engine/document.d.ts +3 -1
  10. package/dist/edit/pdf/engine/document.js +69 -4
  11. package/dist/edit/pdf/engine/elements.d.ts +13 -0
  12. package/dist/edit/pdf/engine/elements.js +12 -1
  13. package/dist/edit/pdf/engine/existing-text.js +6 -4
  14. package/dist/edit/pdf/engine/fonts.js +13 -10
  15. package/dist/edit/pdf/engine/glyph-names.d.ts +2 -0
  16. package/dist/edit/pdf/engine/glyph-names.js +163 -0
  17. package/dist/edit/pdf/engine/handler.js +2 -0
  18. package/dist/edit/pdf/engine/opentype.d.ts +14 -0
  19. package/dist/edit/pdf/engine/opentype.js +317 -0
  20. package/dist/edit/pdf/engine/pages.js +9 -1
  21. package/dist/edit/pdf/engine/text-font.d.ts +4 -0
  22. package/dist/edit/pdf/engine/text-font.js +81 -0
  23. package/dist/edit/pdf/provider.d.ts +3 -1
  24. package/dist/edit/pdf/provider.js +3 -0
  25. package/dist/edit/pdf/schemas.js +4 -1
  26. package/dist/edit/pdf/session.d.ts +2 -1
  27. package/dist/edit/pdf/session.js +3 -0
  28. package/dist/edit/pdf/types.d.ts +55 -3
  29. package/dist/edit/pptx/session.d.ts +7 -2
  30. package/dist/edit/pptx/session.js +41 -3
  31. package/dist/fonts/THIRD_PARTY_NOTICES.md +6 -3
  32. package/dist/fonts/manifest.json +4 -4
  33. package/dist/fonts/noto-sans-latin-cyrillic.ttf +0 -0
  34. package/dist/viewport.js +83 -20
  35. package/dist/worker-protocol.d.ts +1 -1
  36. package/dist/workers/ooxml-edit-worker.js +15 -1
  37. package/dist/workers/pdf-edit-worker.js +1176 -197
  38. package/package.json +1 -1
@@ -3661,7 +3661,7 @@ var init_index_browser = __esm({
3661
3661
  // count: 1
3662
3662
  ];
3663
3663
  generateFuncType(sig, typeSectionBody);
3664
- var bytes = [
3664
+ var bytes2 = [
3665
3665
  0,
3666
3666
  97,
3667
3667
  115,
@@ -3675,9 +3675,9 @@ var init_index_browser = __esm({
3675
3675
  1
3676
3676
  // Type section code
3677
3677
  ];
3678
- uleb128Encode(typeSectionBody.length, bytes);
3679
- bytes.push(...typeSectionBody);
3680
- bytes.push(
3678
+ uleb128Encode(typeSectionBody.length, bytes2);
3679
+ bytes2.push(...typeSectionBody);
3680
+ bytes2.push(
3681
3681
  2,
3682
3682
  7,
3683
3683
  // import section
@@ -3699,7 +3699,7 @@ var init_index_browser = __esm({
3699
3699
  0,
3700
3700
  0
3701
3701
  );
3702
- var module = new WebAssembly.Module(new Uint8Array(bytes));
3702
+ var module = new WebAssembly.Module(new Uint8Array(bytes2));
3703
3703
  var instance = new WebAssembly.Instance(module, { "e": { "f": func } });
3704
3704
  var wrappedFunc = instance.exports["f"];
3705
3705
  return wrappedFunc;
@@ -5850,13 +5850,16 @@ var pdfOperationSchemas = Object.freeze({
5850
5850
  },
5851
5851
  ["from", "to"]
5852
5852
  ),
5853
+ // Exactly one of `rotation` (absolute) and `by` (a turn from the current
5854
+ // angle); the handler reports a batch that gives both or neither.
5853
5855
  rotatePage: operation(
5854
5856
  "rotatePage",
5855
5857
  {
5856
5858
  pageIndex: { type: "integer", minimum: 0 },
5857
- rotation: { enum: [0, 90, 180, 270] }
5859
+ rotation: { enum: [0, 90, 180, 270] },
5860
+ by: { enum: [90, 180, 270] }
5858
5861
  },
5859
- ["pageIndex", "rotation"]
5862
+ ["pageIndex"]
5860
5863
  ),
5861
5864
  insertShape: operation(
5862
5865
  "insertShape",
@@ -6375,15 +6378,15 @@ function readMark(pdfium, object) {
6375
6378
  for (let index = 0; index < lib.FPDFPageObj_CountMarks(object); index += 1) {
6376
6379
  const mark = lib.FPDFPageObj_GetMark(object, index);
6377
6380
  const name = pdfium.readWideStringOut(
6378
- (buffer, bytes, out) => lib.FPDFPageObjMark_GetName(mark, buffer, bytes, out)
6381
+ (buffer, bytes2, out) => lib.FPDFPageObjMark_GetName(mark, buffer, bytes2, out)
6379
6382
  );
6380
6383
  if (name !== MARK_NAME) continue;
6381
6384
  const raw = pdfium.readWideStringOut(
6382
- (buffer, bytes, out) => lib.FPDFPageObjMark_GetParamStringValue(
6385
+ (buffer, bytes2, out) => lib.FPDFPageObjMark_GetParamStringValue(
6383
6386
  mark,
6384
6387
  MARK_PARAM,
6385
6388
  buffer,
6386
- bytes,
6389
+ bytes2,
6387
6390
  out
6388
6391
  )
6389
6392
  );
@@ -6532,17 +6535,21 @@ var TABLE_OPERATIONS = Object.freeze([
6532
6535
  ]);
6533
6536
  function textOf(pdfium, object, textPage) {
6534
6537
  return pdfium.readWideString(
6535
- (buffer, bytes) => pdfium.lib.FPDFTextObj_GetText(object, textPage, buffer, bytes)
6538
+ (buffer, bytes2) => pdfium.lib.FPDFTextObj_GetText(object, textPage, buffer, bytes2)
6536
6539
  );
6537
6540
  }
6541
+ function textScale(matrix) {
6542
+ const scale = matrix ? Math.hypot(matrix[2], matrix[3]) : 1;
6543
+ return Number.isFinite(scale) && scale > 0 ? scale : 1;
6544
+ }
6538
6545
  function textStyle(pdfium, object) {
6539
6546
  const { lib } = pdfium;
6540
6547
  const font = lib.FPDFTextObj_GetFont(object);
6541
6548
  const baseName = pdfium.readUtf8String(
6542
- (buffer, bytes) => lib.FPDFFont_GetBaseFontName(font, buffer, bytes)
6549
+ (buffer, bytes2) => lib.FPDFFont_GetBaseFontName(font, buffer, bytes2)
6543
6550
  );
6544
6551
  const family = lib.FPDFFont_GetIsEmbedded(font) ? pdfium.readUtf8String(
6545
- (buffer, bytes) => lib.FPDFFont_GetFamilyName(font, buffer, bytes)
6552
+ (buffer, bytes2) => lib.FPDFFont_GetFamilyName(font, buffer, bytes2)
6546
6553
  ) || declaredFamily(baseName) : declaredFamily(baseName);
6547
6554
  const flags = lib.FPDFFont_GetFlags(font);
6548
6555
  const weight = lib.FPDFFont_GetWeight(font);
@@ -6551,9 +6558,14 @@ function textStyle(pdfium, object) {
6551
6558
  "float",
6552
6559
  ([pointer]) => lib.FPDFTextObj_GetFontSize(object, pointer)
6553
6560
  )?.[0] ?? 0;
6561
+ const matrix = pdfium.readNumbers(
6562
+ 6,
6563
+ "float",
6564
+ ([pointer]) => lib.FPDFPageObj_GetMatrix(object, pointer)
6565
+ );
6554
6566
  return {
6555
6567
  fontFamily: family,
6556
- fontSize: round(size),
6568
+ fontSize: round(size * textScale(matrix)),
6557
6569
  bold: weight >= 600 || (flags & FLAG_FORCE_BOLD) !== 0 || /bold|black|heavy/i.test(baseName),
6558
6570
  italic: (flags & FLAG_ITALIC) !== 0 || /italic|oblique/i.test(baseName),
6559
6571
  color: fillColor(pdfium, object) ?? "#000000"
@@ -6897,7 +6909,7 @@ var FontLibrary = class {
6897
6909
  if (this.#coveringRegistered(request)) return void 0;
6898
6910
  if (this.#fallback && this.#covers(this.#fallback, request.text))
6899
6911
  return void 0;
6900
- const bad = firstNonWinAnsi(request.text) ?? firstUncovered(request.text, this);
6912
+ const bad = this.#fallback ? this.#firstMissing(this.#fallback, request.text) : firstNonWinAnsi(request.text);
6901
6913
  return {
6902
6914
  path: "/text",
6903
6915
  code: "font-unavailable",
@@ -6944,8 +6956,8 @@ var FontLibrary = class {
6944
6956
  for (const handle of handles.values()) pdfium.lib.FPDFFont_Close(handle);
6945
6957
  this.#handles.delete(document2);
6946
6958
  }
6947
- covers(bytes, text) {
6948
- return this.#covers(bytes, text);
6959
+ covers(bytes2, text) {
6960
+ return this.#covers(bytes2, text);
6949
6961
  }
6950
6962
  #coveringRegistered(request) {
6951
6963
  const candidates = this.#registered.filter((font) => sameFamily(font.family, request.family)).map((font) => ({ font, bytes: this.#loadedBytes(font) })).filter(
@@ -6954,33 +6966,37 @@ var FontLibrary = class {
6954
6966
  const score = (font) => (isBold(font) === request.bold ? 2 : 0) + (isItalic(font) === request.italic ? 1 : 0);
6955
6967
  return candidates.sort((a, b) => score(b.font) - score(a.font))[0];
6956
6968
  }
6957
- #covers(bytes, text) {
6958
- let coverage = this.#coverage.get(bytes);
6969
+ #covers(bytes2, text) {
6970
+ return this.#firstMissing(bytes2, text) === void 0;
6971
+ }
6972
+ /** The first character of `text` the font has no glyph for; layout handles line breaks and spaces. */
6973
+ #firstMissing(bytes2, text) {
6974
+ let coverage = this.#coverage.get(bytes2);
6959
6975
  if (!coverage) {
6960
- coverage = parseCmap(bytes);
6961
- this.#coverage.set(bytes, coverage);
6976
+ coverage = parseCmap(bytes2);
6977
+ this.#coverage.set(bytes2, coverage);
6962
6978
  }
6963
6979
  for (const character of text) {
6964
6980
  const code = character.codePointAt(0);
6965
6981
  if (code === 10 || code === 32) continue;
6966
- if (!coverage.has(code)) return false;
6982
+ if (!coverage.has(code)) return character;
6967
6983
  }
6968
- return true;
6984
+ return void 0;
6969
6985
  }
6970
- #load(pdfium, document2, bytes) {
6986
+ #load(pdfium, document2, bytes2) {
6971
6987
  let handles = this.#handles.get(document2);
6972
6988
  if (!handles) {
6973
6989
  handles = /* @__PURE__ */ new Map();
6974
6990
  this.#handles.set(document2, handles);
6975
6991
  }
6976
- let handle = handles.get(bytes);
6992
+ let handle = handles.get(bytes2);
6977
6993
  if (handle === void 0) {
6978
- const pointer = pdfium.writeBytes(bytes);
6994
+ const pointer = pdfium.writeBytes(bytes2);
6979
6995
  try {
6980
6996
  handle = pdfium.lib.FPDFText_LoadFont(
6981
6997
  document2,
6982
6998
  pointer,
6983
- bytes.byteLength,
6999
+ bytes2.byteLength,
6984
7000
  FONT_TRUETYPE,
6985
7001
  true
6986
7002
  );
@@ -6988,7 +7004,7 @@ var FontLibrary = class {
6988
7004
  pdfium.free(pointer);
6989
7005
  }
6990
7006
  if (!handle) throw new Error("PDFium could not load the font");
6991
- handles.set(bytes, handle);
7007
+ handles.set(bytes2, handle);
6992
7008
  }
6993
7009
  return handle;
6994
7010
  }
@@ -6997,12 +7013,12 @@ var FontLibrary = class {
6997
7013
  }
6998
7014
  async #bytesOf(font) {
6999
7015
  const key = sourceKey(font);
7000
- let bytes = this.#bytes.get(key);
7001
- if (!bytes) {
7002
- bytes = typeof font.source === "string" ? await this.#fetch(font.source) : new Uint8Array(font.source);
7003
- this.#bytes.set(key, bytes);
7016
+ let bytes2 = this.#bytes.get(key);
7017
+ if (!bytes2) {
7018
+ bytes2 = typeof font.source === "string" ? await this.#fetch(font.source) : new Uint8Array(font.source);
7019
+ this.#bytes.set(key, bytes2);
7004
7020
  }
7005
- return bytes;
7021
+ return bytes2;
7006
7022
  }
7007
7023
  };
7008
7024
  function sourceKey(font) {
@@ -7017,18 +7033,12 @@ function isBold(font) {
7017
7033
  function isItalic(font) {
7018
7034
  return font.style !== "normal";
7019
7035
  }
7020
- function firstUncovered(text, library) {
7021
- void library;
7022
- for (const character of text)
7023
- if (character !== "\n" && character !== " ") return character;
7024
- return void 0;
7025
- }
7026
7036
  function glyphOutlines(view, tables) {
7027
- const head = tables.get("head");
7037
+ const head2 = tables.get("head");
7028
7038
  const maxp = tables.get("maxp");
7029
7039
  const loca = tables.get("loca");
7030
- if (!head || !maxp || !loca || !tables.has("glyf")) return void 0;
7031
- const longOffsets = view.getInt16(head.offset + 50) === 1;
7040
+ if (!head2 || !maxp || !loca || !tables.has("glyf")) return void 0;
7041
+ const longOffsets = view.getInt16(head2.offset + 50) === 1;
7032
7042
  const count = view.getUint16(maxp.offset + 4);
7033
7043
  return (glyph) => {
7034
7044
  if (glyph < 0 || glyph >= count) return false;
@@ -7038,8 +7048,8 @@ function glyphOutlines(view, tables) {
7038
7048
  return end > start;
7039
7049
  };
7040
7050
  }
7041
- function parseCmap(bytes) {
7042
- const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
7051
+ function parseCmap(bytes2) {
7052
+ const view = new DataView(bytes2.buffer, bytes2.byteOffset, bytes2.byteLength);
7043
7053
  const ranges = [];
7044
7054
  const glyphs = /* @__PURE__ */ new Map();
7045
7055
  let outlines;
@@ -7050,10 +7060,10 @@ function parseCmap(bytes) {
7050
7060
  for (let index = 0; index < tables; index += 1) {
7051
7061
  const record = 12 + index * 16;
7052
7062
  const tag = String.fromCharCode(
7053
- bytes[record],
7054
- bytes[record + 1],
7055
- bytes[record + 2],
7056
- bytes[record + 3]
7063
+ bytes2[record],
7064
+ bytes2[record + 1],
7065
+ bytes2[record + 2],
7066
+ bytes2[record + 3]
7057
7067
  );
7058
7068
  offsets.set(tag, {
7059
7069
  offset: view.getUint32(record + 8),
@@ -7222,6 +7232,915 @@ var TextMeasurer = class {
7222
7232
  }
7223
7233
  };
7224
7234
 
7235
+ // src/edit/pdf/engine/glyph-names.ts
7236
+ var AGLFN = `
7237
+ A 0041 AE 00C6 AEacute 01FC Aacute 00C1 Abreve 0102 Acircumflex 00C2
7238
+ Adieresis 00C4 Agrave 00C0 Alpha 0391 Alphatonos 0386 Amacron 0100
7239
+ Aogonek 0104 Aring 00C5 Aringacute 01FA Atilde 00C3 B 0042 Beta 0392 C 0043
7240
+ Cacute 0106 Ccaron 010C Ccedilla 00C7 Ccircumflex 0108 Cdotaccent 010A
7241
+ Chi 03A7 D 0044 Dcaron 010E Dcroat 0110 Delta 2206 E 0045 Eacute 00C9
7242
+ Ebreve 0114 Ecaron 011A Ecircumflex 00CA Edieresis 00CB Edotaccent 0116
7243
+ Egrave 00C8 Emacron 0112 Eng 014A Eogonek 0118 Epsilon 0395
7244
+ Epsilontonos 0388 Eta 0397 Etatonos 0389 Eth 00D0 Euro 20AC F 0046 G 0047
7245
+ Gamma 0393 Gbreve 011E Gcaron 01E6 Gcircumflex 011C Gdotaccent 0120 H 0048
7246
+ H18533 25CF H18543 25AA H18551 25AB H22073 25A1 Hbar 0126 Hcircumflex 0124
7247
+ I 0049 IJ 0132 Iacute 00CD Ibreve 012C Icircumflex 00CE Idieresis 00CF
7248
+ Idotaccent 0130 Ifraktur 2111 Igrave 00CC Imacron 012A Iogonek 012E
7249
+ Iota 0399 Iotadieresis 03AA Iotatonos 038A Itilde 0128 J 004A
7250
+ Jcircumflex 0134 K 004B Kappa 039A L 004C Lacute 0139 Lambda 039B
7251
+ Lcaron 013D Ldot 013F Lslash 0141 M 004D Mu 039C N 004E Nacute 0143
7252
+ Ncaron 0147 Ntilde 00D1 Nu 039D O 004F OE 0152 Oacute 00D3 Obreve 014E
7253
+ Ocircumflex 00D4 Odieresis 00D6 Ograve 00D2 Ohorn 01A0 Ohungarumlaut 0150
7254
+ Omacron 014C Omega 2126 Omegatonos 038F Omicron 039F Omicrontonos 038C
7255
+ Oslash 00D8 Oslashacute 01FE Otilde 00D5 P 0050 Phi 03A6 Pi 03A0 Psi 03A8
7256
+ Q 0051 R 0052 Racute 0154 Rcaron 0158 Rfraktur 211C Rho 03A1 S 0053
7257
+ SF010000 250C SF020000 2514 SF030000 2510 SF040000 2518 SF050000 253C
7258
+ SF060000 252C SF070000 2534 SF080000 251C SF090000 2524 SF100000 2500
7259
+ SF110000 2502 SF190000 2561 SF200000 2562 SF210000 2556 SF220000 2555
7260
+ SF230000 2563 SF240000 2551 SF250000 2557 SF260000 255D SF270000 255C
7261
+ SF280000 255B SF360000 255E SF370000 255F SF380000 255A SF390000 2554
7262
+ SF400000 2569 SF410000 2566 SF420000 2560 SF430000 2550 SF440000 256C
7263
+ SF450000 2567 SF460000 2568 SF470000 2564 SF480000 2565 SF490000 2559
7264
+ SF500000 2558 SF510000 2552 SF520000 2553 SF530000 256B SF540000 256A
7265
+ Sacute 015A Scaron 0160 Scedilla 015E Scircumflex 015C Sigma 03A3 T 0054
7266
+ Tau 03A4 Tbar 0166 Tcaron 0164 Theta 0398 Thorn 00DE U 0055 Uacute 00DA
7267
+ Ubreve 016C Ucircumflex 00DB Udieresis 00DC Ugrave 00D9 Uhorn 01AF
7268
+ Uhungarumlaut 0170 Umacron 016A Uogonek 0172 Upsilon 03A5 Upsilon1 03D2
7269
+ Upsilondieresis 03AB Upsilontonos 038E Uring 016E Utilde 0168 V 0056 W 0057
7270
+ Wacute 1E82 Wcircumflex 0174 Wdieresis 1E84 Wgrave 1E80 X 0058 Xi 039E
7271
+ Y 0059 Yacute 00DD Ycircumflex 0176 Ydieresis 0178 Ygrave 1EF2 Z 005A
7272
+ Zacute 0179 Zcaron 017D Zdotaccent 017B Zeta 0396 a 0061 aacute 00E1
7273
+ abreve 0103 acircumflex 00E2 acute 00B4 acutecomb 0301 adieresis 00E4
7274
+ ae 00E6 aeacute 01FD agrave 00E0 aleph 2135 alpha 03B1 alphatonos 03AC
7275
+ amacron 0101 ampersand 0026 angle 2220 angleleft 2329 angleright 232A
7276
+ anoteleia 0387 aogonek 0105 approxequal 2248 aring 00E5 aringacute 01FB
7277
+ arrowboth 2194 arrowdblboth 21D4 arrowdbldown 21D3 arrowdblleft 21D0
7278
+ arrowdblright 21D2 arrowdblup 21D1 arrowdown 2193 arrowleft 2190
7279
+ arrowright 2192 arrowup 2191 arrowupdn 2195 arrowupdnbse 21A8
7280
+ asciicircum 005E asciitilde 007E asterisk 002A asteriskmath 2217 at 0040
7281
+ atilde 00E3 b 0062 backslash 005C bar 007C beta 03B2 block 2588
7282
+ braceleft 007B braceright 007D bracketleft 005B bracketright 005D breve 02D8
7283
+ brokenbar 00A6 bullet 2022 c 0063 cacute 0107 caron 02C7 carriagereturn 21B5
7284
+ ccaron 010D ccedilla 00E7 ccircumflex 0109 cdotaccent 010B cedilla 00B8
7285
+ cent 00A2 chi 03C7 circle 25CB circlemultiply 2297 circleplus 2295
7286
+ circumflex 02C6 club 2663 colon 003A colonmonetary 20A1 comma 002C
7287
+ congruent 2245 copyright 00A9 currency 00A4 d 0064 dagger 2020
7288
+ daggerdbl 2021 dcaron 010F dcroat 0111 degree 00B0 delta 03B4 diamond 2666
7289
+ dieresis 00A8 dieresistonos 0385 divide 00F7 dkshade 2593 dnblock 2584
7290
+ dollar 0024 dong 20AB dotaccent 02D9 dotbelowcomb 0323 dotlessi 0131
7291
+ dotmath 22C5 e 0065 eacute 00E9 ebreve 0115 ecaron 011B ecircumflex 00EA
7292
+ edieresis 00EB edotaccent 0117 egrave 00E8 eight 0038 element 2208
7293
+ ellipsis 2026 emacron 0113 emdash 2014 emptyset 2205 endash 2013 eng 014B
7294
+ eogonek 0119 epsilon 03B5 epsilontonos 03AD equal 003D equivalence 2261
7295
+ estimated 212E eta 03B7 etatonos 03AE eth 00F0 exclam 0021 exclamdbl 203C
7296
+ exclamdown 00A1 existential 2203 f 0066 female 2640 figuredash 2012
7297
+ filledbox 25A0 filledrect 25AC five 0035 fiveeighths 215D florin 0192
7298
+ four 0034 fraction 2044 franc 20A3 g 0067 gamma 03B3 gbreve 011F gcaron 01E7
7299
+ gcircumflex 011D gdotaccent 0121 germandbls 00DF gradient 2207 grave 0060
7300
+ gravecomb 0300 greater 003E greaterequal 2265 guillemotleft 00AB
7301
+ guillemotright 00BB guilsinglleft 2039 guilsinglright 203A h 0068 hbar 0127
7302
+ hcircumflex 0125 heart 2665 hookabovecomb 0309 house 2302 hungarumlaut 02DD
7303
+ hyphen 002D i 0069 iacute 00ED ibreve 012D icircumflex 00EE idieresis 00EF
7304
+ igrave 00EC ij 0133 imacron 012B infinity 221E integral 222B integralbt 2321
7305
+ integraltp 2320 intersection 2229 invbullet 25D8 invcircle 25D9
7306
+ invsmileface 263B iogonek 012F iota 03B9 iotadieresis 03CA
7307
+ iotadieresistonos 0390 iotatonos 03AF itilde 0129 j 006A jcircumflex 0135
7308
+ k 006B kappa 03BA kgreenlandic 0138 l 006C lacute 013A lambda 03BB
7309
+ lcaron 013E ldot 0140 less 003C lessequal 2264 lfblock 258C lira 20A4
7310
+ logicaland 2227 logicalnot 00AC logicalor 2228 longs 017F lozenge 25CA
7311
+ lslash 0142 ltshade 2591 m 006D macron 00AF male 2642 minus 2212 minute 2032
7312
+ mu 00B5 multiply 00D7 musicalnote 266A musicalnotedbl 266B n 006E
7313
+ nacute 0144 napostrophe 0149 ncaron 0148 nine 0039 notelement 2209
7314
+ notequal 2260 notsubset 2284 ntilde 00F1 nu 03BD numbersign 0023 o 006F
7315
+ oacute 00F3 obreve 014F ocircumflex 00F4 odieresis 00F6 oe 0153 ogonek 02DB
7316
+ ograve 00F2 ohorn 01A1 ohungarumlaut 0151 omacron 014D omega 03C9
7317
+ omega1 03D6 omegatonos 03CE omicron 03BF omicrontonos 03CC one 0031
7318
+ onedotenleader 2024 oneeighth 215B onehalf 00BD onequarter 00BC
7319
+ onethird 2153 openbullet 25E6 ordfeminine 00AA ordmasculine 00BA
7320
+ orthogonal 221F oslash 00F8 oslashacute 01FF otilde 00F5 p 0070
7321
+ paragraph 00B6 parenleft 0028 parenright 0029 partialdiff 2202 percent 0025
7322
+ period 002E periodcentered 00B7 perpendicular 22A5 perthousand 2030
7323
+ peseta 20A7 phi 03C6 phi1 03D5 pi 03C0 plus 002B plusminus 00B1
7324
+ prescription 211E product 220F propersubset 2282 propersuperset 2283
7325
+ proportional 221D psi 03C8 q 0071 question 003F questiondown 00BF
7326
+ quotedbl 0022 quotedblbase 201E quotedblleft 201C quotedblright 201D
7327
+ quoteleft 2018 quotereversed 201B quoteright 2019 quotesinglbase 201A
7328
+ quotesingle 0027 r 0072 racute 0155 radical 221A rcaron 0159
7329
+ reflexsubset 2286 reflexsuperset 2287 registered 00AE revlogicalnot 2310
7330
+ rho 03C1 ring 02DA rtblock 2590 s 0073 sacute 015B scaron 0161 scedilla 015F
7331
+ scircumflex 015D second 2033 section 00A7 semicolon 003B seven 0037
7332
+ seveneighths 215E shade 2592 sigma 03C3 sigma1 03C2 similar 223C six 0036
7333
+ slash 002F smileface 263A space 0020 spade 2660 sterling 00A3 suchthat 220B
7334
+ summation 2211 sun 263C t 0074 tau 03C4 tbar 0167 tcaron 0165 therefore 2234
7335
+ theta 03B8 theta1 03D1 thorn 00FE three 0033 threeeighths 215C
7336
+ threequarters 00BE tilde 02DC tildecomb 0303 tonos 0384 trademark 2122
7337
+ triagdn 25BC triaglf 25C4 triagrt 25BA triagup 25B2 two 0032
7338
+ twodotenleader 2025 twothirds 2154 u 0075 uacute 00FA ubreve 016D
7339
+ ucircumflex 00FB udieresis 00FC ugrave 00F9 uhorn 01B0 uhungarumlaut 0171
7340
+ umacron 016B underscore 005F underscoredbl 2017 union 222A universal 2200
7341
+ uogonek 0173 upblock 2580 upsilon 03C5 upsilondieresis 03CB
7342
+ upsilondieresistonos 03B0 upsilontonos 03CD uring 016F utilde 0169 v 0076
7343
+ w 0077 wacute 1E83 wcircumflex 0175 wdieresis 1E85 weierstrass 2118
7344
+ wgrave 1E81 x 0078 xi 03BE y 0079 yacute 00FD ycircumflex 0177
7345
+ ydieresis 00FF yen 00A5 ygrave 1EF3 z 007A zacute 017A zcaron 017E
7346
+ zdotaccent 017C zero 0030 zeta 03B6
7347
+ `;
7348
+ var AGL_EXTRAS = `
7349
+ fi FB01 fl FB02 ff FB00 ffi FB03 ffl FB04 onesuperior 00B9 twosuperior 00B2
7350
+ threesuperior 00B3 nbspace 00A0
7351
+ `;
7352
+ var GLYPH_UNICODE = new Map(
7353
+ `${AGLFN} ${AGL_EXTRAS}`.trim().split(/\s+/).flatMap(
7354
+ (word, index, words) => index % 2 === 0 ? [[word, Number.parseInt(words[index + 1], 16)]] : []
7355
+ )
7356
+ );
7357
+
7358
+ // src/edit/pdf/engine/cff.ts
7359
+ var ISO_ADOBE_NAMES = `
7360
+ .notdef space exclam quotedbl numbersign dollar percent ampersand
7361
+ quoteright parenleft parenright asterisk plus comma hyphen period slash
7362
+ zero one two three four five six seven eight nine colon semicolon less
7363
+ equal greater question at A B C D E F G H I J K L M N O P Q R S T U V W X Y
7364
+ Z bracketleft backslash bracketright asciicircum underscore quoteleft a b c
7365
+ d e f g h i j k l m n o p q r s t u v w x y z braceleft bar braceright
7366
+ asciitilde exclamdown cent sterling fraction yen florin section currency
7367
+ quotesingle quotedblleft guillemotleft guilsinglleft guilsinglright fi fl
7368
+ endash dagger daggerdbl periodcentered paragraph bullet quotesinglbase
7369
+ quotedblbase quotedblright guillemotright ellipsis perthousand questiondown
7370
+ grave acute circumflex tilde macron breve dotaccent dieresis ring cedilla
7371
+ hungarumlaut ogonek caron emdash AE ordfeminine Lslash Oslash OE
7372
+ ordmasculine ae dotlessi lslash oslash oe germandbls onesuperior logicalnot
7373
+ mu trademark Eth onehalf plusminus Thorn onequarter divide brokenbar degree
7374
+ thorn threequarters twosuperior registered minus eth multiply threesuperior
7375
+ copyright Aacute Acircumflex Adieresis Agrave Aring Atilde Ccedilla Eacute
7376
+ Ecircumflex Edieresis Egrave Iacute Icircumflex Idieresis Igrave Ntilde
7377
+ Oacute Ocircumflex Odieresis Ograve Otilde Scaron Uacute Ucircumflex
7378
+ Udieresis Ugrave Yacute Ydieresis Zcaron aacute acircumflex adieresis
7379
+ agrave aring atilde ccedilla eacute ecircumflex edieresis egrave iacute
7380
+ icircumflex idieresis igrave ntilde oacute ocircumflex odieresis ograve
7381
+ otilde scaron uacute ucircumflex udieresis ugrave yacute ydieresis zcaron
7382
+ `.trim().split(/\s+/);
7383
+ function glyphUnicode(name) {
7384
+ if (!name || name === ".notdef") return void 0;
7385
+ const known = GLYPH_UNICODE.get(name);
7386
+ if (known !== void 0) return known;
7387
+ const match = /^uni([0-9A-F]{4})$|^u([0-9A-F]{4,6})$/.exec(name);
7388
+ const hex2 = match?.[1] ?? match?.[2];
7389
+ if (!hex2) return void 0;
7390
+ const code = Number.parseInt(hex2, 16);
7391
+ const surrogate = code >= 55296 && code <= 57343;
7392
+ const nonCharacter = (code & 65534) === 65534;
7393
+ return code <= 1114111 && !surrogate && !nonCharacter ? code : void 0;
7394
+ }
7395
+ var OP = {
7396
+ fontBBox: 5,
7397
+ charset: 15,
7398
+ charStrings: 17,
7399
+ private: 18,
7400
+ defaultWidthX: 20,
7401
+ nominalWidthX: 21,
7402
+ subrs: 19,
7403
+ charstringType: 1206,
7404
+ fontMatrix: 1207,
7405
+ ros: 1230
7406
+ };
7407
+ var STANDARD_STRINGS = 391;
7408
+ function readCff(bytes2) {
7409
+ try {
7410
+ const at = (offset2) => {
7411
+ if (!Number.isInteger(offset2) || offset2 < 0 || offset2 >= bytes2.length)
7412
+ throw new RangeError("Outside the font");
7413
+ return bytes2[offset2];
7414
+ };
7415
+ const u162 = (offset2) => at(offset2) << 8 | at(offset2 + 1);
7416
+ if (at(0) !== 1) return void 0;
7417
+ const names2 = readIndex(bytes2, at(2), at, u162);
7418
+ const tops = readIndex(bytes2, names2.end, at, u162);
7419
+ const strings = readIndex(bytes2, tops.end, at, u162);
7420
+ const globalSubrs = readIndex(bytes2, strings.end, at, u162);
7421
+ if (names2.items.length !== 1 || tops.items.length !== 1) return void 0;
7422
+ const top = readDict(bytes2, ...tops.items[0], at);
7423
+ const charStringsAt = top.get(OP.charStrings)?.[0];
7424
+ if (charStringsAt === void 0) return void 0;
7425
+ if ((top.get(OP.charstringType)?.[0] ?? 2) !== 2) return void 0;
7426
+ const charStrings = readIndex(bytes2, charStringsAt, at, u162);
7427
+ const glyphCount = charStrings.items.length;
7428
+ if (glyphCount === 0) return void 0;
7429
+ const [scale = 1e-3, skewX = 0, skewY = 0, scaleY = 1e-3] = top.get(OP.fontMatrix) ?? [];
7430
+ if (skewX !== 0 || skewY !== 0 || scale !== scaleY || scale <= 0)
7431
+ return void 0;
7432
+ const unitsPerEm = Math.round(1 / scale);
7433
+ if (unitsPerEm < 16 || unitsPerEm > 16384) return void 0;
7434
+ const [xMin = 0, yMin = 0, xMax = 0, yMax = 0] = top.get(OP.fontBBox) ?? [];
7435
+ const cid = top.has(OP.ros);
7436
+ const [nameStart, nameEnd] = names2.items[0];
7437
+ const name = latin1(
7438
+ bytes2.subarray(nameStart, Math.min(nameEnd, nameStart + 127))
7439
+ );
7440
+ if (cid)
7441
+ return {
7442
+ name,
7443
+ cid,
7444
+ glyphNames: [],
7445
+ widths: [],
7446
+ bbox: [xMin, yMin, xMax, yMax],
7447
+ unitsPerEm
7448
+ };
7449
+ const custom = /* @__PURE__ */ new Map();
7450
+ const stringOf = (sid) => {
7451
+ if (sid < ISO_ADOBE_NAMES.length) return ISO_ADOBE_NAMES[sid];
7452
+ const item = strings.items[sid - STANDARD_STRINGS];
7453
+ if (!item) return void 0;
7454
+ if (!custom.has(sid))
7455
+ custom.set(
7456
+ sid,
7457
+ // A glyph name is 63 characters at most.
7458
+ item[1] - item[0] <= 63 ? latin1(bytes2.subarray(...item)) : void 0
7459
+ );
7460
+ return custom.get(sid);
7461
+ };
7462
+ const glyphNames = charsetNames(
7463
+ top.get(OP.charset)?.[0] ?? 0,
7464
+ glyphCount,
7465
+ stringOf,
7466
+ at,
7467
+ u162
7468
+ );
7469
+ const [privateSize, privateAt] = top.get(OP.private) ?? [0, 0];
7470
+ const privateDict = privateSize ? readDict(bytes2, privateAt, privateAt + privateSize, at) : /* @__PURE__ */ new Map();
7471
+ const localAt = privateDict.get(OP.subrs)?.[0];
7472
+ const metrics = {
7473
+ bytes: bytes2,
7474
+ defaultWidth: privateDict.get(OP.defaultWidthX)?.[0] ?? 0,
7475
+ nominalWidth: privateDict.get(OP.nominalWidthX)?.[0] ?? 0,
7476
+ global: globalSubrs.items,
7477
+ local: localAt === void 0 ? [] : readIndex(bytes2, privateAt + localAt, at, u162).items
7478
+ };
7479
+ const widths = charStrings.items.map(
7480
+ ([start, end]) => charstringWidth(metrics, start, end)
7481
+ );
7482
+ return {
7483
+ name,
7484
+ cid,
7485
+ glyphNames,
7486
+ widths,
7487
+ bbox: [xMin, yMin, xMax, yMax],
7488
+ unitsPerEm
7489
+ };
7490
+ } catch (error) {
7491
+ if (error instanceof RangeError) return void 0;
7492
+ throw error;
7493
+ }
7494
+ }
7495
+ function cffWithSafeName(bytes2) {
7496
+ try {
7497
+ const at = (offset2) => {
7498
+ if (offset2 < 0 || offset2 >= bytes2.length)
7499
+ throw new RangeError("Outside the font");
7500
+ return bytes2[offset2];
7501
+ };
7502
+ const u162 = (offset2) => at(offset2) << 8 | at(offset2 + 1);
7503
+ const names2 = readIndex(bytes2, at(2), at, u162);
7504
+ const [start, end] = names2.items[0] ?? [0, 0];
7505
+ if (names2.items.length !== 1 || end - start > 127) return void 0;
7506
+ const safe = bytes2.slice();
7507
+ for (let offset2 = start; offset2 < end; offset2 += 1) {
7508
+ const byte = safe[offset2];
7509
+ if (offset2 === start && byte === 0) continue;
7510
+ if (byte < 33 || byte > 126 || UNSAFE_NAME_BYTES.has(byte))
7511
+ safe[offset2] = 95;
7512
+ }
7513
+ return safe;
7514
+ } catch (error) {
7515
+ if (error instanceof RangeError) return void 0;
7516
+ throw error;
7517
+ }
7518
+ }
7519
+ var UNSAFE_NAME_BYTES = new Set(
7520
+ [..."[](){}<>/%"].map((character) => character.charCodeAt(0))
7521
+ );
7522
+ function readIndex(bytes2, start, at, u162) {
7523
+ const count = u162(start);
7524
+ if (count === 0) return { items: [], end: start + 2 };
7525
+ const offSize = at(start + 2);
7526
+ if (offSize < 1 || offSize > 4) throw new RangeError("Bad offset size");
7527
+ const offset2 = (index) => {
7528
+ let value = 0;
7529
+ for (let byte = 0; byte < offSize; byte += 1)
7530
+ value = value * 256 + at(start + 3 + index * offSize + byte);
7531
+ return value;
7532
+ };
7533
+ if (offset2(0) !== 1) throw new RangeError("Bad INDEX");
7534
+ const base = start + 2 + (count + 1) * offSize;
7535
+ const items = [];
7536
+ for (let index = 0; index < count; index += 1) {
7537
+ const from = base + offset2(index);
7538
+ const to = base + offset2(index + 1);
7539
+ if (to < from || to > bytes2.length) throw new RangeError("Bad INDEX");
7540
+ items.push([from, to]);
7541
+ }
7542
+ return { items, end: base + offset2(count) };
7543
+ }
7544
+ function readDict(bytes2, start, end, at) {
7545
+ if (end > bytes2.length) throw new RangeError("DICT past the font");
7546
+ const entries = /* @__PURE__ */ new Map();
7547
+ let operands = [];
7548
+ let offset2 = start;
7549
+ while (offset2 < end) {
7550
+ const byte = at(offset2);
7551
+ if (byte <= 21) {
7552
+ const operator = byte === 12 ? 1200 + at(offset2 + 1) : byte;
7553
+ offset2 += byte === 12 ? 2 : 1;
7554
+ entries.set(operator, operands);
7555
+ operands = [];
7556
+ } else if (byte === 28) {
7557
+ operands.push(int16(at(offset2 + 1) << 8 | at(offset2 + 2)));
7558
+ offset2 += 3;
7559
+ } else if (byte === 29) {
7560
+ operands.push(
7561
+ at(offset2 + 1) << 24 | at(offset2 + 2) << 16 | at(offset2 + 3) << 8 | at(offset2 + 4)
7562
+ );
7563
+ offset2 += 5;
7564
+ } else if (byte === 30) {
7565
+ let text = "";
7566
+ offset2 += 1;
7567
+ for (let done = false; !done; offset2 += 1) {
7568
+ const pair = at(offset2);
7569
+ for (const nibble of [pair >> 4, pair & 15]) {
7570
+ if (nibble === 15) {
7571
+ done = true;
7572
+ break;
7573
+ }
7574
+ text += REAL_NIBBLES[nibble] ?? "";
7575
+ }
7576
+ }
7577
+ operands.push(Number.parseFloat(text));
7578
+ } else if (byte >= 32 && byte <= 246) {
7579
+ operands.push(byte - 139);
7580
+ offset2 += 1;
7581
+ } else if (byte >= 247 && byte <= 250) {
7582
+ operands.push((byte - 247) * 256 + at(offset2 + 1) + 108);
7583
+ offset2 += 2;
7584
+ } else if (byte >= 251 && byte <= 254) {
7585
+ operands.push(-(byte - 251) * 256 - at(offset2 + 1) - 108);
7586
+ offset2 += 2;
7587
+ } else throw new RangeError("Bad DICT byte");
7588
+ }
7589
+ return entries;
7590
+ }
7591
+ var REAL_NIBBLES = [
7592
+ "0",
7593
+ "1",
7594
+ "2",
7595
+ "3",
7596
+ "4",
7597
+ "5",
7598
+ "6",
7599
+ "7",
7600
+ "8",
7601
+ "9",
7602
+ ".",
7603
+ "E",
7604
+ "E-",
7605
+ "",
7606
+ "-"
7607
+ ];
7608
+ function charsetNames(offset2, glyphCount, stringOf, at, u162) {
7609
+ const names2 = [".notdef"];
7610
+ if (offset2 === 0) {
7611
+ for (let glyph = 1; glyph < glyphCount; glyph += 1)
7612
+ names2.push(stringOf(glyph));
7613
+ return names2;
7614
+ }
7615
+ if (offset2 <= 2) return [...names2, ...Array(glyphCount - 1)];
7616
+ const format = at(offset2);
7617
+ let cursor = offset2 + 1;
7618
+ if (format === 0) {
7619
+ for (let glyph = 1; glyph < glyphCount; glyph += 1, cursor += 2)
7620
+ names2.push(stringOf(u162(cursor)));
7621
+ } else if (format === 1 || format === 2) {
7622
+ while (names2.length < glyphCount) {
7623
+ const first = u162(cursor);
7624
+ const left = format === 1 ? at(cursor + 2) : u162(cursor + 2);
7625
+ cursor += format === 1 ? 3 : 4;
7626
+ for (let step = 0; step <= left && names2.length < glyphCount; step += 1)
7627
+ names2.push(stringOf(first + step));
7628
+ }
7629
+ } else throw new RangeError("Bad charset format");
7630
+ return names2;
7631
+ }
7632
+ function charstringWidth(context, start, end) {
7633
+ const { bytes: bytes2, defaultWidth, nominalWidth } = context;
7634
+ const stack = [];
7635
+ const walk = (from, to, depth) => {
7636
+ let offset2 = from;
7637
+ while (offset2 < to) {
7638
+ const byte = bytes2[offset2];
7639
+ const size = byte === 28 ? 3 : byte === 255 ? 5 : byte >= 247 && byte <= 254 ? 2 : 1;
7640
+ if (offset2 + size > to) return defaultWidth;
7641
+ if (byte === 28 || byte >= 32) {
7642
+ const next = (step) => bytes2[offset2 + step];
7643
+ stack.push(
7644
+ byte === 28 ? int16(next(1) << 8 | next(2)) : byte <= 246 ? byte - 139 : byte <= 250 ? (byte - 247) * 256 + next(1) + 108 : byte <= 254 ? -(byte - 251) * 256 - next(1) - 108 : (next(1) << 24 | next(2) << 16 | next(3) << 8 | next(4)) / 65536
7645
+ );
7646
+ if (stack.length > 48) return defaultWidth;
7647
+ offset2 += size;
7648
+ continue;
7649
+ }
7650
+ offset2 += 1;
7651
+ if (byte === 10 || byte === 29) {
7652
+ const subrs = byte === 10 ? context.local : context.global;
7653
+ const index = stack.pop();
7654
+ const subr = index === void 0 ? void 0 : subrs[index + subrBias(subrs.length)];
7655
+ if (!subr || depth >= 10) return defaultWidth;
7656
+ const width = walk(subr[0], subr[1], depth + 1);
7657
+ if (width !== void 0) return width;
7658
+ continue;
7659
+ }
7660
+ if (byte === 11) return depth > 0 ? void 0 : defaultWidth;
7661
+ const takes = STACK_CLEARING[byte];
7662
+ if (takes === void 0) return defaultWidth;
7663
+ const count = stack.length;
7664
+ const widthFirst = takes === "pairs" ? count % 2 === 1 : takes === "endchar" ? count === 1 || count === 5 : count > takes;
7665
+ return widthFirst ? stack[0] + nominalWidth : defaultWidth;
7666
+ }
7667
+ return depth > 0 ? void 0 : defaultWidth;
7668
+ };
7669
+ return walk(start, end, 0) ?? defaultWidth;
7670
+ }
7671
+ function subrBias(count) {
7672
+ return count < 1240 ? 107 : count < 33900 ? 1131 : 32768;
7673
+ }
7674
+ var STACK_CLEARING = {
7675
+ 1: "pairs",
7676
+ // hstem
7677
+ 3: "pairs",
7678
+ // vstem
7679
+ 18: "pairs",
7680
+ // hstemhm
7681
+ 23: "pairs",
7682
+ // vstemhm
7683
+ 19: "pairs",
7684
+ // hintmask, after implied vstems
7685
+ 20: "pairs",
7686
+ // cntrmask
7687
+ 21: 2,
7688
+ // rmoveto
7689
+ 22: 1,
7690
+ // hmoveto
7691
+ 4: 1,
7692
+ // vmoveto
7693
+ 14: "endchar"
7694
+ // endchar: none, or four for an accented character
7695
+ };
7696
+ function int16(value) {
7697
+ return value >= 32768 ? value - 65536 : value;
7698
+ }
7699
+ function latin1(bytes2) {
7700
+ return String.fromCharCode(...bytes2);
7701
+ }
7702
+
7703
+ // src/edit/pdf/engine/opentype.ts
7704
+ var SFNT_TRUETYPE = 65536;
7705
+ var SFNT_CFF = 1330926671;
7706
+ function cffOpenType(program, font, unicode) {
7707
+ const safe = cffWithSafeName(program);
7708
+ if (unicode.size === 0 || !safe) return void 0;
7709
+ const em = font.unitsPerEm;
7710
+ const [xMin, yMin, xMax, yMax] = fontBox(font.bbox, em);
7711
+ const glyphCount = font.widths.length;
7712
+ const widths = font.widths.map(
7713
+ (width) => Math.max(0, Math.min(65535, Math.round(width)))
7714
+ );
7715
+ const codes = [...unicode.keys()].sort((a, b) => a - b);
7716
+ const tables = /* @__PURE__ */ new Map([
7717
+ ["CFF ", safe],
7718
+ ["OS/2", os2(em, widths, codes, yMin, yMax)],
7719
+ ["cmap", cmap(unicode, codes)],
7720
+ ["head", head(em, xMin, yMin, xMax, yMax)],
7721
+ ["hhea", hhea(widths, glyphCount, xMin, yMin, xMax, yMax)],
7722
+ ["hmtx", concat(widths.map((width) => bytes(u16(width), i16(0))))],
7723
+ ["maxp", bytes(u32(20480), u16(glyphCount))],
7724
+ ["name", names(postScriptName(font.name))],
7725
+ ["post", post(em)]
7726
+ ]);
7727
+ return assemble(tables, SFNT_CFF);
7728
+ }
7729
+ function fontBox(box, em) {
7730
+ const clamp = (value) => Math.max(-32768, Math.min(32767, Math.round(value)));
7731
+ const [x0 = 0, y0 = 0, x1 = 0, y1 = 0] = box.map(clamp);
7732
+ if (y1 <= y0) return [0, clamp(-em * 0.2), clamp(em), clamp(em * 0.8)];
7733
+ return [Math.min(x0, x1), y0, Math.max(x0, x1), y1];
7734
+ }
7735
+ function completeSfnt(program, fontName) {
7736
+ const tables = sfntTables(program);
7737
+ const headTable = tables?.get("head");
7738
+ const hheaTable = tables?.get("hhea");
7739
+ const maxpTable = tables?.get("maxp");
7740
+ const hmtxTable = tables?.get("hmtx");
7741
+ if (!tables || !tables.has("cmap") || !headTable || headTable.length < 54 || !hheaTable || hheaTable.length < 36 || !maxpTable || maxpTable.length < 6 || !hmtxTable)
7742
+ return void 0;
7743
+ const view = (table) => new DataView(table.buffer, table.byteOffset, table.byteLength);
7744
+ const headView = view(headTable);
7745
+ const em = headView.getUint16(18);
7746
+ const yMin = headView.getInt16(38);
7747
+ const yMax = headView.getInt16(42);
7748
+ const ownHead = headTable.slice();
7749
+ ownHead.fill(0, 8, 12);
7750
+ tables.set("head", ownHead);
7751
+ if (!tables.has("OS/2")) {
7752
+ const metrics = view(hheaTable).getUint16(34);
7753
+ const widths = Array.from(
7754
+ { length: Math.min(metrics, hmtxTable.length >> 2) },
7755
+ (_, glyph) => view(hmtxTable).getUint16(glyph * 4)
7756
+ );
7757
+ tables.set("OS/2", os2(em, widths, [], yMin, yMax));
7758
+ }
7759
+ if (!tables.has("name")) tables.set("name", names(postScriptName(fontName)));
7760
+ if (!tables.has("post")) tables.set("post", post(em));
7761
+ const version = view(program).getUint32(0);
7762
+ return assemble(tables, version === SFNT_CFF ? SFNT_CFF : SFNT_TRUETYPE);
7763
+ }
7764
+ function hasUnicodeCmap(program) {
7765
+ const table = sfntTables(program)?.get("cmap");
7766
+ if (!table || table.length < 4) return false;
7767
+ const view = new DataView(table.buffer, table.byteOffset, table.byteLength);
7768
+ const count = Math.min(view.getUint16(2), table.length - 4 >> 3);
7769
+ for (let index = 0; index < count; index += 1) {
7770
+ const platform = view.getUint16(4 + index * 8);
7771
+ const encoding = view.getUint16(6 + index * 8);
7772
+ if (platform === 0 || platform === 3 && (encoding === 1 || encoding === 10))
7773
+ return true;
7774
+ }
7775
+ return false;
7776
+ }
7777
+ function sfntTables(program) {
7778
+ if (program.length < 12) return void 0;
7779
+ const view = new DataView(
7780
+ program.buffer,
7781
+ program.byteOffset,
7782
+ program.byteLength
7783
+ );
7784
+ const count = view.getUint16(4);
7785
+ if (count === 0 || 12 + count * 16 > program.length) return void 0;
7786
+ const tables = /* @__PURE__ */ new Map();
7787
+ for (let index = 0; index < count; index += 1) {
7788
+ const record = 12 + index * 16;
7789
+ const tag = String.fromCharCode(...program.subarray(record, record + 4));
7790
+ const offset2 = view.getUint32(record + 8);
7791
+ const length = view.getUint32(record + 12);
7792
+ if (tables.has(tag) || offset2 + length > program.length) return void 0;
7793
+ tables.set(tag, program.subarray(offset2, offset2 + length));
7794
+ }
7795
+ return tables;
7796
+ }
7797
+ function postScriptName(name) {
7798
+ return name.replace(/^[A-Z]{6}\+/, "").replace(/[^\x21-\x7e]|[[\](){}<>/%]/g, "").slice(0, 63) || "Embedded";
7799
+ }
7800
+ function post(em) {
7801
+ return bytes(
7802
+ u32(196608),
7803
+ u32(0),
7804
+ i16(-Math.round(em / 10)),
7805
+ i16(Math.round(em / 20)),
7806
+ u32(0),
7807
+ u32(0),
7808
+ u32(0),
7809
+ u32(0),
7810
+ u32(0)
7811
+ );
7812
+ }
7813
+ function head(em, xMin, yMin, xMax, yMax) {
7814
+ return bytes(
7815
+ u32(65536),
7816
+ u32(65536),
7817
+ u32(0),
7818
+ // checkSumAdjustment, set once the font is assembled
7819
+ u32(1594834165),
7820
+ u16(11),
7821
+ u16(em),
7822
+ u32(0),
7823
+ u32(0),
7824
+ u32(0),
7825
+ u32(0),
7826
+ i16(xMin),
7827
+ i16(yMin),
7828
+ i16(xMax),
7829
+ i16(yMax),
7830
+ u16(0),
7831
+ u16(8),
7832
+ i16(2),
7833
+ i16(0),
7834
+ i16(0)
7835
+ );
7836
+ }
7837
+ function hhea(widths, glyphCount, xMin, yMin, xMax, yMax) {
7838
+ return bytes(
7839
+ u32(65536),
7840
+ i16(yMax),
7841
+ i16(yMin),
7842
+ i16(0),
7843
+ u16(widths.reduce((most, width) => Math.max(most, width), 0)),
7844
+ i16(xMin),
7845
+ i16(0),
7846
+ i16(xMax),
7847
+ i16(1),
7848
+ i16(0),
7849
+ i16(0),
7850
+ i16(0),
7851
+ i16(0),
7852
+ i16(0),
7853
+ i16(0),
7854
+ i16(0),
7855
+ u16(glyphCount)
7856
+ );
7857
+ }
7858
+ function os2(em, widths, codes, yMin, yMax) {
7859
+ const scaled = (fraction) => Math.round(em * fraction);
7860
+ const drawn = widths.filter((width) => width > 0);
7861
+ const average = drawn.length ? Math.round(drawn.reduce((sum, width) => sum + width, 0) / drawn.length) : 0;
7862
+ return bytes(
7863
+ u16(4),
7864
+ i16(average),
7865
+ u16(400),
7866
+ u16(5),
7867
+ u16(0),
7868
+ i16(scaled(0.65)),
7869
+ i16(scaled(0.6)),
7870
+ i16(0),
7871
+ i16(scaled(0.075)),
7872
+ i16(scaled(0.65)),
7873
+ i16(scaled(0.6)),
7874
+ i16(0),
7875
+ i16(scaled(0.35)),
7876
+ i16(scaled(0.05)),
7877
+ i16(scaled(0.25)),
7878
+ i16(0),
7879
+ new Array(10).fill(0),
7880
+ u32(0),
7881
+ u32(0),
7882
+ u32(0),
7883
+ u32(0),
7884
+ [87, 68, 79, 67],
7885
+ // "WDOC"
7886
+ u16(64),
7887
+ // regular
7888
+ u16(Math.min(codes[0] ?? 32, 65535)),
7889
+ u16(Math.min(codes.at(-1) ?? 65535, 65535)),
7890
+ i16(yMax),
7891
+ i16(yMin),
7892
+ i16(0),
7893
+ u16(Math.max(0, yMax)),
7894
+ u16(Math.max(0, -yMin)),
7895
+ u32(1),
7896
+ u32(0),
7897
+ i16(scaled(0.5)),
7898
+ i16(scaled(0.7)),
7899
+ u16(0),
7900
+ u16(32),
7901
+ u16(0)
7902
+ );
7903
+ }
7904
+ function cmap(unicode, codes) {
7905
+ const basic = codes.filter((code) => code <= 65535);
7906
+ const format4 = cmapFormat4(unicode, basic);
7907
+ const format12 = !format4 || codes.length > basic.length ? cmapFormat12(unicode, codes) : void 0;
7908
+ const subtables = [
7909
+ ...format4 ? [{ platform: 0, encoding: 3, data: format4 }] : [],
7910
+ ...format12 ? [{ platform: 0, encoding: 4, data: format12 }] : [],
7911
+ ...format4 ? [{ platform: 3, encoding: 1, data: format4 }] : [],
7912
+ ...format12 ? [{ platform: 3, encoding: 10, data: format12 }] : []
7913
+ ];
7914
+ const unique = [...new Set(subtables.map((subtable) => subtable.data))];
7915
+ const headerSize = 4 + subtables.length * 8;
7916
+ const offsets = /* @__PURE__ */ new Map();
7917
+ let offset2 = headerSize;
7918
+ for (const data of unique) {
7919
+ offsets.set(data, offset2);
7920
+ offset2 += data.length;
7921
+ }
7922
+ return concat([
7923
+ bytes(u16(0), u16(subtables.length)),
7924
+ ...subtables.map(
7925
+ ({ platform, encoding, data }) => bytes(u16(platform), u16(encoding), u32(offsets.get(data)))
7926
+ ),
7927
+ ...unique
7928
+ ]);
7929
+ }
7930
+ function cmapFormat4(unicode, codes) {
7931
+ const segments = [];
7932
+ for (const code of codes) {
7933
+ const glyph = unicode.get(code);
7934
+ const last = segments.at(-1);
7935
+ if (last && code === last.end + 1 && glyph === last.glyph + (code - last.start))
7936
+ last.end = code;
7937
+ else segments.push({ start: code, end: code, glyph });
7938
+ }
7939
+ segments.push({ start: 65535, end: 65535, glyph: 0 });
7940
+ const count = segments.length;
7941
+ if (16 + count * 8 > 65535) return void 0;
7942
+ const searchRange = 2 * 2 ** Math.floor(Math.log2(count));
7943
+ const delta = (segment) => segment.start === 65535 ? 1 : segment.glyph - segment.start & 65535;
7944
+ return bytes(
7945
+ u16(4),
7946
+ u16(16 + count * 8),
7947
+ u16(0),
7948
+ u16(count * 2),
7949
+ u16(searchRange),
7950
+ u16(Math.floor(Math.log2(count))),
7951
+ u16(count * 2 - searchRange),
7952
+ segments.flatMap((segment) => u16(segment.end)),
7953
+ u16(0),
7954
+ segments.flatMap((segment) => u16(segment.start)),
7955
+ segments.flatMap((segment) => u16(delta(segment))),
7956
+ segments.flatMap(() => u16(0))
7957
+ );
7958
+ }
7959
+ function cmapFormat12(unicode, codes) {
7960
+ const groups = [];
7961
+ for (const code of codes) {
7962
+ const glyph = unicode.get(code);
7963
+ const last = groups.at(-1);
7964
+ if (last && code === last.end + 1 && glyph === last.glyph + (code - last.start))
7965
+ last.end = code;
7966
+ else groups.push({ start: code, end: code, glyph });
7967
+ }
7968
+ return bytes(
7969
+ u16(12),
7970
+ u16(0),
7971
+ u32(16 + groups.length * 12),
7972
+ u32(0),
7973
+ u32(groups.length),
7974
+ groups.flatMap((group) => [
7975
+ ...u32(group.start),
7976
+ ...u32(group.end),
7977
+ ...u32(group.glyph)
7978
+ ])
7979
+ );
7980
+ }
7981
+ function names(postScript) {
7982
+ const records = [
7983
+ [1, postScript],
7984
+ [2, "Regular"],
7985
+ [3, `${postScript}-web-doc`],
7986
+ [4, postScript],
7987
+ [5, "Version 1.0"],
7988
+ [6, postScript]
7989
+ ];
7990
+ const strings = records.map(
7991
+ ([, text]) => [...text].flatMap((character) => u16(character.charCodeAt(0)))
7992
+ );
7993
+ let offset2 = 0;
7994
+ const entries = records.map(([id], index) => {
7995
+ const entry = [
7996
+ ...u16(3),
7997
+ ...u16(1),
7998
+ ...u16(1033),
7999
+ ...u16(id),
8000
+ ...u16(strings[index].length),
8001
+ ...u16(offset2)
8002
+ ];
8003
+ offset2 += strings[index].length;
8004
+ return entry;
8005
+ });
8006
+ return bytes(
8007
+ u16(0),
8008
+ u16(records.length),
8009
+ u16(6 + records.length * 12),
8010
+ entries.flat(),
8011
+ strings.flat()
8012
+ );
8013
+ }
8014
+ function assemble(tables, version) {
8015
+ const tags = [...tables.keys()].sort();
8016
+ const count = tags.length;
8017
+ const selector = Math.floor(Math.log2(count));
8018
+ const headerSize = 12 + count * 16;
8019
+ let offset2 = headerSize;
8020
+ const placed = tags.map((tag) => {
8021
+ const data = tables.get(tag);
8022
+ const at = offset2;
8023
+ offset2 += data.length + 3 & ~3;
8024
+ return { tag, data, at };
8025
+ });
8026
+ const font = new Uint8Array(offset2);
8027
+ font.set(
8028
+ bytes(
8029
+ u32(version),
8030
+ u16(count),
8031
+ u16(2 ** selector * 16),
8032
+ u16(selector),
8033
+ u16(count * 16 - 2 ** selector * 16),
8034
+ placed.flatMap(({ tag, data, at }) => [
8035
+ ...[...tag].map((character) => character.charCodeAt(0)),
8036
+ ...u32(checksum(data)),
8037
+ ...u32(at),
8038
+ ...u32(data.length)
8039
+ ])
8040
+ )
8041
+ );
8042
+ for (const { data, at } of placed) font.set(data, at);
8043
+ const head2 = placed.find((table) => table.tag === "head");
8044
+ const adjustment = 2981146554 - checksum(font) >>> 0;
8045
+ font.set(u32(adjustment), head2.at + 8);
8046
+ return font;
8047
+ }
8048
+ function checksum(data) {
8049
+ let sum = 0;
8050
+ for (let offset2 = 0; offset2 < data.length; offset2 += 4)
8051
+ sum = sum + ((data[offset2] ?? 0) << 24 | (data[offset2 + 1] ?? 0) << 16 | (data[offset2 + 2] ?? 0) << 8 | (data[offset2 + 3] ?? 0)) >>> 0;
8052
+ return sum;
8053
+ }
8054
+ function u16(value) {
8055
+ return [value >> 8 & 255, value & 255];
8056
+ }
8057
+ function i16(value) {
8058
+ return u16(value < 0 ? value + 65536 : value);
8059
+ }
8060
+ function u32(value) {
8061
+ return [
8062
+ value >>> 24 & 255,
8063
+ value >>> 16 & 255,
8064
+ value >>> 8 & 255,
8065
+ value & 255
8066
+ ];
8067
+ }
8068
+ function bytes(...parts) {
8069
+ return concat(parts.map((part) => Uint8Array.from(part)));
8070
+ }
8071
+ function concat(parts) {
8072
+ const out = new Uint8Array(parts.reduce((sum, part) => sum + part.length, 0));
8073
+ let offset2 = 0;
8074
+ for (const part of parts) {
8075
+ out.set(part, offset2);
8076
+ offset2 += part.length;
8077
+ }
8078
+ return out;
8079
+ }
8080
+
8081
+ // src/edit/pdf/engine/text-font.ts
8082
+ function textFaceOf(pdfium, object, cache) {
8083
+ const { lib } = pdfium;
8084
+ const font = lib.FPDFTextObj_GetFont(object);
8085
+ const family = textStyle(pdfium, object).fontFamily;
8086
+ const baseName = pdfium.readUtf8String(
8087
+ (buffer, bytes2) => lib.FPDFFont_GetBaseFontName(font, buffer, bytes2)
8088
+ );
8089
+ if (!lib.FPDFFont_GetIsEmbedded(font))
8090
+ return { key: `name:${baseName}`, family, missing: "not-embedded" };
8091
+ const program = fontProgram(pdfium, font);
8092
+ if (!program)
8093
+ return { key: `embedded:${baseName}`, family, missing: "unreadable" };
8094
+ const key = `${baseName}#${fnv1a(program).toString(16)}`;
8095
+ const known = cache.get(key);
8096
+ if (known) return { ...known, family };
8097
+ const face = { key, family, ...faceOf(program, baseName) };
8098
+ cache.set(key, face);
8099
+ return face;
8100
+ }
8101
+ function faceOf(program, baseName) {
8102
+ const tag = String.fromCharCode(...program.subarray(0, 4));
8103
+ const truetype = tag === "true" || program[0] === 0 && program[1] === 1 && program[2] === 0 && program[3] === 0;
8104
+ if (truetype || tag === "OTTO") {
8105
+ if (!hasUnicodeCmap(program)) return { missing: "no-unicode" };
8106
+ const data2 = completeSfnt(program, baseName);
8107
+ return data2 ? { face: { data: data2, format: truetype ? "truetype" : "opentype" } } : { missing: "unreadable" };
8108
+ }
8109
+ if (tag.startsWith("%!") || program[0] === 128) return { missing: "type1" };
8110
+ const cff = readCff(program);
8111
+ if (!cff) return { missing: "unreadable" };
8112
+ if (cff.cid) return { missing: "cid-keyed" };
8113
+ const unicode = /* @__PURE__ */ new Map();
8114
+ cff.glyphNames.forEach((name, glyph) => {
8115
+ const code = glyphUnicode(name);
8116
+ if (code !== void 0 && !unicode.has(code)) unicode.set(code, glyph);
8117
+ });
8118
+ const data = cffOpenType(program, cff, unicode);
8119
+ return data ? { face: { data, format: "opentype" } } : { missing: "no-unicode" };
8120
+ }
8121
+ function fontProgram(pdfium, font) {
8122
+ const { lib } = pdfium;
8123
+ const size = pdfium.readNumbers(
8124
+ 1,
8125
+ "i32",
8126
+ ([out2]) => lib.FPDFFont_GetFontData(font, 0, 0, out2)
8127
+ )?.[0];
8128
+ if (!size) return void 0;
8129
+ const buffer = pdfium.malloc(size);
8130
+ const out = pdfium.malloc(4);
8131
+ try {
8132
+ return lib.FPDFFont_GetFontData(font, buffer, size, out) ? pdfium.readBytes(buffer, size) : void 0;
8133
+ } finally {
8134
+ pdfium.free(out);
8135
+ pdfium.free(buffer);
8136
+ }
8137
+ }
8138
+ function fnv1a(bytes2) {
8139
+ let hash = 2166136261;
8140
+ for (const byte of bytes2) hash = Math.imul(hash ^ byte, 16777619);
8141
+ return hash >>> 0;
8142
+ }
8143
+
7225
8144
  // src/edit/pdf/engine/text-layout.ts
7226
8145
  function layoutText(input) {
7227
8146
  const step = input.fontSize * input.lineHeight;
@@ -7407,7 +8326,7 @@ function drawTable(context, pageIndex, spec, insertAt) {
7407
8326
  );
7408
8327
  const geometry = context.geometry(pageIndex);
7409
8328
  const overflow = spec.at.y + layout.height > displayedSize(geometry).height + 1e-3;
7410
- const head = JSON.stringify(spec);
8329
+ const head2 = JSON.stringify(spec);
7411
8330
  const memberMark = { kind: "table", id: spec.id };
7412
8331
  const member = JSON.stringify(memberMark);
7413
8332
  const at = (x, y) => pageToUser(geometry, x, y);
@@ -7446,7 +8365,7 @@ function drawTable(context, pageIndex, spec, insertAt) {
7446
8365
  const [r2, g2, b2] = parseColor(style.headerFill);
7447
8366
  lib.FPDFPageObj_SetFillColor(fill, r2, g2, b2, 255);
7448
8367
  lib.FPDFPath_SetDrawMode(fill, FILL_ALTERNATE, false);
7449
- place(fill, OBJECT_PATH, spec, head);
8368
+ place(fill, OBJECT_PATH, spec, head2);
7450
8369
  }
7451
8370
  const frame = [
7452
8371
  at(spec.at.x, spec.at.y),
@@ -7474,7 +8393,7 @@ function drawTable(context, pageIndex, spec, insertAt) {
7474
8393
  lib.FPDFPageObj_SetStrokeColor(grid, r, g, b, 255);
7475
8394
  lib.FPDFPageObj_SetStrokeWidth(grid, style.borderWidth);
7476
8395
  lib.FPDFPath_SetDrawMode(grid, FILL_NONE, true);
7477
- place(grid, OBJECT_PATH, spec, head);
8396
+ place(grid, OBJECT_PATH, spec, head2);
7478
8397
  layout.rows.forEach((row) => {
7479
8398
  row.cells.forEach((lines, columnIndex) => {
7480
8399
  const column = layout.columns[columnIndex];
@@ -8129,7 +9048,7 @@ function canKeepFont(context, target, text) {
8129
9048
  const font = lib.FPDFTextObj_GetFont(object);
8130
9049
  if (!lib.FPDFFont_GetIsEmbedded(font)) {
8131
9050
  const base = pdfium.readUtf8String(
8132
- (buffer, bytes) => lib.FPDFFont_GetBaseFontName(font, buffer, bytes)
9051
+ (buffer, bytes2) => lib.FPDFFont_GetBaseFontName(font, buffer, bytes2)
8133
9052
  );
8134
9053
  return isStandardFamily(base.split(/[-,]/)[0] ?? "") && firstNonWinAnsi(text) === void 0;
8135
9054
  }
@@ -8179,11 +9098,11 @@ function replaceInPlace(context, target, text) {
8179
9098
  const textPage = lib.FPDFText_LoadPage(page);
8180
9099
  try {
8181
9100
  return pdfium.readWideString(
8182
- (buffer, bytes) => lib.FPDFTextObj_GetText(
9101
+ (buffer, bytes2) => lib.FPDFTextObj_GetText(
8183
9102
  lib.FPDFPage_GetObject(page, index),
8184
9103
  textPage,
8185
9104
  buffer,
8186
- bytes
9105
+ bytes2
8187
9106
  )
8188
9107
  );
8189
9108
  } finally {
@@ -8221,7 +9140,7 @@ function splitAround(context, target, before, middle, after) {
8221
9140
  1,
8222
9141
  "float",
8223
9142
  ([pointer]) => lib.FPDFTextObj_GetFontSize(old, pointer)
8224
- )?.[0] ?? style.fontSize;
9143
+ )?.[0] ?? style.fontSize / textScale(matrix);
8225
9144
  const [a, b, c, d, e, f] = matrix;
8226
9145
  const [r, g, bl] = parseColor(style.color);
8227
9146
  const parts = [
@@ -8266,7 +9185,7 @@ function splitAround(context, target, before, middle, after) {
8266
9185
  try {
8267
9186
  objects.forEach((object, at) => {
8268
9187
  const read2 = pdfium.readWideString(
8269
- (buffer, bytes) => lib.FPDFTextObj_GetText(object, textPage, buffer, bytes)
9188
+ (buffer, bytes2) => lib.FPDFTextObj_GetText(object, textPage, buffer, bytes2)
8270
9189
  );
8271
9190
  if (read2.trimEnd() !== parts[at].text.trimEnd()) faithful = false;
8272
9191
  });
@@ -8328,7 +9247,7 @@ function replaceWithFallback(context, target, text) {
8328
9247
  1,
8329
9248
  "float",
8330
9249
  ([pointer]) => lib.FPDFTextObj_GetFontSize(old, pointer)
8331
- )?.[0] ?? style.fontSize;
9250
+ )?.[0] ?? style.fontSize / textScale(matrix);
8332
9251
  const object = lib.FPDFPageObj_CreateTextObj(
8333
9252
  context.document,
8334
9253
  font.handle,
@@ -8371,7 +9290,7 @@ function resize(context, target, fontSize) {
8371
9290
  let text;
8372
9291
  try {
8373
9292
  text = pdfium.readWideString(
8374
- (buffer, bytes) => lib.FPDFTextObj_GetText(old, textPage, buffer, bytes)
9293
+ (buffer, bytes2) => lib.FPDFTextObj_GetText(old, textPage, buffer, bytes2)
8375
9294
  );
8376
9295
  } finally {
8377
9296
  lib.FPDFText_ClosePage(textPage);
@@ -8389,7 +9308,7 @@ function resize(context, target, fontSize) {
8389
9308
  const object = lib.FPDFPageObj_CreateTextObj(
8390
9309
  context.document,
8391
9310
  lib.FPDFTextObj_GetFont(old),
8392
- fontSize
9311
+ fontSize / textScale(matrix)
8393
9312
  );
8394
9313
  setText(pdfium, object, text);
8395
9314
  lib.FPDFPageObj_SetFillColor(
@@ -8663,11 +9582,14 @@ var rotatePage = {
8663
9582
  validate(operation2, context, issue) {
8664
9583
  if (operation2.pageIndex >= context.pageCount)
8665
9584
  issue("/pageIndex", "unknown-target", `No page ${operation2.pageIndex}`);
9585
+ if (operation2.rotation === void 0 === (operation2.by === void 0))
9586
+ issue("", "one-of", "Give exactly one of `rotation` and `by`");
8666
9587
  },
8667
9588
  apply(operation2, context) {
8668
9589
  const { lib } = context.pdfium;
8669
9590
  context.withPage(operation2.pageIndex, (page) => {
8670
- lib.FPDFPage_SetRotation(page, operation2.rotation / 90);
9591
+ const quarters = operation2.by !== void 0 ? (lib.FPDFPage_GetRotation(page) + operation2.by / 90) % 4 : operation2.rotation / 90;
9592
+ lib.FPDFPage_SetRotation(page, quarters);
8671
9593
  });
8672
9594
  context.invalidatePage(operation2.pageIndex);
8673
9595
  return {
@@ -8888,8 +9810,8 @@ var PdfCompactionError = class extends ViewerError {
8888
9810
  });
8889
9811
  }
8890
9812
  };
8891
- function compactPdf(bytes) {
8892
- const { objects, trailer, trailerReferences, headerEnd } = parseFile(bytes);
9813
+ function compactPdf(bytes2) {
9814
+ const { objects, trailer, trailerReferences, headerEnd } = parseFile(bytes2);
8893
9815
  const byNumber = new Map(objects.map((object) => [object.number, object]));
8894
9816
  const reachable = /* @__PURE__ */ new Set();
8895
9817
  const queue = [...trailerReferences];
@@ -8902,12 +9824,12 @@ function compactPdf(bytes) {
8902
9824
  queue.push(...object.references);
8903
9825
  }
8904
9826
  const kept = objects.filter((object) => reachable.has(object.number));
8905
- const parts = [bytes.subarray(0, headerEnd)];
9827
+ const parts = [bytes2.subarray(0, headerEnd)];
8906
9828
  const offsets = /* @__PURE__ */ new Map();
8907
9829
  let position = headerEnd;
8908
9830
  for (const object of kept) {
8909
9831
  offsets.set(object.number, position);
8910
- const slice = bytes.subarray(object.start, object.end);
9832
+ const slice = bytes2.subarray(object.start, object.end);
8911
9833
  parts.push(slice, NEWLINE);
8912
9834
  position += slice.byteLength + 1;
8913
9835
  }
@@ -8933,10 +9855,10 @@ ${position}
8933
9855
  %%EOF
8934
9856
  `)
8935
9857
  );
8936
- return concat(parts);
9858
+ return concat2(parts);
8937
9859
  }
8938
- function parseFile(bytes) {
8939
- const lexer = new Lexer(bytes);
9860
+ function parseFile(bytes2) {
9861
+ const lexer = new Lexer(bytes2);
8940
9862
  const objects = [];
8941
9863
  let trailer;
8942
9864
  let trailerReferences = [];
@@ -8956,7 +9878,7 @@ function parseFile(bytes) {
8956
9878
  throw new PdfCompactionError("an object header is malformed");
8957
9879
  const object = parseObject(
8958
9880
  lexer,
8959
- bytes,
9881
+ bytes2,
8960
9882
  first.value,
8961
9883
  second.value,
8962
9884
  first.start
@@ -8971,7 +9893,7 @@ function parseFile(bytes) {
8971
9893
  const dictionaryStart = lexer.skipWhitespace();
8972
9894
  const references = [];
8973
9895
  const dictionaryEnd = skipValue(lexer, references);
8974
- trailer = latin1(bytes.subarray(dictionaryStart, dictionaryEnd));
9896
+ trailer = latin12(bytes2.subarray(dictionaryStart, dictionaryEnd));
8975
9897
  trailerReferences = references;
8976
9898
  continue;
8977
9899
  }
@@ -8993,7 +9915,7 @@ function parseFile(bytes) {
8993
9915
  throw new PdfCompactionError("the file has no objects");
8994
9916
  return { objects, trailer, trailerReferences, headerEnd };
8995
9917
  }
8996
- function parseObject(lexer, bytes, number, generation, start) {
9918
+ function parseObject(lexer, bytes2, number, generation, start) {
8997
9919
  const references = [];
8998
9920
  let integer;
8999
9921
  let lengthValue;
@@ -9014,7 +9936,7 @@ function parseObject(lexer, bytes, number, generation, start) {
9014
9936
  const dictionaryStart = lexer.position;
9015
9937
  const dictionaryEnd = skipValue(lexer, references);
9016
9938
  if (first.kind === "delimiter" && first.value === "<<") {
9017
- const length = findLength(bytes, dictionaryStart, dictionaryEnd);
9939
+ const length = findLength(bytes2, dictionaryStart, dictionaryEnd);
9018
9940
  lengthValue = length?.value;
9019
9941
  lengthReference = length?.reference;
9020
9942
  }
@@ -9022,16 +9944,16 @@ function parseObject(lexer, bytes, number, generation, start) {
9022
9944
  let next = lexer.next();
9023
9945
  if (next.kind === "keyword" && next.value === "stream") {
9024
9946
  let dataStart = next.end;
9025
- if (bytes[dataStart] === CR) dataStart += 1;
9026
- if (bytes[dataStart] === LF) dataStart += 1;
9947
+ if (bytes2[dataStart] === CR) dataStart += 1;
9948
+ if (bytes2[dataStart] === LF) dataStart += 1;
9027
9949
  let dataEnd;
9028
9950
  if (lengthValue !== void 0) dataEnd = dataStart + lengthValue;
9029
9951
  else if (lengthReference !== void 0) {
9030
9952
  const known = lexer.integerObjects.get(lengthReference);
9031
9953
  if (known !== void 0) dataEnd = dataStart + known;
9032
9954
  }
9033
- if (dataEnd === void 0 || dataEnd > bytes.length || !followedByEndstream(bytes, dataEnd)) {
9034
- dataEnd = findEndstream(bytes, dataStart);
9955
+ if (dataEnd === void 0 || dataEnd > bytes2.length || !followedByEndstream(bytes2, dataEnd)) {
9956
+ dataEnd = findEndstream(bytes2, dataStart);
9035
9957
  if (dataEnd === void 0)
9036
9958
  throw new PdfCompactionError(`object ${number} has no endstream`);
9037
9959
  }
@@ -9086,8 +10008,8 @@ function skipValue(lexer, references) {
9086
10008
  } while (depth > 0);
9087
10009
  return end;
9088
10010
  }
9089
- function findLength(bytes, start, end) {
9090
- const lexer = new Lexer(bytes.subarray(start, end));
10011
+ function findLength(bytes2, start, end) {
10012
+ const lexer = new Lexer(bytes2.subarray(start, end));
9091
10013
  for (; ; ) {
9092
10014
  const token = lexer.next();
9093
10015
  if (token.kind === "end") return void 0;
@@ -9103,20 +10025,20 @@ function findLength(bytes, start, end) {
9103
10025
  return { value: value.value };
9104
10026
  }
9105
10027
  }
9106
- function followedByEndstream(bytes, at) {
10028
+ function followedByEndstream(bytes2, at) {
9107
10029
  let index = at;
9108
- while (index < bytes.length && isWhitespace(bytes[index])) index += 1;
9109
- return matchesAscii(bytes, index, "endstream");
10030
+ while (index < bytes2.length && isWhitespace(bytes2[index])) index += 1;
10031
+ return matchesAscii(bytes2, index, "endstream");
9110
10032
  }
9111
- function findEndstream(bytes, from) {
9112
- for (let index = from; index <= bytes.length - 9; index += 1) {
9113
- if (bytes[index] !== 101 || !matchesAscii(bytes, index, "endstream"))
10033
+ function findEndstream(bytes2, from) {
10034
+ for (let index = from; index <= bytes2.length - 9; index += 1) {
10035
+ if (bytes2[index] !== 101 || !matchesAscii(bytes2, index, "endstream"))
9114
10036
  continue;
9115
10037
  const after = index + 9;
9116
- if (after < bytes.length && !isWhitespace(bytes[after])) continue;
10038
+ if (after < bytes2.length && !isWhitespace(bytes2[after])) continue;
9117
10039
  let end = index;
9118
- if (end > from && bytes[end - 1] === LF) end -= 1;
9119
- if (end > from && bytes[end - 1] === CR) end -= 1;
10040
+ if (end > from && bytes2[end - 1] === LF) end -= 1;
10041
+ if (end > from && bytes2[end - 1] === CR) end -= 1;
9120
10042
  return end;
9121
10043
  }
9122
10044
  return void 0;
@@ -9127,8 +10049,8 @@ var Lexer = class {
9127
10049
  #lastName = "";
9128
10050
  /** Objects whose value is a bare integer, as seen so far: indirect lengths. */
9129
10051
  integerObjects = /* @__PURE__ */ new Map();
9130
- constructor(bytes) {
9131
- this.#bytes = bytes;
10052
+ constructor(bytes2) {
10053
+ this.#bytes = bytes2;
9132
10054
  }
9133
10055
  get position() {
9134
10056
  return this.#position;
@@ -9147,35 +10069,35 @@ var Lexer = class {
9147
10069
  }
9148
10070
  /** Skips whitespace and comments; returns where the next token starts. */
9149
10071
  skipWhitespace() {
9150
- const bytes = this.#bytes;
10072
+ const bytes2 = this.#bytes;
9151
10073
  for (; ; ) {
9152
- while (this.#position < bytes.length && isWhitespace(bytes[this.#position]))
10074
+ while (this.#position < bytes2.length && isWhitespace(bytes2[this.#position]))
9153
10075
  this.#position += 1;
9154
- if (bytes[this.#position] !== 37) return this.#position;
9155
- while (this.#position < bytes.length && bytes[this.#position] !== LF && bytes[this.#position] !== CR)
10076
+ if (bytes2[this.#position] !== 37) return this.#position;
10077
+ while (this.#position < bytes2.length && bytes2[this.#position] !== LF && bytes2[this.#position] !== CR)
9156
10078
  this.#position += 1;
9157
10079
  }
9158
10080
  }
9159
10081
  next() {
9160
- const bytes = this.#bytes;
10082
+ const bytes2 = this.#bytes;
9161
10083
  const start = this.skipWhitespace();
9162
- if (start >= bytes.length) return { kind: "end", start, end: start };
9163
- const byte = bytes[start];
10084
+ if (start >= bytes2.length) return { kind: "end", start, end: start };
10085
+ const byte = bytes2[start];
9164
10086
  if (byte === 40) {
9165
10087
  let depth = 0;
9166
10088
  let index2 = start;
9167
- for (; index2 < bytes.length; index2 += 1) {
9168
- const current = bytes[index2];
10089
+ for (; index2 < bytes2.length; index2 += 1) {
10090
+ const current = bytes2[index2];
9169
10091
  if (current === 92)
9170
10092
  index2 += 1;
9171
10093
  else if (current === 40) depth += 1;
9172
10094
  else if (current === 41 && --depth === 0) break;
9173
10095
  }
9174
- this.#position = Math.min(index2 + 1, bytes.length);
10096
+ this.#position = Math.min(index2 + 1, bytes2.length);
9175
10097
  return { kind: "string", start, end: this.#position };
9176
10098
  }
9177
10099
  if (byte === 60) {
9178
- if (bytes[start + 1] === 60) {
10100
+ if (bytes2[start + 1] === 60) {
9179
10101
  this.#position = start + 2;
9180
10102
  return {
9181
10103
  kind: "delimiter",
@@ -9185,11 +10107,11 @@ var Lexer = class {
9185
10107
  };
9186
10108
  }
9187
10109
  let index2 = start + 1;
9188
- while (index2 < bytes.length && bytes[index2] !== 62) index2 += 1;
9189
- this.#position = Math.min(index2 + 1, bytes.length);
10110
+ while (index2 < bytes2.length && bytes2[index2] !== 62) index2 += 1;
10111
+ this.#position = Math.min(index2 + 1, bytes2.length);
9190
10112
  return { kind: "string", start, end: this.#position };
9191
10113
  }
9192
- if (byte === 62 && bytes[start + 1] === 62) {
10114
+ if (byte === 62 && bytes2[start + 1] === 62) {
9193
10115
  this.#position = start + 2;
9194
10116
  return {
9195
10117
  kind: "delimiter",
@@ -9209,16 +10131,16 @@ var Lexer = class {
9209
10131
  }
9210
10132
  if (byte === 47) {
9211
10133
  let index2 = start + 1;
9212
- while (index2 < bytes.length && isRegular(bytes[index2])) index2 += 1;
10134
+ while (index2 < bytes2.length && isRegular(bytes2[index2])) index2 += 1;
9213
10135
  this.#position = index2;
9214
- this.#lastName = latin1(bytes.subarray(start + 1, index2));
10136
+ this.#lastName = latin12(bytes2.subarray(start + 1, index2));
9215
10137
  return { kind: "name", start, end: index2 };
9216
10138
  }
9217
10139
  let index = start;
9218
- while (index < bytes.length && isRegular(bytes[index])) index += 1;
10140
+ while (index < bytes2.length && isRegular(bytes2[index])) index += 1;
9219
10141
  if (index === start) index += 1;
9220
10142
  this.#position = index;
9221
- const text = latin1(bytes.subarray(start, index));
10143
+ const text = latin12(bytes2.subarray(start, index));
9222
10144
  const value = Number(text);
9223
10145
  if (/^[+-]?(\d+\.?\d*|\.\d+)$/.test(text) && Number.isFinite(value))
9224
10146
  return { kind: "number", value, start, end: index };
@@ -9235,22 +10157,22 @@ function isWhitespace(byte) {
9235
10157
  function isRegular(byte) {
9236
10158
  return !isWhitespace(byte) && byte !== 40 && byte !== 41 && byte !== 60 && byte !== 62 && byte !== 91 && byte !== 93 && byte !== 123 && byte !== 125 && byte !== 47 && byte !== 37;
9237
10159
  }
9238
- function matchesAscii(bytes, at, text) {
9239
- if (at + text.length > bytes.length) return false;
10160
+ function matchesAscii(bytes2, at, text) {
10161
+ if (at + text.length > bytes2.length) return false;
9240
10162
  for (let index = 0; index < text.length; index += 1)
9241
- if (bytes[at + index] !== text.charCodeAt(index)) return false;
10163
+ if (bytes2[at + index] !== text.charCodeAt(index)) return false;
9242
10164
  return true;
9243
10165
  }
9244
- function latin1(bytes) {
10166
+ function latin12(bytes2) {
9245
10167
  let text = "";
9246
- for (let offset2 = 0; offset2 < bytes.length; offset2 += 32768)
9247
- text += String.fromCharCode(...bytes.subarray(offset2, offset2 + 32768));
10168
+ for (let offset2 = 0; offset2 < bytes2.length; offset2 += 32768)
10169
+ text += String.fromCharCode(...bytes2.subarray(offset2, offset2 + 32768));
9248
10170
  return text;
9249
10171
  }
9250
10172
  function ascii(text) {
9251
10173
  return Uint8Array.from(text, (character) => character.charCodeAt(0) & 255);
9252
10174
  }
9253
- function concat(parts) {
10175
+ function concat2(parts) {
9254
10176
  const out = new Uint8Array(
9255
10177
  parts.reduce((total, part) => total + part.byteLength, 0)
9256
10178
  );
@@ -9276,10 +10198,10 @@ var AssetStore = class {
9276
10198
  has(id) {
9277
10199
  return this.#bytes.has(id);
9278
10200
  }
9279
- set(id, bytes) {
10201
+ set(id, bytes2) {
9280
10202
  if (this.#bytes.has(id)) return;
9281
- this.#bytes.set(id, bytes);
9282
- this.#total += bytes.byteLength;
10203
+ this.#bytes.set(id, bytes2);
10204
+ this.#total += bytes2.byteLength;
9283
10205
  }
9284
10206
  /** Bytes held, for limits. */
9285
10207
  get byteLength() {
@@ -9289,8 +10211,8 @@ var AssetStore = class {
9289
10211
  function resolveBinary(data, assets) {
9290
10212
  if (data instanceof Uint8Array) return data;
9291
10213
  if (isAssetReference(data)) {
9292
- const bytes = assets.get(data);
9293
- if (!bytes)
10214
+ const bytes2 = assets.get(data);
10215
+ if (!bytes2)
9294
10216
  throw new ViewerError("invalid-operation", `Unknown asset ${data}`, {
9295
10217
  details: {
9296
10218
  issues: [
@@ -9303,7 +10225,7 @@ function resolveBinary(data, assets) {
9303
10225
  ]
9304
10226
  }
9305
10227
  });
9306
- return bytes;
10228
+ return bytes2;
9307
10229
  }
9308
10230
  return Uint8Array.from(atob(data), (character) => character.charCodeAt(0));
9309
10231
  }
@@ -9318,17 +10240,17 @@ var ImageCache = class {
9318
10240
  const operation2 = raw;
9319
10241
  if (operation2.op !== "insertImage" || operation2.mimeType !== "image/png")
9320
10242
  continue;
9321
- const bytes = resolveBinary(operation2.data, assets);
9322
- if (!isPng(bytes)) continue;
9323
- const key = contentKey(bytes);
10243
+ const bytes2 = resolveBinary(operation2.data, assets);
10244
+ if (!isPng(bytes2)) continue;
10245
+ const key = contentKey(bytes2);
9324
10246
  if (this.#decoded.has(key)) continue;
9325
- this.#decoded.set(key, await decode(bytes, "image/png"));
10247
+ this.#decoded.set(key, await decode(bytes2, "image/png"));
9326
10248
  while (this.#decoded.size > 8)
9327
10249
  this.#decoded.delete(this.#decoded.keys().next().value);
9328
10250
  }
9329
10251
  }
9330
- get(bytes) {
9331
- return this.#decoded.get(contentKey(bytes));
10252
+ get(bytes2) {
10253
+ return this.#decoded.get(contentKey(bytes2));
9332
10254
  }
9333
10255
  };
9334
10256
  var insertImage = {
@@ -9338,19 +10260,19 @@ var insertImage = {
9338
10260
  return;
9339
10261
  }
9340
10262
  validateRect(operation2.rect, context.geometry(operation2.pageIndex), issue);
9341
- const bytes = resolveBinary(operation2.data, context.assets);
9342
- if (bytes.byteLength > context.limits.maxInputBytes)
10263
+ const bytes2 = resolveBinary(operation2.data, context.assets);
10264
+ if (bytes2.byteLength > context.limits.maxInputBytes)
9343
10265
  throw new ViewerError(
9344
10266
  "resource-limit",
9345
10267
  "Image data exceeds maxInputBytes",
9346
10268
  {
9347
10269
  details: {
9348
- bytes: bytes.byteLength,
10270
+ bytes: bytes2.byteLength,
9349
10271
  limit: context.limits.maxInputBytes
9350
10272
  }
9351
10273
  }
9352
10274
  );
9353
- const size = operation2.mimeType === "image/png" ? pngSize(bytes) : jpegSize(bytes);
10275
+ const size = operation2.mimeType === "image/png" ? pngSize(bytes2) : jpegSize(bytes2);
9354
10276
  if (!size) {
9355
10277
  issue(
9356
10278
  "/data",
@@ -9366,19 +10288,19 @@ var insertImage = {
9366
10288
  "Decoded image exceeds maxDecodedPixels",
9367
10289
  { details: { pixels, limit: context.limits.maxDecodedPixels } }
9368
10290
  );
9369
- if (operation2.mimeType === "image/png" && !context.images.get(bytes))
10291
+ if (operation2.mimeType === "image/png" && !context.images.get(bytes2))
9370
10292
  issue("/data", "invalid-data", "The PNG could not be decoded");
9371
10293
  },
9372
10294
  apply(operation2, context) {
9373
10295
  const { pdfium } = context;
9374
10296
  const { lib } = pdfium;
9375
- const bytes = resolveBinary(operation2.data, context.assets);
10297
+ const bytes2 = resolveBinary(operation2.data, context.assets);
9376
10298
  const id = context.newId(operation2.pageIndex);
9377
10299
  const geometry = context.geometry(operation2.pageIndex);
9378
10300
  const image = lib.FPDFPageObj_NewImageObj(context.document);
9379
10301
  if (operation2.mimeType === "image/jpeg") {
9380
10302
  const loaded = pdfium.withFileAccess(
9381
- bytes,
10303
+ bytes2,
9382
10304
  (fileAccess) => lib.FPDFImageObj_LoadJpegFileInline(0, 0, image, fileAccess)
9383
10305
  );
9384
10306
  if (!loaded)
@@ -9386,7 +10308,7 @@ var insertImage = {
9386
10308
  details: { stage: "apply" }
9387
10309
  });
9388
10310
  } else {
9389
- const decoded = context.images.get(bytes);
10311
+ const decoded = context.images.get(bytes2);
9390
10312
  setBitmap(context.pdfium, image, decoded);
9391
10313
  }
9392
10314
  const { rect } = operation2;
@@ -9460,24 +10382,24 @@ function setBitmap(pdfium, image, decoded) {
9460
10382
  pdfium.free(buffer);
9461
10383
  }
9462
10384
  }
9463
- function isPng(bytes) {
9464
- return bytes.length > 24 && bytes[0] === 137 && bytes[1] === 80 && bytes[2] === 78 && bytes[3] === 71;
10385
+ function isPng(bytes2) {
10386
+ return bytes2.length > 24 && bytes2[0] === 137 && bytes2[1] === 80 && bytes2[2] === 78 && bytes2[3] === 71;
9465
10387
  }
9466
- function pngSize(bytes) {
9467
- if (!isPng(bytes)) return void 0;
9468
- const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
10388
+ function pngSize(bytes2) {
10389
+ if (!isPng(bytes2)) return void 0;
10390
+ const view = new DataView(bytes2.buffer, bytes2.byteOffset, bytes2.byteLength);
9469
10391
  const width = view.getUint32(16);
9470
10392
  const height = view.getUint32(20);
9471
10393
  return width > 0 && height > 0 ? { width, height } : void 0;
9472
10394
  }
9473
- function jpegSize(bytes) {
9474
- if (bytes.length < 4 || bytes[0] !== 255 || bytes[1] !== 216)
10395
+ function jpegSize(bytes2) {
10396
+ if (bytes2.length < 4 || bytes2[0] !== 255 || bytes2[1] !== 216)
9475
10397
  return void 0;
9476
- const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
10398
+ const view = new DataView(bytes2.buffer, bytes2.byteOffset, bytes2.byteLength);
9477
10399
  let offset2 = 2;
9478
- while (offset2 + 9 < bytes.length) {
9479
- if (bytes[offset2] !== 255) return void 0;
9480
- const marker = bytes[offset2 + 1];
10400
+ while (offset2 + 9 < bytes2.length) {
10401
+ if (bytes2[offset2] !== 255) return void 0;
10402
+ const marker = bytes2[offset2 + 1];
9481
10403
  if (marker === 216 || marker >= 208 && marker <= 215 || marker === 1) {
9482
10404
  offset2 += 2;
9483
10405
  continue;
@@ -9493,10 +10415,10 @@ function jpegSize(bytes) {
9493
10415
  }
9494
10416
  return void 0;
9495
10417
  }
9496
- function contentKey(bytes) {
10418
+ function contentKey(bytes2) {
9497
10419
  let hash = 2166136261;
9498
- for (const byte of bytes) hash = Math.imul(hash ^ byte, 16777619) >>> 0;
9499
- return `${bytes.byteLength}:${hash.toString(16)}`;
10420
+ for (const byte of bytes2) hash = Math.imul(hash ^ byte, 16777619) >>> 0;
10421
+ return `${bytes2.byteLength}:${hash.toString(16)}`;
9500
10422
  }
9501
10423
 
9502
10424
  // src/limits.ts
@@ -9542,12 +10464,12 @@ function detectFeatures(pdfium, document2, original) {
9542
10464
  if (containsAscii(original, "pdfaid:part")) features.push("pdfa");
9543
10465
  return features;
9544
10466
  }
9545
- function containsAscii(bytes, needle) {
10467
+ function containsAscii(bytes2, needle) {
9546
10468
  const first = needle.charCodeAt(0);
9547
- const limit = bytes.length - needle.length;
9548
- for (let at = bytes.indexOf(first); at >= 0 && at <= limit; at = bytes.indexOf(first, at + 1)) {
10469
+ const limit = bytes2.length - needle.length;
10470
+ for (let at = bytes2.indexOf(first); at >= 0 && at <= limit; at = bytes2.indexOf(first, at + 1)) {
9549
10471
  let index = 1;
9550
- while (index < needle.length && bytes[at + index] === needle.charCodeAt(index))
10472
+ while (index < needle.length && bytes2[at + index] === needle.charCodeAt(index))
9551
10473
  index += 1;
9552
10474
  if (index === needle.length) return true;
9553
10475
  }
@@ -9612,6 +10534,8 @@ var PdfEditDocument = class {
9612
10534
  #measurer;
9613
10535
  #pages;
9614
10536
  #batches = 0;
10537
+ /** Browser faces of the document's fonts, by font program; see `textFont`. */
10538
+ #faces = /* @__PURE__ */ new Map();
9615
10539
  constructor(pdfium, original, fonts = new FontLibrary(async () => {
9616
10540
  throw new Error("No font source is configured");
9617
10541
  }), limits = defaultResourceLimits, assets = new AssetStore(), compact = compactPdf) {
@@ -9887,6 +10811,18 @@ var PdfEditDocument = class {
9887
10811
  return element ? layoutOf(this.#pdfium, scan, element) : void 0;
9888
10812
  });
9889
10813
  }
10814
+ /** The browser face of the font a text or text box element is drawn in, see `TextFont`. */
10815
+ textFont(elementId) {
10816
+ const location = this.#locate(elementId);
10817
+ const element = location && this.getElement(elementId);
10818
+ if (!element || element.kind !== "text" && element.kind !== "textBox")
10819
+ return void 0;
10820
+ const { lib } = this.#pdfium;
10821
+ return this.#withPage(location.pageIndex, (page) => {
10822
+ const object = location.indexes.map((index) => lib.FPDFPage_GetObject(page, index)).find((entry) => lib.FPDFPageObj_GetType(entry) === OBJECT_TEXT);
10823
+ return object === void 0 ? void 0 : { elementId, ...textFaceOf(this.#pdfium, object, this.#faces) };
10824
+ });
10825
+ }
9890
10826
  /** The layouts of every text element on a page, with the page's displayed size. */
9891
10827
  pageLayout(pageIndex) {
9892
10828
  if (pageIndex < 0 || pageIndex >= this.#pages.length) return void 0;
@@ -10043,8 +10979,11 @@ var PdfEditDocument = class {
10043
10979
  },
10044
10980
  // Ids name the state the batch leads to, the operation and the item,
10045
10981
  // so replaying the history reproduces them and an undone id is never
10046
- // handed out again.
10047
- newId: (pageIndex, suffix = "") => `${this.#pages[pageIndex].key}:n${stateId}.${operationIndex}.${created++}${suffix}`,
10982
+ // handed out again. A file saved by an earlier session carries the ids
10983
+ // that session numbered from 1 as well, so one already on the page
10984
+ // gets the first free `~n` instead (ACTION-886); the saved objects are
10985
+ // part of the base, so a replay meets them and picks the same id.
10986
+ newId: (pageIndex, suffix = "") => `${this.#unusedId(pageIndex, `${this.#pages[pageIndex].key}:n${stateId}.${operationIndex}.${created++}`)}${suffix}`,
10048
10987
  withPage: (pageIndex, use) => this.#writePage(pageIndex, use),
10049
10988
  appendObjects: (pageIndex, records) => {
10050
10989
  const page = this.#pages[pageIndex];
@@ -10191,8 +11130,8 @@ var PdfEditDocument = class {
10191
11130
  const textPage = lib.FPDFText_LoadPage(page);
10192
11131
  try {
10193
11132
  for (const [id, indexes] of members) {
10194
- const head = heads.get(id);
10195
- if (!head || !markIsFresh(this.#pdfium, page, textPage, geometry, head, indexes))
11133
+ const head2 = heads.get(id);
11134
+ if (!head2 || !markIsFresh(this.#pdfium, page, textPage, geometry, head2, indexes))
10196
11135
  stale.add(id);
10197
11136
  }
10198
11137
  } finally {
@@ -10200,10 +11139,48 @@ var PdfEditDocument = class {
10200
11139
  }
10201
11140
  }
10202
11141
  record.objects = objects.map(
10203
- (object, index) => object.mark && stale.has(object.id) ? { id: `${record.key}:o${index}`, type: object.type } : object
11142
+ (object, index) => object.mark && stale.has(object.id) ? {
11143
+ id: `${record.key}:o${index}`,
11144
+ type: object.type,
11145
+ staleMarkId: object.id
11146
+ } : object
10204
11147
  );
10205
11148
  return record.objects;
10206
11149
  }
11150
+ /**
11151
+ * `id`, or its first free `~n` when an object saved by an earlier session
11152
+ * has it already. Only ids a session numbered (`<key>:n…`) can collide, so
11153
+ * only those are gathered; the objects are read without the page's text.
11154
+ */
11155
+ #unusedId(pageIndex, id) {
11156
+ const record = this.#pages[pageIndex];
11157
+ const objects = record.objects ?? this.#loadObjects(pageIndex);
11158
+ const numbered = `${record.key}:n`;
11159
+ const taken = /* @__PURE__ */ new Set();
11160
+ for (const object of objects) {
11161
+ if (object.id.startsWith(numbered)) taken.add(object.id);
11162
+ if (object.staleMarkId?.startsWith(numbered))
11163
+ taken.add(object.staleMarkId);
11164
+ }
11165
+ if (!taken.has(id)) return id;
11166
+ let suffix = 1;
11167
+ while (taken.has(`${id}~${suffix}`)) suffix += 1;
11168
+ return `${id}~${suffix}`;
11169
+ }
11170
+ /** A page's objects, loading the page alone when they are not known yet. */
11171
+ #loadObjects(pageIndex) {
11172
+ const { lib } = this.#pdfium;
11173
+ const page = lib.FPDF_LoadPage(this.#document.handle, pageIndex);
11174
+ if (!page)
11175
+ throw new ViewerError("render-failed", "PDFium could not load the page", {
11176
+ details: { pageIndex }
11177
+ });
11178
+ try {
11179
+ return this.#objectsOf(pageIndex, page);
11180
+ } finally {
11181
+ lib.FPDF_ClosePage(page);
11182
+ }
11183
+ }
10207
11184
  /** Marks from other sessions keep their id only if it cannot collide with ours. */
10208
11185
  #ownMark(mark, pageKey) {
10209
11186
  return mark.id.startsWith(`${pageKey}:`) ? mark : { ...mark, id: `${pageKey}:${mark.id}` };
@@ -10272,12 +11249,12 @@ var PdfEditDocument = class {
10272
11249
  const { lib } = this.#pdfium;
10273
11250
  const count = lib.FPDFText_CountChars(textPage);
10274
11251
  if (count === 0) return;
10275
- const bytes = (count + 1) * 2;
10276
- const buffer = this.#pdfium.malloc(bytes);
11252
+ const bytes2 = (count + 1) * 2;
11253
+ const buffer = this.#pdfium.malloc(bytes2);
10277
11254
  let text;
10278
11255
  try {
10279
11256
  lib.FPDFText_GetText(textPage, 0, count, buffer);
10280
- text = this.#pdfium.readWideStringAt(buffer, bytes);
11257
+ text = this.#pdfium.readWideStringAt(buffer, bytes2);
10281
11258
  } finally {
10282
11259
  this.#pdfium.free(buffer);
10283
11260
  }
@@ -10318,7 +11295,7 @@ var PdfEditDocument = class {
10318
11295
  }
10319
11296
  if (!bases.has(object)) {
10320
11297
  const own = this.#pdfium.readWideString(
10321
- (buffer, bytes) => lib.FPDFTextObj_GetText(object, textPage, buffer, bytes)
11298
+ (buffer, bytes2) => lib.FPDFTextObj_GetText(object, textPage, buffer, bytes2)
10322
11299
  );
10323
11300
  const whole = elementTexts.get(elementId) ?? "";
10324
11301
  const from = cursors.get(elementId) ?? 0;
@@ -10450,11 +11427,11 @@ function createPdfEditHandler(host) {
10450
11427
  case "edit-materialize": {
10451
11428
  const { purpose, options } = payload ?? {};
10452
11429
  const mode = options?.mode;
10453
- const { bytes, warnings } = engine().materializeDocument(
11430
+ const { bytes: bytes2, warnings } = engine().materializeDocument(
10454
11431
  purpose ?? "show",
10455
11432
  mode === "full" || mode === "incremental" ? mode : void 0
10456
11433
  );
10457
- return { data: bytes.buffer, warnings };
11434
+ return { data: bytes2.buffer, warnings };
10458
11435
  }
10459
11436
  case "edit-restore": {
10460
11437
  const { batches, base } = payload;
@@ -10495,6 +11472,8 @@ function createPdfEditHandler(host) {
10495
11472
  const { pageIndex, elementIds, scale } = payload;
10496
11473
  return engine().renderPageWithout(pageIndex, elementIds, scale);
10497
11474
  }
11475
+ case "edit-text-font":
11476
+ return engine().textFont(payload.id);
10498
11477
  case "edit-page-layout":
10499
11478
  return engine().pageLayout(
10500
11479
  payload.pageIndex
@@ -10546,9 +11525,9 @@ var Pdfium = class _Pdfium {
10546
11525
  if (pointer) this.#runtime.wasmExports.free(pointer);
10547
11526
  }
10548
11527
  /** Copies bytes into the WASM heap; the caller frees the pointer. */
10549
- writeBytes(bytes) {
10550
- const pointer = this.malloc(bytes.byteLength);
10551
- this.#runtime.HEAPU8.set(bytes, pointer);
11528
+ writeBytes(bytes2) {
11529
+ const pointer = this.malloc(bytes2.byteLength);
11530
+ this.#runtime.HEAPU8.set(bytes2, pointer);
10552
11531
  return pointer;
10553
11532
  }
10554
11533
  readBytes(pointer, length) {
@@ -10556,26 +11535,26 @@ var Pdfium = class _Pdfium {
10556
11535
  }
10557
11536
  /** Writes a NUL-terminated UTF-16LE string; the caller frees the pointer. */
10558
11537
  writeWideString(text) {
10559
- const bytes = (text.length + 1) * 2;
10560
- const pointer = this.malloc(bytes);
10561
- this.#runtime.stringToUTF16(text, pointer, bytes);
11538
+ const bytes2 = (text.length + 1) * 2;
11539
+ const pointer = this.malloc(bytes2);
11540
+ this.#runtime.stringToUTF16(text, pointer, bytes2);
10562
11541
  return pointer;
10563
11542
  }
10564
11543
  /** Reads a NUL-terminated UTF-16LE string of at most `bytes` bytes. */
10565
- readWideStringAt(pointer, bytes) {
10566
- return this.#runtime.UTF16ToString(pointer, bytes);
11544
+ readWideStringAt(pointer, bytes2) {
11545
+ return this.#runtime.UTF16ToString(pointer, bytes2);
10567
11546
  }
10568
11547
  /**
10569
11548
  * Reads a UTF-16LE string from an API that returns the byte size it needs
10570
11549
  * when called with an empty buffer.
10571
11550
  */
10572
11551
  readWideString(read2) {
10573
- const bytes = read2(0, 0);
10574
- if (bytes <= 2) return "";
10575
- const pointer = this.malloc(bytes);
11552
+ const bytes2 = read2(0, 0);
11553
+ if (bytes2 <= 2) return "";
11554
+ const pointer = this.malloc(bytes2);
10576
11555
  try {
10577
- read2(pointer, bytes);
10578
- return this.#runtime.UTF16ToString(pointer, bytes);
11556
+ read2(pointer, bytes2);
11557
+ return this.#runtime.UTF16ToString(pointer, bytes2);
10579
11558
  } finally {
10580
11559
  this.free(pointer);
10581
11560
  }
@@ -10588,12 +11567,12 @@ var Pdfium = class _Pdfium {
10588
11567
  const outBytes = this.malloc(4);
10589
11568
  try {
10590
11569
  if (!read2(0, 0, outBytes)) return "";
10591
- const bytes = this.#runtime.getValue(outBytes, "i32");
10592
- if (bytes <= 2) return "";
10593
- const pointer = this.malloc(bytes);
11570
+ const bytes2 = this.#runtime.getValue(outBytes, "i32");
11571
+ if (bytes2 <= 2) return "";
11572
+ const pointer = this.malloc(bytes2);
10594
11573
  try {
10595
- if (!read2(pointer, bytes, outBytes)) return "";
10596
- return this.#runtime.UTF16ToString(pointer, bytes);
11574
+ if (!read2(pointer, bytes2, outBytes)) return "";
11575
+ return this.#runtime.UTF16ToString(pointer, bytes2);
10597
11576
  } finally {
10598
11577
  this.free(pointer);
10599
11578
  }
@@ -10603,12 +11582,12 @@ var Pdfium = class _Pdfium {
10603
11582
  }
10604
11583
  /** Reads a NUL-terminated UTF-8 string from an API that returns the byte size it needs. */
10605
11584
  readUtf8String(read2) {
10606
- const bytes = read2(0, 0);
10607
- if (bytes <= 1) return "";
10608
- const pointer = this.malloc(bytes);
11585
+ const bytes2 = read2(0, 0);
11586
+ if (bytes2 <= 1) return "";
11587
+ const pointer = this.malloc(bytes2);
10609
11588
  try {
10610
- read2(pointer, bytes);
10611
- const raw = this.readBytes(pointer, bytes);
11589
+ read2(pointer, bytes2);
11590
+ const raw = this.readBytes(pointer, bytes2);
10612
11591
  const end = raw.indexOf(0);
10613
11592
  return new TextDecoder().decode(end < 0 ? raw : raw.subarray(0, end));
10614
11593
  } finally {
@@ -10643,20 +11622,20 @@ var Pdfium = class _Pdfium {
10643
11622
  * a WASM table callback. Only for APIs that read the data before returning,
10644
11623
  * such as the inline JPEG loader.
10645
11624
  */
10646
- withFileAccess(bytes, use) {
11625
+ withFileAccess(bytes2, use) {
10647
11626
  const runtime = this.#runtime;
10648
11627
  const callback = runtime.addFunction(
10649
11628
  (_parameter, position, buffer, size) => {
10650
- if (position < 0 || size < 0 || position + size > bytes.byteLength)
11629
+ if (position < 0 || size < 0 || position + size > bytes2.byteLength)
10651
11630
  return 0;
10652
- runtime.HEAPU8.set(bytes.subarray(position, position + size), buffer);
11631
+ runtime.HEAPU8.set(bytes2.subarray(position, position + size), buffer);
10653
11632
  return 1;
10654
11633
  },
10655
11634
  "iiiii"
10656
11635
  );
10657
11636
  const fileAccess = this.malloc(FILE_ACCESS_BYTES);
10658
11637
  try {
10659
- runtime.setValue(fileAccess, bytes.byteLength, "i32");
11638
+ runtime.setValue(fileAccess, bytes2.byteLength, "i32");
10660
11639
  runtime.setValue(fileAccess + 4, callback, "i32");
10661
11640
  runtime.setValue(fileAccess + 8, 0, "i32");
10662
11641
  return use(fileAccess);
@@ -10666,9 +11645,9 @@ var Pdfium = class _Pdfium {
10666
11645
  }
10667
11646
  }
10668
11647
  /** Opens a document from bytes; PDFium reads them lazily, so they stay in the heap. */
10669
- openDocument(bytes) {
10670
- const data = this.writeBytes(bytes);
10671
- const handle = this.lib.FPDF_LoadMemDocument(data, bytes.byteLength, "");
11648
+ openDocument(bytes2) {
11649
+ const data = this.writeBytes(bytes2);
11650
+ const handle = this.lib.FPDF_LoadMemDocument(data, bytes2.byteLength, "");
10672
11651
  if (!handle) {
10673
11652
  const error = this.lib.FPDF_GetLastError();
10674
11653
  this.free(data);
@@ -10733,9 +11712,9 @@ var PdfiumDocument = class {
10733
11712
  this.#pdfium.free(this.#data);
10734
11713
  }
10735
11714
  };
10736
- function exactArrayBuffer(bytes) {
10737
- if (bytes instanceof ArrayBuffer) return bytes;
10738
- return bytes.buffer instanceof ArrayBuffer && bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength ? bytes.buffer : bytes.slice().buffer;
11715
+ function exactArrayBuffer(bytes2) {
11716
+ if (bytes2 instanceof ArrayBuffer) return bytes2;
11717
+ return bytes2.buffer instanceof ArrayBuffer && bytes2.byteOffset === 0 && bytes2.byteLength === bytes2.buffer.byteLength ? bytes2.buffer : bytes2.slice().buffer;
10739
11718
  }
10740
11719
 
10741
11720
  // src/worker-protocol.ts
@@ -10820,9 +11799,9 @@ attachWorkerEndpoint(
10820
11799
  createPdfEditHandler({
10821
11800
  loadPdfium: async (wasmUrl) => Pdfium.load(await fetchBytes(wasmUrl)),
10822
11801
  fetchBytes,
10823
- decodeImage: async (bytes, mimeType) => {
11802
+ decodeImage: async (bytes2, mimeType) => {
10824
11803
  const bitmap = await createImageBitmap(
10825
- new Blob([new Uint8Array(bytes)], { type: mimeType })
11804
+ new Blob([new Uint8Array(bytes2)], { type: mimeType })
10826
11805
  );
10827
11806
  try {
10828
11807
  const canvas = new OffscreenCanvas(bitmap.width, bitmap.height);