superdoc 1.0.0-beta.22 → 1.0.0-beta.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/dist/chunks/{PdfViewer-CswYWp1h.cjs → PdfViewer-C9mryfp4.cjs} +1 -1
  2. package/dist/chunks/{PdfViewer-B42JCeYb.es.js → PdfViewer-umOKwA1g.es.js} +1 -1
  3. package/dist/chunks/{index-895wSAjT-CWlZl8zz.cjs → index-DYBG7Xab-CoI6fike.cjs} +1 -1
  4. package/dist/chunks/{index-895wSAjT-C4ksiI6n.es.js → index-DYBG7Xab-mIeLdlWI.es.js} +1 -1
  5. package/dist/chunks/{index-DBmh710D.es.js → index-DyY842H4.es.js} +3 -3
  6. package/dist/chunks/{index-CK4eX_iu.cjs → index-Q-l_lwcU.cjs} +3 -3
  7. package/dist/chunks/{super-editor.es-BkRizaIE.cjs → super-editor.es-49DW4_-r.cjs} +243 -62
  8. package/dist/chunks/{super-editor.es-V792hb-6.es.js → super-editor.es-Cj9Sb-Qv.es.js} +243 -62
  9. package/dist/super-editor/ai-writer.es.js +2 -2
  10. package/dist/super-editor/chunks/{converter-DhkZt4Pv.js → converter-DJyfDFNm.js} +33 -29
  11. package/dist/super-editor/chunks/{docx-zipper-Bajmc-RM.js → docx-zipper-C-9Tqy8I.js} +1 -1
  12. package/dist/super-editor/chunks/{editor-oZjoYNA0.js → editor-f37DOCIX.js} +212 -35
  13. package/dist/super-editor/chunks/{index-895wSAjT.js → index-DYBG7Xab.js} +1 -1
  14. package/dist/super-editor/chunks/{toolbar-Bn5LTbq9.js → toolbar-Devgq8w3.js} +2 -2
  15. package/dist/super-editor/converter.es.js +1 -1
  16. package/dist/super-editor/docx-zipper.es.js +2 -2
  17. package/dist/super-editor/editor.es.js +3 -3
  18. package/dist/super-editor/file-zipper.es.js +1 -1
  19. package/dist/super-editor/super-editor.es.js +6 -6
  20. package/dist/super-editor/toolbar.es.js +2 -2
  21. package/dist/super-editor.cjs +1 -1
  22. package/dist/super-editor.es.js +1 -1
  23. package/dist/superdoc.cjs +2 -2
  24. package/dist/superdoc.es.js +2 -2
  25. package/dist/superdoc.umd.js +245 -64
  26. package/dist/superdoc.umd.js.map +1 -1
  27. package/package.json +1 -1
