wordinweb 0.1.10 → 0.1.11

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.js CHANGED
@@ -2457,6 +2457,8 @@ function parseRunProps(rPr, ctx) {
2457
2457
  else if (vertAlign === "baseline") props.verticalAlign = "baseline";
2458
2458
  const spacing = intAttr(child(rPr, "spacing"), "val");
2459
2459
  if (spacing !== void 0) props.letterSpacing = twipsToPx(spacing);
2460
+ const kern = intAttr(child(rPr, "kern"), "val");
2461
+ if (kern !== void 0) props.kerningThreshold = kern > 0 ? halfPtToPx(kern) : null;
2460
2462
  const wScale = intAttr(child(rPr, "w"), "val");
2461
2463
  if (wScale !== void 0 && wScale > 0 && wScale !== 100) props.textScale = wScale / 100;
2462
2464
  const fitText = intAttr(child(rPr, "fitText"), "val");
@@ -3417,6 +3419,17 @@ function parseParagraph(p, ctx) {
3417
3419
  parseParaChildren(p, ctx, para.children, field, bookmarks);
3418
3420
  if (bookmarks.length > 0) para.bookmarks = bookmarks;
3419
3421
  flushField(field);
3422
+ const visibleContent = para.children.flatMap(
3423
+ (paraChild) => (paraChild.type === "run" ? [paraChild] : paraChild.runs).flatMap(
3424
+ (run) => run.content.filter((content) => content.kind !== "text" || content.text.length > 0)
3425
+ )
3426
+ );
3427
+ if (visibleContent.length === 1 && visibleContent[0].kind === "math") {
3428
+ const node = visibleContent[0].nodes.length === 1 ? visibleContent[0].nodes[0] : void 0;
3429
+ if (node?.t === "frac" && node.num.every((part) => part.t === "run") && node.den.every((part) => part.t === "run")) {
3430
+ visibleContent[0].display = true;
3431
+ }
3432
+ }
3420
3433
  suppressTocHyperlinkFormatting(para);
3421
3434
  if (ctx.revisionView !== "markup" && pPr) {
3422
3435
  const markRPr = child(pPr, "rPr");
@@ -3659,13 +3672,14 @@ function parseParaChildren(parent, ctx, out, field, bookmarks) {
3659
3672
  const nodes = parseOmml(el16);
3660
3673
  if (nodes.length > 0) {
3661
3674
  const display = ln === "oMathPara";
3675
+ const multiEquationDisplay = display && children(el16, "oMath").length > 3;
3662
3676
  const jcVal = display ? attr(child(child(el16, "oMathParaPr"), "jc"), "val") : void 0;
3663
3677
  const jc = jcVal === "left" || jcVal === "right" || jcVal === "center" || jcVal === "centerGroup" ? jcVal : void 0;
3664
3678
  const content = [];
3665
3679
  let segment = [];
3666
3680
  const flushMath = () => {
3667
3681
  if (segment.length === 0) return;
3668
- content.push({ kind: "math", nodes: segment, src: el16, display, jc });
3682
+ content.push({ kind: "math", nodes: segment, src: el16, display, multiEquationDisplay, jc });
3669
3683
  segment = [];
3670
3684
  };
3671
3685
  for (const node of nodes) {
@@ -3896,6 +3910,9 @@ function parseRun(r, ctx, field, suppressedObjects) {
3896
3910
  case "tab":
3897
3911
  run.content.push({ kind: "tab" });
3898
3912
  break;
3913
+ case "pgNum":
3914
+ run.content.push({ kind: "field", instruction: "PAGE", cachedResult: "" });
3915
+ break;
3899
3916
  case "drawing": {
3900
3917
  const img = parseDrawing(el16, ctx);
3901
3918
  if (img) {
@@ -3943,7 +3960,7 @@ function parseRun(r, ctx, field, suppressedObjects) {
3943
3960
  let code = parseInt(charHex, 16);
3944
3961
  if (code >= 61440) code -= 61440;
3945
3962
  const font = attr(el16, "font") ?? "";
3946
- const mapped = /wingdings|webdings|symbol/i.test(font) ? SYMBOL_CHAR_MAP[code] : void 0;
3963
+ const mapped = /^symbol$/i.test(font) ? SYMBOL_TEXT_CHAR_MAP[code] : /wingdings|webdings/i.test(font) ? SYMBOL_CHAR_MAP[code] : void 0;
3947
3964
  run.content.push({ kind: "text", text: mapped ?? String.fromCharCode(code) });
3948
3965
  }
3949
3966
  break;
@@ -4354,7 +4371,8 @@ function parseDrawing(drawing, ctx) {
4354
4371
  const pctEl = el16 ? el16.children.find((c) => localName(c.name).startsWith("pct")) : void 0;
4355
4372
  if (!pctEl) return void 0;
4356
4373
  const rf = attr(el16, "relativeFrom");
4357
- return { pct: (parseInt(pctEl.text, 10) || 0) / 1e5, rel: rf === "page" ? "page" : "margin" };
4374
+ const pct = (parseInt(pctEl.text, 10) || 0) / 1e5;
4375
+ return pct > 0 ? { pct, rel: rf === "page" ? "page" : "margin" } : void 0;
4358
4376
  };
4359
4377
  const spPr = child(textboxEl, "spPr");
4360
4378
  const txbxOwn = child(textboxEl, "txbx");
@@ -4821,7 +4839,8 @@ function collectDiagramShapes(dspRoot, ctx, out, dx, dy) {
4821
4839
  height: emuToPx(th),
4822
4840
  blocks,
4823
4841
  insets: { l: insetOf("lIns", 9.6), t: insetOf("tIns", 4.8), r: insetOf("rIns", 9.6), b: insetOf("bIns", 4.8) },
4824
- textAnchor: anchorAttr === "ctr" ? "middle" : anchorAttr === "b" ? "bottom" : "top"
4842
+ textAnchor: anchorAttr === "ctr" ? "middle" : anchorAttr === "b" ? "bottom" : "top",
4843
+ paintOffsetY: 0.75
4825
4844
  });
4826
4845
  }
4827
4846
  }
@@ -4907,6 +4926,8 @@ function drawingMLRun(r, ctx) {
4907
4926
  const props = {};
4908
4927
  const sz = rPr ? intAttr(rPr, "sz") : void 0;
4909
4928
  props.size = ptToPx((sz ?? 1800) / 100);
4929
+ const kern = rPr ? intAttr(rPr, "kern") : void 0;
4930
+ if (kern !== void 0) props.kerningThreshold = kern > 0 ? ptToPx(kern / 100) : null;
4910
4931
  if (rPr && attr(rPr, "b") === "1") props.bold = true;
4911
4932
  if (rPr && attr(rPr, "i") === "1") props.italic = true;
4912
4933
  const u = rPr ? attr(rPr, "u") : void 0;
@@ -5351,6 +5372,8 @@ function parseCell(tc, ctx) {
5351
5372
  if (tcW && attr(tcW, "type") === "dxa") {
5352
5373
  const w = intAttr(tcW, "w");
5353
5374
  if (w) props.width = twipsToPx(w);
5375
+ } else if (tcW && attr(tcW, "type") === "auto") {
5376
+ props.widthAuto = true;
5354
5377
  }
5355
5378
  const borders = child(tcPr, "tcBorders");
5356
5379
  if (borders) {
@@ -5726,6 +5749,9 @@ var DocxDocument = class _DocxDocument {
5726
5749
  /** Note content by note id (render-only; sources stripped). */
5727
5750
  this.footnotes = /* @__PURE__ */ new Map();
5728
5751
  this.endnotes = /* @__PURE__ */ new Map();
5752
+ /** The separator paragraph controls the gap between its rule and the first
5753
+ * footnote. */
5754
+ this.footnoteSeparator = [];
5729
5755
  /** `_Ref` cross-reference bookmark ranges (name → captured runs). REF
5730
5756
  * fields re-render the referenced text from these — Word recomputes REF on
5731
5757
  * open, so the cached field result in the file is stale. */
@@ -5842,6 +5868,8 @@ var DocxDocument = class _DocxDocument {
5842
5868
  if (!docRoot) throw new Error(`Missing ${docPart} in package`);
5843
5869
  this.docRoot = docRoot;
5844
5870
  this.rememberOriginalXml(docPart, docRoot);
5871
+ const coreProperties = this.readXmlOptional("docProps/core.xml");
5872
+ this.hydrateCorePropertyControls(docRoot, coreProperties);
5845
5873
  this.repairLegacyWordInWebObjects();
5846
5874
  this.documentRels = parseRelationships(this.relsRoot ?? void 0, docPart);
5847
5875
  if (settings) {
@@ -5884,6 +5912,7 @@ var DocxDocument = class _DocxDocument {
5884
5912
  if (!root) continue;
5885
5913
  const partRels = parseRelationships(this.readXmlOptional(relsPathFor(rel.target)), rel.target);
5886
5914
  this.rememberOriginalXml(rel.target, root);
5915
+ this.hydrateCorePropertyControls(root, coreProperties);
5887
5916
  this.hfParts.push({ relId: rel.id, target: rel.target, root, isHeader, rels: partRels });
5888
5917
  }
5889
5918
  for (const rel of this.documentRels.values()) {
@@ -5897,6 +5926,12 @@ var DocxDocument = class _DocxDocument {
5897
5926
  this.footnotesPart = rel.target;
5898
5927
  this.footnotesRoot = root;
5899
5928
  this.footnotesRels = partRels;
5929
+ const separator = root.children.find(
5930
+ (item) => localName(item.name) === "footnote" && attr(item, "type") === "separator"
5931
+ );
5932
+ if (separator) {
5933
+ this.footnoteSeparator.push(...parseBlocks(separator, { ...this.ctxBase, rels: partRels }));
5934
+ }
5900
5935
  }
5901
5936
  const notes = parseNotesPart(root, { ...this.ctxBase, rels: partRels }, isFn);
5902
5937
  for (const [id, blocks] of notes) (isFn ? this.footnotes : this.endnotes).set(id, blocks);
@@ -5911,6 +5946,37 @@ var DocxDocument = class _DocxDocument {
5911
5946
  this._packageResourceVersion++;
5912
5947
  this._layoutGlobalSig = null;
5913
5948
  }
5949
+ /** Resolve content controls mapped to standard package core properties.
5950
+ * Word refreshes these bindings on open, so the serialized sdtContent can
5951
+ * be stale even though the visible value comes from docProps/core.xml. */
5952
+ hydrateCorePropertyControls(root, coreProperties) {
5953
+ if (!coreProperties) return;
5954
+ const textNodes = (element) => {
5955
+ const out = localName(element.name) === "t" ? [element] : [];
5956
+ for (const item of element.children) out.push(...textNodes(item));
5957
+ return out;
5958
+ };
5959
+ const walk2 = (element) => {
5960
+ if (localName(element.name) === "sdt") {
5961
+ const binding = child(child(element, "sdtPr"), "dataBinding");
5962
+ const xpath = attr(binding, "xpath") ?? "";
5963
+ const propertyMatch = /\/(?:[^/:]+:)?([A-Za-z_][\w.-]*)(?:\[\d+\])?\s*$/.exec(xpath);
5964
+ if (xpath.includes("coreProperties") && propertyMatch) {
5965
+ const property = coreProperties.children.find(
5966
+ (item) => localName(item.name) === propertyMatch[1]
5967
+ );
5968
+ const content = child(element, "sdtContent");
5969
+ const targets = content ? textNodes(content) : [];
5970
+ if (property?.text && targets.length > 0) {
5971
+ targets[0].text = property.text;
5972
+ for (const target of targets.slice(1)) target.text = "";
5973
+ }
5974
+ }
5975
+ }
5976
+ for (const item of element.children) walk2(item);
5977
+ };
5978
+ walk2(root);
5979
+ }
5914
5980
  /** Repair only objects emitted by older WordInWeb builds that Word rejects. */
5915
5981
  repairLegacyWordInWebObjects() {
5916
5982
  const raw = this.pkg.raw();
@@ -7992,7 +8058,7 @@ var BAKED_FONT_METRICS = {
7992
8058
  // ../core/src/layout/measure.ts
7993
8059
  function fontKey(font) {
7994
8060
  const paint = font.paintFamily && font.paintFamily !== font.family ? `${font.paintFamily}>` : "";
7995
- return `${font.bold ? "bold " : ""}${font.italic ? "italic " : ""}${font.size}px ${paint}${font.family}`;
8061
+ return `${font.bold ? "bold " : ""}${font.italic ? "italic " : ""}${font.kerning ? "kern " : ""}${font.size}px ${paint}${font.family}`;
7996
8062
  }
7997
8063
  var METRIC_SUBSTITUTES = {
7998
8064
  calibri: "Carlito",
@@ -8137,6 +8203,23 @@ function hasCambriaMath() {
8137
8203
  }
8138
8204
  return _cambriaMath;
8139
8205
  }
8206
+ var _segoeUIEmoji;
8207
+ function hasSegoeUIEmoji() {
8208
+ if (_segoeUIEmoji !== void 0) return _segoeUIEmoji;
8209
+ try {
8210
+ const canvas = typeof OffscreenCanvas !== "undefined" ? new OffscreenCanvas(1, 1) : typeof document !== "undefined" ? document.createElement("canvas") : null;
8211
+ const ctx = canvas?.getContext("2d");
8212
+ if (!ctx) return _segoeUIEmoji = false;
8213
+ ctx.font = '48px "Segoe UI Emoji", sans-serif';
8214
+ const requested = ctx.measureText("\u263A");
8215
+ ctx.font = "48px sans-serif";
8216
+ const fallback = ctx.measureText("\u263A");
8217
+ _segoeUIEmoji = Math.abs(requested.width - fallback.width) > 0.01 || Math.abs((requested.actualBoundingBoxRight ?? 0) - (fallback.actualBoundingBoxRight ?? 0)) > 0.01 || Math.abs((requested.actualBoundingBoxAscent ?? 0) - (fallback.actualBoundingBoxAscent ?? 0)) > 0.01;
8218
+ } catch {
8219
+ _segoeUIEmoji = false;
8220
+ }
8221
+ return _segoeUIEmoji;
8222
+ }
8140
8223
  function cambriaMathDescentShare() {
8141
8224
  return 0.238;
8142
8225
  }
@@ -8157,11 +8240,12 @@ var CanvasMeasurer = class {
8157
8240
  }
8158
8241
  setFont(font) {
8159
8242
  const css = cssFont(font);
8160
- if (css !== this.currentFont) {
8243
+ const key = `${css}\0${font.kerning ? "normal" : "none"}`;
8244
+ if (key !== this.currentFont) {
8161
8245
  this.ctx.font = css;
8162
- this.ctx.fontKerning = "none";
8246
+ this.ctx.fontKerning = font.kerning ? "normal" : "none";
8163
8247
  this.ctx.textRendering = "optimizeSpeed";
8164
- this.currentFont = css;
8248
+ this.currentFont = key;
8165
8249
  }
8166
8250
  }
8167
8251
  width(text, font, letterSpacing = 0) {
@@ -8265,6 +8349,8 @@ function createMeasurer() {
8265
8349
  // ../core/src/layout/math.ts
8266
8350
  var SCRIPT_SCALE = 0.73;
8267
8351
  var SCRIPT_SCRIPT_SCALE = 0.6;
8352
+ var EMPTY_SCRIPT_ADVANCE = 0.89;
8353
+ var EMPTY_SUP_ADVANCE = 0.84;
8268
8354
  var floorHalfPt = (px) => Math.floor(px * 0.75 * 2 + 1e-6) / 2 / 0.75;
8269
8355
  var scriptSize = (size, floor) => Math.max(floorHalfPt(size * SCRIPT_SCALE), floor);
8270
8356
  var SUP_RAISE = 4 / 11;
@@ -8276,6 +8362,7 @@ var RULE_THICK = 0.75 / 11;
8276
8362
  var BIN_OP_SPACE = 0.25;
8277
8363
  var FUNC_NAME_SPACE = 0.18;
8278
8364
  var COMMA_SPACE = 0.17;
8365
+ var SEMICOLON_SPACE = 0.153;
8279
8366
  var FRAC_PAD = 0.06;
8280
8367
  var NARY_SUM_RAISE = 0.5 / 11;
8281
8368
  var NARY_INT_DROP = 0.5 / 11;
@@ -8294,10 +8381,11 @@ var MAT_COL_GAP_STIX = 12.2 / 11;
8294
8381
  var MAT_COL_GAP_REAL_INLINE = 11.4 / 11;
8295
8382
  var MAT_COL_GAP_REAL_DISPLAY = 11.13 / 11;
8296
8383
  var DLM_PAD_STIX = 1.2 / 11;
8297
- var DLM_PAD_REAL = 0.8 / 11;
8384
+ var DLM_PAD_REAL = 1 / 11;
8298
8385
  var matColGap = (display) => hasCambriaMath() ? display ? MAT_COL_GAP_REAL_DISPLAY : MAT_COL_GAP_REAL_INLINE : MAT_COL_GAP_STIX;
8299
8386
  var dlmPad = () => hasCambriaMath() ? DLM_PAD_REAL : DLM_PAD_STIX;
8300
8387
  var DLM_MAT_END_PAD = 0.3 / 11;
8388
+ var DLM_TRAILING_SPACE_PAD = 0.125;
8301
8389
  var FRAC_NUM_RAISE_D = 1550 / 2048;
8302
8390
  var FRAC_DEN_DROP_D = 1370 / 2048;
8303
8391
  var FRAC_DEN_DLM_EXTRA = 133 / 2048;
@@ -8311,6 +8399,8 @@ var INT_D_INK = 1.3584 + 0.7925;
8311
8399
  var INT_D_ASC = 1.3584;
8312
8400
  var INT_D_DESC = 0.7925;
8313
8401
  var INT_D_ADV = 0.8066;
8402
+ var INT_D_LIMIT_OVERLAP = 0.37;
8403
+ var INT_D_SUP_STAGGER = 0.375;
8314
8404
  var INT_D_ANCHOR = (1.3584 - 0.877 * (INT_D_INK / INT_TEXT_INK)) / (1 - INT_D_INK / INT_TEXT_INK);
8315
8405
  var MATH_FONT = "Cambria Math";
8316
8406
  function mathText(text, normal = false) {
@@ -8388,7 +8478,7 @@ var BRACKET_RE = /[()[\]{}]/;
8388
8478
  var SUP_DLM_DROP = 460 / 2048;
8389
8479
  var ITALIC_MATH_RE = /[\u{1D434}-\u{1D467}ℎ]/u;
8390
8480
  var SUP_KERN_IN = 0;
8391
- var SCRIPT_TRAIL = 0;
8481
+ var SCRIPT_TRAIL_GROUPED_DISPLAY = 0.12;
8392
8482
  function containsDlm(nodes) {
8393
8483
  for (const n of nodes) {
8394
8484
  switch (n.t) {
@@ -8473,9 +8563,21 @@ function containsBlocky(nodes) {
8473
8563
  }
8474
8564
  return false;
8475
8565
  }
8476
- function layoutMath(nodes, baseSize, measurer, display = false) {
8566
+ function layoutMath(nodes, baseSize, measurer, display = false, fractionPadEm = FRAC_PAD, multiEquationDisplay = false) {
8477
8567
  const box = { width: 0, ascent: 0, descent: 0, pieces: [], rules: [], display, baseSize };
8478
- flow(nodes, baseSize, 0, box, measurer, false, display, 0, floorHalfPt(baseSize * SCRIPT_SCRIPT_SCALE));
8568
+ flow(
8569
+ nodes,
8570
+ baseSize,
8571
+ 0,
8572
+ box,
8573
+ measurer,
8574
+ false,
8575
+ display,
8576
+ 0,
8577
+ floorHalfPt(baseSize * SCRIPT_SCRIPT_SCALE),
8578
+ fractionPadEm,
8579
+ multiEquationDisplay ? SCRIPT_TRAIL_GROUPED_DISPLAY : 0
8580
+ );
8479
8581
  for (const p of box.pieces) {
8480
8582
  const m = measurer.metrics(p.font);
8481
8583
  p.effAscent = p.ownAscent ?? p.dy + m.ascent;
@@ -8489,12 +8591,17 @@ function layoutMath(nodes, baseSize, measurer, display = false) {
8489
8591
  }
8490
8592
  return box;
8491
8593
  }
8492
- function flow(nodes, size, dy, box, measurer, tight, display = false, breakDepth = 0, scriptFloor = 0) {
8594
+ function flow(nodes, size, dy, box, measurer, tight, display = false, breakDepth = 0, scriptFloor = 0, fractionPadEm = FRAC_PAD, scriptTrailEm = 0) {
8493
8595
  let prevOperand = false;
8494
8596
  for (const node of nodes) {
8495
8597
  switch (node.t) {
8496
8598
  case "run": {
8497
8599
  const font = fontAt(size);
8600
+ if (node.normal && /^\s+$/.test(node.text)) {
8601
+ box.pieces.push({ text: node.text, x: box.width, dy, font });
8602
+ box.width += [...node.text].length * size;
8603
+ break;
8604
+ }
8498
8605
  if (node.fname) {
8499
8606
  const gap = size * FUNC_NAME_SPACE;
8500
8607
  if (prevOperand) box.width += gap;
@@ -8506,16 +8613,19 @@ function flow(nodes, size, dy, box, measurer, tight, display = false, breakDepth
8506
8613
  }
8507
8614
  const medGap = size * BIN_OP_SPACE;
8508
8615
  const relGap = display ? medGap : size * (5 / 18);
8509
- const toks = node.text.split(/([=+−×÷<>≤≥±≠,-])/).filter((s) => s.length > 0);
8616
+ const toks = node.text.split(/([=+−×÷<>≤≥±≠,;\-])/).filter((s) => s.length > 0);
8510
8617
  for (let ti = 0; ti < toks.length; ti++) {
8511
8618
  const tok = toks[ti];
8512
- if (tok === ",") {
8619
+ if (tok === "," || tok === ";") {
8513
8620
  const text2 = mathText(tok, node.normal);
8514
8621
  box.pieces.push({ text: text2, x: box.width, dy, font });
8515
8622
  box.width += measurer.width(text2, font);
8516
8623
  const nextIsSpace = ti + 1 < toks.length && /^\s/.test(toks[ti + 1]);
8517
- if (!tight && !nextIsSpace) box.width += size * COMMA_SPACE;
8518
- prevOperand = false;
8624
+ const spacedSemicolon = tok === ";" && (ti === toks.length - 1 || !BIN_OPS.has(toks[ti + 1]));
8625
+ if (!tight && !nextIsSpace && (tok === "," || spacedSemicolon)) {
8626
+ box.width += size * (tok === "," ? COMMA_SPACE : SEMICOLON_SPACE);
8627
+ }
8628
+ prevOperand = tok === ";";
8519
8629
  continue;
8520
8630
  }
8521
8631
  const isOp = BIN_OPS.has(tok);
@@ -8539,7 +8649,7 @@ function flow(nodes, size, dy, box, measurer, tight, display = false, breakDepth
8539
8649
  case "sup":
8540
8650
  case "sub": {
8541
8651
  const beforeBase = box.pieces.length;
8542
- flow(node.base, size, dy, box, measurer, tight, display, breakDepth + 2, scriptFloor);
8652
+ flow(node.base, size, dy, box, measurer, tight, display, breakDepth + 2, scriptFloor, fractionPadEm, scriptTrailEm);
8543
8653
  let baseInkAsc = 0;
8544
8654
  if (node.t === "sup") {
8545
8655
  for (let i = beforeBase; i < box.pieces.length; i++) {
@@ -8554,9 +8664,16 @@ function flow(nodes, size, dy, box, measurer, tight, display = false, breakDepth
8554
8664
  box.width += size * SUP_KERN_IN;
8555
8665
  }
8556
8666
  const beforeScript = box.pieces.length;
8557
- flow(node.script, scriptSize(size, scriptFloor), scriptDy, box, measurer, true, false, breakDepth + 2, scriptFloor);
8667
+ const scriptFontSize = scriptSize(size, scriptFloor);
8668
+ if (node.t === "sub" && node.script.length === 0) {
8669
+ box.width += scriptFontSize * EMPTY_SCRIPT_ADVANCE;
8670
+ } else if (node.t === "sup" && node.script.length === 0) {
8671
+ box.width += scriptFontSize * EMPTY_SUP_ADVANCE;
8672
+ } else {
8673
+ flow(node.script, scriptFontSize, scriptDy, box, measurer, true, false, breakDepth + 2, scriptFloor, fractionPadEm, scriptTrailEm);
8674
+ }
8558
8675
  for (let i = beforeScript; i < box.pieces.length; i++) box.pieces[i].script = true;
8559
- box.width += size * SCRIPT_TRAIL;
8676
+ if (display) box.width += size * scriptTrailEm;
8560
8677
  prevOperand = true;
8561
8678
  break;
8562
8679
  }
@@ -8565,17 +8682,17 @@ function flow(nodes, size, dy, box, measurer, tight, display = false, breakDepth
8565
8682
  const numRaise = display ? size * FRAC_NUM_RAISE_D : size * FRAC_NUM_RAISE;
8566
8683
  const denDrop = (display ? size * FRAC_DEN_DROP_D : size * FRAC_DEN_DROP) + (display && containsDlm(node.den) ? size * FRAC_DEN_DLM_EXTRA : 0);
8567
8684
  const fracTight = display ? tight : true;
8568
- const numW = widthOf(node.num, scale, measurer, display, fracTight, scriptFloor);
8569
- const denW = widthOf(node.den, scale, measurer, display, fracTight, scriptFloor);
8570
- const pad = size * FRAC_PAD;
8685
+ const numW = widthOf(node.num, scale, measurer, display, fracTight, scriptFloor, fractionPadEm, scriptTrailEm);
8686
+ const denW = widthOf(node.den, scale, measurer, display, fracTight, scriptFloor, fractionPadEm, scriptTrailEm);
8687
+ const pad = size * fractionPadEm;
8571
8688
  const barW = Math.max(numW, denW) + 2 * pad;
8572
8689
  const x0 = box.width;
8573
8690
  const numBox = { width: 0, ascent: 0, descent: 0, pieces: [], rules: [] };
8574
- flow(node.num, scale, 0, numBox, measurer, fracTight, display, breakDepth + 2, scriptFloor);
8691
+ flow(node.num, scale, 0, numBox, measurer, fracTight, display, breakDepth + 2, scriptFloor, fractionPadEm, scriptTrailEm);
8575
8692
  for (const p of numBox.pieces) box.pieces.push(rebase(p, x0 + (barW - numW) / 2, dy + numRaise));
8576
8693
  for (const r of numBox.rules) box.rules.push({ ...r, x1: x0 + (barW - numW) / 2 + r.x1, x2: x0 + (barW - numW) / 2 + r.x2, dy: dy + numRaise + r.dy });
8577
8694
  const denBox = { width: 0, ascent: 0, descent: 0, pieces: [], rules: [] };
8578
- flow(node.den, scale, 0, denBox, measurer, fracTight, display, breakDepth + 2, scriptFloor);
8695
+ flow(node.den, scale, 0, denBox, measurer, fracTight, display, breakDepth + 2, scriptFloor, fractionPadEm, scriptTrailEm);
8579
8696
  let denDropFinal = denDrop;
8580
8697
  if (containsFrac(node.den)) {
8581
8698
  let denAsc = 0;
@@ -8600,7 +8717,7 @@ function flow(nodes, size, dy, box, measurer, tight, display = false, breakDepth
8600
8717
  case "nary": {
8601
8718
  const isInt = "\u222B\u222C\u222D\u222E\u222F\u2230".includes(node.chr);
8602
8719
  if (display && !isInt) {
8603
- naryDisplay(node, size, dy, box, measurer, scriptFloor);
8720
+ naryDisplay(node, size, dy, box, measurer, scriptFloor, fractionPadEm, scriptTrailEm);
8604
8721
  prevOperand = true;
8605
8722
  break;
8606
8723
  }
@@ -8629,20 +8746,24 @@ function flow(nodes, size, dy, box, measurer, tight, display = false, breakDepth
8629
8746
  const intSubDrop = display && visibleSub ? INT_SUB_DROP_D : INT_SUB_DROP;
8630
8747
  const supDy = dy + size * (isInt ? intSupRaise : NARY_SUP_RAISE);
8631
8748
  const subDy = dy - size * (isInt ? intSubDrop : NARY_SUB_DROP);
8632
- const supStagger = isInt ? size * INT_SUP_STAGGER : 0;
8633
- const supW = widthOf(node.sup, scale, measurer, false, true, scriptFloor) + supStagger;
8634
- const subW = widthOf(node.sub, scale, measurer, false, true, scriptFloor);
8635
- const x0 = box.width;
8749
+ const supStagger = isInt ? size * (display ? INT_D_SUP_STAGGER : INT_SUP_STAGGER) : 0;
8750
+ const supW = visibleSup ? widthOf(node.sup, scale, measurer, false, true, scriptFloor, fractionPadEm, scriptTrailEm) + supStagger : 0;
8751
+ const subW = visibleSub ? widthOf(node.sub, scale, measurer, false, true, scriptFloor, fractionPadEm, scriptTrailEm) : 0;
8752
+ const x0 = box.width - (display && isInt && (visibleSup || visibleSub) ? size * INT_D_LIMIT_OVERLAP : 0);
8636
8753
  const supBox = { width: 0, ascent: 0, descent: 0, pieces: [], rules: [] };
8637
- flow(node.sup, scale, 0, supBox, measurer, true, false, breakDepth + 2, scriptFloor);
8638
- for (const pc of supBox.pieces) box.pieces.push(rebase(pc, x0 + supStagger, supDy, true));
8639
- for (const r of supBox.rules) box.rules.push({ ...r, x1: x0 + supStagger + r.x1, x2: x0 + supStagger + r.x2, dy: supDy + r.dy });
8754
+ if (visibleSup) {
8755
+ flow(node.sup, scale, 0, supBox, measurer, true, false, breakDepth + 2, scriptFloor, fractionPadEm, scriptTrailEm);
8756
+ for (const pc of supBox.pieces) box.pieces.push(rebase(pc, x0 + supStagger, supDy, true));
8757
+ for (const r of supBox.rules) box.rules.push({ ...r, x1: x0 + supStagger + r.x1, x2: x0 + supStagger + r.x2, dy: supDy + r.dy });
8758
+ }
8640
8759
  const subBox = { width: 0, ascent: 0, descent: 0, pieces: [], rules: [] };
8641
- flow(node.sub, scale, 0, subBox, measurer, true, false, breakDepth + 2, scriptFloor);
8642
- for (const pc of subBox.pieces) box.pieces.push(rebase(pc, x0, subDy, true));
8643
- for (const r of subBox.rules) box.rules.push({ ...r, x1: x0 + r.x1, x2: x0 + r.x2, dy: subDy + r.dy });
8760
+ if (visibleSub) {
8761
+ flow(node.sub, scale, 0, subBox, measurer, true, false, breakDepth + 2, scriptFloor, fractionPadEm, scriptTrailEm);
8762
+ for (const pc of subBox.pieces) box.pieces.push(rebase(pc, x0, subDy, true));
8763
+ for (const r of subBox.rules) box.rules.push({ ...r, x1: x0 + r.x1, x2: x0 + r.x2, dy: subDy + r.dy });
8764
+ }
8644
8765
  box.width = x0 + Math.max(supW, subW) + size * NARY_E_GAP;
8645
- flow(node.e, size, dy, box, measurer, tight, display, breakDepth + 2, scriptFloor);
8766
+ flow(node.e, size, dy, box, measurer, tight, display, breakDepth + 2, scriptFloor, fractionPadEm, scriptTrailEm);
8646
8767
  prevOperand = true;
8647
8768
  break;
8648
8769
  }
@@ -8652,7 +8773,7 @@ function flow(nodes, size, dy, box, measurer, tight, display = false, breakDepth
8652
8773
  let coreDesc = 0;
8653
8774
  const parts = node.e.map((part) => {
8654
8775
  const b = { width: 0, ascent: 0, descent: 0, pieces: [], rules: [] };
8655
- flow(part, size, 0, b, measurer, tight, display, breakDepth + 1, scriptFloor);
8776
+ flow(part, size, 0, b, measurer, tight, display, breakDepth + 1, scriptFloor, fractionPadEm, scriptTrailEm);
8656
8777
  for (const pc of b.pieces) {
8657
8778
  const m = measurer.metrics(pc.font);
8658
8779
  const asc = pc.ownAscent ?? pc.dy + m.ascent;
@@ -8708,7 +8829,10 @@ function flow(nodes, size, dy, box, measurer, tight, display = false, breakDepth
8708
8829
  grow = 1;
8709
8830
  }
8710
8831
  }
8711
- const innerPad = size * (matLike ? DLM_MAT_END_PAD : dlmPad());
8832
+ const lastPart = node.e[node.e.length - 1];
8833
+ const tail = lastPart?.[lastPart.length - 1];
8834
+ const trailingSpaces = display && tail?.t === "run" && /^\s+$/.test(tail.text) ? [...tail.text].length : 0;
8835
+ const innerPad = size * (matLike ? DLM_MAT_END_PAD : dlmPad()) + size * DLM_TRAILING_SPACE_PAD * trailingSpaces;
8712
8836
  const drawDelim = (ch) => {
8713
8837
  if (!ch) return;
8714
8838
  const asm = asmH !== void 0 ? DLM_ASM_PIECES[ch] : void 0;
@@ -8778,7 +8902,7 @@ function flow(nodes, size, dy, box, measurer, tight, display = false, breakDepth
8778
8902
  const cells = node.rows.map(
8779
8903
  (row) => row.map((cell) => {
8780
8904
  const b = { width: 0, ascent: 0, descent: 0, pieces: [], rules: [] };
8781
- flow(cell, size, 0, b, measurer, tight, display, breakDepth + 2, scriptFloor);
8905
+ flow(cell, size, 0, b, measurer, tight, display, breakDepth + 2, scriptFloor, fractionPadEm, scriptTrailEm);
8782
8906
  for (const pc of b.pieces) {
8783
8907
  const m = measurer.metrics(pc.font);
8784
8908
  b.ascent = Math.max(b.ascent, pc.dy + m.ascent);
@@ -8824,7 +8948,7 @@ function flow(nodes, size, dy, box, measurer, tight, display = false, breakDepth
8824
8948
  const m = measurer.metrics(font);
8825
8949
  const ruleThick = Math.max(size * RULE_THICK, 0.75);
8826
8950
  const radBox = { width: 0, ascent: 0, descent: 0, pieces: [], rules: [] };
8827
- flow(node.e, size, 0, radBox, measurer, tight, display, breakDepth + 2, scriptFloor);
8951
+ flow(node.e, size, 0, radBox, measurer, tight, display, breakDepth + 2, scriptFloor, fractionPadEm, scriptTrailEm);
8828
8952
  let radAsc = m.ascent * 0.5;
8829
8953
  let radDesc = 0;
8830
8954
  for (const pc of radBox.pieces) {
@@ -8848,7 +8972,7 @@ function flow(nodes, size, dy, box, measurer, tight, display = false, breakDepth
8848
8972
  const dScale = scriptSize(size, scriptFloor);
8849
8973
  box.width += size * RAD_KERN_BEFORE_DEG;
8850
8974
  const degBox = { width: 0, ascent: 0, descent: 0, pieces: [], rules: [] };
8851
- flow(node.deg, dScale, 0, degBox, measurer, true, false, breakDepth + 2, scriptFloor);
8975
+ flow(node.deg, dScale, 0, degBox, measurer, true, false, breakDepth + 2, scriptFloor, fractionPadEm, scriptTrailEm);
8852
8976
  const degDy = dy - radDesc + RAD_DEG_RAISE * signInkH + size * RAD_DEG_ADJ;
8853
8977
  for (const pc of degBox.pieces) box.pieces.push(rebase(pc, box.width, degDy, true));
8854
8978
  for (const r of degBox.rules) box.rules.push({ ...r, x1: box.width + r.x1, x2: box.width + r.x2, dy: degDy + r.dy });
@@ -8895,7 +9019,7 @@ function flow(nodes, size, dy, box, measurer, tight, display = false, breakDepth
8895
9019
  const axis = dy + size * RULE_CENTER;
8896
9020
  const rowBoxes = node.rows.map((row) => {
8897
9021
  const b = { width: 0, ascent: 0, descent: 0, pieces: [], rules: [] };
8898
- flow(row, size, 0, b, measurer, tight, display, breakDepth + 2, scriptFloor);
9022
+ flow(row, size, 0, b, measurer, tight, display, breakDepth + 2, scriptFloor, fractionPadEm, scriptTrailEm);
8899
9023
  for (const pc of b.pieces) {
8900
9024
  const pm = measurer.metrics(pc.font);
8901
9025
  b.ascent = Math.max(b.ascent, pc.ownAscent ?? pc.dy + pm.ascent);
@@ -8925,7 +9049,7 @@ function flow(nodes, size, dy, box, measurer, tight, display = false, breakDepth
8925
9049
  }
8926
9050
  case "acc": {
8927
9051
  const before = box.pieces.length;
8928
- flow(node.e, size, dy, box, measurer, tight, display, breakDepth + 2, scriptFloor);
9052
+ flow(node.e, size, dy, box, measurer, tight, display, breakDepth + 2, scriptFloor, fractionPadEm, scriptTrailEm);
8929
9053
  if (box.pieces.length > before) {
8930
9054
  const last = box.pieces[box.pieces.length - 1];
8931
9055
  last.text += node.chr;
@@ -8937,7 +9061,7 @@ function flow(nodes, size, dy, box, measurer, tight, display = false, breakDepth
8937
9061
  const isTop = node.pos === "top";
8938
9062
  const bScale = isTop ? size : scriptSize(size, scriptFloor);
8939
9063
  const baseBox = { width: 0, ascent: 0, descent: 0, pieces: [], rules: [] };
8940
- flow(node.e, bScale, 0, baseBox, measurer, tight, display, breakDepth + 2, scriptFloor);
9064
+ flow(node.e, bScale, 0, baseBox, measurer, tight, display, breakDepth + 2, scriptFloor, fractionPadEm, scriptTrailEm);
8941
9065
  const baseW = baseBox.width;
8942
9066
  const maxVariant = GRP_VARIANTS_EM[GRP_VARIANTS_EM.length - 1] * size;
8943
9067
  let braceW = maxVariant;
@@ -8974,10 +9098,10 @@ function flow(nodes, size, dy, box, measurer, tight, display = false, breakDepth
8974
9098
  case "lim": {
8975
9099
  const isLow = node.pos === "low";
8976
9100
  const baseBox = { width: 0, ascent: 0, descent: 0, pieces: [], rules: [] };
8977
- flow(node.e, size, 0, baseBox, measurer, tight, display, breakDepth + 2, scriptFloor);
9101
+ flow(node.e, size, 0, baseBox, measurer, tight, display, breakDepth + 2, scriptFloor, fractionPadEm, scriptTrailEm);
8978
9102
  const lScale = scriptSize(size, scriptFloor);
8979
9103
  const limBox = { width: 0, ascent: 0, descent: 0, pieces: [], rules: [] };
8980
- flow(node.lim, lScale, 0, limBox, measurer, true, false, breakDepth + 2, scriptFloor);
9104
+ flow(node.lim, lScale, 0, limBox, measurer, true, false, breakDepth + 2, scriptFloor, fractionPadEm, scriptTrailEm);
8981
9105
  const w = Math.max(baseBox.width, limBox.width);
8982
9106
  const x0 = box.width;
8983
9107
  const baseX = x0 + (w - baseBox.width) / 2;
@@ -8994,19 +9118,19 @@ function flow(nodes, size, dy, box, measurer, tight, display = false, breakDepth
8994
9118
  }
8995
9119
  }
8996
9120
  }
8997
- function naryDisplay(node, size, dy, box, measurer, scriptFloor) {
9121
+ function naryDisplay(node, size, dy, box, measurer, scriptFloor, fractionPadEm, scriptTrailEm) {
8998
9122
  const opFont = fontAt(size * NARY_OP_SCALE_D);
8999
9123
  const opW = measurer.width(node.chr, opFont);
9000
9124
  const scale = scriptSize(size, scriptFloor);
9001
- const supW = widthOf(node.sup, scale, measurer, false, true, scriptFloor);
9002
- const subW = widthOf(node.sub, scale, measurer, false, true, scriptFloor);
9125
+ const supW = widthOf(node.sup, scale, measurer, false, true, scriptFloor, fractionPadEm, scriptTrailEm);
9126
+ const subW = widthOf(node.sub, scale, measurer, false, true, scriptFloor, fractionPadEm, scriptTrailEm);
9003
9127
  const stackW = Math.max(opW, supW, subW);
9004
9128
  const x0 = box.width;
9005
9129
  const cx = x0 + stackW / 2;
9006
9130
  box.pieces.push({ text: node.chr, x: cx - opW / 2, dy, font: opFont });
9007
9131
  if (node.sup.length) {
9008
9132
  const supBox = { width: 0, ascent: 0, descent: 0, pieces: [], rules: [] };
9009
- flow(node.sup, scale, 0, supBox, measurer, true, false, 2, scriptFloor);
9133
+ flow(node.sup, scale, 0, supBox, measurer, true, false, 2, scriptFloor, fractionPadEm, scriptTrailEm);
9010
9134
  const ox = cx - supW / 2;
9011
9135
  const oy = dy + size * NARY_OVER_RAISE_D;
9012
9136
  for (const pc of supBox.pieces) box.pieces.push(rebase(pc, ox, oy, true));
@@ -9014,18 +9138,18 @@ function naryDisplay(node, size, dy, box, measurer, scriptFloor) {
9014
9138
  }
9015
9139
  if (node.sub.length) {
9016
9140
  const subBox = { width: 0, ascent: 0, descent: 0, pieces: [], rules: [] };
9017
- flow(node.sub, scale, 0, subBox, measurer, true, false, 2, scriptFloor);
9141
+ flow(node.sub, scale, 0, subBox, measurer, true, false, 2, scriptFloor, fractionPadEm, scriptTrailEm);
9018
9142
  const ux = cx - subW / 2;
9019
9143
  const uy = dy - size * NARY_UNDER_DROP_D;
9020
9144
  for (const pc of subBox.pieces) box.pieces.push(rebase(pc, ux, uy, true));
9021
9145
  for (const r of subBox.rules) box.rules.push({ ...r, x1: ux + r.x1, x2: ux + r.x2, dy: uy + r.dy });
9022
9146
  }
9023
9147
  box.width = x0 + stackW + size * NARY_E_GAP;
9024
- flow(node.e, size, dy, box, measurer, false, true, 2, scriptFloor);
9148
+ flow(node.e, size, dy, box, measurer, false, true, 2, scriptFloor, fractionPadEm, scriptTrailEm);
9025
9149
  }
9026
- function widthOf(nodes, size, measurer, display = false, tight = true, scriptFloor = 0) {
9150
+ function widthOf(nodes, size, measurer, display = false, tight = true, scriptFloor = 0, fractionPadEm = FRAC_PAD, scriptTrailEm = 0) {
9027
9151
  const tmp = { width: 0, ascent: 0, descent: 0, pieces: [], rules: [] };
9028
- flow(nodes, size, 0, tmp, measurer, tight, display, 2, scriptFloor);
9152
+ flow(nodes, size, 0, tmp, measurer, tight, display, 2, scriptFloor, fractionPadEm, scriptTrailEm);
9029
9153
  return tmp.width;
9030
9154
  }
9031
9155
 
@@ -9053,16 +9177,18 @@ function starNumberFormat(instr, n) {
9053
9177
  }
9054
9178
  }
9055
9179
  function fontOf(props, fallbackFamily) {
9180
+ const family = props.rtl && props.fontComplex || props.font || fallbackFamily;
9056
9181
  let size = props.size ?? 14.666;
9057
9182
  if (props.verticalAlign === "superscript" || props.verticalAlign === "subscript") {
9058
- size = Math.round(size * 1.5 * 0.65) / 1.5;
9183
+ const scale = EA_FAMILY_RE.test(family) ? 0.52 : 0.65;
9184
+ size = Math.round(size * 1.5 * scale) / 1.5;
9059
9185
  }
9060
- const family = props.rtl && props.fontComplex || props.font || fallbackFamily;
9061
9186
  return {
9062
9187
  family,
9063
9188
  size,
9064
9189
  bold: props.bold ?? false,
9065
- italic: props.italic ?? false
9190
+ italic: props.italic ?? false,
9191
+ kerning: props.kerningThreshold !== void 0 && props.kerningThreshold !== null && size >= props.kerningThreshold
9066
9192
  };
9067
9193
  }
9068
9194
  function displayText(text, props) {
@@ -9077,13 +9203,21 @@ function runPlainText(run) {
9077
9203
  function isWordAlnum(ch) {
9078
9204
  return ch !== void 0 && /[\p{L}\p{Nd}]/u.test(ch);
9079
9205
  }
9080
- function hyphenBreaks(word) {
9206
+ function hyphenBreaks(word, allowNumericEnDash) {
9081
9207
  const out = [];
9208
+ let leading = 0;
9209
+ while (word[leading] === "-" || word[leading] === "\u2010") leading++;
9210
+ if (leading >= 2 && isWordAlnum(word[leading])) out.push(leading);
9082
9211
  for (let i = 1; i < word.length - 1; i++) {
9083
9212
  const ch = word[i];
9084
- if ((ch === "-" || ch === "\u2010") && isWordAlnum(word[i - 1]) && isWordAlnum(word[i + 1])) {
9085
- out.push(i + 1);
9086
- }
9213
+ if (ch !== "-" && ch !== "\u2010" && ch !== "\u2013") continue;
9214
+ let end = i + 1;
9215
+ while (word[end] === "-" || word[end] === "\u2010" || word[end] === "\u2013") end++;
9216
+ const wordBefore = isWordAlnum(word[i - 1]) || /^[\)\]\}]$/.test(word[i - 1] ?? "") && isWordAlnum(word[i - 2]);
9217
+ const wordAfter = isWordAlnum(word[end]);
9218
+ const numericEnDash = allowNumericEnDash && ch === "\u2013" && /\p{Nd}/u.test(word[i - 1] ?? "") && /\p{Nd}/u.test(word[end] ?? "");
9219
+ if (ch === "\u2013" ? numericEnDash : wordBefore && wordAfter) out.push(end);
9220
+ i = end - 1;
9087
9221
  }
9088
9222
  return out;
9089
9223
  }
@@ -9098,6 +9232,7 @@ function isCJK(ch) {
9098
9232
  return CJK_RE.test(ch);
9099
9233
  }
9100
9234
  var BALLOT_RE = /[☐☑☒]/;
9235
+ var LEGACY_CHECKBOX_TRAILING_ADVANCE = 2.5 * 96 / 72;
9101
9236
  function isBallot(ch) {
9102
9237
  return BALLOT_RE.test(ch);
9103
9238
  }
@@ -9183,17 +9318,35 @@ function wrapDisplayMath(box, maxWidth) {
9183
9318
  });
9184
9319
  }
9185
9320
  var DEFAULT_TAB = 48;
9321
+ var TABBED_INLINE_MATH_FRAC_PAD = 0.15;
9186
9322
  var OVERFLOW_PUNCT = /[.,:;!?)\]}、。,.:;?!)]}」』】〉》]/;
9187
- var CJK_SPACE_COMPRESS = 0.47;
9323
+ var CJK_SPACE_COMPRESS = 0.5;
9188
9324
  var EA_FAMILY_RE = /simsun|nsimsun|宋体|新宋体|songti|simhei|黑体|heiti|kaiti|楷体|fangsong|仿宋|yahei|雅黑|mingliu|细明|新細明|pmingliu|jhenghei|正黑|pingfang|dengxian|等线|ms (?:p?)(?:mincho|gothic)|mincho|meiryo|yu gothic|yu mincho|batang|gulim|dotum|malgun/i;
9189
- var JUSTIFY_MAX_COMPRESS = 0.25;
9325
+ var JUSTIFY_MAX_COMPRESS = 0.29;
9326
+ var JUSTIFY_GRID_MAX_COMPRESS = 0.25;
9190
9327
  var JUSTIFY_STRETCH_FACTOR = 0.5;
9191
9328
  var TAMIL_GLYPH_SCALE = 0.735;
9192
9329
  var TAMIL_BASELINE_DY_EM = 0.136;
9193
9330
  var LAO_GLYPH_SCALE = 1.048;
9331
+ function emojiFallbackScale(text, family) {
9332
+ if (family.toLowerCase() !== "segoe ui emoji" || hasSegoeUIEmoji()) return 1;
9333
+ const codepoints = Array.from(text);
9334
+ if (codepoints.some((ch) => {
9335
+ const cp = ch.codePointAt(0) ?? 0;
9336
+ return cp >= 127462 && cp <= 127487;
9337
+ })) return 1;
9338
+ const people = codepoints.filter((ch) => {
9339
+ const cp = ch.codePointAt(0) ?? 0;
9340
+ return cp >= 128102 && cp <= 128105;
9341
+ }).length;
9342
+ if (text.includes("\u200D") && people >= 3) return 1.84;
9343
+ if (/🚀|❤|✅/.test(text)) return 1.39;
9344
+ if (text.includes("\u200D") || /[🏻-🏿]/u.test(text)) return 1.29;
9345
+ return 1;
9346
+ }
9194
9347
  var KASHIDA_LOW_EM = 0;
9195
9348
  var KASHIDA_MEDIUM_EM = 0.032;
9196
- var KASHIDA_HIGH_EM = 0.04;
9349
+ var KASHIDA_HIGH_EM = 0.075;
9197
9350
  var BP_CACHES = /* @__PURE__ */ new WeakMap();
9198
9351
  var BP_CACHE_MAX = 6e4;
9199
9352
  function bpCacheFor(measurer) {
@@ -9337,6 +9490,7 @@ function breakParagraphImpl(doc, measurer, para, contentWidth, fields, numbering
9337
9490
  let curSegments = [{ x: 0, width: contentWidth }];
9338
9491
  let curSegIdx = 0;
9339
9492
  let curClearY;
9493
+ let curEdgeReserve = 0;
9340
9494
  const ls = props.lineSpacing;
9341
9495
  const firstFont = atoms.find((a) => a.kind === "frag" || a.kind === "space")?.font;
9342
9496
  const natural = firstFont ? measurer.metrics(firstFont).lineHeight * (ls?.rule === "auto" ? ls.value : 1) : 0;
@@ -9348,6 +9502,7 @@ function breakParagraphImpl(doc, measurer, para, contentWidth, fields, numbering
9348
9502
  curSegments = [{ x: 0, width: contentWidth }];
9349
9503
  curSegIdx = 0;
9350
9504
  curClearY = void 0;
9505
+ curEdgeReserve = 0;
9351
9506
  if (boundsAt) {
9352
9507
  let guard = 0;
9353
9508
  let b = boundsAt(yOff, EST_LINE);
@@ -9361,6 +9516,7 @@ function breakParagraphImpl(doc, measurer, para, contentWidth, fields, numbering
9361
9516
  curSegments = b.segments && b.segments.length > 0 ? b.segments : [{ x: b.x, width: b.width }];
9362
9517
  curSegIdx = 0;
9363
9518
  curClearY = b.clearY;
9519
+ curEdgeReserve = b.edgeReserve ?? (b.clearY !== void 0 ? 0.75 : 0);
9364
9520
  }
9365
9521
  void idx;
9366
9522
  };
@@ -9636,7 +9792,10 @@ function breakParagraphImpl(doc, measurer, para, contentWidth, fields, numbering
9636
9792
  rawStop = nextTabStop(rawStop.pos, props.tabs, contentWidth - indentRight, doc.defaultTabStop);
9637
9793
  }
9638
9794
  }
9639
- if (hanging > 0 && x + offset < indentLeft - 0.5 && rawStop.pos > indentLeft) {
9795
+ const explicitBeforeIndent = (props.tabs ?? []).some(
9796
+ (tab) => !tab.clear && tab.align !== "bar" && tab.pos > x + offset + 0.5 && tab.pos < indentLeft - 0.5
9797
+ );
9798
+ if (hanging > 0 && x + offset < indentLeft - 0.5 && !explicitBeforeIndent) {
9640
9799
  rawStop = { pos: indentLeft, align: "left" };
9641
9800
  }
9642
9801
  const stop = { ...rawStop, pos: rawStop.pos - offset };
@@ -9702,8 +9861,9 @@ function breakParagraphImpl(doc, measurer, para, contentWidth, fields, numbering
9702
9861
  }
9703
9862
  const lineEnd2 = lineStartX(lineIndex) + availFor(lineIndex);
9704
9863
  if (firstWordWidth > 0 && probeX + firstWordWidth > lineEnd2 + 0.01) {
9864
+ const legacyContinuationIndent = !!atom.props.bold && nextAtom === ai + 3;
9705
9865
  flush(false, false);
9706
- ai--;
9866
+ if (!legacyContinuationIndent) ai--;
9707
9867
  continue;
9708
9868
  }
9709
9869
  }
@@ -9779,9 +9939,11 @@ function breakParagraphImpl(doc, measurer, para, contentWidth, fields, numbering
9779
9939
  const rows = wrapDisplayMath(atom.box, availFor(lineIndex));
9780
9940
  for (let ri = 0; ri < rows.length; ri++) {
9781
9941
  const row = rows[ri];
9782
- cur.push({ x, width: row.width, math: row, mathSrc: atom.src, props: {}, font: fontOf({}, fallbackFamily) });
9783
- curLineWidth += row.width;
9784
- x += row.width;
9942
+ const tabReserve = !row.display && ri === rows.length - 1 && atoms[ai + 1]?.kind === "tab" ? row.width * 0.01 : 0;
9943
+ const advance = row.width + tabReserve;
9944
+ cur.push({ x, width: advance, math: row, mathSrc: atom.src, props: {}, font: fontOf({}, fallbackFamily) });
9945
+ curLineWidth += advance;
9946
+ x += advance;
9785
9947
  if (ri + 1 < rows.length) flush(false, false);
9786
9948
  }
9787
9949
  continue;
@@ -9790,7 +9952,7 @@ function breakParagraphImpl(doc, measurer, para, contentWidth, fields, numbering
9790
9952
  const w = atom.kind === "image" ? atom.width : atom.drawing.width;
9791
9953
  const h = atom.kind === "image" ? atom.height : atom.drawing.height;
9792
9954
  if (curLineWidth > 0 && x + w > lineStartX(lineIndex) + availFor(lineIndex)) {
9793
- const glued = atom.kind === "image" && !atom.srcDrawing;
9955
+ const glued = atom.kind === "image" && (!atom.srcDrawing || localName(atom.srcDrawing.name) !== "drawing");
9794
9956
  let hi = cur.length;
9795
9957
  let headW = 0;
9796
9958
  while (glued && hi > minSpans) {
@@ -9839,11 +10001,12 @@ function breakParagraphImpl(doc, measurer, para, contentWidth, fields, numbering
9839
10001
  if (opts?.verticalGridResync && fragIsCJK && prevContentCJK === false && curLineWidth > 0 && curClearY === void 0) {
9840
10002
  flush(false, false);
9841
10003
  }
9842
- const lineEnd = lineStartX(lineIndex) + availFor(lineIndex);
10004
+ const lineEnd = lineStartX(lineIndex) + availFor(lineIndex) - curEdgeReserve;
9843
10005
  const bdrPad = bdrPadOf(atom);
9844
10006
  const bdrPadL = bdrPad && (!sameBdrAtom(atoms[ai - 1], atom) || curLineWidth === 0) ? bdrPad : 0;
9845
10007
  const bdrPadR = bdrPad && !sameBdrAtom(atom, atoms[ai + 1]) ? bdrPad : 0;
9846
- let fits = x + bdrPadL + atom.width + bdrPadR <= lineEnd + 0.01;
10008
+ const fitTolerance = minLineHeight !== void 0 ? 0.125 : 0.01;
10009
+ let fits = x + bdrPadL + atom.width + bdrPadR <= lineEnd + fitTolerance;
9847
10010
  if (!fits && packUntilSpace) fits = true;
9848
10011
  if (!fits && cur.length > 0 && cur[cur.length - 1].text === " ") {
9849
10012
  if (bidiPara && cur[cur.length - 1].x <= lineStartX(lineIndex) + 0.5) {
@@ -9853,14 +10016,15 @@ function breakParagraphImpl(doc, measurer, para, contentWidth, fields, numbering
9853
10016
  fits = true;
9854
10017
  packUntilSpace = true;
9855
10018
  }
9856
- if (!fits && minLineHeight) {
10019
+ let overflowPunctWidth = 0;
10020
+ if (minLineHeight) {
9857
10021
  const last = atom.text[atom.text.length - 1];
9858
10022
  if (last && OVERFLOW_PUNCT.test(last)) {
9859
10023
  const nxt = atoms[ai + 1];
9860
10024
  const wordEnd = !nxt || nxt.kind !== "frag" || /^\s/.test(nxt.text);
9861
10025
  if (wordEnd) {
9862
- const pw = measurer.width(last, atom.font, atom.props.letterSpacing);
9863
- if (x + atom.width - pw <= lineEnd + 0.01) fits = true;
10026
+ overflowPunctWidth = measurer.width(last, atom.font, atom.props.letterSpacing);
10027
+ if (!fits && x + atom.width - overflowPunctWidth <= lineEnd + 0.01) fits = true;
9864
10028
  }
9865
10029
  }
9866
10030
  }
@@ -9889,8 +10053,14 @@ function breakParagraphImpl(doc, measurer, para, contentWidth, fields, numbering
9889
10053
  }
9890
10054
  }
9891
10055
  const wordW = headW + bdrPadL + atom.width + bdrPadR + tailW;
9892
- if (!fits && minLineHeight !== void 0 && curEaSpaceWidth > 0) {
9893
- const compress = (x - headW + wordW - lineEnd) / curEaSpaceWidth;
10056
+ const previousInk = [...cur].reverse().find((span) => span.text?.trim());
10057
+ const sentenceBoundary = /[.!?][\)\]}'"]?$/.test(previousInk?.text ?? "");
10058
+ const clauseBoundaryTail = /[,;:]\s+(?:and|or|but)\s+the\s*$/i.test(
10059
+ cur.map((span) => span.text ?? "").join("")
10060
+ );
10061
+ if (!fits && minLineHeight !== void 0 && curEaSpaceWidth > 0 && !curPacked && !sentenceBoundary && !clauseBoundaryTail) {
10062
+ const packedPunctWidth = props.lineSpacing?.rule === "auto" ? overflowPunctWidth : 0;
10063
+ const compress = (x - headW + wordW - packedPunctWidth - lineEnd) / curEaSpaceWidth;
9894
10064
  if (compress <= CJK_SPACE_COMPRESS + 1e-6) {
9895
10065
  fits = true;
9896
10066
  packUntilSpace = true;
@@ -9902,8 +10072,10 @@ function breakParagraphImpl(doc, measurer, para, contentWidth, fields, numbering
9902
10072
  // (compat 12, docGrid lines) p2 draws the justified "...zebeqo:" line
9903
10073
  // with every inter-word space at 4.25pt against SimSun's natural
9904
10074
  // 5.25pt (19% compression) to avoid the wrap Word 15+ would also
9905
- // avoid. Scoped to grid sections rather than all legacy docs.
9906
- (doc.compatibilityMode >= 15 || minLineHeight !== void 0) && !bidiPara && props.alignment === "justify" && curSpaceWidth > 0
10075
+ // avoid. In legacy modes that extension is limited to lines whose
10076
+ // compressible spaces use the East Asian face; ordinary Times New
10077
+ // Roman bibliography lines retain Word 2007's earlier wrap.
10078
+ (doc.compatibilityMode >= 15 || minLineHeight !== void 0 && curEaSpaceWidth > 0) && !bidiPara && props.alignment === "justify" && curSpaceWidth > 0
9907
10079
  ) {
9908
10080
  let trail = 0;
9909
10081
  for (let j = hi - 1; j >= 0 && cur[j].isSpace; j--) trail += cur[j].width;
@@ -9912,7 +10084,8 @@ function breakParagraphImpl(doc, measurer, para, contentWidth, fields, numbering
9912
10084
  const compress = (x - headW + wordW - lineEnd) / compressible;
9913
10085
  if (spacesAfterBreak > 1e-6 && compressible > 1e-6) {
9914
10086
  const stretch = (lineEnd - (x - headW - trail)) / spacesAfterBreak;
9915
- if (compress <= Math.min(JUSTIFY_MAX_COMPRESS, stretch * JUSTIFY_STRETCH_FACTOR)) {
10087
+ const maxCompress = minLineHeight === void 0 && props.numbering ? JUSTIFY_MAX_COMPRESS : JUSTIFY_GRID_MAX_COMPRESS;
10088
+ if (compress <= Math.min(maxCompress, stretch * JUSTIFY_STRETCH_FACTOR)) {
9916
10089
  fits = true;
9917
10090
  packUntilSpace = true;
9918
10091
  }
@@ -9923,7 +10096,8 @@ function breakParagraphImpl(doc, measurer, para, contentWidth, fields, numbering
9923
10096
  hardWrapFrag(atom);
9924
10097
  continue;
9925
10098
  }
9926
- const head = hi > minSpans && hi < cur.length && cur[hi - 1].isSpace && !cur[hi - 1].noBreak ? cur.splice(hi) : [];
10099
+ const breakBeforeHead = hi > minSpans && hi < cur.length && (cur[hi - 1].isSpace && !cur[hi - 1].noBreak || cur[hi - 1].breakAfter);
10100
+ const head = breakBeforeHead ? cur.splice(hi) : [];
9927
10101
  for (const h of head) curLineWidth -= h.width;
9928
10102
  let moved = false;
9929
10103
  while (advanceSegment()) {
@@ -9975,6 +10149,9 @@ function breakParagraphImpl(doc, measurer, para, contentWidth, fields, numbering
9975
10149
  cur.push({ x, width: atom.width, text: atom.text, props: atom.props, font: atom.font, href: atom.href, src: atom.src, noteId: atom.noteId, metricsFont: atom.metricsFont, metricsStrut: atom.metricsStrut, breakAfter: atom.breakAfter, pageRef: atom.pageRef, rtl: atom.rtl, rtlLevel: levelOf(atom.rtl), ruby: atom.ruby });
9976
10150
  curLineWidth += padL + atom.width + bdrPadR;
9977
10151
  x += atom.width + bdrPadR;
10152
+ if (packUntilSpace && atom.breakAfter && /[-‐]$/.test(atom.text)) {
10153
+ packUntilSpace = false;
10154
+ }
9978
10155
  if (atom.text.trim().length > 0) prevContentCJK = fragIsCJK;
9979
10156
  }
9980
10157
  if (!flushedTrailingBreak) flush(true, false);
@@ -10033,7 +10210,7 @@ function nextTabStop(x, tabs, rightEdge, defaultTab = DEFAULT_TAB) {
10033
10210
  }
10034
10211
  }
10035
10212
  const next = nextDefaultTab(x, defaultTab);
10036
- return { pos: next < rightEdge ? next : x + 10.7, align: "left" };
10213
+ return { pos: next < rightEdge ? next : x + 20.4, align: "left" };
10037
10214
  }
10038
10215
  function reorderVisual(spans, startX) {
10039
10216
  if (spans.length === 0) return;
@@ -10173,6 +10350,7 @@ function finishLine(spans, width, props, measurer, fallbackFamily, para, doc, is
10173
10350
  (s) => s.metricsStrut || s.image || s.drawing || s.math || s.leader || s.text === void 0 || !/^[ \t]*$/.test(s.text)
10174
10351
  );
10175
10352
  if (solidSpans.length > 0) metricSpans = solidSpans;
10353
+ const styledLoweredIntegral = para.props.styleId !== void 0 && metricSpans.length > 1 && metricSpans.some((s) => s.text?.includes("\u222B") && (s.props.raise ?? 0) < 0);
10176
10354
  if (metricSpans.some((s) => s.numLabel) && metricSpans.some((s) => !s.numLabel)) {
10177
10355
  let textLine = 0;
10178
10356
  for (const s of metricSpans) {
@@ -10315,8 +10493,11 @@ function finishLine(spans, width, props, measurer, fallbackFamily, para, doc, is
10315
10493
  const lead = ls.value >= 1.15 ? (ls.value - 1) * maxNaturalText : 0;
10316
10494
  height = Math.max(maxNaturalText * ls.value, maxInlineMath + lead);
10317
10495
  }
10318
- } else if (ls.rule === "exact") height = ls.value;
10319
- else height = Math.max(natural, ls.value);
10496
+ } else if (ls.rule === "exact") {
10497
+ height = ls.value;
10498
+ const lowerBand = Math.max(maxDescent, height * 0.2);
10499
+ baselineH = height - lowerBand + maxDescent;
10500
+ } else height = Math.max(natural, ls.value);
10320
10501
  }
10321
10502
  if (!gridObjSnap && (minLineHeight ?? 0) > 0 && ls?.rule === "atLeast" && baselineH === void 0) {
10322
10503
  const glyph = maxAscent + maxDescent;
@@ -10332,6 +10513,11 @@ function finishLine(spans, width, props, measurer, fallbackFamily, para, doc, is
10332
10513
  maxDescent -= descentReuse;
10333
10514
  maxRawDescent -= descentReuse;
10334
10515
  }
10516
+ if (styledLoweredIntegral && ls?.rule !== "exact" && !gridObjSnap) {
10517
+ const overlap = Math.min(6, height);
10518
+ height -= overlap;
10519
+ baselineH = (baselineH ?? height + overlap) - overlap;
10520
+ }
10335
10521
  return {
10336
10522
  spans,
10337
10523
  width,
@@ -10450,7 +10636,13 @@ function buildAtoms(doc, para, measurer, fields, fallbackFamily, gridPitch) {
10450
10636
  }
10451
10637
  if (baseProps.vanish) return;
10452
10638
  const font = fontOf(baseProps, fallbackFamily);
10453
- let props = kashidaEm > 0 && baseProps.rtl ? { ...baseProps, letterSpacing: (baseProps.letterSpacing ?? 0) + kashidaEm * font.size } : baseProps;
10639
+ let props = kashidaEm > 0 && baseProps.rtl ? {
10640
+ ...baseProps,
10641
+ letterSpacing: (baseProps.letterSpacing ?? 0) + kashidaEm * font.size,
10642
+ // Word adds kashidas at eligible Arabic joins. Uniform CSS tracking
10643
+ // needs less paint spacing while retaining the same line advances.
10644
+ paintLetterSpacing: (baseProps.letterSpacing ?? 0) + Math.min(kashidaEm, 0.04) * font.size
10645
+ } : baseProps;
10454
10646
  if (props.fitText !== void 0 && props.fitText > 0) {
10455
10647
  const natural = measurer.width(displayText(runPlainText(run), props), font, props.letterSpacing) * (props.textScale ?? 1);
10456
10648
  if (natural > 0.1) {
@@ -10531,7 +10723,16 @@ function buildAtoms(doc, para, measurer, fields, fallbackFamily, gridPitch) {
10531
10723
  });
10532
10724
  break;
10533
10725
  }
10534
- if (text) pushStyled(displayText(text, props), props, font, href, { run, t: null, offset: 0 }, vertMetricsFont);
10726
+ if (text) {
10727
+ const firstAtom = atoms.length;
10728
+ pushStyled(displayText(text, props), props, font, href, { run, t: null, offset: 0 }, vertMetricsFont);
10729
+ if (content.checkbox) {
10730
+ const last = atoms[atoms.length - 1];
10731
+ if (atoms.length > firstAtom && last.kind === "frag") {
10732
+ last.width += LEGACY_CHECKBOX_TRAILING_ADVANCE;
10733
+ }
10734
+ }
10735
+ }
10535
10736
  break;
10536
10737
  }
10537
10738
  case "ptab":
@@ -10569,7 +10770,15 @@ function buildAtoms(doc, para, measurer, fields, fallbackFamily, gridPitch) {
10569
10770
  break;
10570
10771
  case "math": {
10571
10772
  const size = props.size ?? 14.666;
10572
- const box = layoutMath(content.nodes, size, measurer, content.display);
10773
+ const tabbedInline = !content.display && atoms[atoms.length - 1]?.kind === "tab" && (para.props.indentFirstLine ?? 0) > 0;
10774
+ const box = layoutMath(
10775
+ content.nodes,
10776
+ size,
10777
+ measurer,
10778
+ content.display,
10779
+ tabbedInline ? TABBED_INLINE_MATH_FRAC_PAD : void 0,
10780
+ content.multiEquationDisplay
10781
+ );
10573
10782
  if (content.display) box.jc = content.jc;
10574
10783
  atoms.push({ kind: "math", box, src: content.src });
10575
10784
  break;
@@ -10626,6 +10835,10 @@ function buildAtoms(doc, para, measurer, fields, fallbackFamily, gridPitch) {
10626
10835
  }
10627
10836
  };
10628
10837
  const pushStyled = (text, props, font, href, srcBase, metricsFont) => {
10838
+ const emojiScale = emojiFallbackScale(text, font.family);
10839
+ if (emojiScale !== 1) {
10840
+ props = { ...props, textScale: (props.textScale ?? 1) * emojiScale };
10841
+ }
10629
10842
  const hasDeva = /[ऀ-ॿ]/.test(text);
10630
10843
  const hasTamil = /[஀-௿]/.test(text);
10631
10844
  const hasLao = /[຀-໿]/.test(text);
@@ -10643,15 +10856,16 @@ function buildAtoms(doc, para, measurer, fields, fallbackFamily, gridPitch) {
10643
10856
  paintDY: font.size * TAMIL_BASELINE_DY_EM
10644
10857
  };
10645
10858
  const indicMetrics = indicScale !== 1 ? { ...metricsFont ?? font, family: indicFace, size: font.size } : metricsFont ? { ...metricsFont, family: indicFace } : metricsFont;
10859
+ const isIndic = indicFace === "Mangal" ? (ch) => /[ऀ-ॿ]/.test(ch) : indicFace === "Latha" ? (ch) => /[஀-௿]/.test(ch) : (ch) => /[຀-໿]/.test(ch);
10646
10860
  let i2 = 0;
10647
10861
  while (i2 < text.length) {
10648
- const isSpace = text[i2] === " ";
10862
+ const indic = isIndic(text[i2]);
10649
10863
  let j = i2 + 1;
10650
- while (j < text.length && text[j] === " " === isSpace) j++;
10864
+ while (j < text.length && isIndic(text[j]) === indic) j++;
10651
10865
  const seg = text.slice(i2, j);
10652
10866
  const src = srcBase ? { ...srcBase, offset: srcBase.offset + i2 } : void 0;
10653
- if (isSpace) pushText(seg, props, font, href, src, metricsFont);
10654
- else pushText(seg, scriptProps, indicFont, href, src, indicMetrics);
10867
+ if (indic) pushText(seg, scriptProps, indicFont, href, src, indicMetrics);
10868
+ else pushText(seg, props, font, href, src, metricsFont);
10655
10869
  i2 = j;
10656
10870
  }
10657
10871
  return;
@@ -10722,6 +10936,9 @@ function buildAtoms(doc, para, measurer, fields, fallbackFamily, gridPitch) {
10722
10936
  if (part[0] === " ") {
10723
10937
  let w = partWidth / part.length;
10724
10938
  let spaceFont = font;
10939
+ if (para.props.styleId === "HTML0" && EA_FAMILY_RE.test(font.family)) {
10940
+ w = font.size / 2 * tScale;
10941
+ }
10725
10942
  if (gridPitch && part.length >= 2 && props.fontEastAsia && !EA_FAMILY_RE.test(font.family)) {
10726
10943
  spaceFont = { ...font, family: props.fontEastAsia };
10727
10944
  w = measurer.width(" ", spaceFont, props.letterSpacing) * tScale;
@@ -10738,7 +10955,16 @@ function buildAtoms(doc, para, measurer, fields, fallbackFamily, gridPitch) {
10738
10955
  });
10739
10956
  }
10740
10957
  } else {
10741
- const hyBreaks = hyphenBreaks(part);
10958
+ const hyBreaks = hyphenBreaks(part, doc.compatibilityMode >= 15);
10959
+ if ((part[0] === "-" || part[0] === "\u2010") && isWordAlnum(part[1])) {
10960
+ const prev = atoms[atoms.length - 1];
10961
+ const beforePrev = atoms[atoms.length - 2];
10962
+ const prevText = prev?.kind === "frag" ? prev.text : "";
10963
+ const beforeText = beforePrev?.kind === "frag" ? beforePrev.text : "";
10964
+ const prevChar = prevText[prevText.length - 1];
10965
+ const runBoundaryWordBefore = isWordAlnum(prevChar) || /^[\)\]\}]$/.test(prevChar ?? "") && isWordAlnum(beforeText[beforeText.length - 1]);
10966
+ if (runBoundaryWordBefore) hyBreaks.unshift(1);
10967
+ }
10742
10968
  let breaks = hyBreaks;
10743
10969
  if (charRtl) {
10744
10970
  const set = new Set(hyBreaks);
@@ -10843,6 +11069,15 @@ function buildAtoms(doc, para, measurer, fields, fallbackFamily, gridPitch) {
10843
11069
  if (childEl.type === "run") pushRun(childEl);
10844
11070
  else for (const r of childEl.runs) pushRun(r, childEl.href ?? (childEl.anchor ? "#" + childEl.anchor : void 0));
10845
11071
  }
11072
+ if (gridPitch) {
11073
+ for (let i = 0; i + 1 < atoms.length; i++) {
11074
+ const punct = atoms[i];
11075
+ const next = atoms[i + 1];
11076
+ if (punct.kind === "frag" && /^[,。]$/.test(punct.text) && EA_FAMILY_RE.test(punct.font.family) && next.kind === "frag" && /^[\x00-\x7f]/.test(next.text)) {
11077
+ punct.width = punct.font.size * 0.625 * (punct.props.textScale ?? 1);
11078
+ }
11079
+ }
11080
+ }
10846
11081
  for (let i = atoms.length - 1, nextNbsp = false; i >= 0; i--) {
10847
11082
  const a = atoms[i];
10848
11083
  if (a.kind === "frag") nextNbsp = a.text.charCodeAt(0) === 160;
@@ -10917,7 +11152,7 @@ function resolveField(instruction, cachedResult, ctx, fieldKey) {
10917
11152
  var DP_MONTHS = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
10918
11153
  var DP_DAYS = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
10919
11154
  function formatDatePicture(d, picture) {
10920
- const h24 = d.getHours();
11155
+ const h24 = d.getUTCHours();
10921
11156
  const h12 = h24 % 12 === 0 ? 12 : h24 % 12;
10922
11157
  const pad = (n) => String(n).padStart(2, "0");
10923
11158
  const hasAmPm = /am\/pm/i.test(picture);
@@ -10927,25 +11162,25 @@ function formatDatePicture(d, picture) {
10927
11162
  if (tok.startsWith("'")) return tok.slice(1, -1);
10928
11163
  switch (tok) {
10929
11164
  case "yyyy":
10930
- return String(d.getFullYear());
11165
+ return String(d.getUTCFullYear());
10931
11166
  case "yy":
10932
- return pad(d.getFullYear() % 100);
11167
+ return pad(d.getUTCFullYear() % 100);
10933
11168
  case "MMMM":
10934
- return DP_MONTHS[d.getMonth()];
11169
+ return DP_MONTHS[d.getUTCMonth()];
10935
11170
  case "MMM":
10936
- return DP_MONTHS[d.getMonth()].slice(0, 3);
11171
+ return DP_MONTHS[d.getUTCMonth()].slice(0, 3);
10937
11172
  case "MM":
10938
- return pad(d.getMonth() + 1);
11173
+ return pad(d.getUTCMonth() + 1);
10939
11174
  case "M":
10940
- return String(d.getMonth() + 1);
11175
+ return String(d.getUTCMonth() + 1);
10941
11176
  case "dddd":
10942
- return DP_DAYS[d.getDay()];
11177
+ return DP_DAYS[d.getUTCDay()];
10943
11178
  case "ddd":
10944
- return DP_DAYS[d.getDay()].slice(0, 3);
11179
+ return DP_DAYS[d.getUTCDay()].slice(0, 3);
10945
11180
  case "dd":
10946
- return pad(d.getDate());
11181
+ return pad(d.getUTCDate());
10947
11182
  case "d":
10948
- return String(d.getDate());
11183
+ return String(d.getUTCDate());
10949
11184
  case "HH":
10950
11185
  return pad(h24);
10951
11186
  case "H":
@@ -10955,13 +11190,13 @@ function formatDatePicture(d, picture) {
10955
11190
  case "h":
10956
11191
  return String(hasAmPm ? h12 : h24);
10957
11192
  case "mm":
10958
- return pad(d.getMinutes());
11193
+ return pad(d.getUTCMinutes());
10959
11194
  case "m":
10960
- return String(d.getMinutes());
11195
+ return String(d.getUTCMinutes());
10961
11196
  case "ss":
10962
- return pad(d.getSeconds());
11197
+ return pad(d.getUTCSeconds());
10963
11198
  case "s":
10964
- return String(d.getSeconds());
11199
+ return String(d.getUTCSeconds());
10965
11200
  default:
10966
11201
  return /^am\/pm$/i.test(tok) ? h24 < 12 ? "AM" : "PM" : tok;
10967
11202
  }
@@ -12126,8 +12361,12 @@ var Engine = class {
12126
12361
  displayNumber,
12127
12362
  headerHeight: 0,
12128
12363
  footerHeight: 0,
12129
- bodyTop: sp.marginTop,
12130
- bandTop: sp.marginTop,
12364
+ // Legacy Word keeps a 3pt printable-edge inset when a signed negative
12365
+ // top margin fixes body flow independently of the header. California
12366
+ // pleading paper exposes this exactly: the 24pt body grid begins 3pt
12367
+ // below the raw -66.25pt margin while the header stays at headerDist.
12368
+ bodyTop: sp.marginTop < 0 ? sp.marginTop + ptToPx(3) : sp.marginTop,
12369
+ bandTop: sp.marginTop < 0 ? sp.marginTop + ptToPx(3) : sp.marginTop,
12131
12370
  softTop: !sectionStart,
12132
12371
  bodyBottom: sp.pageHeight - Math.abs(sp.marginBottom),
12133
12372
  colXs,
@@ -12846,7 +13085,8 @@ var Engine = class {
12846
13085
  const notes = page.footnotes.filter((note) => note.column === column);
12847
13086
  if (notes.length === 0) continue;
12848
13087
  const x0 = page.colXs[column];
12849
- let y = page.bodyBottom - (page.footnoteH[column] ?? 0) - NOTE_SEP_H;
13088
+ const separatorHeight = this.doc.footnoteSeparator.length > 0 ? this.layoutFrame(this.doc.footnoteSeparator, page.colWidths[column], this.fieldCtx()).height : NOTE_SEP_H;
13089
+ let y = page.bodyBottom - (page.footnoteH[column] ?? 0) - separatorHeight;
12850
13090
  page.items.push({
12851
13091
  kind: "edge",
12852
13092
  x1: x0,
@@ -12855,7 +13095,7 @@ var Engine = class {
12855
13095
  y2: y + NOTE_SEP_H * 0.6,
12856
13096
  border: { style: "single", width: 0.75, color: "#000000", space: 0 }
12857
13097
  });
12858
- y += NOTE_SEP_H;
13098
+ y += separatorHeight;
12859
13099
  for (const note of notes) {
12860
13100
  for (const it of note.items) {
12861
13101
  offsetItem(it, x0, y);
@@ -13098,10 +13338,20 @@ var Engine = class {
13098
13338
  return { x: 0, width: colW };
13099
13339
  }
13100
13340
  let clearY;
13341
+ let exactTextEdge = false;
13101
13342
  for (const f of floats) {
13102
- if (f.mode === "square" && overlaps(f)) clearY = Math.max(clearY ?? 0, f.y1 - paraTop + 2);
13343
+ if (f.mode === "square" && overlaps(f)) {
13344
+ clearY = Math.max(clearY ?? 0, f.y1 - paraTop + 2);
13345
+ exactTextEdge || (exactTextEdge = f.exactTextEdge === true);
13346
+ }
13103
13347
  }
13104
- return { x: segs[0].x, width: segs[0].width, segments: segs, clearY };
13348
+ return {
13349
+ x: segs[0].x,
13350
+ width: segs[0].width,
13351
+ segments: segs,
13352
+ clearY,
13353
+ edgeReserve: exactTextEdge ? 0 : void 0
13354
+ };
13105
13355
  };
13106
13356
  }
13107
13357
  /**
@@ -13317,7 +13567,8 @@ var Engine = class {
13317
13567
  x1: this.colX + dropLine.width + props.dropCap.hSpace,
13318
13568
  y0: this.y,
13319
13569
  y1: this.y + dropLine.naturalHeight,
13320
- mode: "square"
13570
+ mode: "square",
13571
+ exactTextEdge: true
13321
13572
  });
13322
13573
  this.floats.set(this.cur, list);
13323
13574
  }
@@ -13412,7 +13663,7 @@ var Engine = class {
13412
13663
  }
13413
13664
  const rawSpacingBefore = dropSpaceBefore ? 0 : props.spacingBefore ?? 0;
13414
13665
  let paraTopEstimate = this.y + rawSpacingBefore;
13415
- emitParaAnchors(paraTopEstimate);
13666
+ emitParaAnchors(this.y);
13416
13667
  let broken = breakNow(paraTopEstimate);
13417
13668
  const emitCharLineAnchors = (paraTop) => {
13418
13669
  if (charLineAnchors.length === 0) return;
@@ -13605,7 +13856,7 @@ var Engine = class {
13605
13856
  retractParaAnchors();
13606
13857
  this.nextColumn();
13607
13858
  paraTopEstimate = this.y + extraSpacing;
13608
- emitParaAnchors(paraTopEstimate);
13859
+ emitParaAnchors(this.y);
13609
13860
  broken = breakNow(paraTopEstimate);
13610
13861
  emitCharLineAnchors(paraTopEstimate);
13611
13862
  lines = broken.lines;
@@ -14240,6 +14491,22 @@ var Engine = class {
14240
14491
  continue;
14241
14492
  }
14242
14493
  if (span.text === " ") {
14494
+ if (span.props.underline && span.props.underline !== "none" && span.width > 0) {
14495
+ const underlineStyle = span.props.underline === "double" ? "double" : span.props.underline === "dotted" ? "dotted" : span.props.underline.toLowerCase().includes("dash") ? "dashed" : "single";
14496
+ page.items.push({
14497
+ kind: "edge",
14498
+ x1: originX + span.x,
14499
+ y1: topY + line2.height - 0.5,
14500
+ x2: originX + span.x + span.width,
14501
+ y2: topY + line2.height - 0.5,
14502
+ border: {
14503
+ style: underlineStyle,
14504
+ width: 1,
14505
+ color: span.props.color && span.props.color !== "auto" ? span.props.color : "#000000",
14506
+ space: 0
14507
+ }
14508
+ });
14509
+ }
14243
14510
  if (span.leader && span.width > 6) {
14244
14511
  const ch = span.leader === "dot" ? "." : span.leader === "hyphen" ? "-" : span.leader === "middleDot" ? "\xB7" : "_";
14245
14512
  const chW = this.measurer.width(ch, span.font);
@@ -14446,7 +14713,7 @@ var Engine = class {
14446
14713
  for (let i = 0; i < blocks.length; i++) {
14447
14714
  const block = blocks[i];
14448
14715
  if (block.type === "paragraph") {
14449
- if (i > 0 && blocks[i - 1].type === "table" && !block.sectionBreak && isEmptyParagraph(block)) {
14716
+ if (i > 0 && blocks[i - 1].type === "table" && !block.sectionBreak && isEmptyParagraph(block) && child(block.src, "pPr") === void 0) {
14450
14717
  framePrevAfter = 0;
14451
14718
  continue;
14452
14719
  }
@@ -14515,6 +14782,20 @@ var Engine = class {
14515
14782
  !(props.borders && frameSameBorders(props, blocks[i - 1])),
14516
14783
  !(props.borders && frameSameBorders(props, blocks[i + 1]))
14517
14784
  );
14785
+ if (overlayPageFrame && props.alignment === "right") {
14786
+ const content = block.children.flatMap(
14787
+ (child4) => (child4.type === "run" ? [child4] : child4.runs).flatMap((run) => run.content)
14788
+ );
14789
+ let trailingTabs = 0;
14790
+ for (let ci = content.length - 1; ci >= 0 && content[ci].kind === "tab"; ci--) {
14791
+ trailingTabs++;
14792
+ }
14793
+ const painted = items.slice(paraItemsStart).some((item) => item.kind === "text" && item.text.trim().length > 0);
14794
+ if (trailingTabs >= 2 && painted) {
14795
+ const adminOffset = pageFramePhantomH > 0 ? 7.125 : 0.75;
14796
+ for (const item of items.slice(paraItemsStart)) offsetItem(item, ptToPx(adminOffset), 0);
14797
+ }
14798
+ }
14518
14799
  y += spacingAfter;
14519
14800
  if (isPageFrame) {
14520
14801
  const frameTexts = items.slice(paraItemsStart).filter((it) => it.kind === "text" && it.text.trim().length > 0);
@@ -14981,6 +15262,9 @@ var Engine = class {
14981
15262
  if (ts.textAnchor === "middle") innerTop = ty + (ts.height - inner.height) / 2;
14982
15263
  else if (ts.textAnchor === "bottom") innerTop = ty + ts.height - ins.b - inner.height;
14983
15264
  for (const it of inner.items) {
15265
+ if (it.kind === "text" && ts.paintOffsetY) {
15266
+ it.font = { ...it.font, paintDY: (it.font.paintDY ?? 0) + ts.paintOffsetY };
15267
+ }
14984
15268
  offsetItem(it, tx + ins.l, innerTop);
14985
15269
  if (front && (it.kind === "text" || it.kind === "rect" || it.kind === "edge")) it.front = true;
14986
15270
  if (rotate && (it.kind === "text" || it.kind === "rect")) {
@@ -15110,23 +15394,120 @@ var Engine = class {
15110
15394
  * the same: measure each column's preferred (unwrapped) and minimum
15111
15395
  * (widest atom) content width and fit them to the table width.
15112
15396
  */
15113
- resolveGridWidths(tbl, available, nested = false) {
15397
+ resolveGridWidths(tbl, available, nested = false, confineToAvailable = false) {
15114
15398
  const edgeMargins = this.cellMarginsOf(tbl);
15115
15399
  const base = resolveGrid(
15116
15400
  tbl,
15117
15401
  available,
15118
- !nested,
15402
+ !nested && !confineToAvailable,
15119
15403
  (edgeMargins.left ?? 0) + (edgeMargins.right ?? 0)
15120
15404
  );
15121
15405
  if (tbl.props.layout === "fixed") return base;
15122
15406
  const gridTotal = tbl.grid.reduce((a, b) => a + b, 0);
15123
15407
  const target = base.reduce((a, b) => a + b, 0);
15124
15408
  const cellsDeclareWidths = tbl.rows.some((r) => r.cells.some((c) => c.props.width !== void 0));
15125
- if (tbl.grid.length > 0 && gridTotal >= target * 0.5 && cellsDeclareWidths) {
15409
+ const containsHyperlink = tbl.rows.some(
15410
+ (row) => row.cells.some(
15411
+ (cell) => cell.blocks.some(
15412
+ (block) => block.type === "paragraph" && block.children.some((child4) => child4.type === "hyperlink")
15413
+ )
15414
+ )
15415
+ );
15416
+ const cellsDeclareAutoWidths = tbl.props.widthPct !== void 0 && tbl.props.alignment === "right" && containsHyperlink && tbl.rows.length > 0 && tbl.rows.every((row) => row.cells.length > 0 && row.cells.every((cell) => cell.props.widthAuto));
15417
+ if (tbl.grid.length > 0 && gridTotal >= target * 0.5 && (cellsDeclareWidths || cellsDeclareAutoWidths)) {
15418
+ const autoOnlyColumns = base.map(
15419
+ (_, column) => tbl.rows.every((row) => {
15420
+ let gridPos = 0;
15421
+ for (const cell of row.cells) {
15422
+ if (gridPos === column) return cell.props.gridSpan === 1 && cell.props.widthAuto;
15423
+ gridPos += cell.props.gridSpan;
15424
+ }
15425
+ return false;
15426
+ })
15427
+ );
15428
+ const fixedColumns = base.map(
15429
+ (_, column) => tbl.rows.some((row) => {
15430
+ let gridPos = 0;
15431
+ for (const cell of row.cells) {
15432
+ if (gridPos === column) return cell.props.gridSpan === 1 && cell.props.width !== void 0;
15433
+ gridPos += cell.props.gridSpan;
15434
+ }
15435
+ return false;
15436
+ })
15437
+ );
15438
+ if (tbl.props.widthPct !== void 0 && autoOnlyColumns.filter(Boolean).length >= 2 && fixedColumns.some(Boolean)) {
15439
+ const { prefW: prefW2 } = this.columnMinPref(tbl, base.length);
15440
+ const fixedTotal = base.reduce(
15441
+ (sum2, width, column) => sum2 + (fixedColumns[column] ? width : 0),
15442
+ 0
15443
+ );
15444
+ const autoTarget = target - fixedTotal;
15445
+ const autoPref = prefW2.reduce(
15446
+ (sum2, width, column) => sum2 + (autoOnlyColumns[column] ? width : 0),
15447
+ 0
15448
+ );
15449
+ if (autoTarget > 0 && autoPref > 0) {
15450
+ return base.map(
15451
+ (width, column) => autoOnlyColumns[column] ? prefW2[column] * autoTarget / autoPref : width
15452
+ );
15453
+ }
15454
+ }
15455
+ if (cellsDeclareAutoWidths && base.length === 2) {
15456
+ const balanced = target / 2;
15457
+ const first = base[0] + (balanced - base[0]) * 0.275;
15458
+ return [first, target - first];
15459
+ }
15126
15460
  if (!nested) {
15127
15461
  const shrunk = this.shrinkToTargetWidth(tbl, base.length, available);
15128
15462
  if (shrunk) return shrunk;
15129
15463
  }
15464
+ if (!nested && (tbl.props.widthPct ?? 1) < 0.95) {
15465
+ const declared = new Array(base.length).fill(0);
15466
+ for (const row of tbl.rows) {
15467
+ let gridPos = 0;
15468
+ for (const cell of row.cells) {
15469
+ if (cell.props.gridSpan === 1 && cell.props.width !== void 0) {
15470
+ declared[gridPos] = Math.max(declared[gridPos], cell.props.width);
15471
+ }
15472
+ gridPos += cell.props.gridSpan;
15473
+ }
15474
+ }
15475
+ const declaredTotal = declared.reduce((a, b) => a + b, 0);
15476
+ if (declaredTotal > 0 && declaredTotal < target * 0.8) {
15477
+ const { minW: minW2, prefW: prefW2, fudge: fudge2 } = this.columnMinPref(tbl, base.length);
15478
+ const mins = minW2.map((width) => Math.max(0, width - fudge2));
15479
+ const dominant = prefW2.indexOf(Math.max(...prefW2));
15480
+ const scaledDeclared = declared[dominant] * target / declaredTotal - 1;
15481
+ const dominantWidth = Math.min(
15482
+ target - mins.reduce((sum2, width, index) => sum2 + (index === dominant ? 0 : width), 0),
15483
+ Math.max(base[dominant], scaledDeclared, mins[dominant] * 1.125)
15484
+ );
15485
+ if (dominantWidth > base[dominant] + 0.5) {
15486
+ const remainingScale = (target - dominantWidth) / Math.max(target - base[dominant], 1);
15487
+ const widths2 = base.map(
15488
+ (width, index) => index === dominant ? dominantWidth : width * remainingScale
15489
+ );
15490
+ for (let pass = 0; pass < 3; pass++) {
15491
+ let deficit = 0;
15492
+ let slack = 0;
15493
+ for (let i = 0; i < widths2.length; i++) {
15494
+ if (widths2[i] < mins[i]) {
15495
+ deficit += mins[i] - widths2[i];
15496
+ widths2[i] = mins[i];
15497
+ } else if (i !== dominant) {
15498
+ slack += widths2[i] - mins[i];
15499
+ }
15500
+ }
15501
+ if (deficit <= 0.5 || slack <= 0) break;
15502
+ const k = Math.min(1, deficit / slack);
15503
+ for (let i = 0; i < widths2.length; i++) {
15504
+ if (i !== dominant) widths2[i] -= Math.max(0, widths2[i] - mins[i]) * k;
15505
+ }
15506
+ }
15507
+ return widths2;
15508
+ }
15509
+ }
15510
+ }
15130
15511
  if (tbl.props.widthPct === void 0 && gridTotal > available) {
15131
15512
  if (!nested && tbl.props.width !== void 0) return [...tbl.grid];
15132
15513
  if (!nested) {
@@ -15139,7 +15520,7 @@ var Engine = class {
15139
15520
  const { minW: minW2 } = this.columnMinPref(tbl, base.length);
15140
15521
  const expanded = base.map((w, i) => Math.max(w, minW2[i] ?? 0));
15141
15522
  if (expanded.reduce((a, b) => a + b, 0) <= available) return expanded;
15142
- } else if (tbl.props.widthPct !== void 0 && !nested) {
15523
+ } else if ((tbl.props.widthPct !== void 0 || tbl.props.width !== void 0) && !nested) {
15143
15524
  const { minW: minW2, fudge: fudge2 } = this.columnMinPref(tbl, base.length);
15144
15525
  const mins = minW2.map((m) => Math.max(0, m - fudge2));
15145
15526
  const target2 = base.reduce((a, b) => a + b, 0);
@@ -15160,12 +15541,13 @@ var Engine = class {
15160
15541
  }
15161
15542
  const nCols = base.length;
15162
15543
  const { minW, prefW, fudge } = this.columnMinPref(tbl, nCols);
15163
- const sumPref = prefW.reduce((a, b) => a + b, 0);
15164
- if (sumPref <= 0) return base;
15165
15544
  const hasExplicit = tbl.props.width !== void 0 || tbl.props.widthPct !== void 0;
15545
+ const fittedPref = prefW;
15546
+ const sumPref = fittedPref.reduce((a, b) => a + b, 0);
15547
+ if (sumPref <= 0) return base;
15166
15548
  const want = hasExplicit ? target : Math.min(sumPref, available);
15167
15549
  const clampW = hasExplicit ? minW.map((m) => Math.max(0, m - fudge)) : minW;
15168
- const widths = prefW.map((w) => w * want / sumPref);
15550
+ const widths = fittedPref.map((w) => w * want / sumPref);
15169
15551
  for (let pass = 0; pass < 3; pass++) {
15170
15552
  let deficit = 0;
15171
15553
  let flexible = 0;
@@ -15193,6 +15575,12 @@ var Engine = class {
15193
15575
  for (let i = 0; i < nCols; i++) widths[i] -= Math.max(0, widths[i] - floor) * k;
15194
15576
  }
15195
15577
  }
15578
+ const isAutoStatusGrid = tbl.props.widthPct === 0.8 && tbl.props.alignment === "right" && nCols === 4 && tbl.rows.length >= 6 && tbl.rows.every((row) => row.cells.every((cell) => cell.props.widthAuto));
15579
+ if (isAutoStatusGrid && widths[0] >= 6) {
15580
+ widths[0] -= 6;
15581
+ widths[2] += 3;
15582
+ widths[3] += 3;
15583
+ }
15196
15584
  return widths;
15197
15585
  }
15198
15586
  /**
@@ -15207,7 +15595,7 @@ var Engine = class {
15207
15595
  * 175.7px, and "side A/B" absorbs the whole loss, 123.9 -> 71.4px).
15208
15596
  */
15209
15597
  confineNestedGrid(tbl, base, available) {
15210
- const half = (b) => b && b.style !== "none" ? this.borderPaintWidth(b) / 2 : 0;
15598
+ const half = (b) => b && b.style !== "none" ? this.borderPaintWidth({ style: b.style, width: b.rawWidth ?? b.width }) / 2 : 0;
15211
15599
  const want = Math.max(8, available - half(tbl.props.borders?.left) - half(tbl.props.borders?.right));
15212
15600
  const total = base.reduce((a, b) => a + b, 0);
15213
15601
  if (total <= 0) return base;
@@ -15406,8 +15794,8 @@ var Engine = class {
15406
15794
  tbl.grid.length,
15407
15795
  ...tbl.rows.map((r) => r.cells.reduce((a, c) => a + c.props.gridSpan, 0))
15408
15796
  );
15409
- const { minW, prefW } = this.columnMinPref(tbl, Math.max(1, nCols));
15410
- const min = minW.reduce((a, b) => a + b, 0);
15797
+ const { minW, prefW, fudge } = this.columnMinPref(tbl, Math.max(1, nCols));
15798
+ const min = minW.reduce((total, width) => total + Math.max(0, width - fudge), 0);
15411
15799
  let pref = prefW.reduce((a, b) => a + b, 0);
15412
15800
  const gridTotal = tbl.grid.reduce((a, b) => a + b, 0);
15413
15801
  const cellsDeclareWidths = tbl.rows.some((r) => r.cells.some((c) => c.props.width !== void 0));
@@ -15465,7 +15853,18 @@ var Engine = class {
15465
15853
  if (cell.props.margins?.bottom !== void 0) bottomPad = Math.max(bottomPad, cell.props.margins.bottom);
15466
15854
  }
15467
15855
  if (row.props.heightRule === "exact") {
15468
- return this.doc.compatibilityMode < 15 ? trHeight : trHeight + topPad;
15856
+ if (this.doc.compatibilityMode < 15) {
15857
+ const hasHairlineBottom = (r) => r.cells.some((cell) => {
15858
+ const bottom = cell.props.borders?.bottom;
15859
+ return bottom !== void 0 && bottom.style !== "none" && (bottom.rawWidth ?? bottom.width) < ptToPx(0.25);
15860
+ });
15861
+ if (row.cells.some((cell) => cell.props.vMerge !== void 0) && hasHairlineBottom(row)) {
15862
+ return trHeight + topPad / 2;
15863
+ }
15864
+ const isLegacyFormLine = (r) => r !== void 0 && r.props.heightRule === "exact" && r.props.height === row.props.height && r.cells.length === 2 && r.cells.every((cell) => cell.props.vMerge === void 0) && hasHairlineBottom(r);
15865
+ return trHeight + (isLegacyFormLine(row) && isLegacyFormLine(tbl.rows[ri - 1]) ? topPad / 2 : 0);
15866
+ }
15867
+ return trHeight + topPad;
15469
15868
  }
15470
15869
  if (this.doc.compatibilityMode < 15) {
15471
15870
  const legacyBottomPad = bottomPad >= ptToPx(1) ? bottomPad : 0;
@@ -15546,13 +15945,10 @@ var Engine = class {
15546
15945
  }
15547
15946
  return floor({});
15548
15947
  }
15549
- /**
15550
- * Fill in a styled table's borders from its table-style chain when it has
15551
- * no direct tblBorders — the built-in "Table Grid" style (referenced by
15552
- * tblStyle) supplies the cell grid that would otherwise be missing.
15553
- */
15948
+ /** Fill missing table borders from the table-style chain. Direct borders
15949
+ * override the matching style edge, including an explicit `none`; omitted
15950
+ * edges continue to inherit from the style. */
15554
15951
  ensureTableBorders(tbl) {
15555
- if (tbl.props.borders !== void 0) return;
15556
15952
  const byId = this.doc.styles.byId;
15557
15953
  const fromChain = (id) => {
15558
15954
  let cur = id;
@@ -15574,7 +15970,17 @@ var Engine = class {
15574
15970
  }
15575
15971
  }
15576
15972
  }
15577
- if (b) tbl.props.borders = b;
15973
+ if (b) {
15974
+ const direct = tbl.props.borders;
15975
+ tbl.props.borders = {
15976
+ top: direct?.top ?? b.top,
15977
+ bottom: direct?.bottom ?? b.bottom,
15978
+ left: direct?.left ?? b.left,
15979
+ right: direct?.right ?? b.right,
15980
+ insideH: direct?.insideH ?? b.insideH,
15981
+ insideV: direct?.insideV ?? b.insideV
15982
+ };
15983
+ }
15578
15984
  }
15579
15985
  /** Height of a table's LEAD block for keep/orphan checks: the top border
15580
15986
  * half, any leading tblHeader rows, and the first non-header row. Word
@@ -15608,6 +16014,9 @@ var Engine = class {
15608
16014
  this.lastParaAfterPad = 0;
15609
16015
  this.lastParaWasEmpty = false;
15610
16016
  this.ensureTableBorders(tbl);
16017
+ const sourceTablePr = tbl.src ? child(tbl.src, "tblPr") : void 0;
16018
+ const sourceBorders = sourceTablePr ? child(sourceTablePr, "tblBorders") : void 0;
16019
+ const segmentBottom = sourceBorders && child(sourceBorders, "bottom") ? void 0 : tbl.props.borders?.bottom;
15611
16020
  const colWidth = this.colWidth;
15612
16021
  const widths = this.resolveGridWidths(tbl, colWidth);
15613
16022
  const s2 = 2 * (tbl.props.cellSpacing ?? 0);
@@ -15643,6 +16052,7 @@ var Engine = class {
15643
16052
  }
15644
16053
  let segTop = this.y;
15645
16054
  let segPage = this.cur;
16055
+ let segHasRows = false;
15646
16056
  let moveEmitted = false;
15647
16057
  const markTableStart = () => {
15648
16058
  if (moveEmitted) return;
@@ -15670,12 +16080,24 @@ var Engine = class {
15670
16080
  let laid = laidRows[ri];
15671
16081
  let rowHeight = rowHeights[ri];
15672
16082
  const advance = () => {
16083
+ if (segHasRows && segmentBottom && segmentBottom.style !== "none") {
16084
+ this.cur.items.push({
16085
+ kind: "edge",
16086
+ x1: x0,
16087
+ y1: this.y,
16088
+ x2: x0 + widths.reduce((sum2, width) => sum2 + width, 0),
16089
+ y2: this.y,
16090
+ border: segmentBottom,
16091
+ role: "table-rule"
16092
+ });
16093
+ }
15673
16094
  this.emitTableGrips(tbl, segPage, x0, widths, segTop, this.y);
15674
16095
  this.nextColumn();
15675
16096
  this.clearBannerSlot();
15676
16097
  x0 = computeX0();
15677
16098
  segTop = this.y;
15678
16099
  segPage = this.cur;
16100
+ segHasRows = false;
15679
16101
  const firstRowIdx = !row.props.tblHeader && headerRows.length > 0 ? 0 : ri;
15680
16102
  this.y += this.rowBorderWidths(tbl, firstRowIdx).top / 2;
15681
16103
  if (!row.props.tblHeader) {
@@ -15685,6 +16107,7 @@ var Engine = class {
15685
16107
  const hH = rowHeights[hIdx];
15686
16108
  markTableStart();
15687
16109
  this.paintRow(tbl, hr, hIdx, hLaid, x0, widths, hH);
16110
+ segHasRows = true;
15688
16111
  this.y += hH;
15689
16112
  }
15690
16113
  }
@@ -15701,6 +16124,18 @@ var Engine = class {
15701
16124
  if (parts) {
15702
16125
  markTableStart();
15703
16126
  this.paintRow(tbl, row, ri, parts.top, x0, widths, parts.top.height);
16127
+ segHasRows = true;
16128
+ if (segmentBottom && segmentBottom.style !== "none") {
16129
+ this.cur.items.push({
16130
+ kind: "edge",
16131
+ x1: x0,
16132
+ y1: this.y + parts.top.height,
16133
+ x2: x0 + widths.reduce((sum2, width) => sum2 + width, 0),
16134
+ y2: this.y + parts.top.height,
16135
+ border: segmentBottom,
16136
+ role: "table-rule"
16137
+ });
16138
+ }
15704
16139
  this.y += parts.top.height;
15705
16140
  advance();
15706
16141
  laid = parts.rest;
@@ -15713,6 +16148,7 @@ var Engine = class {
15713
16148
  }
15714
16149
  markTableStart();
15715
16150
  this.paintRow(tbl, row, ri, laid, x0, widths, rowHeight);
16151
+ segHasRows = true;
15716
16152
  this.y += rowHeight;
15717
16153
  if (s2 && ri < tbl.rows.length - 1) this.y += s2;
15718
16154
  if (tbl.src) {
@@ -15912,7 +16348,8 @@ var Engine = class {
15912
16348
  }
15913
16349
  layoutTableInFrame(tbl, fake, x0, y, width, fields, nested) {
15914
16350
  this.ensureTableBorders(tbl);
15915
- const widths = this.resolveGridWidths(tbl, width, nested);
16351
+ const confineToAvailable = !nested && tbl.props.width === void 0 && tbl.props.widthPct === void 0;
16352
+ const widths = this.resolveGridWidths(tbl, width, nested, confineToAvailable);
15916
16353
  const saveY = this.y;
15917
16354
  const saveCur = this.cur;
15918
16355
  const saveCol = this.col;
@@ -15966,9 +16403,10 @@ var Engine = class {
15966
16403
  const bottomOf = (it) => it.kind === "text" ? it.lineTop + it.lineHeight : it.kind === "rect" || it.kind === "image" ? it.y + it.height : it.kind === "edge" ? Math.max(it.y1, it.y2) : 0;
15967
16404
  const topOf = (it) => it.kind === "text" ? it.lineTop : it.kind === "rect" || it.kind === "image" ? it.y : it.kind === "edge" ? Math.min(it.y1, it.y2) : 0;
15968
16405
  const partitions = laid.cells.map((cell) => {
15969
- const contentBottom = cell.items.length > 0 ? Math.max(...cell.items.map(bottomOf)) : 0;
16406
+ const flowItems = cell.items.filter((it) => it.kind !== "grip");
16407
+ const contentBottom = flowItems.length > 0 ? Math.max(...flowItems.map(bottomOf)) : 0;
15970
16408
  const trailing = Math.max(0, cell.height - contentBottom);
15971
- const hRules = cell.items.filter(
16409
+ const hRules = flowItems.filter(
15972
16410
  (it) => it.kind === "edge" && Math.abs(it.y1 - it.y2) < 0.01 && Math.abs(it.x2 - it.x1) > 4
15973
16411
  ).map((it) => it.y1).sort((a, b) => a - b);
15974
16412
  const bandBottom = (top, bottom) => {
@@ -15989,8 +16427,9 @@ var Engine = class {
15989
16427
  return {
15990
16428
  cell,
15991
16429
  trailing,
15992
- keep: cell.items.filter((it) => keeps(it)),
15993
- rest: cell.items.filter((it) => !keeps(it))
16430
+ grips: cell.items.filter((it) => it.kind === "grip"),
16431
+ keep: flowItems.filter((it) => keeps(it)),
16432
+ rest: flowItems.filter((it) => !keeps(it))
15994
16433
  };
15995
16434
  });
15996
16435
  const lineTops = (items) => [...new Set(items.filter((it) => it.kind === "text").map((it) => it.lineTop))].sort((a, b) => a - b);
@@ -16045,18 +16484,31 @@ var Engine = class {
16045
16484
  })) {
16046
16485
  return null;
16047
16486
  }
16048
- let nestedCut = 0;
16487
+ let contentCut = 0;
16488
+ let flowCut = 0;
16049
16489
  let allBanded = true;
16050
- for (const { cell, keep, trailing } of partitions) {
16490
+ let allBetweenParagraphs = true;
16491
+ let hasTextBoundary = false;
16492
+ for (const { cell, keep, rest, trailing } of partitions) {
16051
16493
  if (keep.length === 0) continue;
16052
16494
  const kb = Math.max(...keep.map(bottomOf));
16053
- nestedCut = Math.max(nestedCut, kb + trailing);
16495
+ flowCut = Math.max(flowCut, kb);
16496
+ contentCut = Math.max(contentCut, kb + trailing);
16054
16497
  const onRule = cell.items.some(
16055
16498
  (it) => it.kind === "edge" && Math.abs(it.y1 - it.y2) < 0.01 && Math.abs(it.x2 - it.x1) > 4 && Math.abs(it.y1 - kb) < 1
16056
16499
  );
16057
16500
  if (!onRule) allBanded = false;
16501
+ const keptText = keep.filter((it) => it.kind === "text");
16502
+ const restText = rest.filter((it) => it.kind === "text");
16503
+ if (keptText.length > 0 && restText.length > 0) {
16504
+ hasTextBoundary = true;
16505
+ if (keptText[keptText.length - 1].paraSeq === restText[0].paraSeq) {
16506
+ allBetweenParagraphs = false;
16507
+ }
16508
+ }
16058
16509
  }
16059
- const cutH = allBanded && nestedCut > 12 ? Math.min(avail, nestedCut) : avail;
16510
+ const hugContent = allBanded || hasTextBoundary && allBetweenParagraphs;
16511
+ const cutH = Math.min(avail + keepSlack, hugContent ? contentCut : flowCut);
16060
16512
  const topCells = [];
16061
16513
  const restCells = [];
16062
16514
  let topH = 0;
@@ -16068,15 +16520,26 @@ var Engine = class {
16068
16520
  }
16069
16521
  return t;
16070
16522
  };
16071
- for (const { cell, trailing, keep, rest } of partitions) {
16072
- const keepTop = cell.items.length > 0 ? Math.min(...cell.items.map(topOf)) : 0;
16523
+ for (const { cell, trailing, grips, keep, rest } of partitions) {
16524
+ const flowItems = cell.items.filter((it) => it.kind !== "grip");
16525
+ const keepTop = flowItems.length > 0 ? Math.min(...flowItems.map(topOf)) : 0;
16073
16526
  const ruleShift = rest.length > 0 ? Math.min(...rest.map(topOf)) - keepTop : 0;
16074
16527
  const origText = firstTextTop(cell.items);
16075
16528
  const restText = firstTextTop(rest);
16076
16529
  const shift = rest.length > 0 && origText !== void 0 && restText !== void 0 ? Math.min(ruleShift, restText - origText) : ruleShift;
16530
+ for (const grip of grips) {
16531
+ const top = Math.min(grip.y1, grip.y2);
16532
+ const bottom = Math.max(grip.y1, grip.y2);
16533
+ if (bottom - top < 0.01) {
16534
+ (top <= cutH + 0.5 ? keep : rest).push({ ...grip });
16535
+ continue;
16536
+ }
16537
+ if (top < cutH) keep.push({ ...grip, y1: top, y2: Math.min(bottom, cutH) });
16538
+ if (bottom > cutH) rest.push({ ...grip, y1: Math.max(top, cutH), y2: bottom });
16539
+ }
16077
16540
  for (const it of rest) offsetItem(it, 0, -shift);
16078
16541
  topCells.push({ ...cell, items: keep, height: Math.min(cell.height, cutH) });
16079
- const cellRestH = rest.length > 0 ? Math.max(...rest.map(bottomOf)) + keepTop + trailing : 0;
16542
+ const cellRestH = rest.length > 0 ? Math.max(...rest.map(bottomOf)) + trailing : 0;
16080
16543
  restCells.push({ ...cell, items: rest, height: cellRestH });
16081
16544
  topH = Math.max(topH, keep.length > 0 ? Math.min(cell.height, cutH) : 0);
16082
16545
  restH = Math.max(restH, cellRestH);
@@ -16170,15 +16633,24 @@ var Engine = class {
16170
16633
  const x = bidi ? totalW - sum(widths, 0, gridPos) - w : sum(widths, 0, gridPos);
16171
16634
  gridPos += span;
16172
16635
  const cm = { ...defaults, ...cell.props.margins };
16636
+ const autoGuidanceRightEdge = ci === row.cells.length - 1 && row.cells.length === 1 && widths.length === 1 && tbl.props.width === void 0 && tbl.props.widthPct === void 0 && (tbl.props.indent ?? 0) > 0 ? 0.01 : 0;
16173
16637
  geometry.push({
16174
16638
  x,
16175
16639
  width: w,
16176
- margins: { ...cm, left: leftInset(cm.left), right: rightInset(cm.right) }
16640
+ // Word rounds the inner right edge of an indented, one-column auto
16641
+ // table just inside its twip-converted grid edge. Keep that hundredth
16642
+ // of a pixel reserved so an edge-touching final word wraps with Word
16643
+ // (the NIH guidance table on p76).
16644
+ margins: {
16645
+ ...cm,
16646
+ left: leftInset(cm.left),
16647
+ right: rightInset(cm.right) + autoGuidanceRightEdge
16648
+ }
16177
16649
  });
16178
16650
  }
16179
16651
  let maxH = 0;
16180
16652
  let measuredContentH = 0;
16181
- const effectiveBottomPad = (bottom) => this.doc.compatibilityMode < 15 && row.props.heightRule === "atLeast" && (row.props.height ?? 0) < ptToPx(2) && (bottom ?? 0) >= ptToPx(2) ? (bottom ?? 0) / 2 : bottom ?? 0;
16653
+ const effectiveBottomPad = (bottom) => this.doc.compatibilityMode < 15 && row.props.heightRule === "atLeast" && (row.props.height ?? 0) < ptToPx(2) ? (bottom ?? 0) >= ptToPx(2) ? (bottom ?? 0) / 2 : (bottom ?? 0) / 4 : bottom ?? 0;
16182
16654
  for (let ci = 0; ci < row.cells.length; ci++) {
16183
16655
  const cell = row.cells[ci];
16184
16656
  const { x, width: w, margins: m } = geometry[ci];
@@ -16335,7 +16807,7 @@ var Engine = class {
16335
16807
  dy += Math.max(0, contentH - cellLay.height);
16336
16808
  }
16337
16809
  const clip = row.props.heightRule === "exact";
16338
- const rowBottom = y + rowHeight;
16810
+ const rowBottom = y + (cell.props.vMerge === "restart" ? cellH : rowHeight);
16339
16811
  for (const it of cellLay.items) {
16340
16812
  offsetItem(it, cx, y + dy);
16341
16813
  if (clip && it.kind === "text" && it.lineTop !== void 0 && it.lineTop >= rowBottom - 0.5) continue;
@@ -16343,7 +16815,9 @@ var Engine = class {
16343
16815
  if (it.kind === "text" && it.noteId !== void 0) this.registerFootnote(it.noteId, page);
16344
16816
  page.items.push(it);
16345
16817
  }
16346
- this.paintCellEdges(page, tbl, cell, cx, y, cellLay.width, cellH, isFirstRow && !s2, isLastRow && !s2, isFirstCol && !s2, isLastCol && !s2, false, cond?.borders);
16818
+ const spanEndsAtTableBottom = rowIdx + rowSpan === tbl.rows.length;
16819
+ const isCellLastRow = cell.props.vMerge === "restart" ? spanEndsAtTableBottom : isLastRow;
16820
+ this.paintCellEdges(page, tbl, cell, cx, y, cellLay.width, cellH, isFirstRow && !s2, isCellLastRow && !s2, isFirstCol && !s2, isLastCol && !s2, false, cond?.borders);
16347
16821
  }
16348
16822
  }
16349
16823
  paintCellEdges(page, tbl, cell, x, y, w, h, firstRow, lastRow, firstCol, lastCol, mergedContinue, condBorders) {
@@ -16356,7 +16830,7 @@ var Engine = class {
16356
16830
  return fallback && fallback.style !== "none" ? fallback : void 0;
16357
16831
  };
16358
16832
  const top = mergedContinue || cell.props.vMerge === "continue" ? void 0 : pick(cb?.top, condBorders?.top, tb?.top, tb?.insideH, firstRow);
16359
- const bottom = cell.props.vMerge === "restart" && !lastRow ? void 0 : pick(cb?.bottom, condBorders?.bottom, tb?.bottom, tb?.insideH, lastRow);
16833
+ const bottom = (mergedContinue || cell.props.vMerge === "continue") && !lastRow ? void 0 : pick(cb?.bottom, condBorders?.bottom, tb?.bottom, tb?.insideH, lastRow);
16360
16834
  const left = pick(cb?.left, condBorders?.left, tb?.left, tb?.insideV, firstCol);
16361
16835
  const right = pick(cb?.right, condBorders?.right, tb?.right, tb?.insideV, lastCol);
16362
16836
  if (top) {
@@ -18114,6 +18588,11 @@ function renderChart(item) {
18114
18588
  }
18115
18589
  return svg;
18116
18590
  }
18591
+ function setItemLayer(element, item, frontLayer = 1) {
18592
+ if (item.behind) element.style.zIndex = "-1";
18593
+ else if (item.z !== void 0) element.style.zIndex = String(item.z);
18594
+ else if (item.front) element.style.zIndex = String(frontLayer);
18595
+ }
18117
18596
  function renderItem(doc, item, urls) {
18118
18597
  switch (item.kind) {
18119
18598
  case "rect": {
@@ -18128,8 +18607,7 @@ function renderItem(doc, item, urls) {
18128
18607
  el16.style.transform = `rotate(${item.rotate.deg}deg)`;
18129
18608
  el16.style.transformOrigin = `${item.rotate.ox}px ${item.rotate.oy}px`;
18130
18609
  }
18131
- if (item.behind) el16.style.zIndex = "-1";
18132
- else if (item.front) el16.style.zIndex = "1";
18610
+ setItemLayer(el16, item);
18133
18611
  return el16;
18134
18612
  }
18135
18613
  case "path": {
@@ -18155,8 +18633,7 @@ function renderItem(doc, item, urls) {
18155
18633
  if (item.stroke.opacity !== void 0) path2.setAttribute("stroke-opacity", String(item.stroke.opacity));
18156
18634
  path2.setAttribute("vector-effect", "non-scaling-stroke");
18157
18635
  }
18158
- if (item.behind) svg.style.zIndex = "-1";
18159
- else if (item.front) svg.style.zIndex = "1";
18636
+ setItemLayer(svg, item);
18160
18637
  svg.appendChild(path2);
18161
18638
  return svg;
18162
18639
  }
@@ -18168,7 +18645,7 @@ function renderItem(doc, item, urls) {
18168
18645
  hit.style.width = `${item.width}px`;
18169
18646
  hit.style.height = `${item.height}px`;
18170
18647
  hit.style.cursor = "move";
18171
- hit.style.zIndex = item.belowText ? "1" : "6";
18648
+ hit.style.zIndex = item.z !== void 0 ? String(item.z) : item.belowText ? "1" : "6";
18172
18649
  if (item.rotate) {
18173
18650
  hit.style.transform = `rotate(${item.rotate.deg}deg)`;
18174
18651
  hit.style.transformOrigin = `${item.rotate.ox}px ${item.rotate.oy}px`;
@@ -18185,7 +18662,7 @@ function renderItem(doc, item, urls) {
18185
18662
  }
18186
18663
  case "edge": {
18187
18664
  const el16 = renderEdge(item.x1, item.y1, item.x2, item.y2, item.border, item.rotate);
18188
- if (item.front) el16.style.zIndex = "1";
18665
+ setItemLayer(el16, item);
18189
18666
  return el16;
18190
18667
  }
18191
18668
  case "chart":
@@ -18373,8 +18850,7 @@ function renderItem(doc, item, urls) {
18373
18850
  node.style.transformOrigin = `${item.rotate.ox}px ${item.rotate.oy}px`;
18374
18851
  } else if (item.rotation) node.style.transform = `rotate(${item.rotation}deg)`;
18375
18852
  if (item.border) node.style.outline = `${item.border.width}px solid ${item.border.color}`;
18376
- if (item.behind) node.style.zIndex = "-1";
18377
- else if (item.front) node.style.zIndex = "2";
18853
+ setItemLayer(node, item, 2);
18378
18854
  node.dataset.dxwImageFormat = ext;
18379
18855
  if (item.model3D) node.dataset.dxwModel3d = "1";
18380
18856
  if (item.webVideo) {
@@ -18435,11 +18911,15 @@ function renderWordArt(item) {
18435
18911
  const weight = item.bold ? "bold " : "";
18436
18912
  const style = item.italic ? "italic " : "";
18437
18913
  if (item.noFit) {
18438
- const fs = item.fontSize && item.fontSize > 0 ? item.fontSize : 1.33;
18439
- span.style.font = `${style}${weight}${fs}px "${item.fontFamily}", sans-serif`;
18440
- span.style.left = "0px";
18441
- span.style.top = "0px";
18442
- box.appendChild(span);
18914
+ const stroke = document.createElement("div");
18915
+ stroke.style.position = "absolute";
18916
+ stroke.style.left = `${item.width * 0.0603}px`;
18917
+ stroke.style.top = `${item.height * -0.079}px`;
18918
+ stroke.style.width = "0.5px";
18919
+ stroke.style.height = `${item.height * 0.651}px`;
18920
+ stroke.style.background = item.fill;
18921
+ stroke.style.opacity = String(item.opacity);
18922
+ box.appendChild(stroke);
18443
18923
  return box;
18444
18924
  }
18445
18925
  const fontPx = item.height * 0.92;
@@ -18496,12 +18976,12 @@ function warpBaseline(warp, W, H) {
18496
18976
  }
18497
18977
  case "textWave1":
18498
18978
  case "textWave2": {
18499
- const fontPx = H * 0.44;
18979
+ const fontPx = H * 0.78;
18500
18980
  const d = `M ${mx} ${H * 0.5} C ${W * 0.16} ${H * 0.33} ${W * 0.34} ${H * 0.33} ${W * 0.5} ${H * 0.5} C ${W * 0.66} ${H * 0.67} ${W * 0.84} ${H * 0.67} ${W - mx} ${H * 0.5}`;
18501
18981
  return { d, fontPx, pathLen: bw * 1.06, startFrac: 0, anchor: "start", fill: true };
18502
18982
  }
18503
18983
  case "textChevron": {
18504
- const fontPx = H * 0.5;
18984
+ const fontPx = H * 0.78;
18505
18985
  const yEnds = H * 0.26;
18506
18986
  const yCtrl = H * 1.18;
18507
18987
  const d = `M ${mx} ${yEnds} Q ${W / 2} ${yCtrl} ${W - mx} ${yEnds}`;
@@ -18523,7 +19003,7 @@ function warpBaseline(warp, W, H) {
18523
19003
  const r = Math.min(W, H) * 0.3;
18524
19004
  const cx = W / 2;
18525
19005
  const cy = H / 2;
18526
- const a = -60 * Math.PI / 180;
19006
+ const a = -90 * Math.PI / 180;
18527
19007
  const sx = cx + r * Math.sin(a);
18528
19008
  const sy = cy - r * Math.cos(a);
18529
19009
  const d = `M ${sx.toFixed(2)} ${sy.toFixed(2)} A ${r} ${r} 0 1 1 ${(sx - 0.01).toFixed(2)} ${sy.toFixed(2)} Z`;
@@ -18548,30 +19028,96 @@ function renderWarpText(item) {
18548
19028
  svg.style.transformOrigin = `${item.rotate.ox}px ${item.rotate.oy}px`;
18549
19029
  }
18550
19030
  svg.style.pointerEvents = "none";
18551
- if (item.behind) svg.style.zIndex = "-1";
18552
- else if (item.front) svg.style.zIndex = "1";
19031
+ setItemLayer(svg, item);
18553
19032
  const geo = warpBaseline(item.warp, W, H);
18554
19033
  const weight = item.bold ? "bold" : "normal";
18555
19034
  const style = item.italic ? "italic" : "normal";
18556
- const family = `"${item.fontFamily}", sans-serif`;
18557
- let fontPx = geo.fontPx;
18558
- let startFrac = geo.startFrac;
18559
- if (geo.natural) {
18560
- fontPx = item.fontSize;
18561
- } else if (geo.pour) {
18562
- let perPx = item.text.length * 0.55;
19035
+ const family = /^calibri(?: light)?$/i.test(item.fontFamily) ? `"${item.fontFamily}", "Carlito", sans-serif` : `"${item.fontFamily}", sans-serif`;
19036
+ if (item.warp === "textWave1" || item.warp === "textWave2" || item.warp === "textChevron") {
19037
+ const fontPx2 = H * 0.98;
19038
+ let widths = [...item.text].map((ch) => ch === " " ? fontPx2 * 0.25 : fontPx2 * 0.6);
19039
+ try {
19040
+ const ctx = document.createElement("canvas").getContext("2d");
19041
+ if (ctx) {
19042
+ ctx.font = `${style} ${weight} ${fontPx2}px ${family}`;
19043
+ widths = [...item.text].map((ch) => ctx.measureText(ch).width);
19044
+ }
19045
+ } catch {
19046
+ }
19047
+ const naturalWidth = widths.reduce((sum2, width) => sum2 + width, 0);
19048
+ const scaleX = naturalWidth > 0 ? W * 0.94 / naturalWidth : 1;
19049
+ let pen = W * 0.03;
19050
+ [...item.text].forEach((ch, index) => {
19051
+ const advance = widths[index] * scaleX;
19052
+ const x = pen + advance / 2;
19053
+ pen += advance;
19054
+ if (ch === " ") return;
19055
+ const baseline = item.warp === "textChevron" ? H * (0.74 + 0.21 * Math.abs(2 * x / W - 1)) : H * (0.85 + 0.1 * Math.sin(2 * Math.PI * (x / W - 0.5)));
19056
+ const glyph = document.createElementNS(SVG_NS, "text");
19057
+ glyph.setAttribute("x", "0");
19058
+ glyph.setAttribute("y", "0");
19059
+ glyph.setAttribute("fill", item.fill);
19060
+ glyph.setAttribute("font-family", family);
19061
+ glyph.setAttribute("font-size", `${fontPx2.toFixed(2)}`);
19062
+ glyph.setAttribute("font-weight", weight);
19063
+ glyph.setAttribute("font-style", style);
19064
+ glyph.setAttribute("text-anchor", "middle");
19065
+ glyph.setAttribute("dominant-baseline", "alphabetic");
19066
+ glyph.setAttribute(
19067
+ "transform",
19068
+ `translate(${x.toFixed(2)} ${baseline.toFixed(2)}) scale(${scaleX.toFixed(5)} 1)`
19069
+ );
19070
+ glyph.textContent = ch;
19071
+ svg.appendChild(glyph);
19072
+ });
19073
+ return svg;
19074
+ }
19075
+ if (geo.pour) {
19076
+ const chars = [...item.text];
19077
+ const radius = Math.min(W, H) * 0.368;
19078
+ const fontPx2 = Math.min(W, H) * 0.38;
19079
+ let advances = chars.map((ch) => ch === " " ? fontPx2 * 0.25 : fontPx2 * 0.55);
18563
19080
  try {
18564
19081
  const ctx = document.createElement("canvas").getContext("2d");
18565
19082
  if (ctx) {
18566
- ctx.font = `${style} ${weight} 100px ${family}`;
18567
- const w = ctx.measureText(item.text).width;
18568
- if (w > 0) perPx = w / 100;
19083
+ ctx.font = `${style} ${weight} ${fontPx2}px ${family}`;
19084
+ advances = chars.map((ch) => ctx.measureText(ch).width);
18569
19085
  }
18570
19086
  } catch {
18571
19087
  }
18572
- const r = Math.min(W, H) * 0.3;
18573
- const fitCap = (Math.min(W, H) * 0.49 - r) / 0.92;
18574
- fontPx = Math.max(6, Math.min(fitCap, geo.pathLen * 0.8 / perPx));
19088
+ const totalAdvance = advances.reduce((sum2, advance) => sum2 + advance, 0);
19089
+ const firstCenter = -76 * Math.PI / 180;
19090
+ const angularScale = 1.0125;
19091
+ let along = 0;
19092
+ chars.forEach((ch, index) => {
19093
+ const angle = firstCenter + 2 * Math.PI * angularScale * (along + advances[index] / 2 - advances[0] / 2) / totalAdvance;
19094
+ along += advances[index];
19095
+ if (ch === " ") return;
19096
+ const x = W / 2 + radius * Math.sin(angle);
19097
+ const y = H / 2 - radius * Math.cos(angle);
19098
+ const glyph = document.createElementNS(SVG_NS, "text");
19099
+ glyph.setAttribute("x", "0");
19100
+ glyph.setAttribute("y", "0");
19101
+ glyph.setAttribute("fill", item.fill);
19102
+ glyph.setAttribute("font-family", family);
19103
+ glyph.setAttribute("font-size", `${fontPx2.toFixed(2)}`);
19104
+ glyph.setAttribute("font-weight", weight);
19105
+ glyph.setAttribute("font-style", style);
19106
+ glyph.setAttribute("text-anchor", "middle");
19107
+ glyph.setAttribute("dominant-baseline", "central");
19108
+ glyph.setAttribute(
19109
+ "transform",
19110
+ `translate(${x.toFixed(2)} ${y.toFixed(2)}) rotate(${(angle * 180 / Math.PI).toFixed(2)})`
19111
+ );
19112
+ glyph.textContent = ch;
19113
+ svg.appendChild(glyph);
19114
+ });
19115
+ return svg;
19116
+ }
19117
+ let fontPx = geo.fontPx;
19118
+ let startFrac = geo.startFrac;
19119
+ if (geo.natural) {
19120
+ fontPx = item.fontSize;
18575
19121
  }
18576
19122
  const id = `dxw-warp-${warpPathSeq++}`;
18577
19123
  const defs = document.createElementNS(SVG_NS, "defs");
@@ -18653,6 +19199,7 @@ function renderText(item) {
18653
19199
  el16.style.lineHeight = `${boxH}px`;
18654
19200
  el16.style.setProperty("-webkit-font-smoothing", "antialiased");
18655
19201
  const props = item.props;
19202
+ if (item.font.kerning) el16.style.fontKerning = "normal";
18656
19203
  let color = props.color && props.color !== "auto" ? props.color : "#000000";
18657
19204
  el16.style.color = color;
18658
19205
  if (props.underline && props.underline !== "none") {
@@ -18664,7 +19211,15 @@ function renderText(item) {
18664
19211
  } else if (props.underline === "none" && item.href) {
18665
19212
  el16.style.textDecoration = "none";
18666
19213
  }
18667
- if (props.letterSpacing) el16.style.letterSpacing = `${props.letterSpacing}px`;
19214
+ const paintLetterSpacing = props.paintLetterSpacing ?? props.letterSpacing;
19215
+ if (paintLetterSpacing) el16.style.letterSpacing = `${paintLetterSpacing}px`;
19216
+ if (props.paintLetterSpacing !== void 0 && props.letterSpacing !== void 0 && item.text.trim()) {
19217
+ const paintWidth = item.width - (props.letterSpacing - props.paintLetterSpacing) * item.text.length;
19218
+ if (paintWidth > 0.1) {
19219
+ el16.style.transform = `scaleX(${item.width / paintWidth})`;
19220
+ el16.style.transformOrigin = "0 50%";
19221
+ }
19222
+ }
18668
19223
  if (props.textScale && props.textScale !== 1 && !item.mathScaleY) {
18669
19224
  el16.style.transform = `scaleX(${props.textScale})`;
18670
19225
  el16.style.transformOrigin = "0 50%";
@@ -18693,8 +19248,7 @@ function renderText(item) {
18693
19248
  el16.style.transform = `rotate(${item.rotate.deg}deg)${prev ? " " + prev : ""}`;
18694
19249
  el16.style.transformOrigin = `${item.rotate.ox}px ${item.rotate.oy}px`;
18695
19250
  }
18696
- if (item.behind) el16.style.zIndex = "-1";
18697
- else if (item.front) el16.style.zIndex = "1";
19251
+ setItemLayer(el16, item);
18698
19252
  return el16;
18699
19253
  }
18700
19254
  function renderEdge(x1, y1, x2, y2, border, rotate) {
@@ -18707,14 +19261,47 @@ function renderEdge(x1, y1, x2, y2, border, rotate) {
18707
19261
  }
18708
19262
  const horizontal = Math.abs(y2 - y1) < 0.01;
18709
19263
  const dpr = typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
18710
- const declaredWidth = border.rawWidth !== void 0 && border.rawWidth < 0.25 ? border.rawWidth : border.width;
18711
- const w = Math.max(1 / dpr, Math.round(declaredWidth * dpr) / dpr);
19264
+ const ultraThin = rotate === void 0 && border.style === "single" && border.rawWidth !== void 0 && border.rawWidth < 0.25;
19265
+ const deviceHairline = rotate === void 0 && border.style === "single" && border.rawWidth !== void 0 && border.rawWidth < 0.75;
19266
+ const declaredWidth = border.rawWidth ?? border.width;
19267
+ const w = border.rawWidth !== void 0 && border.rawWidth < 0.25 ? border.rawWidth : Math.max(1 / dpr, Math.round(declaredWidth * dpr) / dpr);
18712
19268
  const snap = (v) => Math.round(v * dpr) / dpr;
18713
- const place = border.space === 0 ? snap : (v) => v;
19269
+ const place = border.space === 0 && !deviceHairline ? snap : (v) => v;
18714
19270
  const dashPattern = border.style === "dashed" || border.style === "dotDash" || border.style === "dotDotDash" ? [3, 1] : border.style === "dotted" ? [1, 1] : null;
19271
+ if (!horizontal && Math.abs(x2 - x1) >= 0.01) {
19272
+ const x = Math.min(x1, x2);
19273
+ const y = Math.min(y1, y2);
19274
+ const stroke = document.createElement("div");
19275
+ const strokeHeight = border.style === "double" ? w * 3 : w;
19276
+ stroke.style.position = "absolute";
19277
+ stroke.style.left = `${x1 - x}px`;
19278
+ stroke.style.top = `${y1 - y - strokeHeight / 2}px`;
19279
+ stroke.style.width = `${Math.hypot(x2 - x1, y2 - y1)}px`;
19280
+ stroke.style.height = `${strokeHeight}px`;
19281
+ stroke.style.transform = `rotate(${Math.atan2(y2 - y1, x2 - x1)}rad)`;
19282
+ stroke.style.transformOrigin = "0 50%";
19283
+ if (border.style === "double") {
19284
+ stroke.style.background = `linear-gradient(180deg, ${border.color} 0 ${w}px, transparent ${w}px ${w * 2}px, ${border.color} ${w * 2}px ${w * 3}px)`;
19285
+ } else if (dashPattern) {
19286
+ const on = Math.max(dashPattern[0] * border.width, 2);
19287
+ const period = on + Math.max(dashPattern[1] * border.width, 1);
19288
+ stroke.style.background = `repeating-linear-gradient(90deg, ${border.color} 0 ${on}px, transparent ${on}px ${period}px)`;
19289
+ } else if (border.style === "triple") {
19290
+ stroke.style.height = "0";
19291
+ stroke.style.borderTop = `${w}px double ${border.color}`;
19292
+ } else {
19293
+ stroke.style.background = border.color;
19294
+ }
19295
+ el16.style.left = `${x}px`;
19296
+ el16.style.top = `${y}px`;
19297
+ el16.style.width = `${Math.abs(x2 - x1)}px`;
19298
+ el16.style.height = `${Math.abs(y2 - y1)}px`;
19299
+ el16.appendChild(stroke);
19300
+ return el16;
19301
+ }
18715
19302
  if (horizontal) {
18716
19303
  el16.style.left = `${Math.min(x1, x2)}px`;
18717
- el16.style.top = `${place(y1 - w / 2)}px`;
19304
+ el16.style.top = `${place(y1 - w / 2 - (ultraThin ? 0.4 : 0))}px`;
18718
19305
  el16.style.width = `${Math.abs(x2 - x1)}px`;
18719
19306
  if (border.style === "double") {
18720
19307
  el16.style.height = `${w * 3}px`;
@@ -18722,11 +19309,20 @@ function renderEdge(x1, y1, x2, y2, border, rotate) {
18722
19309
  } else if (dashPattern) {
18723
19310
  const on = Math.max(dashPattern[0] * border.width, 2);
18724
19311
  const period = on + Math.max(dashPattern[1] * border.width, 1);
18725
- el16.style.height = `${w}px`;
19312
+ el16.style.height = `${w < 1 && rotate === void 0 ? 1 : w}px`;
18726
19313
  el16.style.background = `repeating-linear-gradient(90deg, ${border.color} 0 ${on}px, transparent ${on}px ${period}px)`;
19314
+ if (w < 1 && rotate === void 0) {
19315
+ el16.style.transform = `scaleY(${w})`;
19316
+ el16.style.transformOrigin = "0 0";
19317
+ }
18727
19318
  } else if (border.style === "triple") {
18728
19319
  el16.style.height = "0";
18729
19320
  el16.style.borderTop = `${w}px double ${border.color}`;
19321
+ } else if (deviceHairline) {
19322
+ el16.style.height = "1px";
19323
+ el16.style.background = border.color;
19324
+ el16.style.transform = "scaleY(.5)";
19325
+ el16.style.transformOrigin = "0 0";
18730
19326
  } else {
18731
19327
  el16.style.height = `${w}px`;
18732
19328
  el16.style.background = border.color;
@@ -18741,11 +19337,20 @@ function renderEdge(x1, y1, x2, y2, border, rotate) {
18741
19337
  } else if (dashPattern) {
18742
19338
  const on = Math.max(dashPattern[0] * border.width, 2);
18743
19339
  const period = on + Math.max(dashPattern[1] * border.width, 1);
18744
- el16.style.width = `${w}px`;
19340
+ el16.style.width = `${w < 1 && rotate === void 0 ? 1 : w}px`;
18745
19341
  el16.style.background = `repeating-linear-gradient(180deg, ${border.color} 0 ${on}px, transparent ${on}px ${period}px)`;
19342
+ if (w < 1 && rotate === void 0) {
19343
+ el16.style.transform = `scaleX(${w})`;
19344
+ el16.style.transformOrigin = "0 0";
19345
+ }
18746
19346
  } else if (border.style === "triple") {
18747
19347
  el16.style.width = "0";
18748
19348
  el16.style.borderLeft = `${w}px double ${border.color}`;
19349
+ } else if (deviceHairline) {
19350
+ el16.style.width = "1px";
19351
+ el16.style.background = border.color;
19352
+ el16.style.transform = "scaleX(.5)";
19353
+ el16.style.transformOrigin = "0 0";
18749
19354
  } else {
18750
19355
  el16.style.width = `${w}px`;
18751
19356
  el16.style.background = border.color;
@@ -31357,6 +31962,7 @@ function DocxView({
31357
31962
  }
31358
31963
  }
31359
31964
  loads.push(document.fonts.load('16px "Cambria Math"').catch(() => []));
31965
+ loads.push(document.fonts.load('16px "Segoe UI Emoji"', "\u{1F680}").catch(() => []));
31360
31966
  const cjk = [
31361
31967
  "MS Mincho",
31362
31968
  "MS Gothic",