pdfnative 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -2324,6 +2324,16 @@ var MYANMAR_EXTENDED_A_END = 43647;
2324
2324
  var MYANMAR_EXTENDED_B_START = 43488;
2325
2325
  var MYANMAR_EXTENDED_B_END = 43519;
2326
2326
  var MYANMAR_VIRAMA = 4153;
2327
+ var MATH_OPERATORS_START = 8704;
2328
+ var MATH_OPERATORS_END = 8959;
2329
+ var SUPPLEMENTAL_MATH_OPERATORS_START = 10752;
2330
+ var SUPPLEMENTAL_MATH_OPERATORS_END = 11007;
2331
+ var GEOMETRIC_SHAPES_START = 9632;
2332
+ var GEOMETRIC_SHAPES_END = 9727;
2333
+ var MISC_MATH_SYMBOLS_A_START = 10176;
2334
+ var MISC_MATH_SYMBOLS_A_END = 10223;
2335
+ var MISC_MATH_SYMBOLS_B_START = 10624;
2336
+ var MISC_MATH_SYMBOLS_B_END = 10751;
2327
2337
  var EMOJI_RANGES = [
2328
2338
  [127744, 128511],
2329
2339
  // Miscellaneous Symbols and Pictographs
@@ -2402,6 +2412,9 @@ function isMyanmarCodepoint(cp) {
2402
2412
  function isDevanagariCodepoint(cp) {
2403
2413
  return cp >= DEVANAGARI_START && cp <= DEVANAGARI_END || cp >= DEVANAGARI_EXT_START && cp <= DEVANAGARI_EXT_END;
2404
2414
  }
2415
+ function isMathCodepoint(cp) {
2416
+ return cp >= MATH_OPERATORS_START && cp <= MATH_OPERATORS_END || cp >= SUPPLEMENTAL_MATH_OPERATORS_START && cp <= SUPPLEMENTAL_MATH_OPERATORS_END || cp >= GEOMETRIC_SHAPES_START && cp <= GEOMETRIC_SHAPES_END || cp >= MISC_MATH_SYMBOLS_A_START && cp <= MISC_MATH_SYMBOLS_A_END || cp >= MISC_MATH_SYMBOLS_B_START && cp <= MISC_MATH_SYMBOLS_B_END;
2417
+ }
2405
2418
  function isEmojiCodepoint(cp) {
2406
2419
  if (cp >= FITZPATRICK_START && cp <= FITZPATRICK_END) return true;
2407
2420
  for (const [lo, hi] of EMOJI_RANGES) {
@@ -2488,10 +2501,16 @@ function containsDevanagari(str) {
2488
2501
  }
2489
2502
  return false;
2490
2503
  }
2504
+ function containsMath(str) {
2505
+ for (let i = 0; i < str.length; i++) {
2506
+ if (isMathCodepoint(str.charCodeAt(i))) return true;
2507
+ }
2508
+ return false;
2509
+ }
2491
2510
 
2492
2511
  // src/shaping/script-detect.ts
2493
2512
  function needsUnicodeFont(lang) {
2494
- return ["th", "ja", "zh", "ko", "el", "hi", "te", "tr", "vi", "pl", "ar", "he", "ru", "ka", "hy", "am", "si", "bo", "km", "my", "emoji"].includes(lang);
2513
+ return ["th", "ja", "zh", "ko", "el", "hi", "te", "tr", "vi", "pl", "ar", "he", "ru", "ka", "hy", "am", "si", "bo", "km", "my", "math", "emoji"].includes(lang);
2495
2514
  }
2496
2515
  function detectFallbackLangs(texts, primaryLang) {
2497
2516
  const needed = /* @__PURE__ */ new Set();
@@ -2592,6 +2611,10 @@ function detectFallbackLangs(texts, primaryLang) {
2592
2611
  needed.add("hy");
2593
2612
  continue;
2594
2613
  }
2614
+ if (isMathCodepoint(cp)) {
2615
+ needed.add("math");
2616
+ continue;
2617
+ }
2595
2618
  if (isEmojiCodepoint(cp)) {
2596
2619
  needed.add("emoji");
2597
2620
  continue;
@@ -2622,6 +2645,7 @@ function detectCharLang(cp) {
2622
2645
  if (cp >= 1024 && cp <= 1279 || cp >= 1280 && cp <= 1327) return "ru";
2623
2646
  if (cp >= 4256 && cp <= 4351 || cp >= 11520 && cp <= 11567) return "ka";
2624
2647
  if (cp >= 1328 && cp <= 1423 || cp >= 64275 && cp <= 64279) return "hy";
2648
+ if (isMathCodepoint(cp)) return "math";
2625
2649
  if (isEmojiCodepoint(cp)) return "emoji";
2626
2650
  return null;
2627
2651
  }
@@ -6251,6 +6275,10 @@ function buildTextRunsWithFallback(text, fontRef, fd, sz, trackGid, pdfA = false
6251
6275
  for (let i = 0; i < text.length; ) {
6252
6276
  const rawCp = text.codePointAt(i) ?? 0;
6253
6277
  const charLen = rawCp > 65535 ? 2 : 1;
6278
+ if (rawCp < 32 || rawCp === 127) {
6279
+ i += charLen;
6280
+ continue;
6281
+ }
6254
6282
  const cp = rawCp === 8239 || rawCp === 160 ? 32 : rawCp;
6255
6283
  const char = text.substring(i, i + charLen);
6256
6284
  const gid = fd.cmap[cp] ?? 0;
@@ -8794,6 +8822,61 @@ function buildViewerPreferences(prefs) {
8794
8822
  // src/core/pdf-document.ts
8795
8823
  init_pdf_encrypt();
8796
8824
 
8825
+ // src/core/pdf-layout-debug.ts
8826
+ function resolveDebugOptions(debug) {
8827
+ if (!debug) return null;
8828
+ if (debug === true) {
8829
+ return { showMargins: true, showContentBounds: true, showCells: true };
8830
+ }
8831
+ const showMargins = debug.showMargins === true;
8832
+ const showContentBounds = debug.showContentBounds === true;
8833
+ const showCells = debug.showCells === true;
8834
+ if (!showMargins && !showContentBounds && !showCells) return null;
8835
+ return { showMargins, showContentBounds, showCells };
8836
+ }
8837
+ var COL_MARGIN = "0 0.45 0.95";
8838
+ var COL_BLOCK = "0.90 0.20 0.30";
8839
+ var COL_CELL = "0 0.62 0.30";
8840
+ function rectOp(x, y, w, h, color, lineW) {
8841
+ return `q ${color} RG ${fmtNum(lineW)} w ${fmtNum(x)} ${fmtNum(y)} ${fmtNum(w)} ${fmtNum(h)} re S Q`;
8842
+ }
8843
+ function marginBoxOps(pgW, pgH, mg, footerH) {
8844
+ const x = mg.l;
8845
+ const yBottom = mg.b + footerH;
8846
+ const w = pgW - mg.l - mg.r;
8847
+ const h = pgH - mg.t - yBottom;
8848
+ return rectOp(x, yBottom, w, h, COL_MARGIN, 0.5);
8849
+ }
8850
+ function blockBoundsOps(x, width, yTop, yBottom) {
8851
+ const h = yTop - yBottom;
8852
+ if (h <= 0) return "";
8853
+ return rectOp(x, yBottom, width, h, COL_BLOCK, 0.4);
8854
+ }
8855
+ function tableCellOps(plan, slice, yTop) {
8856
+ const drawCaption = slice ? slice.drawCaption : true;
8857
+ const drawHeader = slice ? slice.drawHeader : true;
8858
+ const fromRow = slice ? slice.fromRow : 0;
8859
+ const toRow = slice ? slice.toRow : plan.rowHeights.length;
8860
+ const parts = [];
8861
+ let y = yTop - (drawCaption ? plan.captionHeight : 0);
8862
+ if (drawHeader) {
8863
+ const hBottom = y - plan.headerHeight;
8864
+ for (let c = 0; c < plan.cx.length; c++) {
8865
+ parts.push(rectOp(plan.cx[c], hBottom, plan.cwi[c], plan.headerHeight, COL_CELL, 0.3));
8866
+ }
8867
+ y = hBottom;
8868
+ }
8869
+ for (let r = fromRow; r < toRow; r++) {
8870
+ const rh = plan.rowHeights[r];
8871
+ const rBottom = y - rh;
8872
+ for (let c = 0; c < plan.cx.length; c++) {
8873
+ parts.push(rectOp(plan.cx[c], rBottom, plan.cwi[c], rh, COL_CELL, 0.3));
8874
+ }
8875
+ y = rBottom;
8876
+ }
8877
+ return parts.join("\n");
8878
+ }
8879
+
8797
8880
  // src/core/pdf-form.ts
8798
8881
  var DEFAULT_FIELD_HEIGHT_TEXT = 22;
8799
8882
  var DEFAULT_FIELD_HEIGHT_MULTILINE = 60;
@@ -10928,6 +11011,51 @@ function getNum(attrs, name, def = 0) {
10928
11011
  const v = getAttr(attrs, name);
10929
11012
  return v !== void 0 ? +v : def;
10930
11013
  }
11014
+ function getLen(attrs, name) {
11015
+ const v = getAttr(attrs, name);
11016
+ if (v === void 0) return void 0;
11017
+ const n2 = parseFloat(v);
11018
+ return Number.isFinite(n2) ? n2 : void 0;
11019
+ }
11020
+ function decodeSvgEntities(s) {
11021
+ return s.replace(/&(#x[0-9a-fA-F]+|#[0-9]+|[a-zA-Z]+);/g, (_m, ent) => {
11022
+ if (ent[0] === "#") {
11023
+ const cp = ent[1] === "x" || ent[1] === "X" ? parseInt(ent.slice(2), 16) : parseInt(ent.slice(1), 10);
11024
+ return Number.isFinite(cp) && cp >= 0 && cp <= 1114111 ? String.fromCodePoint(cp) : "";
11025
+ }
11026
+ switch (ent) {
11027
+ case "amp":
11028
+ return "&";
11029
+ case "lt":
11030
+ return "<";
11031
+ case "gt":
11032
+ return ">";
11033
+ case "quot":
11034
+ return '"';
11035
+ case "apos":
11036
+ return "'";
11037
+ case "nbsp":
11038
+ return "\xA0";
11039
+ default:
11040
+ return "";
11041
+ }
11042
+ });
11043
+ }
11044
+ function stripSvgTags(raw) {
11045
+ let out = "";
11046
+ let inTag = false;
11047
+ for (const ch2 of raw) {
11048
+ if (ch2 === "<") inTag = true;
11049
+ else if (ch2 === ">") inTag = false;
11050
+ else if (!inTag) out += ch2;
11051
+ }
11052
+ return out;
11053
+ }
11054
+ function sanitizeSvgText(raw) {
11055
+ const noTags = stripSvgTags(raw);
11056
+ const decoded = decodeSvgEntities(noTags);
11057
+ return decoded.replace(/[\u0000-\u001F\u007F-\u009F]+/g, " ").replace(/\s+/g, " ").trim();
11058
+ }
10931
11059
  function rectToPath(x, y, w, h, rx, ry) {
10932
11060
  if (w <= 0 || h <= 0) return "";
10933
11061
  if (rx <= 0 && ry <= 0) {
@@ -11009,7 +11137,60 @@ function parseSvgMarkup(svg) {
11009
11137
  });
11010
11138
  }
11011
11139
  }
11012
- return { elements, viewBox };
11140
+ const textElements = parseSvgText(svg);
11141
+ return { elements, textElements, viewBox };
11142
+ }
11143
+ function parseSvgText(svg) {
11144
+ const out = [];
11145
+ const textRe = /<text\b([^>]*)>([\s\S]*?)<\/text>/gi;
11146
+ let tm;
11147
+ while ((tm = textRe.exec(svg)) !== null) {
11148
+ const attrs = tm[1];
11149
+ const inner = tm[2];
11150
+ const baseX = getLen(attrs, "x") ?? 0;
11151
+ const baseY = getLen(attrs, "y") ?? 0;
11152
+ const baseSize = getLen(attrs, "font-size") ?? 16;
11153
+ const baseFillRaw = getAttr(attrs, "fill");
11154
+ const baseFill = baseFillRaw ? normalizeSvgColor(baseFillRaw) : void 0;
11155
+ const baseAnchor = normalizeAnchor(getAttr(attrs, "text-anchor"));
11156
+ const tspanRe = /<tspan\b([^>]*)>([\s\S]*?)<\/tspan>/gi;
11157
+ let sm;
11158
+ let penX = baseX;
11159
+ let penY = baseY;
11160
+ let found = false;
11161
+ while ((sm = tspanRe.exec(inner)) !== null) {
11162
+ found = true;
11163
+ const sa = sm[1];
11164
+ const text = sanitizeSvgText(sm[2]);
11165
+ const ax = getLen(sa, "x");
11166
+ const ay = getLen(sa, "y");
11167
+ const adx = getLen(sa, "dx");
11168
+ const ady = getLen(sa, "dy");
11169
+ penX = ax !== void 0 ? ax : penX + (adx ?? 0);
11170
+ penY = ay !== void 0 ? ay : penY + (ady ?? 0);
11171
+ if (!text) continue;
11172
+ const fillRaw = getAttr(sa, "fill");
11173
+ out.push({
11174
+ text,
11175
+ x: penX,
11176
+ y: penY,
11177
+ fontSize: getLen(sa, "font-size") ?? baseSize,
11178
+ fill: fillRaw ? normalizeSvgColor(fillRaw) : baseFill,
11179
+ anchor: normalizeAnchor(getAttr(sa, "text-anchor")) ?? baseAnchor
11180
+ });
11181
+ }
11182
+ if (!found) {
11183
+ const text = sanitizeSvgText(inner);
11184
+ if (text) {
11185
+ out.push({ text, x: baseX, y: baseY, fontSize: baseSize, fill: baseFill, anchor: baseAnchor });
11186
+ }
11187
+ }
11188
+ }
11189
+ return out;
11190
+ }
11191
+ function normalizeAnchor(v) {
11192
+ if (v === "middle" || v === "end" || v === "start") return v;
11193
+ return void 0;
11013
11194
  }
11014
11195
  var fn = (n2) => n2.toFixed(2);
11015
11196
  function resolveColor(color, fallback) {
@@ -11045,36 +11226,62 @@ function buildPathOps(segments, fillRgb, strokeRgb, strokeWidth) {
11045
11226
  ops.push("Q");
11046
11227
  return ops.join("\n");
11047
11228
  }
11048
- function renderSvg(data, x, y, w, h, options) {
11229
+ function renderSvg(data, x, y, w, h, options, enc) {
11049
11230
  if (!data || w <= 0 || h <= 0) return "";
11050
11231
  const isSvgMarkup = data.trimStart().charAt(0) === "<";
11051
11232
  let vb;
11052
11233
  let elements;
11234
+ let textElements = [];
11053
11235
  if (isSvgMarkup) {
11054
11236
  const parsed = parseSvgMarkup(data);
11055
11237
  vb = options?.viewBox ?? parsed.viewBox ?? [0, 0, w, h];
11056
11238
  elements = parsed.elements;
11239
+ textElements = parsed.textElements;
11057
11240
  } else {
11058
11241
  vb = options?.viewBox ?? [0, 0, w, h];
11059
11242
  elements = [{ pathData: data }];
11060
11243
  }
11061
- if (elements.length === 0 || vb[2] <= 0 || vb[3] <= 0) return "";
11244
+ if (elements.length === 0 && textElements.length === 0 || vb[2] <= 0 || vb[3] <= 0) return "";
11062
11245
  const sx = w / vb[2];
11063
11246
  const sy = h / vb[3];
11064
- const cmOp = `${fn(sx)} 0 0 ${fn(-sy)} ${fn(x - vb[0] * sx)} ${fn(y + vb[1] * sy)} cm`;
11247
+ const tx2 = x - vb[0] * sx;
11248
+ const ty = y + vb[1] * sy;
11249
+ const cmOp = `${fn(sx)} 0 0 ${fn(-sy)} ${fn(tx2)} ${fn(ty)} cm`;
11065
11250
  const defaultFill = resolveColor(options?.fill, "0 0 0");
11066
11251
  const defaultStroke = resolveColor(options?.stroke, null);
11067
11252
  const defaultSw = options?.strokeWidth ?? 1;
11068
- const allOps = ["q", cmOp];
11253
+ const shapeOps = [];
11069
11254
  for (const elem of elements) {
11070
11255
  const segments = parseSvgPath(elem.pathData);
11071
11256
  if (segments.length === 0) continue;
11072
11257
  const fillRgb = resolveColor(elem.fill, defaultFill);
11073
11258
  const strokeRgb = resolveColor(elem.stroke, defaultStroke);
11074
11259
  const sw = elem.strokeWidth ?? defaultSw;
11075
- allOps.push(buildPathOps(segments, fillRgb, strokeRgb, sw));
11076
- }
11077
- allOps.push("Q");
11260
+ shapeOps.push(buildPathOps(segments, fillRgb, strokeRgb, sw));
11261
+ }
11262
+ const textOps = [];
11263
+ if (enc && textElements.length > 0) {
11264
+ for (const t of textElements) {
11265
+ const szPdf = t.fontSize * sy;
11266
+ if (szPdf <= 0 || !t.text) continue;
11267
+ const pdfBaseX = t.x * sx + tx2;
11268
+ const pdfBaseY = -sy * t.y + ty;
11269
+ const width = enc.tw(t.text, szPdf);
11270
+ const anchorOffset = t.anchor === "middle" ? width / 2 : t.anchor === "end" ? width : 0;
11271
+ const pdfX = pdfBaseX - anchorOffset;
11272
+ const fillRgb = resolveColor(t.fill, "0 0 0");
11273
+ textOps.push("q");
11274
+ if (fillRgb) textOps.push(`${fillRgb} rg`);
11275
+ textOps.push(txt(t.text, pdfX, pdfBaseY, enc.f1, szPdf, enc));
11276
+ textOps.push("Q");
11277
+ }
11278
+ }
11279
+ if (shapeOps.length === 0 && textOps.length === 0) return "";
11280
+ const allOps = [];
11281
+ if (shapeOps.length > 0) {
11282
+ allOps.push("q", cmOp, ...shapeOps, "Q");
11283
+ }
11284
+ allOps.push(...textOps);
11078
11285
  return allOps.join("\n");
11079
11286
  }
11080
11287
 
@@ -11380,7 +11587,7 @@ function planTable(block, enc, mgL, cw) {
11380
11587
  headerLines.push(lines);
11381
11588
  if (lines.length > headerMaxLines) headerMaxLines = lines.length;
11382
11589
  }
11383
- const headerHeight = headerMaxLines === 1 ? TH_H : Math.max(TH_H, headerMaxLines * fs.th * TABLE_LINE_HEIGHT + CELL_PAD_BOTTOM + 2);
11590
+ const headerHeight = headerMaxLines === 1 ? TH_H : Math.max(TH_H, headerMaxLines * fs.th * TABLE_LINE_HEIGHT + pad + 2);
11384
11591
  const rowLines = [];
11385
11592
  const rowHeights = [];
11386
11593
  for (let r = 0; r < block.rows.length; r++) {
@@ -11393,7 +11600,7 @@ function planTable(block, enc, mgL, cw) {
11393
11600
  if (lines.length > maxLines) maxLines = lines.length;
11394
11601
  }
11395
11602
  rowLines.push(cells);
11396
- const h = maxLines === 1 ? minRowH : Math.max(minRowH, maxLines * fs.td * TABLE_LINE_HEIGHT + CELL_PAD_BOTTOM + 2);
11603
+ const h = maxLines === 1 ? minRowH : Math.max(minRowH, maxLines * fs.td * TABLE_LINE_HEIGHT + pad + 2);
11397
11604
  rowHeights.push(h);
11398
11605
  }
11399
11606
  const captionLines = block.caption ? wrapText(block.caption, cw, CAPTION_FONT_SIZE, enc) : [];
@@ -11819,7 +12026,7 @@ function renderBarcodeBlock(block, y, mgL, cw, tagCtx, documentChildren) {
11819
12026
  y = by - 6;
11820
12027
  return { ops, y };
11821
12028
  }
11822
- function renderSvgBlock(block, y, mgL, cw, tagCtx, documentChildren) {
12029
+ function renderSvgBlock(block, y, mgL, cw, tagCtx, documentChildren, enc) {
11823
12030
  const ops = [];
11824
12031
  const w = block.width ?? DEFAULT_SVG_SIZE;
11825
12032
  const h = block.height ?? DEFAULT_SVG_SIZE;
@@ -11835,7 +12042,7 @@ function renderSvgBlock(block, y, mgL, cw, tagCtx, documentChildren) {
11835
12042
  stroke: block.stroke,
11836
12043
  strokeWidth: block.strokeWidth,
11837
12044
  viewBox: block.viewBox
11838
- });
12045
+ }, enc);
11839
12046
  if (svgOps) {
11840
12047
  if (tagCtx?.tagged) {
11841
12048
  const mcid = tagCtx.mcidAlloc.next(tagCtx.pageObjNum);
@@ -12017,6 +12224,7 @@ function assembleDocumentParts(params, layoutOptions) {
12017
12224
  if (watermarkOpts) {
12018
12225
  validateWatermark(watermarkOpts, layout?.tagged);
12019
12226
  }
12227
+ const debugOpts = resolveDebugOptions(layout?.debug);
12020
12228
  const attachments = layout?.attachments;
12021
12229
  validateAttachments(attachments, layout?.tagged);
12022
12230
  const mcidAlloc = tagged ? createMCIDAllocator() : void 0;
@@ -12204,6 +12412,10 @@ function assembleDocumentParts(params, layoutOptions) {
12204
12412
  const tagCtx = tagged && mcidAlloc ? { tagged: true, mcidAlloc, pageObjNum} : void 0;
12205
12413
  const ops = [];
12206
12414
  let y = pgH - mg.t;
12415
+ const debugOps = [];
12416
+ if (debugOpts?.showMargins) {
12417
+ debugOps.push(marginBoxOps(pgW, pgH, mg, FT_H));
12418
+ }
12207
12419
  if (headerTpl) {
12208
12420
  const hOps = renderPageTemplate(
12209
12421
  headerTpl,
@@ -12244,6 +12456,7 @@ function assembleDocumentParts(params, layoutOptions) {
12244
12456
  }
12245
12457
  const blocks = pageBlocks[p] ?? [];
12246
12458
  for (const block of blocks) {
12459
+ const yBefore = debugOpts ? y : 0;
12247
12460
  switch (block.type) {
12248
12461
  case "heading": {
12249
12462
  if (headingDestIdx < headingDests.length) {
@@ -12324,7 +12537,7 @@ function assembleDocumentParts(params, layoutOptions) {
12324
12537
  break;
12325
12538
  }
12326
12539
  case "svg": {
12327
- const result = renderSvgBlock(block, y, mg.l, cw, tagCtx, documentChildren);
12540
+ const result = renderSvgBlock(block, y, mg.l, cw, tagCtx, documentChildren, enc);
12328
12541
  ops.push(...result.ops);
12329
12542
  y = result.y;
12330
12543
  break;
@@ -12336,6 +12549,19 @@ function assembleDocumentParts(params, layoutOptions) {
12336
12549
  break;
12337
12550
  }
12338
12551
  }
12552
+ if (debugOpts) {
12553
+ if (debugOpts.showContentBounds) {
12554
+ const bo = blockBoundsOps(mg.l, cw, yBefore, y);
12555
+ if (bo) debugOps.push(bo);
12556
+ }
12557
+ if (debugOpts.showCells) {
12558
+ if (block.type === "__tableSlice") {
12559
+ debugOps.push(tableCellOps(block.slice.plan, block.slice, yBefore));
12560
+ } else if (block.type === "table") {
12561
+ debugOps.push(tableCellOps(planTable(block, enc, mg.l, cw), void 0, yBefore));
12562
+ }
12563
+ }
12564
+ }
12339
12565
  }
12340
12566
  if (wmState?.foregroundOps) {
12341
12567
  ops.push(wmState.foregroundOps);
@@ -12356,6 +12582,9 @@ function assembleDocumentParts(params, layoutOptions) {
12356
12582
  documentChildren
12357
12583
  );
12358
12584
  ops.push(...ftOps);
12585
+ if (debugOps.length > 0) {
12586
+ ops.push(...debugOps);
12587
+ }
12359
12588
  pageStreams.push(ops.join("\n"));
12360
12589
  }
12361
12590
  const annotsByPage = /* @__PURE__ */ new Map();
@@ -12953,6 +13182,173 @@ function autoOutlineFromHeadings(headings) {
12953
13182
  return root;
12954
13183
  }
12955
13184
 
13185
+ // src/core/pdf-layout-inspect.ts
13186
+ var TITLE_BAND_H = 22 + 12;
13187
+ function inspectDocumentLayout(params, layoutOptions) {
13188
+ if (!params || typeof params !== "object" || !Array.isArray(params.blocks)) {
13189
+ throw new Error("inspectDocumentLayout: params.blocks must be an array");
13190
+ }
13191
+ const layout = layoutOptions ?? params.layout;
13192
+ const pgW = layout?.pageWidth ?? PG_W;
13193
+ const pgH = layout?.pageHeight ?? PG_H;
13194
+ const mg = layout?.margins ?? { ...DEFAULT_MARGINS };
13195
+ const cw = pgW - mg.l - mg.r;
13196
+ const fontEntries = params.fontEntries ? [...params.fontEntries] : [];
13197
+ const tagged = resolvePdfAConfig(layout?.tagged).enabled;
13198
+ const enc = createEncodingContext(fontEntries, tagged, layout?.normalize ?? false);
13199
+ const headerH = layout?.headerTemplate ? HEADER_H : 0;
13200
+ const availableH = pgH - mg.t - mg.b - FT_H - headerH;
13201
+ const pages = [[]];
13202
+ let remainH = availableH;
13203
+ let curY = pgH - mg.t - headerH;
13204
+ if (params.title) {
13205
+ remainH -= TITLE_BAND_H;
13206
+ curY -= TITLE_BAND_H;
13207
+ }
13208
+ const newPage = () => {
13209
+ pages.push([]);
13210
+ remainH = availableH;
13211
+ curY = pgH - mg.t - headerH;
13212
+ };
13213
+ for (const block of params.blocks) {
13214
+ if (block.type === "pageBreak") {
13215
+ newPage();
13216
+ continue;
13217
+ }
13218
+ if (block.type === "table") {
13219
+ const plan = planTable(block, enc, mg.l, cw);
13220
+ const repeatHeader = block.repeatHeader !== false;
13221
+ const totalRows = block.rows.length;
13222
+ if (totalRows === 0) {
13223
+ const totalH = plan.captionHeight + plan.headerHeight + plan.trailerSpacing;
13224
+ if (totalH > remainH && pages[pages.length - 1].length > 0) newPage();
13225
+ pages[pages.length - 1].push({ type: "table", page: pages.length - 1, x: mg.l, top: curY, width: cw, height: totalH });
13226
+ remainH -= totalH;
13227
+ curY -= totalH;
13228
+ continue;
13229
+ }
13230
+ let rowIdx = 0;
13231
+ let isFirstSlice = true;
13232
+ while (rowIdx < totalRows) {
13233
+ const drawCaption = isFirstSlice;
13234
+ const drawHeader = isFirstSlice || repeatHeader;
13235
+ const tCapH = drawCaption ? plan.captionHeight : 0;
13236
+ const tHdrH = drawHeader ? plan.headerHeight : 0;
13237
+ const availableForRows = remainH - tCapH - tHdrH - plan.trailerSpacing;
13238
+ let usedH = 0;
13239
+ let count = 0;
13240
+ while (rowIdx + count < totalRows && usedH + plan.rowHeights[rowIdx + count] <= availableForRows) {
13241
+ usedH += plan.rowHeights[rowIdx + count];
13242
+ count++;
13243
+ }
13244
+ if (count === 0 && pages[pages.length - 1].length > 0) {
13245
+ newPage();
13246
+ continue;
13247
+ }
13248
+ if (count === 0) count = 1;
13249
+ rowIdx += count;
13250
+ const isFinalSlice = rowIdx >= totalRows;
13251
+ const sliceH = tCapH + tHdrH + usedH + (isFinalSlice ? plan.trailerSpacing : 0);
13252
+ pages[pages.length - 1].push({ type: "table", page: pages.length - 1, x: mg.l, top: curY, width: cw, height: sliceH });
13253
+ remainH -= sliceH;
13254
+ curY -= sliceH;
13255
+ isFirstSlice = false;
13256
+ if (!isFinalSlice) newPage();
13257
+ }
13258
+ continue;
13259
+ }
13260
+ const blockH = estimateBlockHeight(block, enc, cw);
13261
+ if (blockH > remainH && pages[pages.length - 1].length > 0) newPage();
13262
+ pages[pages.length - 1].push({ type: block.type, page: pages.length - 1, x: mg.l, top: curY, width: cw, height: blockH });
13263
+ remainH -= blockH;
13264
+ curY -= blockH;
13265
+ }
13266
+ const inspectedPages = pages.map((blocks, index) => ({ index, blocks }));
13267
+ return {
13268
+ pageWidth: pgW,
13269
+ pageHeight: pgH,
13270
+ margins: { t: mg.t, r: mg.r, b: mg.b, l: mg.l },
13271
+ totalPages: Math.max(1, pages.length),
13272
+ pages: inspectedPages
13273
+ };
13274
+ }
13275
+
13276
+ // src/core/pdf-annot-markup.ts
13277
+ var SUBTYPE = {
13278
+ text: "Text",
13279
+ highlight: "Highlight",
13280
+ underline: "Underline",
13281
+ strikeout: "StrikeOut",
13282
+ squiggly: "Squiggly",
13283
+ square: "Square",
13284
+ circle: "Circle",
13285
+ line: "Line",
13286
+ freetext: "FreeText"
13287
+ };
13288
+ function rectStr(r) {
13289
+ return `[${fmtNum(r[0])} ${fmtNum(r[1])} ${fmtNum(r[2])} ${fmtNum(r[3])}]`;
13290
+ }
13291
+ function quadFromRect(r) {
13292
+ const [x1, y1, x2, y2] = r;
13293
+ return [x1, y2, x2, y2, x1, y1, x2, y1];
13294
+ }
13295
+ function commonEntries(a) {
13296
+ const parts = [];
13297
+ if (a.contents !== void 0) parts.push(`/Contents ${encodePdfTextString(a.contents)}`);
13298
+ if (a.title !== void 0) parts.push(`/T ${encodePdfTextString(a.title)}`);
13299
+ if (a.color !== void 0) parts.push(`/C [${parseColor(a.color)}]`);
13300
+ if (a.opacity !== void 0) parts.push(`/CA ${fmtNum(a.opacity)}`);
13301
+ if (a.modified !== void 0) parts.push(`/M ${encodePdfTextString(a.modified)}`);
13302
+ parts.push(`/F ${a.flags ?? 4}`);
13303
+ return parts.join(" ");
13304
+ }
13305
+ function buildAnnotation(annot, objNum) {
13306
+ return `${objNum} 0 obj
13307
+ ${buildAnnotationBody(annot)}
13308
+ endobj`;
13309
+ }
13310
+ function buildAnnotationBody(annot) {
13311
+ const entries = [
13312
+ `/Type /Annot`,
13313
+ `/Subtype /${SUBTYPE[annot.type]}`,
13314
+ `/Rect ${rectStr(annot.rect)}`,
13315
+ commonEntries(annot)
13316
+ ];
13317
+ switch (annot.type) {
13318
+ case "text": {
13319
+ if (annot.open !== void 0) entries.push(`/Open ${annot.open ? "true" : "false"}`);
13320
+ if (annot.icon !== void 0) entries.push(`/Name /${annot.icon}`);
13321
+ break;
13322
+ }
13323
+ case "highlight":
13324
+ case "underline":
13325
+ case "strikeout":
13326
+ case "squiggly": {
13327
+ const quad = annot.quadPoints && annot.quadPoints.length >= 8 ? annot.quadPoints : quadFromRect(annot.rect);
13328
+ entries.push(`/QuadPoints [${quad.map(fmtNum).join(" ")}]`);
13329
+ break;
13330
+ }
13331
+ case "square":
13332
+ case "circle": {
13333
+ if (annot.interiorColor !== void 0) entries.push(`/IC [${parseColor(annot.interiorColor)}]`);
13334
+ entries.push(`/BS << /W ${fmtNum(annot.borderWidth ?? 1)} >>`);
13335
+ break;
13336
+ }
13337
+ case "line": {
13338
+ entries.push(`/L [${fmtNum(annot.start[0])} ${fmtNum(annot.start[1])} ${fmtNum(annot.end[0])} ${fmtNum(annot.end[1])}]`);
13339
+ entries.push(`/BS << /W ${fmtNum(annot.borderWidth ?? 1)} >>`);
13340
+ break;
13341
+ }
13342
+ case "freetext": {
13343
+ const sz = annot.fontSize ?? 12;
13344
+ const col = annot.color !== void 0 ? parseColor(annot.color) : "0 0 0";
13345
+ entries.push(`/DA (/Helv ${fmtNum(sz)} Tf ${col} rg)`);
13346
+ break;
13347
+ }
13348
+ }
13349
+ return `<< ${entries.filter(Boolean).join(" ")} >>`;
13350
+ }
13351
+
12956
13352
  // src/core/pdf-signature.ts
12957
13353
  init_sha();
12958
13354
 
@@ -14300,6 +14696,28 @@ function openPdf(bytes) {
14300
14696
  if (!entry || entry.type === 0) return null;
14301
14697
  return resolveRef({ num, gen: entry.gen });
14302
14698
  }
14699
+ let _pageRefs;
14700
+ function collectPageRefs() {
14701
+ if (_pageRefs) return _pageRefs;
14702
+ const catalog = getCatalog();
14703
+ const refs = [];
14704
+ const walk = (nodeVal, depth) => {
14705
+ if (depth > 100) return;
14706
+ const node = resolveValue2(nodeVal);
14707
+ if (!isDict(node)) return;
14708
+ if (dictGetName(node, "Type") === "Page") {
14709
+ if (isRef(nodeVal)) refs.push(nodeVal);
14710
+ return;
14711
+ }
14712
+ const kids = node.get("Kids");
14713
+ if (isArray(kids)) {
14714
+ for (const k of kids) walk(k, depth + 1);
14715
+ }
14716
+ };
14717
+ walk(catalog.get("Pages") ?? null, 0);
14718
+ _pageRefs = refs;
14719
+ return refs;
14720
+ }
14303
14721
  function decodeStreamData(stream) {
14304
14722
  let data = stream.data;
14305
14723
  const filterName = dictGetName(stream.dict, "Filter");
@@ -14351,10 +14769,107 @@ function openPdf(bytes) {
14351
14769
  const info = resolveValue2(infoRef);
14352
14770
  return isDict(info) ? info : null;
14353
14771
  },
14772
+ getPageLabels() {
14773
+ const catalog = getCatalog();
14774
+ const plVal = resolveValue2(catalog.get("PageLabels") ?? null);
14775
+ if (!isDict(plVal)) return null;
14776
+ const entries = /* @__PURE__ */ new Map();
14777
+ collectNumberTree(plVal, resolveValue2, entries);
14778
+ if (entries.size === 0) return null;
14779
+ const ranges = [];
14780
+ for (const startPage of [...entries.keys()].sort((a, b) => a - b)) {
14781
+ const dict = resolveValue2(entries.get(startPage) ?? null);
14782
+ if (!isDict(dict)) continue;
14783
+ const range = { startPage };
14784
+ const sOp = dictGetName(dict, "S");
14785
+ range.style = sOp === void 0 ? "none" : STYLE_FROM_OP[sOp] ?? "none";
14786
+ const prefix = dict.get("P");
14787
+ if (typeof prefix === "string") range.prefix = prefix;
14788
+ const start = dictGetNum(dict, "St");
14789
+ if (start !== void 0) range.start = start;
14790
+ ranges.push(range);
14791
+ }
14792
+ return ranges.length > 0 ? ranges : null;
14793
+ },
14794
+ getAnnotations(pageIndex) {
14795
+ const pages = collectPages();
14796
+ if (pageIndex < 0 || pageIndex >= pages.length) return [];
14797
+ const annotsVal = resolveValue2(pages[pageIndex].get("Annots") ?? null);
14798
+ if (!isArray(annotsVal)) return [];
14799
+ const out = [];
14800
+ for (const a of annotsVal) {
14801
+ const d = resolveValue2(a);
14802
+ if (!isDict(d)) continue;
14803
+ const subtype = dictGetName(d, "Subtype") ?? "";
14804
+ const rectVal = resolveValue2(d.get("Rect") ?? null);
14805
+ let rect = null;
14806
+ if (isArray(rectVal) && rectVal.length === 4 && rectVal.every((n2) => typeof n2 === "number")) {
14807
+ rect = [rectVal[0], rectVal[1], rectVal[2], rectVal[3]];
14808
+ }
14809
+ const parsed = { subtype, rect };
14810
+ const contents = d.get("Contents");
14811
+ if (typeof contents === "string") parsed.contents = decodePdfTextString(contents);
14812
+ const title = d.get("T");
14813
+ if (typeof title === "string") parsed.title = decodePdfTextString(title);
14814
+ const c = resolveValue2(d.get("C") ?? null);
14815
+ if (isArray(c)) {
14816
+ const nums = c.filter((x) => typeof x === "number");
14817
+ if (nums.length > 0) parsed.color = nums;
14818
+ }
14819
+ const qp = resolveValue2(d.get("QuadPoints") ?? null);
14820
+ if (isArray(qp)) {
14821
+ parsed.quadPoints = qp.filter((x) => typeof x === "number");
14822
+ }
14823
+ const action = resolveValue2(d.get("A") ?? null);
14824
+ if (isDict(action)) {
14825
+ const uri = action.get("URI");
14826
+ if (typeof uri === "string") parsed.url = uri;
14827
+ }
14828
+ out.push(parsed);
14829
+ }
14830
+ return out;
14831
+ },
14832
+ getPageRef(pageIndex) {
14833
+ const refs = collectPageRefs();
14834
+ return pageIndex >= 0 && pageIndex < refs.length ? refs[pageIndex] : null;
14835
+ },
14354
14836
  decodeStream: decodeStreamData,
14355
14837
  getObject
14356
14838
  };
14357
14839
  }
14840
+ function decodePdfTextString(raw) {
14841
+ if (raw.length >= 2 && raw.charCodeAt(0) === 254 && raw.charCodeAt(1) === 255) {
14842
+ let out = "";
14843
+ for (let i = 2; i + 1 < raw.length; i += 2) {
14844
+ out += String.fromCharCode(raw.charCodeAt(i) << 8 | raw.charCodeAt(i + 1));
14845
+ }
14846
+ return out;
14847
+ }
14848
+ return raw;
14849
+ }
14850
+ var STYLE_FROM_OP = {
14851
+ D: "decimal",
14852
+ r: "roman",
14853
+ R: "Roman",
14854
+ a: "alpha",
14855
+ A: "Alpha"
14856
+ };
14857
+ function collectNumberTree(node, resolve, out) {
14858
+ const nums = resolve(node.get("Nums") ?? null);
14859
+ if (isArray(nums)) {
14860
+ for (let i = 0; i + 1 < nums.length; i += 2) {
14861
+ const key = resolve(nums[i]);
14862
+ if (typeof key === "number") out.set(key, nums[i + 1]);
14863
+ }
14864
+ }
14865
+ const kids = resolve(node.get("Kids") ?? null);
14866
+ if (isArray(kids)) {
14867
+ for (const kid of kids) {
14868
+ const kd = resolve(kid);
14869
+ if (isDict(kd)) collectNumberTree(kd, resolve, out);
14870
+ }
14871
+ }
14872
+ }
14358
14873
  function flattenPageTree(node, resolve, pages) {
14359
14874
  const type = dictGetName(node, "Type");
14360
14875
  if (type === "Page") {
@@ -14489,6 +15004,21 @@ function createModifier(reader) {
14489
15004
  if (modified.has(num)) return modified.get(num) ?? null;
14490
15005
  return reader.getObject(num);
14491
15006
  }
15007
+ function addAnnotation(pageIndex, annotationBody) {
15008
+ const pageRef = reader.getPageRef(pageIndex);
15009
+ if (!pageRef) throw new Error(`addAnnotation: no page at index ${pageIndex}`);
15010
+ const objNum = addRawObject(annotationBody);
15011
+ const page = getObject(pageRef.num);
15012
+ if (!isDict(page)) throw new Error(`addAnnotation: page ${pageIndex} is not a dictionary`);
15013
+ const clone = new Map(page);
15014
+ const existing = clone.get("Annots");
15015
+ const resolved = isRef(existing) ? reader.resolveValue(existing) : existing;
15016
+ const annots = isArray(resolved) ? [...resolved] : [];
15017
+ annots.push({ type: "ref", num: objNum, gen: 0 });
15018
+ clone.set("Annots", annots);
15019
+ setObject(pageRef.num, clone);
15020
+ return objNum;
15021
+ }
14492
15022
  function save() {
14493
15023
  if (modified.size === 0) {
14494
15024
  return reader.bytes;
@@ -14534,6 +15064,7 @@ ${xrefOffset}
14534
15064
  setObject,
14535
15065
  addObject,
14536
15066
  addRawObject,
15067
+ addAnnotation,
14537
15068
  getObject,
14538
15069
  save,
14539
15070
  get nextObjNum() {
@@ -16173,6 +16704,8 @@ exports.WORKER_TIMEOUT_MS = WORKER_TIMEOUT_MS;
16173
16704
  exports.addSignaturePlaceholder = addSignaturePlaceholder;
16174
16705
  exports.applyDecodeFilter = applyDecodeFilter;
16175
16706
  exports.buildAcroFormDict = buildAcroFormDict;
16707
+ exports.buildAnnotation = buildAnnotation;
16708
+ exports.buildAnnotationBody = buildAnnotationBody;
16176
16709
  exports.buildAppearanceStreamDict = buildAppearanceStreamDict;
16177
16710
  exports.buildCmsSignedData = buildCmsSignedData;
16178
16711
  exports.buildDocumentPDF = buildDocumentPDF;
@@ -16207,6 +16740,7 @@ exports.containsDevanagari = containsDevanagari;
16207
16740
  exports.containsEthiopic = containsEthiopic;
16208
16741
  exports.containsHebrew = containsHebrew;
16209
16742
  exports.containsKhmer = containsKhmer;
16743
+ exports.containsMath = containsMath;
16210
16744
  exports.containsMyanmar = containsMyanmar;
16211
16745
  exports.containsRTL = containsRTL;
16212
16746
  exports.containsSinhala = containsSinhala;
@@ -16271,6 +16805,7 @@ exports.inflateSync = inflateSync;
16271
16805
  exports.initCrypto = initCrypto;
16272
16806
  exports.initNodeCompression = initNodeCompression;
16273
16807
  exports.initNodeDecompression_parser = initNodeDecompression;
16808
+ exports.inspectDocumentLayout = inspectDocumentLayout;
16274
16809
  exports.isArmenianCodepoint = isArmenianCodepoint;
16275
16810
  exports.isArray = isArray;
16276
16811
  exports.isBengaliCodepoint = isBengaliCodepoint;
@@ -16281,6 +16816,7 @@ exports.isEthiopicCodepoint = isEthiopicCodepoint;
16281
16816
  exports.isGeorgianCodepoint = isGeorgianCodepoint;
16282
16817
  exports.isKhmerCodepoint = isKhmerCodepoint;
16283
16818
  exports.isLinkAnnotation = isLinkAnnotation;
16819
+ exports.isMathCodepoint = isMathCodepoint;
16284
16820
  exports.isMyanmarCodepoint = isMyanmarCodepoint;
16285
16821
  exports.isName = isName;
16286
16822
  exports.isRef = isRef;