@@ -24995,24 +24995,32 @@ function extractGradientFill(gradFill) {
24995
24995
  const DRAWING_XML_TAG = "w:drawing";
24996
24996
  const SHAPE_URI = "http://schemas.microsoft.com/office/word/2010/wordprocessingShape";
24997
24997
  const GROUP_URI = "http://schemas.microsoft.com/office/word/2010/wordprocessingGroup";
24998
+ const normalizeTargetPath = (targetPath = "") => {
24999
+ if (!targetPath) return targetPath;
25000
+ const trimmed = targetPath.replace(/^\/+/, "");
25001
+ if (trimmed.startsWith("word/")) return trimmed;
25002
+ if (trimmed.startsWith("media/")) return `word/${trimmed}`;
25003
+ return `word/${trimmed}`;
25004
+ };
24998
25005
  const DEFAULT_SHAPE_WIDTH = 100;
24999
25006
  const DEFAULT_SHAPE_HEIGHT = 100;
25000
25007
  function handleImageNode$1(node, params2, isAnchor) {
25008
+ if (!node) return null;
25001
25009
  const { docx, filename } = params2;
25002
- const { attributes } = node;
25010
+ const attributes = node?.attributes || {};
25003
25011
  const padding = {
25004
- top: emuToPixels(attributes["distT"]),
25005
- bottom: emuToPixels(attributes["distB"]),
25006
- left: emuToPixels(attributes["distL"]),
25007
- right: emuToPixels(attributes["distR"])
25012
+ top: emuToPixels(attributes?.["distT"]),
25013
+ bottom: emuToPixels(attributes?.["distB"]),
25014
+ left: emuToPixels(attributes?.["distL"]),
25015
+ right: emuToPixels(attributes?.["distR"])
25008
25016
  };
25009
- const extent = node.elements.find((el) => el.name === "wp:extent");
25017
+ const extent = node?.elements?.find((el) => el.name === "wp:extent");
25010
25018
  const size2 = {
25011
25019
  width: emuToPixels(extent?.attributes?.cx),
25012
25020
  height: emuToPixels(extent?.attributes?.cy)
25013
25021
  };
25014
25022
  let transformData = {};
25015
- const effectExtent = node.elements.find((el) => el.name === "wp:effectExtent");
25023
+ const effectExtent = node?.elements?.find((el) => el.name === "wp:effectExtent");
25016
25024
  if (effectExtent) {
25017
25025
  const sanitizeEmuValue = (value) => {
25018
25026
  if (value === null || value === void 0) return 0;
@@ -25026,12 +25034,12 @@ function handleImageNode$1(node, params2, isAnchor) {
25026
25034
  bottom: emuToPixels(sanitizeEmuValue(effectExtent.attributes?.["b"]))
25027
25035
  };
25028
25036
  }
25029
- const positionHTag = node.elements.find((el) => el.name === "wp:positionH");
25030
- const positionH = positionHTag?.elements.find((el) => el.name === "wp:posOffset");
25037
+ const positionHTag = node?.elements?.find((el) => el.name === "wp:positionH");
25038
+ const positionH = positionHTag?.elements?.find((el) => el.name === "wp:posOffset");
25031
25039
  const positionHValue = emuToPixels(positionH?.elements[0]?.text);
25032
25040
  const hRelativeFrom = positionHTag?.attributes?.relativeFrom;
25033
- const alignH = positionHTag?.elements.find((el) => el.name === "wp:align")?.elements?.[0]?.text;
25034
- const positionVTag = node.elements.find((el) => el.name === "wp:positionV");
25041
+ const alignH = positionHTag?.elements?.find((el) => el.name === "wp:align")?.elements?.[0]?.text;
25042
+ const positionVTag = node?.elements?.find((el) => el.name === "wp:positionV");
25035
25043
  const positionV = positionVTag?.elements?.find((el) => el.name === "wp:posOffset");
25036
25044
  const positionVValue = emuToPixels(positionV?.elements[0]?.text);
25037
25045
  const vRelativeFrom = positionVTag?.attributes?.relativeFrom;
@@ -25041,8 +25049,8 @@ function handleImageNode$1(node, params2, isAnchor) {
25041
25049
  top: positionVValue
25042
25050
  };
25043
25051
  const useSimplePos = attributes["simplePos"] === "1" || attributes["simplePos"] === 1 || attributes["simplePos"] === true;
25044
- const simplePosNode = node.elements.find((el) => el.name === "wp:simplePos");
25045
- const wrapNode = isAnchor ? node.elements.find(
25052
+ const simplePosNode = node?.elements?.find((el) => el.name === "wp:simplePos");
25053
+ const wrapNode = isAnchor ? node?.elements?.find(
25046
25054
  (el) => ["wp:wrapNone", "wp:wrapSquare", "wp:wrapThrough", "wp:wrapTight", "wp:wrapTopAndBottom"].includes(el.name)
25047
25055
  ) : null;
25048
25056
  const wrap2 = isAnchor ? { type: wrapNode?.name.slice(7) || "None", attrs: {} } : { type: "Inline" };
@@ -25163,9 +25171,8 @@ function handleImageNode$1(node, params2, isAnchor) {
25163
25171
  if (!rel) return null;
25164
25172
  const { attributes: relAttributes } = rel;
25165
25173
  const targetPath = relAttributes["Target"];
25166
- let path = `word/${targetPath}`;
25167
- if (targetPath.startsWith("/word") || targetPath.startsWith("/media")) path = targetPath.substring(1);
25168
- const extension = targetPath.substring(targetPath.lastIndexOf(".") + 1);
25174
+ const path = normalizeTargetPath(targetPath);
25175
+ const extension = path.substring(path.lastIndexOf(".") + 1);
25169
25176
  return {
25170
25177
  type: "image",
25171
25178
  attrs: {
@@ -25183,8 +25190,8 @@ function handleImageNode$1(node, params2, isAnchor) {
25183
25190
  transformData,
25184
25191
  ...useSimplePos && {
25185
25192
  simplePos: {
25186
- x: simplePosNode.attributes?.x,
25187
- y: simplePosNode.attributes?.y
25193
+ x: simplePosNode?.attributes?.x,
25194
+ y: simplePosNode?.attributes?.y
25188
25195
  }
25189
25196
  },
25190
25197
  wrap: wrap2,
@@ -25194,12 +25201,12 @@ function handleImageNode$1(node, params2, isAnchor) {
25194
25201
  wrapTopAndBottom: wrap2.type === "TopAndBottom",
25195
25202
  shouldStretch,
25196
25203
  originalPadding: {
25197
- distT: attributes["distT"],
25198
- distB: attributes["distB"],
25199
- distL: attributes["distL"],
25200
- distR: attributes["distR"]
25204
+ distT: attributes?.["distT"],
25205
+ distB: attributes?.["distB"],
25206
+ distL: attributes?.["distL"],
25207
+ distR: attributes?.["distR"]
25201
25208
  },
25202
- originalAttributes: node.attributes,
25209
+ originalAttributes: node?.attributes || {},
25203
25210
  rId: relAttributes["Id"]
25204
25211
  }
25205
25212
  };
@@ -25363,11 +25370,8 @@ const handleShapeGroup = (params2, node, graphicData, size2, padding, marginOffs
25363
25370
  const { elements } = relationships || [];
25364
25371
  const rel = elements?.find((el) => el.attributes["Id"] === rEmbed);
25365
25372
  if (!rel) return null;
25366
- const targetPath = rel.attributes?.["Target"];
25367
- let path = `word/${targetPath}`;
25368
- if (targetPath.startsWith("/word") || targetPath.startsWith("/media")) {
25369
- path = targetPath.substring(1);
25370
- }
25373
+ const targetPath = normalizeTargetPath(rel.attributes?.["Target"]);
25374
+ const path = targetPath;
25371
25375
  const nvPicPr = pic.elements?.find((el) => el.name === "pic:nvPicPr");
25372
25376
  const cNvPr = nvPicPr?.elements?.find((el) => el.name === "pic:cNvPr");
25373
25377
  const picId = cNvPr?.attributes?.["id"];
@@ -36388,7 +36392,7 @@ const _SuperConverter = class _SuperConverter2 {
36388
36392
  static getStoredSuperdocVersion(docx) {
36389
36393
  return _SuperConverter2.getStoredCustomProperty(docx, "SuperdocVersion");
36390
36394
  }
36391
- static setStoredSuperdocVersion(docx = this.convertedXml, version2 = "1.0.0-beta.22") {
36395
+ static setStoredSuperdocVersion(docx = this.convertedXml, version2 = "1.0.0-beta.23") {
36392
36396
  return _SuperConverter2.setStoredCustomProperty(docx, "SuperdocVersion", version2, false);
36393
36397
  }
36394
36398
  /**
@@ -53553,7 +53557,7 @@ const isHeadless = (editor) => {
53553
53557
  const shouldSkipNodeView = (editor) => {
53554
53558
  return isHeadless(editor);
53555
53559
  };
53556
- const summaryVersion = "1.0.0-beta.22";
53560
+ const summaryVersion = "1.0.0-beta.23";
53557
53561
  const nodeKeys = ["group", "content", "marks", "inline", "atom", "defining", "code", "tableRole", "summary"];
53558
53562
  const markKeys = ["group", "inclusive", "excludes", "spanning", "code"];
53559
53563
  function mapAttributes(attrs) {
@@ -54342,7 +54346,7 @@ const _Editor = class _Editor2 extends EventEmitter$1 {
54342
54346
  { default: remarkStringify },
54343
54347
  { default: remarkGfm }
54344
54348
  ] = await Promise.all([
54345
- Promise.resolve().then(() => require("./index-895wSAjT-CWlZl8zz.cjs")),
54349
+ Promise.resolve().then(() => require("./index-DYBG7Xab-CoI6fike.cjs")),
54346
54350
  Promise.resolve().then(() => require("./index-DRCvimau-H4Ck3S9a.cjs")),
54347
54351
  Promise.resolve().then(() => require("./index-C_x_N6Uh-Db3CUJMX.cjs")),
54348
54352
  Promise.resolve().then(() => require("./index-D_sWOSiG-BtDZzJ6I.cjs")),
@@ -54547,7 +54551,7 @@ const _Editor = class _Editor2 extends EventEmitter$1 {
54547
54551
  * Process collaboration migrations
54548
54552
  */
54549
54553
  processCollaborationMigrations() {
54550
- console.debug("[checkVersionMigrations] Current editor version", "1.0.0-beta.22");
54554
+ console.debug("[checkVersionMigrations] Current editor version", "1.0.0-beta.23");
54551
54555
  if (!this.options.ydoc) return;
54552
54556
  const metaMap = this.options.ydoc.getMap("meta");
54553
54557
  let docVersion = metaMap.get("version");
@@ -56999,6 +57003,11 @@ const normalizeAlignment = (value) => {
56999
57003
  case "right":
57000
57004
  case "justify":
57001
57005
  return value;
57006
+ case "both":
57007
+ case "distribute":
57008
+ case "numTab":
57009
+ case "thaiDistribute":
57010
+ return "justify";
57002
57011
  case "end":
57003
57012
  return "right";
57004
57013
  case "start":
@@ -59213,6 +59222,11 @@ const computeParagraphAttrs = (para, styleContext, listCounterContext, converter
59213
59222
  } else if (computed2.paragraph.alignment) {
59214
59223
  paragraphAttrs.alignment = computed2.paragraph.alignment;
59215
59224
  }
59225
+ const isJustified = paragraphAttrs.alignment === "justify" || paragraphAttrs.alignment === "both";
59226
+ const hasFirstLineIndent = normalizedIndent?.firstLine && normalizedIndent.firstLine > 0;
59227
+ if (isJustified && hasFirstLineIndent) {
59228
+ paragraphAttrs.suppressFirstLineIndent = true;
59229
+ }
59216
59230
  const spacingPx = spacingPtToPx(spacing, normalizedSpacing);
59217
59231
  if (spacingPx) paragraphAttrs.spacing = spacingPx;
59218
59232
  if (normalizedSpacing?.beforeAutospacing != null || normalizedSpacing?.afterAutospacing != null) {
@@ -59430,7 +59444,7 @@ const computeParagraphAttrs = (para, styleContext, listCounterContext, converter
59430
59444
  }
59431
59445
  }
59432
59446
  paragraphAttrs.wordLayout = wordLayout;
59433
- if (enrichedNumberingProps.resolvedLevelIndent) {
59447
+ if (enrichedNumberingProps.resolvedLevelIndent && !hasExplicitIndent) {
59434
59448
  const resolvedIndentPx = convertIndentTwipsToPx(enrichedNumberingProps.resolvedLevelIndent);
59435
59449
  paragraphAttrs.indent = {
59436
59450
  ...paragraphAttrs.indent,
@@ -62796,8 +62810,14 @@ function computeNextSectionPropsAtBreak(blocks) {
62796
62810
  });
62797
62811
  return nextSectionPropsAtBreak;
62798
62812
  }
62799
- function scheduleSectionBreak(block, state2, baseMargins) {
62813
+ function scheduleSectionBreak(block, state2, baseMargins, maxHeaderContentHeight = 0) {
62800
62814
  const next = { ...state2 };
62815
+ const calcRequiredTopMargin = (headerDistance, baseTop) => {
62816
+ if (maxHeaderContentHeight > 0) {
62817
+ return Math.max(baseTop, headerDistance + maxHeaderContentHeight);
62818
+ }
62819
+ return Math.max(baseTop, headerDistance);
62820
+ };
62801
62821
  if (block.attrs?.isFirstSection && !next.hasAnyPages) {
62802
62822
  if (block.pageSize) {
62803
62823
  next.activePageSize = { w: block.pageSize.w, h: block.pageSize.h };
@@ -62811,7 +62831,7 @@ function scheduleSectionBreak(block, state2, baseMargins) {
62811
62831
  const headerDistance = Math.max(0, block.margins.header);
62812
62832
  next.activeHeaderDistance = headerDistance;
62813
62833
  next.pendingHeaderDistance = headerDistance;
62814
- next.activeTopMargin = Math.max(baseMargins.top, headerDistance);
62834
+ next.activeTopMargin = calcRequiredTopMargin(headerDistance, baseMargins.top);
62815
62835
  next.pendingTopMargin = next.activeTopMargin;
62816
62836
  }
62817
62837
  if (block.margins?.footer !== void 0) {
@@ -62833,9 +62853,15 @@ function scheduleSectionBreak(block, state2, baseMargins) {
62833
62853
  const nextBottom = next.pendingBottomMargin ?? next.activeBottomMargin;
62834
62854
  const nextHeader = next.pendingHeaderDistance ?? next.activeHeaderDistance;
62835
62855
  const nextFooter = next.pendingFooterDistance ?? next.activeFooterDistance;
62836
- next.pendingTopMargin = typeof headerPx === "number" ? Math.max(baseMargins.top, headerPx) : nextTop;
62856
+ if (typeof headerPx === "number") {
62857
+ const newHeaderDist = Math.max(0, headerPx);
62858
+ next.pendingHeaderDistance = newHeaderDist;
62859
+ next.pendingTopMargin = calcRequiredTopMargin(newHeaderDist, baseMargins.top);
62860
+ } else {
62861
+ next.pendingTopMargin = nextTop;
62862
+ next.pendingHeaderDistance = nextHeader;
62863
+ }
62837
62864
  next.pendingBottomMargin = typeof footerPx === "number" ? Math.max(baseMargins.bottom, footerPx) : nextBottom;
62838
- next.pendingHeaderDistance = typeof headerPx === "number" ? Math.max(0, headerPx) : nextHeader;
62839
62865
  next.pendingFooterDistance = typeof footerPx === "number" ? Math.max(0, footerPx) : nextFooter;
62840
62866
  if (block.pageSize) {
62841
62867
  next.pendingPageSize = { w: block.pageSize.w, h: block.pageSize.h };
@@ -64300,7 +64326,22 @@ function layoutDocument(blocks, measures, options = {}) {
64300
64326
  if (contentWidth <= 0) {
64301
64327
  throw new Error("layoutDocument: pageSize and margins yield non-positive content area");
64302
64328
  }
64303
- let activeTopMargin = margins.top;
64329
+ const validateHeaderHeight = (height) => {
64330
+ if (height === void 0) return 0;
64331
+ if (!Number.isFinite(height) || height < 0) return 0;
64332
+ return height;
64333
+ };
64334
+ const headerContentHeights = options.headerContentHeights;
64335
+ const maxHeaderContentHeight = headerContentHeights ? Math.max(
64336
+ 0,
64337
+ validateHeaderHeight(headerContentHeights.default),
64338
+ validateHeaderHeight(headerContentHeights.first),
64339
+ validateHeaderHeight(headerContentHeights.even),
64340
+ validateHeaderHeight(headerContentHeights.odd)
64341
+ ) : 0;
64342
+ const headerDistance = margins.header ?? margins.top;
64343
+ const effectiveTopMargin = maxHeaderContentHeight > 0 ? Math.max(margins.top, headerDistance + maxHeaderContentHeight) : margins.top;
64344
+ let activeTopMargin = effectiveTopMargin;
64304
64345
  let activeBottomMargin = margins.bottom;
64305
64346
  let pendingTopMargin = null;
64306
64347
  let pendingBottomMargin = null;
@@ -64324,7 +64365,7 @@ function layoutDocument(blocks, measures, options = {}) {
64324
64365
  const nextSectionPropsAtBreak = computeNextSectionPropsAtBreak(blocks);
64325
64366
  const scheduleSectionBreakCompat = (block, state2, baseMargins) => {
64326
64367
  if (typeof scheduleSectionBreak === "function") {
64327
- return scheduleSectionBreak(block, state2, baseMargins);
64368
+ return scheduleSectionBreak(block, state2, baseMargins, maxHeaderContentHeight);
64328
64369
  }
64329
64370
  const next = { ...state2 };
64330
64371
  if (block.attrs?.isFirstSection && !next.hasAnyPages) {
@@ -64337,10 +64378,11 @@ function layoutDocument(blocks, measures, options = {}) {
64337
64378
  next.pendingOrientation = null;
64338
64379
  }
64339
64380
  if (block.margins?.header !== void 0) {
64340
- const headerDistance = Math.max(0, block.margins.header);
64341
- next.activeHeaderDistance = headerDistance;
64342
- next.pendingHeaderDistance = headerDistance;
64343
- next.activeTopMargin = Math.max(baseMargins.top, headerDistance);
64381
+ const headerDist = Math.max(0, block.margins.header);
64382
+ next.activeHeaderDistance = headerDist;
64383
+ next.pendingHeaderDistance = headerDist;
64384
+ const requiredTop = maxHeaderContentHeight > 0 ? headerDist + maxHeaderContentHeight : headerDist;
64385
+ next.activeTopMargin = Math.max(baseMargins.top, requiredTop);
64344
64386
  next.pendingTopMargin = next.activeTopMargin;
64345
64387
  }
64346
64388
  if (block.margins?.footer !== void 0) {
@@ -64377,14 +64419,22 @@ function layoutDocument(blocks, measures, options = {}) {
64377
64419
  }
64378
64420
  const headerPx = block.margins?.header;
64379
64421
  const footerPx = block.margins?.footer;
64422
+ const topPx = block.margins?.top;
64380
64423
  const nextTop = next.pendingTopMargin ?? next.activeTopMargin;
64381
64424
  const nextBottom = next.pendingBottomMargin ?? next.activeBottomMargin;
64382
64425
  const nextHeader = next.pendingHeaderDistance ?? next.activeHeaderDistance;
64383
64426
  const nextFooter = next.pendingFooterDistance ?? next.activeFooterDistance;
64384
- next.pendingTopMargin = typeof headerPx === "number" ? Math.max(baseMargins.top, headerPx) : nextTop;
64385
- next.pendingBottomMargin = typeof footerPx === "number" ? Math.max(baseMargins.bottom, footerPx) : nextBottom;
64386
64427
  next.pendingHeaderDistance = typeof headerPx === "number" ? Math.max(0, headerPx) : nextHeader;
64387
64428
  next.pendingFooterDistance = typeof footerPx === "number" ? Math.max(0, footerPx) : nextFooter;
64429
+ if (typeof headerPx === "number" || typeof topPx === "number") {
64430
+ const sectionTop = topPx ?? baseMargins.top;
64431
+ const sectionHeader = next.pendingHeaderDistance;
64432
+ const requiredTop = maxHeaderContentHeight > 0 ? sectionHeader + maxHeaderContentHeight : sectionHeader;
64433
+ next.pendingTopMargin = Math.max(sectionTop, requiredTop);
64434
+ } else {
64435
+ next.pendingTopMargin = nextTop;
64436
+ }
64437
+ next.pendingBottomMargin = typeof footerPx === "number" ? Math.max(baseMargins.bottom, footerPx) : nextBottom;
64388
64438
  if (block.pageSize) next.pendingPageSize = { w: block.pageSize.w, h: block.pageSize.h };
64389
64439
  if (block.orientation) next.pendingOrientation = block.orientation;
64390
64440
  const sectionType = block.type ?? "continuous";
@@ -65135,7 +65185,7 @@ const resolveTrackedChangesEnabled = (attrs, defaultEnabled = true) => {
65135
65185
  }
65136
65186
  return attrs.trackedChangesEnabled !== false;
65137
65187
  };
65138
- const MAX_CACHE_SIZE = 1e4;
65188
+ const MAX_CACHE_SIZE$1 = 1e4;
65139
65189
  const BYTES_PER_ENTRY_ESTIMATE = 5e3;
65140
65190
  const NORMALIZED_WHITESPACE = /\s+/g;
65141
65191
  const normalizeText = (text) => text.replace(NORMALIZED_WHITESPACE, " ");
@@ -65235,7 +65285,7 @@ class MeasureCache {
65235
65285
  if (this.cache.has(key2)) {
65236
65286
  this.cache.delete(key2);
65237
65287
  }
65238
- if (this.cache.size >= MAX_CACHE_SIZE) {
65288
+ if (this.cache.size >= MAX_CACHE_SIZE$1) {
65239
65289
  const oldestKey = this.cache.keys().next().value;
65240
65290
  if (oldestKey !== void 0) {
65241
65291
  this.cache.delete(oldestKey);
@@ -65309,13 +65359,13 @@ class MeasureCache {
65309
65359
  * Get maximum cache size
65310
65360
  */
65311
65361
  getMaxSize() {
65312
- return MAX_CACHE_SIZE;
65362
+ return MAX_CACHE_SIZE$1;
65313
65363
  }
65314
65364
  /**
65315
65365
  * Check if cache is near capacity
65316
65366
  */
65317
65367
  isNearCapacity(threshold = 0.9) {
65318
- return this.cache.size >= MAX_CACHE_SIZE * threshold;
65368
+ return this.cache.size >= MAX_CACHE_SIZE$1 * threshold;
65319
65369
  }
65320
65370
  /**
65321
65371
  * Update size statistics
@@ -66327,9 +66377,46 @@ async function incrementalLayout(previousBlocks, _previousLayout, nextBlocks, op
66327
66377
  perfLog(
66328
66378
  `[Perf] 4.1 Measure all blocks: ${(measureEnd - measureStart).toFixed(2)}ms (${cacheMisses} measured, ${cacheHits} cached)`
66329
66379
  );
66380
+ let headerContentHeights;
66381
+ if (headerFooter?.constraints && headerFooter.headerBlocks) {
66382
+ const hfPreStart = performance.now();
66383
+ const measureFn = headerFooter.measure ?? measureBlock2;
66384
+ invalidateHeaderFooterCache(
66385
+ headerMeasureCache,
66386
+ headerFooterCacheState,
66387
+ headerFooter.headerBlocks,
66388
+ headerFooter.footerBlocks,
66389
+ headerFooter.constraints,
66390
+ options.sectionMetadata
66391
+ );
66392
+ const HEADER_PRELAYOUT_PLACEHOLDER_PAGE_COUNT = 1;
66393
+ const preHeaderLayouts = await layoutHeaderFooterWithCache(
66394
+ headerFooter.headerBlocks,
66395
+ headerFooter.constraints,
66396
+ measureFn,
66397
+ headerMeasureCache,
66398
+ HEADER_PRELAYOUT_PLACEHOLDER_PAGE_COUNT,
66399
+ void 0
66400
+ // No page resolver needed for height calculation
66401
+ );
66402
+ const isValidHeaderType = (key2) => {
66403
+ return ["default", "first", "even", "odd"].includes(key2);
66404
+ };
66405
+ headerContentHeights = {};
66406
+ for (const [type2, value] of Object.entries(preHeaderLayouts)) {
66407
+ if (!isValidHeaderType(type2)) continue;
66408
+ if (value?.layout && typeof value.layout.height === "number") {
66409
+ headerContentHeights[type2] = value.layout.height;
66410
+ }
66411
+ }
66412
+ const hfPreEnd = performance.now();
66413
+ perfLog(`[Perf] 4.1.5 Pre-layout headers for height: ${(hfPreEnd - hfPreStart).toFixed(2)}ms`);
66414
+ }
66330
66415
  const layoutStart = performance.now();
66331
66416
  let layout = layoutDocument(nextBlocks, measures, {
66332
66417
  ...options,
66418
+ headerContentHeights,
66419
+ // Pass header heights to prevent overlap
66333
66420
  remeasureParagraph: (block, maxWidth) => remeasureParagraph(block, maxWidth)
66334
66421
  });
66335
66422
  const layoutEnd = performance.now();
@@ -66376,6 +66463,8 @@ async function incrementalLayout(previousBlocks, _previousLayout, nextBlocks, op
66376
66463
  const relayoutStart = performance.now();
66377
66464
  layout = layoutDocument(currentBlocks, currentMeasures, {
66378
66465
  ...options,
66466
+ headerContentHeights,
66467
+ // Pass header heights to prevent overlap
66379
66468
  remeasureParagraph: (block, maxWidth) => remeasureParagraph(block, maxWidth)
66380
66469
  });
66381
66470
  const relayoutEnd = performance.now();
@@ -69425,7 +69514,12 @@ const lineStyles = (lineHeight2) => ({
69425
69514
  height: `${lineHeight2}px`,
69426
69515
  position: "relative",
69427
69516
  display: "block",
69428
- whiteSpace: "pre"
69517
+ whiteSpace: "pre",
69518
+ // Allow text to overflow the line container as a safety net.
69519
+ // The primary fix uses accurate font metrics from Canvas API, but this
69520
+ // provides defense-in-depth against any remaining sub-pixel rendering
69521
+ // differences between measurement and display.
69522
+ overflow: "visible"
69429
69523
  });
69430
69524
  const PRINT_STYLES = `
69431
69525
  @media print {
@@ -70991,7 +71085,8 @@ const _DomPainter = class _DomPainter2 {
70991
71085
  const paraIndent = block.attrs?.indent;
70992
71086
  const paraIndentLeft = paraIndent?.left ?? 0;
70993
71087
  const paraIndentRight = paraIndent?.right ?? 0;
70994
- const firstLineOffset = (paraIndent?.firstLine ?? 0) - (paraIndent?.hanging ?? 0);
71088
+ const suppressFirstLineIndent = block.attrs?.suppressFirstLineIndent === true;
71089
+ const firstLineOffset = suppressFirstLineIndent ? 0 : (paraIndent?.firstLine ?? 0) - (paraIndent?.hanging ?? 0);
70995
71090
  lines.forEach((line, index2) => {
70996
71091
  const lineEl = this.renderLine(block, line, context);
70997
71092
  const isListFirstLine = index2 === 0 && !fragment.continuesFromPrev && fragment.markerWidth && wordLayout?.marker;
@@ -73180,6 +73275,61 @@ function evictIfNeeded() {
73180
73275
  cache$1.delete(oldestKey);
73181
73276
  }
73182
73277
  }
73278
+ const fontMetricsCache = /* @__PURE__ */ new Map();
73279
+ const MAX_CACHE_SIZE = 1e3;
73280
+ const METRICS_TEST_STRING = "MHgypbdlÁÉÍ";
73281
+ function getFontKey(fontInfo) {
73282
+ return `${fontInfo.fontFamily}|${fontInfo.fontSize}|${fontInfo.bold ?? false}|${fontInfo.italic ?? false}`;
73283
+ }
73284
+ function buildFontStringForMetrics(fontInfo, mode, fonts) {
73285
+ const parts = [];
73286
+ if (fontInfo.italic) parts.push("italic");
73287
+ if (fontInfo.bold) parts.push("bold");
73288
+ parts.push(`${fontInfo.fontSize}px`);
73289
+ {
73290
+ parts.push(fontInfo.fontFamily);
73291
+ }
73292
+ return parts.join(" ");
73293
+ }
73294
+ function getFontMetrics(ctx2, fontInfo, mode, fonts) {
73295
+ if (!ctx2 || typeof ctx2 !== "object") {
73296
+ throw new TypeError("Canvas context must be a valid CanvasRenderingContext2D object");
73297
+ }
73298
+ if (typeof fontInfo.fontSize !== "number" || !Number.isFinite(fontInfo.fontSize) || fontInfo.fontSize <= 0) {
73299
+ throw new TypeError(
73300
+ `Font size must be a positive finite number, got: ${typeof fontInfo.fontSize === "number" ? fontInfo.fontSize : typeof fontInfo.fontSize}`
73301
+ );
73302
+ }
73303
+ if (typeof fontInfo.fontFamily !== "string" || fontInfo.fontFamily.trim().length === 0) {
73304
+ throw new TypeError("Font family must be a non-empty string");
73305
+ }
73306
+ const key2 = getFontKey(fontInfo);
73307
+ const cached = fontMetricsCache.get(key2);
73308
+ if (cached) {
73309
+ return cached;
73310
+ }
73311
+ const font = buildFontStringForMetrics(fontInfo);
73312
+ ctx2.font = font;
73313
+ const textMetrics = ctx2.measureText(METRICS_TEST_STRING);
73314
+ let ascent;
73315
+ let descent;
73316
+ if (typeof textMetrics.actualBoundingBoxAscent === "number" && typeof textMetrics.actualBoundingBoxDescent === "number" && textMetrics.actualBoundingBoxAscent > 0) {
73317
+ ascent = textMetrics.actualBoundingBoxAscent;
73318
+ descent = textMetrics.actualBoundingBoxDescent;
73319
+ } else {
73320
+ ascent = fontInfo.fontSize * 0.8;
73321
+ descent = fontInfo.fontSize * 0.2;
73322
+ }
73323
+ const result = { ascent, descent };
73324
+ if (fontMetricsCache.size >= MAX_CACHE_SIZE) {
73325
+ const firstKey = fontMetricsCache.keys().next().value;
73326
+ if (firstKey) {
73327
+ fontMetricsCache.delete(firstKey);
73328
+ }
73329
+ }
73330
+ fontMetricsCache.set(key2, result);
73331
+ return result;
73332
+ }
73183
73333
  const { computeTabStops } = Engines;
73184
73334
  let canvasContext = null;
73185
73335
  const DEFAULT_TAB_INTERVAL_TWIPS = 720;
@@ -73227,10 +73377,20 @@ function measureText(text, font, ctx2, _fontFamily, _letterSpacing) {
73227
73377
  return Math.max(advanceWidth, paintedWidth);
73228
73378
  }
73229
73379
  const MIN_SINGLE_LINE_PX = 12 * 96 / 72;
73230
- function calculateTypographyMetrics(fontSize2, spacing) {
73231
- const ascent = roundValue(fontSize2 * 0.8);
73232
- const descent = roundValue(fontSize2 * 0.2);
73233
- const baseLineHeight = Math.max(fontSize2, MIN_SINGLE_LINE_PX);
73380
+ const LINE_HEIGHT_SAFETY_MARGIN_PX = 1;
73381
+ function calculateTypographyMetrics(fontSize2, spacing, fontInfo) {
73382
+ let ascent;
73383
+ let descent;
73384
+ if (fontInfo) {
73385
+ const ctx2 = getCanvasContext();
73386
+ const metrics = getFontMetrics(ctx2, fontInfo);
73387
+ ascent = roundValue(metrics.ascent);
73388
+ descent = roundValue(metrics.descent);
73389
+ } else {
73390
+ ascent = roundValue(fontSize2 * 0.8);
73391
+ descent = roundValue(fontSize2 * 0.2);
73392
+ }
73393
+ const baseLineHeight = Math.max(ascent + descent + LINE_HEIGHT_SAFETY_MARGIN_PX, MIN_SINGLE_LINE_PX);
73234
73394
  const lineHeight2 = roundValue(resolveLineHeight(spacing, baseLineHeight));
73235
73395
  return {
73236
73396
  ascent,
@@ -73238,6 +73398,20 @@ function calculateTypographyMetrics(fontSize2, spacing) {
73238
73398
  lineHeight: lineHeight2
73239
73399
  };
73240
73400
  }
73401
+ function getFontInfoFromRun(run2) {
73402
+ return {
73403
+ fontFamily: run2.fontFamily,
73404
+ fontSize: run2.fontSize,
73405
+ bold: run2.bold,
73406
+ italic: run2.italic
73407
+ };
73408
+ }
73409
+ function updateMaxFontInfo(currentMaxSize, currentMaxInfo, newRun) {
73410
+ if (newRun.fontSize >= currentMaxSize) {
73411
+ return getFontInfoFromRun(newRun);
73412
+ }
73413
+ return currentMaxInfo;
73414
+ }
73241
73415
  function isTabRun(run2) {
73242
73416
  return run2.kind === "tab";
73243
73417
  }
@@ -73283,7 +73457,8 @@ async function measureParagraphBlock(block, maxWidth) {
73283
73457
  const indentRight = sanitizePositive(indent?.right);
73284
73458
  const firstLine = indent?.firstLine ?? 0;
73285
73459
  const hanging = indent?.hanging ?? 0;
73286
- const firstLineOffset = firstLine - hanging;
73460
+ const suppressFirstLine = block.attrs?.suppressFirstLineIndent === true;
73461
+ const firstLineOffset = suppressFirstLine ? 0 : firstLine - hanging;
73287
73462
  const contentWidth = Math.max(1, maxWidth - indentLeft - indentRight);
73288
73463
  const initialAvailableWidth = Math.max(1, contentWidth - firstLineOffset);
73289
73464
  const tabStops = buildTabStopsPx(
@@ -73419,7 +73594,7 @@ async function measureParagraphBlock(block, maxWidth) {
73419
73594
  const run2 = runsToProcess[runIndex];
73420
73595
  if (run2.kind === "break") {
73421
73596
  if (currentLine) {
73422
- const metrics = calculateTypographyMetrics(currentLine.maxFontSize, spacing);
73597
+ const metrics = calculateTypographyMetrics(currentLine.maxFontSize, spacing, currentLine.maxFontInfo);
73423
73598
  const completedLine = { ...currentLine, ...metrics };
73424
73599
  addBarTabsToLine(completedLine);
73425
73600
  lines.push(completedLine);
@@ -73449,7 +73624,7 @@ async function measureParagraphBlock(block, maxWidth) {
73449
73624
  }
73450
73625
  if (isLineBreakRun(run2)) {
73451
73626
  if (currentLine) {
73452
- const metrics = calculateTypographyMetrics(currentLine.maxFontSize, spacing);
73627
+ const metrics = calculateTypographyMetrics(currentLine.maxFontSize, spacing, currentLine.maxFontInfo);
73453
73628
  const completedLine = {
73454
73629
  ...currentLine,
73455
73630
  ...metrics
@@ -73562,7 +73737,7 @@ async function measureParagraphBlock(block, maxWidth) {
73562
73737
  }
73563
73738
  const appliedTabAlign = lastAppliedTabAlign;
73564
73739
  if (currentLine.width + imageWidth > currentLine.maxWidth && currentLine.width > 0) {
73565
- const metrics = calculateTypographyMetrics(currentLine.maxFontSize, spacing);
73740
+ const metrics = calculateTypographyMetrics(currentLine.maxFontSize, spacing, currentLine.maxFontInfo);
73566
73741
  const completedLine = {
73567
73742
  ...currentLine,
73568
73743
  ...metrics
@@ -73650,6 +73825,7 @@ async function measureParagraphBlock(block, maxWidth) {
73650
73825
  toChar: wordEndNoSpace,
73651
73826
  width: wordOnlyWidth,
73652
73827
  maxFontSize: run2.fontSize,
73828
+ maxFontInfo: getFontInfoFromRun(run2),
73653
73829
  maxWidth: getEffectiveWidth(initialAvailableWidth),
73654
73830
  segments: [{ runIndex, fromChar: wordStartChar, toChar: wordEndNoSpace, width: wordOnlyWidth }]
73655
73831
  };
@@ -73666,7 +73842,7 @@ async function measureParagraphBlock(block, maxWidth) {
73666
73842
  const isTocEntry = block.attrs?.isTocEntry;
73667
73843
  const boundarySpacing = currentLine.width > 0 ? run2.letterSpacing ?? 0 : 0;
73668
73844
  if (currentLine.width + boundarySpacing + wordOnlyWidth > currentLine.maxWidth - WIDTH_FUDGE_PX && currentLine.width > 0 && !isTocEntry) {
73669
- const metrics = calculateTypographyMetrics(currentLine.maxFontSize, spacing);
73845
+ const metrics = calculateTypographyMetrics(currentLine.maxFontSize, spacing, currentLine.maxFontInfo);
73670
73846
  const completedLine = {
73671
73847
  ...currentLine,
73672
73848
  ...metrics
@@ -73682,6 +73858,7 @@ async function measureParagraphBlock(block, maxWidth) {
73682
73858
  toChar: wordEndNoSpace,
73683
73859
  width: wordOnlyWidth,
73684
73860
  maxFontSize: run2.fontSize,
73861
+ maxFontInfo: getFontInfoFromRun(run2),
73685
73862
  maxWidth: getEffectiveWidth(contentWidth),
73686
73863
  segments: [{ runIndex, fromChar: wordStartChar, toChar: wordEndNoSpace, width: wordOnlyWidth }]
73687
73864
  };
@@ -73697,9 +73874,10 @@ async function measureParagraphBlock(block, maxWidth) {
73697
73874
  if (!isLastWord && currentLine.width + boundarySpacing + wordOnlyWidth + spaceWidth > currentLine.maxWidth - WIDTH_FUDGE_PX) {
73698
73875
  currentLine.toChar = wordEndNoSpace;
73699
73876
  currentLine.width = roundValue(currentLine.width + boundarySpacing + wordOnlyWidth);
73877
+ currentLine.maxFontInfo = updateMaxFontInfo(currentLine.maxFontSize, currentLine.maxFontInfo, run2);
73700
73878
  currentLine.maxFontSize = Math.max(currentLine.maxFontSize, run2.fontSize);
73701
73879
  appendSegment(currentLine.segments, runIndex, wordStartChar, wordEndNoSpace, wordOnlyWidth, segmentStartX);
73702
- const metrics = calculateTypographyMetrics(currentLine.maxFontSize, spacing);
73880
+ const metrics = calculateTypographyMetrics(currentLine.maxFontSize, spacing, currentLine.maxFontInfo);
73703
73881
  const completedLine = { ...currentLine, ...metrics };
73704
73882
  addBarTabsToLine(completedLine);
73705
73883
  lines.push(completedLine);
@@ -73716,6 +73894,7 @@ async function measureParagraphBlock(block, maxWidth) {
73716
73894
  currentLine.width = roundValue(
73717
73895
  currentLine.width + boundarySpacing + wordCommitWidth + (isLastWord ? 0 : run2.letterSpacing ?? 0)
73718
73896
  );
73897
+ currentLine.maxFontInfo = updateMaxFontInfo(currentLine.maxFontSize, currentLine.maxFontInfo, run2);
73719
73898
  currentLine.maxFontSize = Math.max(currentLine.maxFontSize, run2.fontSize);
73720
73899
  appendSegment(currentLine.segments, runIndex, wordStartChar, newToChar, wordCommitWidth, explicitX);
73721
73900
  }
@@ -73738,6 +73917,7 @@ async function measureParagraphBlock(block, maxWidth) {
73738
73917
  toChar: charPosInRun,
73739
73918
  width: 0,
73740
73919
  maxFontSize: run2.fontSize,
73920
+ maxFontInfo: getFontInfoFromRun(run2),
73741
73921
  maxWidth: getEffectiveWidth(initialAvailableWidth),
73742
73922
  segments: []
73743
73923
  };
@@ -73747,6 +73927,7 @@ async function measureParagraphBlock(block, maxWidth) {
73747
73927
  tabStopCursor = nextIndex;
73748
73928
  const tabAdvance = Math.max(0, target - currentLine.width);
73749
73929
  currentLine.width = roundValue(currentLine.width + tabAdvance);
73930
+ currentLine.maxFontInfo = updateMaxFontInfo(currentLine.maxFontSize, currentLine.maxFontInfo, run2);
73750
73931
  currentLine.maxFontSize = Math.max(currentLine.maxFontSize, run2.fontSize);
73751
73932
  currentLine.toRun = runIndex;
73752
73933
  currentLine.toChar = charPosInRun;
@@ -73783,7 +73964,7 @@ async function measureParagraphBlock(block, maxWidth) {
73783
73964
  lines.push(fallbackLine);
73784
73965
  }
73785
73966
  if (currentLine) {
73786
- const metrics = calculateTypographyMetrics(currentLine.maxFontSize, spacing);
73967
+ const metrics = calculateTypographyMetrics(currentLine.maxFontSize, spacing, currentLine.maxFontInfo);
73787
73968
  const finalLine = {
73788
73969
  ...currentLine,
73789
73970
  ...metrics