@harbour-enterprises/superdoc 1.5.0 → 1.6.0-next.1

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.
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  const jszip = require("./jszip-C8_CqJxM.cjs");
3
3
  const helpers$1 = require("./helpers-nOdwpmwb.cjs");
4
- const superEditor_converter = require("./SuperConverter-DmX1tktl.cjs");
4
+ const superEditor_converter = require("./SuperConverter-DpKjVrPl.cjs");
5
5
  const vue = require("./vue-De9wkgLl.cjs");
6
6
  require("./jszip.min-BPh2MMAa.cjs");
7
7
  const eventemitter3 = require("./eventemitter3-BQuRcMPI.cjs");
@@ -15524,7 +15524,7 @@ const canUseDOM = () => {
15524
15524
  return false;
15525
15525
  }
15526
15526
  };
15527
- const summaryVersion = "1.5.0";
15527
+ const summaryVersion = "1.6.0-next.1";
15528
15528
  const nodeKeys = ["group", "content", "marks", "inline", "atom", "defining", "code", "tableRole", "summary"];
15529
15529
  const markKeys = ["group", "inclusive", "excludes", "spanning", "code"];
15530
15530
  function mapAttributes(attrs) {
@@ -17781,6 +17781,10 @@ class Editor extends EventEmitter {
17781
17781
  const hasCustomSettings = !!this.converter.convertedXml["word/settings.xml"]?.elements?.length;
17782
17782
  const customSettings = hasCustomSettings ? this.converter.schemaToXml(this.converter.convertedXml["word/settings.xml"]?.elements?.[0]) : null;
17783
17783
  const rels = this.converter.schemaToXml(this.converter.convertedXml["word/_rels/document.xml.rels"].elements[0]);
17784
+ const footnotesData = this.converter.convertedXml["word/footnotes.xml"];
17785
+ const footnotesXml = footnotesData?.elements?.[0] ? this.converter.schemaToXml(footnotesData.elements[0]) : null;
17786
+ const footnotesRelsData = this.converter.convertedXml["word/_rels/footnotes.xml.rels"];
17787
+ const footnotesRelsXml = footnotesRelsData?.elements?.[0] ? this.converter.schemaToXml(footnotesRelsData.elements[0]) : null;
17784
17788
  const media = this.converter.addedMedia;
17785
17789
  const updatedHeadersFooters = {};
17786
17790
  Object.entries(this.converter.convertedXml).forEach(([name, json2]) => {
@@ -17805,6 +17809,12 @@ class Editor extends EventEmitter {
17805
17809
  if (hasCustomSettings) {
17806
17810
  updatedDocs["word/settings.xml"] = String(customSettings);
17807
17811
  }
17812
+ if (footnotesXml) {
17813
+ updatedDocs["word/footnotes.xml"] = String(footnotesXml);
17814
+ }
17815
+ if (footnotesRelsXml) {
17816
+ updatedDocs["word/_rels/footnotes.xml.rels"] = String(footnotesRelsXml);
17817
+ }
17808
17818
  if (comments.length) {
17809
17819
  const commentsXml = this.converter.schemaToXml(this.converter.convertedXml["word/comments.xml"].elements[0]);
17810
17820
  const commentsExtendedXml = this.converter.schemaToXml(
@@ -18181,7 +18191,7 @@ class Editor extends EventEmitter {
18181
18191
  * Process collaboration migrations
18182
18192
  */
18183
18193
  processCollaborationMigrations() {
18184
- console.debug("[checkVersionMigrations] Current editor version", "1.5.0");
18194
+ console.debug("[checkVersionMigrations] Current editor version", "1.6.0-next.1");
18185
18195
  if (!this.options.ydoc) return;
18186
18196
  const metaMap = this.options.ydoc.getMap("meta");
18187
18197
  let docVersion = metaMap.get("version");
@@ -34062,7 +34072,7 @@ function getParagraphSpacingBefore(block) {
34062
34072
  const value = spacing?.before ?? spacing?.lineSpaceBefore;
34063
34073
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
34064
34074
  }
34065
- function getParagraphSpacingAfter(block) {
34075
+ function getParagraphSpacingAfter$1(block) {
34066
34076
  const spacing = block.attrs?.spacing;
34067
34077
  const value = spacing?.after ?? spacing?.lineSpaceAfter;
34068
34078
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
@@ -34089,7 +34099,7 @@ function getMeasureHeight(block, measure) {
34089
34099
  }
34090
34100
  const DEFAULT_PAGE_SIZE$2 = { w: 612, h: 792 };
34091
34101
  const DEFAULT_MARGINS$2 = { top: 72, right: 72, bottom: 72, left: 72 };
34092
- const COLUMN_EPSILON = 1e-4;
34102
+ const COLUMN_EPSILON$1 = 1e-4;
34093
34103
  const layoutDebugEnabled$1 = typeof vue.process$1 !== "undefined" && typeof vue.process$1.env !== "undefined" && Boolean(vue.process$1.env.SD_DEBUG_LAYOUT);
34094
34104
  const layoutLog = (...args) => {
34095
34105
  if (!layoutDebugEnabled$1) return;
@@ -34400,7 +34410,13 @@ function layoutDocument(blocks, measures, options = {}) {
34400
34410
  const paginator = createPaginator({
34401
34411
  margins: paginatorMargins,
34402
34412
  getActiveTopMargin: () => activeTopMargin,
34403
- getActiveBottomMargin: () => activeBottomMargin,
34413
+ getActiveBottomMargin: () => {
34414
+ const reserves = options.footnoteReservedByPageIndex;
34415
+ const pageIndex = Math.max(0, pageCount - 1);
34416
+ const reserve = Array.isArray(reserves) ? reserves[pageIndex] : 0;
34417
+ const reservePx = typeof reserve === "number" && Number.isFinite(reserve) && reserve > 0 ? reserve : 0;
34418
+ return activeBottomMargin + reservePx;
34419
+ },
34404
34420
  getActiveHeaderDistance: () => activeHeaderDistance,
34405
34421
  getActiveFooterDistance: () => activeFooterDistance,
34406
34422
  getActivePageSize: () => activePageSize,
@@ -34884,7 +34900,7 @@ function layoutDocument(blocks, measures, options = {}) {
34884
34900
  if (!shouldSkipAnchoredTable) {
34885
34901
  let state = paginator.ensurePage();
34886
34902
  const availableHeight = state.contentBottom - state.cursorY;
34887
- const spacingAfter = getParagraphSpacingAfter(paraBlock);
34903
+ const spacingAfter = getParagraphSpacingAfter$1(paraBlock);
34888
34904
  const currentHeight = getMeasureHeight(paraBlock, measure);
34889
34905
  const nextHeight = getMeasureHeight(nextBlock, nextMeasure);
34890
34906
  const nextIsParagraph = nextBlock.kind === "paragraph" && nextMeasure.kind === "paragraph";
@@ -35197,7 +35213,7 @@ function normalizeColumns(input, contentWidth) {
35197
35213
  const gap = Math.max(0, input?.gap ?? 0);
35198
35214
  const totalGap = gap * (count - 1);
35199
35215
  const width = (contentWidth - totalGap) / count;
35200
- if (width <= COLUMN_EPSILON) {
35216
+ if (width <= COLUMN_EPSILON$1) {
35201
35217
  return {
35202
35218
  count: 1,
35203
35219
  gap: 0,
@@ -36960,6 +36976,417 @@ const perfLog = (...args) => {
36960
36976
  if (!layoutDebugEnabled) return;
36961
36977
  console.log(...args);
36962
36978
  };
36979
+ const isFootnotesLayoutInput = (value) => {
36980
+ if (!value || typeof value !== "object") return false;
36981
+ const v = value;
36982
+ if (!Array.isArray(v.refs)) return false;
36983
+ if (!(v.blocksById instanceof Map)) return false;
36984
+ return true;
36985
+ };
36986
+ const findPageIndexForPos = (layout, pos) => {
36987
+ if (!Number.isFinite(pos)) return null;
36988
+ const fallbackRanges = [];
36989
+ for (let pageIndex = 0; pageIndex < layout.pages.length; pageIndex++) {
36990
+ const page = layout.pages[pageIndex];
36991
+ let minStart = null;
36992
+ let maxEnd = null;
36993
+ for (const fragment of page.fragments) {
36994
+ const pmStart = fragment.pmStart;
36995
+ const pmEnd = fragment.pmEnd;
36996
+ if (pmStart == null || pmEnd == null) continue;
36997
+ if (minStart == null || pmStart < minStart) minStart = pmStart;
36998
+ if (maxEnd == null || pmEnd > maxEnd) maxEnd = pmEnd;
36999
+ if (pos >= pmStart && pos <= pmEnd) {
37000
+ return pageIndex;
37001
+ }
37002
+ }
37003
+ fallbackRanges[pageIndex] = minStart != null && maxEnd != null ? { pageIndex, minStart, maxEnd } : null;
37004
+ }
37005
+ let best = null;
37006
+ for (const entry of fallbackRanges) {
37007
+ if (!entry) continue;
37008
+ const distance = pos < entry.minStart ? entry.minStart - pos : pos > entry.maxEnd ? pos - entry.maxEnd : 0;
37009
+ if (!best || distance < best.distance) {
37010
+ best = { pageIndex: entry.pageIndex, distance };
37011
+ }
37012
+ }
37013
+ if (best) return best.pageIndex;
37014
+ if (layout.pages.length > 0) return layout.pages.length - 1;
37015
+ return null;
37016
+ };
37017
+ const footnoteColumnKey = (pageIndex, columnIndex) => `${pageIndex}:${columnIndex}`;
37018
+ const COLUMN_EPSILON = 0.01;
37019
+ const normalizeColumnsForFootnotes = (input, contentWidth) => {
37020
+ const rawCount = Number.isFinite(input?.count) ? Math.floor(input.count) : 1;
37021
+ const count = Math.max(1, rawCount || 1);
37022
+ const gap = Math.max(0, input?.gap ?? 0);
37023
+ const totalGap = gap * (count - 1);
37024
+ const width = (contentWidth - totalGap) / count;
37025
+ if (!Number.isFinite(width) || width <= COLUMN_EPSILON) {
37026
+ return {
37027
+ count: 1,
37028
+ gap: 0,
37029
+ width: Math.max(0, contentWidth)
37030
+ };
37031
+ }
37032
+ return { count, gap, width };
37033
+ };
37034
+ const resolveSectionColumnsByIndex = (options, blocks) => {
37035
+ const result = /* @__PURE__ */ new Map();
37036
+ let activeColumns = options.columns ?? { count: 1, gap: 0 };
37037
+ if (blocks && blocks.length > 0) {
37038
+ for (const block of blocks) {
37039
+ if (block.kind !== "sectionBreak") continue;
37040
+ const sectionIndexRaw = block.attrs?.sectionIndex;
37041
+ const sectionIndex = typeof sectionIndexRaw === "number" && Number.isFinite(sectionIndexRaw) ? sectionIndexRaw : result.size;
37042
+ if (block.columns) {
37043
+ activeColumns = { count: block.columns.count, gap: block.columns.gap };
37044
+ }
37045
+ result.set(sectionIndex, { ...activeColumns });
37046
+ }
37047
+ }
37048
+ if (result.size === 0) {
37049
+ result.set(0, { ...activeColumns });
37050
+ }
37051
+ return result;
37052
+ };
37053
+ const resolvePageColumns = (layout, options, blocks) => {
37054
+ const sectionColumns = resolveSectionColumnsByIndex(options, blocks);
37055
+ const result = /* @__PURE__ */ new Map();
37056
+ for (let pageIndex = 0; pageIndex < layout.pages.length; pageIndex += 1) {
37057
+ const page = layout.pages[pageIndex];
37058
+ const pageSize = page.size ?? layout.pageSize ?? DEFAULT_PAGE_SIZE$1;
37059
+ const marginLeft = normalizeMargin(
37060
+ page.margins?.left,
37061
+ normalizeMargin(options.margins?.left, DEFAULT_MARGINS$1.left)
37062
+ );
37063
+ const marginRight = normalizeMargin(
37064
+ page.margins?.right,
37065
+ normalizeMargin(options.margins?.right, DEFAULT_MARGINS$1.right)
37066
+ );
37067
+ const contentWidth = pageSize.w - (marginLeft + marginRight);
37068
+ const sectionIndex = page.sectionIndex ?? 0;
37069
+ const columnsConfig = sectionColumns.get(sectionIndex) ?? options.columns ?? { count: 1, gap: 0 };
37070
+ const normalized = normalizeColumnsForFootnotes(columnsConfig, contentWidth);
37071
+ result.set(pageIndex, { ...normalized, left: marginLeft, contentWidth });
37072
+ }
37073
+ return result;
37074
+ };
37075
+ const findFragmentForPos = (page, pos) => {
37076
+ for (const fragment of page.fragments) {
37077
+ const pmStart = fragment.pmStart;
37078
+ const pmEnd = fragment.pmEnd;
37079
+ if (pmStart == null || pmEnd == null) continue;
37080
+ if (pos >= pmStart && pos <= pmEnd) {
37081
+ return fragment;
37082
+ }
37083
+ }
37084
+ return null;
37085
+ };
37086
+ const assignFootnotesToColumns = (layout, refs, pageColumns) => {
37087
+ const result = /* @__PURE__ */ new Map();
37088
+ const seenByColumn = /* @__PURE__ */ new Map();
37089
+ for (const ref of refs) {
37090
+ const pageIndex = findPageIndexForPos(layout, ref.pos);
37091
+ if (pageIndex == null) continue;
37092
+ const columns = pageColumns.get(pageIndex);
37093
+ const page = layout.pages[pageIndex];
37094
+ let columnIndex = 0;
37095
+ if (columns && columns.count > 1 && page) {
37096
+ const fragment = findFragmentForPos(page, ref.pos);
37097
+ if (fragment && typeof fragment.x === "number") {
37098
+ const columnStride = columns.width + columns.gap;
37099
+ const rawIndex = columnStride > 0 ? Math.floor((fragment.x - columns.left) / columnStride) : 0;
37100
+ columnIndex = Math.max(0, Math.min(columns.count - 1, rawIndex));
37101
+ }
37102
+ }
37103
+ const key2 = footnoteColumnKey(pageIndex, columnIndex);
37104
+ let seen = seenByColumn.get(key2);
37105
+ if (!seen) {
37106
+ seen = /* @__PURE__ */ new Set();
37107
+ seenByColumn.set(key2, seen);
37108
+ }
37109
+ if (seen.has(ref.id)) continue;
37110
+ seen.add(ref.id);
37111
+ const pageMap = result.get(pageIndex) ?? /* @__PURE__ */ new Map();
37112
+ const list = pageMap.get(columnIndex) ?? [];
37113
+ list.push(ref.id);
37114
+ pageMap.set(columnIndex, list);
37115
+ result.set(pageIndex, pageMap);
37116
+ }
37117
+ return result;
37118
+ };
37119
+ const resolveFootnoteMeasurementWidth = (options, blocks) => {
37120
+ const pageSize = options.pageSize ?? DEFAULT_PAGE_SIZE$1;
37121
+ const margins = {
37122
+ right: normalizeMargin(options.margins?.right, DEFAULT_MARGINS$1.right),
37123
+ left: normalizeMargin(options.margins?.left, DEFAULT_MARGINS$1.left)
37124
+ };
37125
+ let width = pageSize.w - (margins.left + margins.right);
37126
+ let activeColumns = options.columns ?? { count: 1, gap: 0 };
37127
+ let activePageSize = pageSize;
37128
+ let activeMargins = { ...margins };
37129
+ const resolveColumnWidth = () => {
37130
+ const contentWidth = activePageSize.w - (activeMargins.left + activeMargins.right);
37131
+ const normalized = normalizeColumnsForFootnotes(activeColumns, contentWidth);
37132
+ return normalized.width;
37133
+ };
37134
+ width = resolveColumnWidth();
37135
+ if (blocks && blocks.length > 0) {
37136
+ for (const block of blocks) {
37137
+ if (block.kind !== "sectionBreak") continue;
37138
+ activePageSize = block.pageSize ?? activePageSize;
37139
+ activeMargins = {
37140
+ right: normalizeMargin(block.margins?.right, activeMargins.right),
37141
+ left: normalizeMargin(block.margins?.left, activeMargins.left)
37142
+ };
37143
+ if (block.columns) {
37144
+ activeColumns = { count: block.columns.count, gap: block.columns.gap };
37145
+ }
37146
+ const w = resolveColumnWidth();
37147
+ if (w > 0 && w < width) width = w;
37148
+ }
37149
+ }
37150
+ if (!Number.isFinite(width) || width <= 0) return 0;
37151
+ return width;
37152
+ };
37153
+ const MIN_FOOTNOTE_BODY_HEIGHT = 1;
37154
+ const DEFAULT_FOOTNOTE_SEPARATOR_SPACING_BEFORE = 12;
37155
+ const computeMaxFootnoteReserve = (layoutForPages, pageIndex, baseReserve = 0) => {
37156
+ const page = layoutForPages.pages?.[pageIndex];
37157
+ if (!page) return 0;
37158
+ const pageSize = page.size ?? layoutForPages.pageSize ?? DEFAULT_PAGE_SIZE$1;
37159
+ const topMargin = normalizeMargin(page.margins?.top, DEFAULT_MARGINS$1.top);
37160
+ const bottomWithReserve = normalizeMargin(page.margins?.bottom, DEFAULT_MARGINS$1.bottom);
37161
+ const baseReserveSafe = Number.isFinite(baseReserve) ? Math.max(0, baseReserve) : 0;
37162
+ const bottomMargin = Math.max(0, bottomWithReserve - baseReserveSafe);
37163
+ const availableForBody = pageSize.h - topMargin - bottomMargin;
37164
+ if (!Number.isFinite(availableForBody)) return 0;
37165
+ return Math.max(0, availableForBody - MIN_FOOTNOTE_BODY_HEIGHT);
37166
+ };
37167
+ const sumLineHeights$1 = (lines, fromLine, toLine) => {
37168
+ if (!lines || fromLine >= toLine) return 0;
37169
+ let total = 0;
37170
+ for (let i = fromLine; i < toLine; i += 1) {
37171
+ total += lines[i]?.lineHeight ?? 0;
37172
+ }
37173
+ return total;
37174
+ };
37175
+ const getParagraphSpacingAfter = (block) => {
37176
+ const spacing = block.attrs?.spacing;
37177
+ const value = spacing?.after ?? spacing?.lineSpaceAfter;
37178
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
37179
+ };
37180
+ const resolveSeparatorSpacingBefore = (rangesByFootnoteId, measuresById, explicitValue, fallbackValue) => {
37181
+ if (typeof explicitValue === "number" && Number.isFinite(explicitValue)) {
37182
+ return Math.max(0, explicitValue);
37183
+ }
37184
+ for (const ranges of rangesByFootnoteId.values()) {
37185
+ for (const range of ranges) {
37186
+ if (range.kind === "paragraph") {
37187
+ const measure = measuresById.get(range.blockId);
37188
+ if (measure?.kind !== "paragraph") continue;
37189
+ const lineHeight2 = measure.lines?.[range.fromLine]?.lineHeight ?? measure.lines?.[0]?.lineHeight;
37190
+ if (typeof lineHeight2 === "number" && Number.isFinite(lineHeight2) && lineHeight2 > 0) {
37191
+ return lineHeight2;
37192
+ }
37193
+ }
37194
+ if (range.kind === "list-item") {
37195
+ const measure = measuresById.get(range.blockId);
37196
+ if (measure?.kind !== "list") continue;
37197
+ const itemMeasure = measure.items.find((item) => item.itemId === range.itemId);
37198
+ const lineHeight2 = itemMeasure?.paragraph?.lines?.[range.fromLine]?.lineHeight ?? itemMeasure?.paragraph?.lines?.[0]?.lineHeight;
37199
+ if (typeof lineHeight2 === "number" && Number.isFinite(lineHeight2) && lineHeight2 > 0) {
37200
+ return lineHeight2;
37201
+ }
37202
+ }
37203
+ }
37204
+ }
37205
+ return Math.max(0, fallbackValue);
37206
+ };
37207
+ const getRangeRenderHeight = (range) => {
37208
+ if (range.kind === "paragraph" || range.kind === "list-item") {
37209
+ const spacing = range.toLine >= range.totalLines ? range.spacingAfter : 0;
37210
+ return range.height + spacing;
37211
+ }
37212
+ return range.height;
37213
+ };
37214
+ const buildFootnoteRanges = (blocks, measuresById) => {
37215
+ const ranges = [];
37216
+ blocks.forEach((block) => {
37217
+ const measure = measuresById.get(block.id);
37218
+ if (!measure) return;
37219
+ if (block.kind === "paragraph") {
37220
+ if (measure.kind !== "paragraph") return;
37221
+ const lineCount = measure.lines?.length ?? 0;
37222
+ if (lineCount === 0) return;
37223
+ ranges.push({
37224
+ kind: "paragraph",
37225
+ blockId: block.id,
37226
+ fromLine: 0,
37227
+ toLine: lineCount,
37228
+ totalLines: lineCount,
37229
+ height: sumLineHeights$1(measure.lines, 0, lineCount),
37230
+ spacingAfter: getParagraphSpacingAfter(block)
37231
+ });
37232
+ return;
37233
+ }
37234
+ if (block.kind === "list") {
37235
+ if (measure.kind !== "list") return;
37236
+ block.items.forEach((item) => {
37237
+ const itemMeasure = measure.items.find((entry) => entry.itemId === item.id);
37238
+ if (!itemMeasure) return;
37239
+ const lineCount = itemMeasure.paragraph.lines?.length ?? 0;
37240
+ if (lineCount === 0) return;
37241
+ ranges.push({
37242
+ kind: "list-item",
37243
+ blockId: block.id,
37244
+ itemId: item.id,
37245
+ fromLine: 0,
37246
+ toLine: lineCount,
37247
+ totalLines: lineCount,
37248
+ height: sumLineHeights$1(itemMeasure.paragraph.lines, 0, lineCount),
37249
+ spacingAfter: getParagraphSpacingAfter(item.paragraph)
37250
+ });
37251
+ });
37252
+ return;
37253
+ }
37254
+ if (block.kind === "table" && measure.kind === "table") {
37255
+ const height = Math.max(0, measure.totalHeight ?? 0);
37256
+ if (height > 0) {
37257
+ ranges.push({ kind: "table", blockId: block.id, height });
37258
+ }
37259
+ return;
37260
+ }
37261
+ if (block.kind === "image" && measure.kind === "image") {
37262
+ const height = Math.max(0, measure.height ?? 0);
37263
+ if (height > 0) {
37264
+ ranges.push({ kind: "image", blockId: block.id, height });
37265
+ }
37266
+ return;
37267
+ }
37268
+ if (block.kind === "drawing" && measure.kind === "drawing") {
37269
+ const height = Math.max(0, measure.height ?? 0);
37270
+ if (height > 0) {
37271
+ ranges.push({ kind: "drawing", blockId: block.id, height });
37272
+ }
37273
+ }
37274
+ });
37275
+ return ranges;
37276
+ };
37277
+ const splitRangeAtHeight = (range, availableHeight, measuresById) => {
37278
+ if (availableHeight <= 0) return { fitted: null, remaining: range };
37279
+ if (range.kind !== "paragraph") {
37280
+ return getRangeRenderHeight(range) <= availableHeight ? { fitted: range, remaining: null } : { fitted: null, remaining: range };
37281
+ }
37282
+ const measure = measuresById.get(range.blockId);
37283
+ if (!measure || measure.kind !== "paragraph" || !measure.lines) {
37284
+ return getRangeRenderHeight(range) <= availableHeight ? { fitted: range, remaining: null } : { fitted: null, remaining: range };
37285
+ }
37286
+ let accumulatedHeight = 0;
37287
+ let splitLine = range.fromLine;
37288
+ for (let i = range.fromLine; i < range.toLine; i += 1) {
37289
+ const lineHeight2 = measure.lines[i]?.lineHeight ?? 0;
37290
+ if (accumulatedHeight + lineHeight2 > availableHeight) break;
37291
+ accumulatedHeight += lineHeight2;
37292
+ splitLine = i + 1;
37293
+ }
37294
+ if (splitLine === range.fromLine) {
37295
+ return { fitted: null, remaining: range };
37296
+ }
37297
+ const fitted = {
37298
+ ...range,
37299
+ toLine: splitLine,
37300
+ height: sumLineHeights$1(measure.lines, range.fromLine, splitLine)
37301
+ };
37302
+ if (splitLine >= range.toLine) {
37303
+ return getRangeRenderHeight(fitted) <= availableHeight ? { fitted, remaining: null } : { fitted: null, remaining: range };
37304
+ }
37305
+ const remaining = {
37306
+ ...range,
37307
+ fromLine: splitLine,
37308
+ height: sumLineHeights$1(measure.lines, splitLine, range.toLine)
37309
+ };
37310
+ return { fitted, remaining };
37311
+ };
37312
+ const forceFitFirstRange = (range, measuresById) => {
37313
+ if (range.kind !== "paragraph") {
37314
+ return { fitted: range, remaining: null };
37315
+ }
37316
+ const measure = measuresById.get(range.blockId);
37317
+ if (!measure || measure.kind !== "paragraph" || !measure.lines?.length) {
37318
+ return { fitted: range, remaining: null };
37319
+ }
37320
+ const nextLine = Math.min(range.fromLine + 1, range.toLine);
37321
+ const fitted = {
37322
+ ...range,
37323
+ toLine: nextLine,
37324
+ height: sumLineHeights$1(measure.lines, range.fromLine, nextLine)
37325
+ };
37326
+ if (nextLine >= range.toLine) {
37327
+ return { fitted, remaining: null };
37328
+ }
37329
+ const remaining = {
37330
+ ...range,
37331
+ fromLine: nextLine,
37332
+ height: sumLineHeights$1(measure.lines, nextLine, range.toLine)
37333
+ };
37334
+ return { fitted, remaining };
37335
+ };
37336
+ const fitFootnoteContent = (id, inputRanges, availableHeight, pageIndex, columnIndex, isContinuation, measuresById, forceFirstRange) => {
37337
+ const fittedRanges = [];
37338
+ let remainingRanges = [];
37339
+ let usedHeight = 0;
37340
+ const maxHeight = Math.max(0, availableHeight);
37341
+ for (let index2 = 0; index2 < inputRanges.length; index2 += 1) {
37342
+ const range = inputRanges[index2];
37343
+ const remainingSpace = maxHeight - usedHeight;
37344
+ const rangeHeight = getRangeRenderHeight(range);
37345
+ if (rangeHeight <= remainingSpace) {
37346
+ fittedRanges.push(range);
37347
+ usedHeight += rangeHeight;
37348
+ continue;
37349
+ }
37350
+ if (range.kind === "paragraph") {
37351
+ const split = splitRangeAtHeight(range, remainingSpace, measuresById);
37352
+ if (split.fitted && getRangeRenderHeight(split.fitted) <= remainingSpace) {
37353
+ fittedRanges.push(split.fitted);
37354
+ usedHeight += getRangeRenderHeight(split.fitted);
37355
+ }
37356
+ if (split.remaining) {
37357
+ remainingRanges = [split.remaining, ...inputRanges.slice(index2 + 1)];
37358
+ } else {
37359
+ remainingRanges = inputRanges.slice(index2 + 1);
37360
+ }
37361
+ break;
37362
+ }
37363
+ remainingRanges = [range, ...inputRanges.slice(index2 + 1)];
37364
+ break;
37365
+ }
37366
+ if (fittedRanges.length === 0 && forceFirstRange && inputRanges.length > 0) {
37367
+ const forced = forceFitFirstRange(inputRanges[0], measuresById);
37368
+ if (forced.fitted) {
37369
+ fittedRanges.push(forced.fitted);
37370
+ usedHeight = getRangeRenderHeight(forced.fitted);
37371
+ remainingRanges = [];
37372
+ if (forced.remaining) {
37373
+ remainingRanges.push(forced.remaining);
37374
+ }
37375
+ remainingRanges.push(...inputRanges.slice(1));
37376
+ }
37377
+ }
37378
+ return {
37379
+ slice: {
37380
+ id,
37381
+ pageIndex,
37382
+ columnIndex,
37383
+ isContinuation,
37384
+ ranges: fittedRanges,
37385
+ totalHeight: usedHeight
37386
+ },
37387
+ remainingRanges
37388
+ };
37389
+ };
36963
37390
  async function incrementalLayout(previousBlocks, _previousLayout, nextBlocks, options, measureBlock2, headerFooter) {
36964
37391
  performance.now();
36965
37392
  const dirty = computeDirtyRegions(previousBlocks, nextBlocks);
@@ -37223,6 +37650,497 @@ async function incrementalLayout(previousBlocks, _previousLayout, nextBlocks, op
37223
37650
  converged
37224
37651
  });
37225
37652
  }
37653
+ let extraBlocks;
37654
+ let extraMeasures;
37655
+ const footnotesInput = isFootnotesLayoutInput(options.footnotes) ? options.footnotes : null;
37656
+ if (footnotesInput && footnotesInput.refs.length > 0 && footnotesInput.blocksById.size > 0) {
37657
+ const gap = typeof footnotesInput.gap === "number" && Number.isFinite(footnotesInput.gap) ? footnotesInput.gap : 2;
37658
+ const topPadding = typeof footnotesInput.topPadding === "number" && Number.isFinite(footnotesInput.topPadding) ? footnotesInput.topPadding : 6;
37659
+ const dividerHeight = typeof footnotesInput.dividerHeight === "number" && Number.isFinite(footnotesInput.dividerHeight) ? footnotesInput.dividerHeight : 6;
37660
+ const safeGap = Math.max(0, gap);
37661
+ const safeTopPadding = Math.max(0, topPadding);
37662
+ const safeDividerHeight = Math.max(0, dividerHeight);
37663
+ const continuationDividerHeight = safeDividerHeight;
37664
+ const continuationDividerWidthFactor = 0.3;
37665
+ const footnoteWidth = resolveFootnoteMeasurementWidth(options, currentBlocks);
37666
+ if (footnoteWidth > 0) {
37667
+ const footnoteConstraints = { maxWidth: footnoteWidth, maxHeight: measurementHeight };
37668
+ const collectFootnoteIdsByColumn = (idsByColumn2) => {
37669
+ const ids = /* @__PURE__ */ new Set();
37670
+ idsByColumn2.forEach((columns) => {
37671
+ columns.forEach((list) => {
37672
+ list.forEach((id) => ids.add(id));
37673
+ });
37674
+ });
37675
+ return ids;
37676
+ };
37677
+ const measureFootnoteBlocks = async (ids) => {
37678
+ const needed = /* @__PURE__ */ new Map();
37679
+ ids.forEach((id) => {
37680
+ const blocks2 = footnotesInput.blocksById.get(id) ?? [];
37681
+ blocks2.forEach((block) => {
37682
+ if (block?.id && !needed.has(block.id)) {
37683
+ needed.set(block.id, block);
37684
+ }
37685
+ });
37686
+ });
37687
+ const blocks = Array.from(needed.values());
37688
+ const measuresById2 = /* @__PURE__ */ new Map();
37689
+ await Promise.all(
37690
+ blocks.map(async (block) => {
37691
+ const cached = measureCache.get(block, footnoteConstraints.maxWidth, footnoteConstraints.maxHeight);
37692
+ if (cached) {
37693
+ measuresById2.set(block.id, cached);
37694
+ return;
37695
+ }
37696
+ const measurement = await measureBlock2(block, footnoteConstraints);
37697
+ measureCache.set(block, footnoteConstraints.maxWidth, footnoteConstraints.maxHeight, measurement);
37698
+ measuresById2.set(block.id, measurement);
37699
+ })
37700
+ );
37701
+ return { blocks, measuresById: measuresById2 };
37702
+ };
37703
+ const computeFootnoteLayoutPlan = (layoutForPages, idsByColumn2, measuresById2, baseReserves = [], pageColumns2) => {
37704
+ const pageCount = layoutForPages.pages.length;
37705
+ const slicesByPage = /* @__PURE__ */ new Map();
37706
+ const reserves2 = new Array(pageCount).fill(0);
37707
+ const hasContinuationByColumn = /* @__PURE__ */ new Map();
37708
+ const rangesByFootnoteId = /* @__PURE__ */ new Map();
37709
+ const cappedPages = /* @__PURE__ */ new Set();
37710
+ const allIds = collectFootnoteIdsByColumn(idsByColumn2);
37711
+ allIds.forEach((id) => {
37712
+ const blocks = footnotesInput.blocksById.get(id) ?? [];
37713
+ rangesByFootnoteId.set(id, buildFootnoteRanges(blocks, measuresById2));
37714
+ });
37715
+ const separatorSpacingBefore = resolveSeparatorSpacingBefore(
37716
+ rangesByFootnoteId,
37717
+ measuresById2,
37718
+ footnotesInput.separatorSpacingBefore,
37719
+ DEFAULT_FOOTNOTE_SEPARATOR_SPACING_BEFORE
37720
+ );
37721
+ const safeSeparatorSpacingBefore = Math.max(0, separatorSpacingBefore);
37722
+ let pendingByColumn = /* @__PURE__ */ new Map();
37723
+ for (let pageIndex = 0; pageIndex < pageCount; pageIndex += 1) {
37724
+ const baseReserve = Number.isFinite(baseReserves?.[pageIndex]) ? Math.max(0, baseReserves[pageIndex]) : 0;
37725
+ const maxReserve = computeMaxFootnoteReserve(layoutForPages, pageIndex, baseReserve);
37726
+ const columns = pageColumns2.get(pageIndex);
37727
+ const columnCount = Math.max(1, Math.floor(columns?.count ?? 1));
37728
+ const pendingForPage = /* @__PURE__ */ new Map();
37729
+ pendingByColumn.forEach((entries, columnIndex) => {
37730
+ const targetIndex = columnIndex < columnCount ? columnIndex : Math.max(0, columnCount - 1);
37731
+ const list = pendingForPage.get(targetIndex) ?? [];
37732
+ list.push(...entries);
37733
+ pendingForPage.set(targetIndex, list);
37734
+ });
37735
+ pendingByColumn = /* @__PURE__ */ new Map();
37736
+ const pageSlices = [];
37737
+ let pageReserve = 0;
37738
+ for (let columnIndex = 0; columnIndex < columnCount; columnIndex += 1) {
37739
+ let usedHeight = 0;
37740
+ const columnSlices = [];
37741
+ const nextPending = [];
37742
+ let stopPlacement = false;
37743
+ const columnKey = footnoteColumnKey(pageIndex, columnIndex);
37744
+ const placeFootnote = (id, ranges, isContinuation) => {
37745
+ if (!ranges || ranges.length === 0) {
37746
+ return { placed: false, remaining: [] };
37747
+ }
37748
+ const isFirstSlice = columnSlices.length === 0;
37749
+ const separatorBefore = isFirstSlice ? safeSeparatorSpacingBefore : 0;
37750
+ const separatorHeight = isFirstSlice ? isContinuation ? continuationDividerHeight : safeDividerHeight : 0;
37751
+ const overhead = isFirstSlice ? separatorBefore + separatorHeight + safeTopPadding : 0;
37752
+ const gapBefore = !isFirstSlice ? safeGap : 0;
37753
+ const availableHeight = Math.max(0, maxReserve - usedHeight - overhead - gapBefore);
37754
+ const { slice: slice2, remainingRanges } = fitFootnoteContent(
37755
+ id,
37756
+ ranges,
37757
+ availableHeight,
37758
+ pageIndex,
37759
+ columnIndex,
37760
+ isContinuation,
37761
+ measuresById2,
37762
+ isFirstSlice && maxReserve > 0
37763
+ );
37764
+ if (slice2.ranges.length === 0) {
37765
+ return { placed: false, remaining: ranges };
37766
+ }
37767
+ if (isFirstSlice) {
37768
+ usedHeight += overhead;
37769
+ if (isContinuation) {
37770
+ hasContinuationByColumn.set(columnKey, true);
37771
+ }
37772
+ }
37773
+ if (gapBefore > 0) {
37774
+ usedHeight += gapBefore;
37775
+ }
37776
+ usedHeight += slice2.totalHeight;
37777
+ columnSlices.push(slice2);
37778
+ return { placed: true, remaining: remainingRanges };
37779
+ };
37780
+ const pending = pendingForPage.get(columnIndex) ?? [];
37781
+ for (const entry of pending) {
37782
+ if (stopPlacement) {
37783
+ nextPending.push(entry);
37784
+ continue;
37785
+ }
37786
+ if (!entry.ranges || entry.ranges.length === 0) continue;
37787
+ const result = placeFootnote(entry.id, entry.ranges, true);
37788
+ if (!result.placed) {
37789
+ nextPending.push(entry);
37790
+ stopPlacement = true;
37791
+ continue;
37792
+ }
37793
+ if (result.remaining.length > 0) {
37794
+ nextPending.push({ id: entry.id, ranges: result.remaining });
37795
+ }
37796
+ }
37797
+ if (!stopPlacement) {
37798
+ const ids = idsByColumn2.get(pageIndex)?.get(columnIndex) ?? [];
37799
+ for (let idIndex = 0; idIndex < ids.length; idIndex += 1) {
37800
+ const id = ids[idIndex];
37801
+ const ranges = rangesByFootnoteId.get(id) ?? [];
37802
+ if (ranges.length === 0) continue;
37803
+ const result = placeFootnote(id, ranges, false);
37804
+ if (!result.placed) {
37805
+ nextPending.push({ id, ranges });
37806
+ for (let remainingIndex = idIndex + 1; remainingIndex < ids.length; remainingIndex += 1) {
37807
+ const remainingId = ids[remainingIndex];
37808
+ const remainingRanges = rangesByFootnoteId.get(remainingId) ?? [];
37809
+ nextPending.push({ id: remainingId, ranges: remainingRanges });
37810
+ }
37811
+ stopPlacement = true;
37812
+ break;
37813
+ }
37814
+ if (result.remaining.length > 0) {
37815
+ nextPending.push({ id, ranges: result.remaining });
37816
+ }
37817
+ }
37818
+ }
37819
+ if (columnSlices.length > 0) {
37820
+ const rawReserve = Math.max(0, Math.ceil(usedHeight));
37821
+ const cappedReserve = Math.min(rawReserve, maxReserve);
37822
+ if (cappedReserve < rawReserve) {
37823
+ cappedPages.add(pageIndex);
37824
+ }
37825
+ pageReserve = Math.max(pageReserve, cappedReserve);
37826
+ pageSlices.push(...columnSlices);
37827
+ }
37828
+ if (nextPending.length > 0) {
37829
+ pendingByColumn.set(columnIndex, nextPending);
37830
+ }
37831
+ }
37832
+ if (pageSlices.length > 0) {
37833
+ slicesByPage.set(pageIndex, pageSlices);
37834
+ }
37835
+ reserves2[pageIndex] = pageReserve;
37836
+ }
37837
+ if (cappedPages.size > 0) {
37838
+ console.warn("[layout] Footnote reserve capped to preserve body area", {
37839
+ pages: Array.from(cappedPages)
37840
+ });
37841
+ }
37842
+ if (pendingByColumn.size > 0) {
37843
+ const pendingIds = /* @__PURE__ */ new Set();
37844
+ pendingByColumn.forEach((entries) => entries.forEach((entry) => pendingIds.add(entry.id)));
37845
+ console.warn("[layout] Footnote content truncated: extends beyond document pages", {
37846
+ ids: Array.from(pendingIds)
37847
+ });
37848
+ }
37849
+ return { slicesByPage, reserves: reserves2, hasContinuationByColumn, separatorSpacingBefore: safeSeparatorSpacingBefore };
37850
+ };
37851
+ const injectFragments = (layoutForPages, plan2, measuresById2, reservesByPageIndex, blockById, pageColumns2) => {
37852
+ const decorativeBlocks = [];
37853
+ const decorativeMeasures = [];
37854
+ for (let pageIndex = 0; pageIndex < layoutForPages.pages.length; pageIndex++) {
37855
+ const page = layoutForPages.pages[pageIndex];
37856
+ page.footnoteReserved = Math.max(0, reservesByPageIndex[pageIndex] ?? plan2.reserves[pageIndex] ?? 0);
37857
+ const slices = plan2.slicesByPage.get(pageIndex) ?? [];
37858
+ if (slices.length === 0) continue;
37859
+ if (!page.margins) continue;
37860
+ const pageSize = page.size ?? layoutForPages.pageSize;
37861
+ const marginLeft = normalizeMargin(
37862
+ page.margins.left,
37863
+ normalizeMargin(options.margins?.left, DEFAULT_MARGINS$1.left)
37864
+ );
37865
+ const marginRight = normalizeMargin(
37866
+ page.margins.right,
37867
+ normalizeMargin(options.margins?.right, DEFAULT_MARGINS$1.right)
37868
+ );
37869
+ const pageContentWidth = pageSize.w - (marginLeft + marginRight);
37870
+ const fallbackColumns = normalizeColumnsForFootnotes(
37871
+ options.columns ?? { count: 1, gap: 0 },
37872
+ pageContentWidth
37873
+ );
37874
+ const columns = pageColumns2.get(pageIndex) ?? {
37875
+ ...fallbackColumns,
37876
+ left: marginLeft
37877
+ };
37878
+ const bandTopY = pageSize.h - (page.margins.bottom ?? 0);
37879
+ const slicesByColumn = /* @__PURE__ */ new Map();
37880
+ slices.forEach((slice2) => {
37881
+ const columnIndex = Number.isFinite(slice2.columnIndex) ? slice2.columnIndex : 0;
37882
+ const list = slicesByColumn.get(columnIndex) ?? [];
37883
+ list.push(slice2);
37884
+ slicesByColumn.set(columnIndex, list);
37885
+ });
37886
+ slicesByColumn.forEach((columnSlices, rawColumnIndex) => {
37887
+ if (columnSlices.length === 0) return;
37888
+ const columnIndex = Math.max(0, Math.min(columns.count - 1, rawColumnIndex));
37889
+ const columnStride = columns.width + columns.gap;
37890
+ const columnX = columns.left + columnIndex * columnStride;
37891
+ const contentWidth = Math.min(columns.width, footnoteWidth);
37892
+ if (!Number.isFinite(contentWidth) || contentWidth <= 0) return;
37893
+ const columnKey = footnoteColumnKey(pageIndex, columnIndex);
37894
+ const isContinuation = plan2.hasContinuationByColumn.get(columnKey) ?? false;
37895
+ let cursorY = bandTopY + Math.max(0, plan2.separatorSpacingBefore);
37896
+ const separatorHeight = isContinuation ? continuationDividerHeight : safeDividerHeight;
37897
+ const separatorWidth = isContinuation ? Math.max(0, contentWidth * continuationDividerWidthFactor) : contentWidth;
37898
+ if (separatorHeight > 0 && separatorWidth > 0) {
37899
+ const separatorId = isContinuation ? `footnote-continuation-separator-page-${page.number}-col-${columnIndex}` : `footnote-separator-page-${page.number}-col-${columnIndex}`;
37900
+ decorativeBlocks.push({
37901
+ kind: "drawing",
37902
+ id: separatorId,
37903
+ drawingKind: "vectorShape",
37904
+ geometry: { width: separatorWidth, height: separatorHeight },
37905
+ shapeKind: "rect",
37906
+ fillColor: "#000000",
37907
+ strokeColor: null,
37908
+ strokeWidth: 0
37909
+ });
37910
+ decorativeMeasures.push({
37911
+ kind: "drawing",
37912
+ drawingKind: "vectorShape",
37913
+ width: separatorWidth,
37914
+ height: separatorHeight,
37915
+ scale: 1,
37916
+ naturalWidth: separatorWidth,
37917
+ naturalHeight: separatorHeight,
37918
+ geometry: { width: separatorWidth, height: separatorHeight }
37919
+ });
37920
+ page.fragments.push({
37921
+ kind: "drawing",
37922
+ blockId: separatorId,
37923
+ drawingKind: "vectorShape",
37924
+ x: columnX,
37925
+ y: cursorY,
37926
+ width: separatorWidth,
37927
+ height: separatorHeight,
37928
+ geometry: { width: separatorWidth, height: separatorHeight },
37929
+ scale: 1
37930
+ });
37931
+ cursorY += separatorHeight;
37932
+ }
37933
+ cursorY += safeTopPadding;
37934
+ columnSlices.forEach((slice2, sliceIndex) => {
37935
+ slice2.ranges.forEach((range) => {
37936
+ if (range.kind === "paragraph") {
37937
+ const measure = measuresById2.get(range.blockId);
37938
+ if (!measure || measure.kind !== "paragraph") return;
37939
+ const marker = measure.marker;
37940
+ page.fragments.push({
37941
+ kind: "para",
37942
+ blockId: range.blockId,
37943
+ fromLine: range.fromLine,
37944
+ toLine: range.toLine,
37945
+ x: columnX,
37946
+ y: cursorY,
37947
+ width: contentWidth,
37948
+ continuesFromPrev: range.fromLine > 0,
37949
+ continuesOnNext: range.toLine < range.totalLines,
37950
+ ...marker?.markerWidth != null ? { markerWidth: marker.markerWidth } : {},
37951
+ ...marker?.markerTextWidth != null ? { markerTextWidth: marker.markerTextWidth } : {},
37952
+ ...marker?.gutterWidth != null ? { markerGutter: marker.gutterWidth } : {}
37953
+ });
37954
+ cursorY += getRangeRenderHeight(range);
37955
+ return;
37956
+ }
37957
+ if (range.kind === "list-item") {
37958
+ const measure = measuresById2.get(range.blockId);
37959
+ const block = blockById.get(range.blockId);
37960
+ if (!measure || measure.kind !== "list") return;
37961
+ if (!block || block.kind !== "list") return;
37962
+ const itemMeasure = measure.items.find((entry) => entry.itemId === range.itemId);
37963
+ if (!itemMeasure) return;
37964
+ const indentLeft = Number.isFinite(itemMeasure.indentLeft) ? itemMeasure.indentLeft : 0;
37965
+ const markerWidth = Number.isFinite(itemMeasure.markerWidth) ? itemMeasure.markerWidth : 0;
37966
+ const itemWidth = Math.max(0, contentWidth - indentLeft - markerWidth);
37967
+ page.fragments.push({
37968
+ kind: "list-item",
37969
+ blockId: range.blockId,
37970
+ itemId: range.itemId,
37971
+ fromLine: range.fromLine,
37972
+ toLine: range.toLine,
37973
+ x: columnX + indentLeft + markerWidth,
37974
+ y: cursorY,
37975
+ width: itemWidth,
37976
+ markerWidth,
37977
+ continuesFromPrev: range.fromLine > 0,
37978
+ continuesOnNext: range.toLine < range.totalLines
37979
+ });
37980
+ cursorY += getRangeRenderHeight(range);
37981
+ return;
37982
+ }
37983
+ if (range.kind === "table") {
37984
+ const measure = measuresById2.get(range.blockId);
37985
+ const block = blockById.get(range.blockId);
37986
+ if (!measure || measure.kind !== "table") return;
37987
+ if (!block || block.kind !== "table") return;
37988
+ const tableWidthRaw = Math.max(0, measure.totalWidth ?? 0);
37989
+ let tableWidth = Math.min(contentWidth, tableWidthRaw);
37990
+ let tableX = columnX;
37991
+ const justification = typeof block.attrs?.justification === "string" ? block.attrs.justification : void 0;
37992
+ if (justification === "center") {
37993
+ tableX = columnX + Math.max(0, (contentWidth - tableWidth) / 2);
37994
+ } else if (justification === "right" || justification === "end") {
37995
+ tableX = columnX + Math.max(0, contentWidth - tableWidth);
37996
+ } else {
37997
+ const indentValue = block.attrs?.tableIndent?.width;
37998
+ const indent = typeof indentValue === "number" && Number.isFinite(indentValue) ? indentValue : 0;
37999
+ tableX += indent;
38000
+ tableWidth = Math.max(0, tableWidth - indent);
38001
+ }
38002
+ page.fragments.push({
38003
+ kind: "table",
38004
+ blockId: range.blockId,
38005
+ fromRow: 0,
38006
+ toRow: block.rows.length,
38007
+ x: tableX,
38008
+ y: cursorY,
38009
+ width: tableWidth,
38010
+ height: Math.max(0, measure.totalHeight ?? 0)
38011
+ });
38012
+ cursorY += getRangeRenderHeight(range);
38013
+ return;
38014
+ }
38015
+ if (range.kind === "image") {
38016
+ const measure = measuresById2.get(range.blockId);
38017
+ if (!measure || measure.kind !== "image") return;
38018
+ page.fragments.push({
38019
+ kind: "image",
38020
+ blockId: range.blockId,
38021
+ x: columnX,
38022
+ y: cursorY,
38023
+ width: Math.min(contentWidth, Math.max(0, measure.width ?? 0)),
38024
+ height: Math.max(0, measure.height ?? 0)
38025
+ });
38026
+ cursorY += getRangeRenderHeight(range);
38027
+ return;
38028
+ }
38029
+ if (range.kind === "drawing") {
38030
+ const measure = measuresById2.get(range.blockId);
38031
+ const block = blockById.get(range.blockId);
38032
+ if (!measure || measure.kind !== "drawing") return;
38033
+ if (!block || block.kind !== "drawing") return;
38034
+ page.fragments.push({
38035
+ kind: "drawing",
38036
+ blockId: range.blockId,
38037
+ drawingKind: block.drawingKind,
38038
+ x: columnX,
38039
+ y: cursorY,
38040
+ width: Math.min(contentWidth, Math.max(0, measure.width ?? 0)),
38041
+ height: Math.max(0, measure.height ?? 0),
38042
+ geometry: measure.geometry,
38043
+ scale: measure.scale
38044
+ });
38045
+ cursorY += getRangeRenderHeight(range);
38046
+ }
38047
+ });
38048
+ if (sliceIndex < columnSlices.length - 1) {
38049
+ cursorY += safeGap;
38050
+ }
38051
+ });
38052
+ });
38053
+ }
38054
+ return { decorativeBlocks, decorativeMeasures };
38055
+ };
38056
+ const resolveFootnoteAssignments = (layoutForPages) => {
38057
+ const columns = resolvePageColumns(layoutForPages, options, currentBlocks);
38058
+ const idsByColumn2 = assignFootnotesToColumns(layoutForPages, footnotesInput.refs, columns);
38059
+ return { columns, idsByColumn: idsByColumn2 };
38060
+ };
38061
+ let { columns: pageColumns, idsByColumn } = resolveFootnoteAssignments(layout);
38062
+ let { measuresById } = await measureFootnoteBlocks(collectFootnoteIdsByColumn(idsByColumn));
38063
+ let plan = computeFootnoteLayoutPlan(layout, idsByColumn, measuresById, [], pageColumns);
38064
+ let reserves = plan.reserves;
38065
+ if (reserves.some((h2) => h2 > 0)) {
38066
+ layout = layoutDocument(currentBlocks, currentMeasures, {
38067
+ ...options,
38068
+ footnoteReservedByPageIndex: reserves,
38069
+ headerContentHeights,
38070
+ footerContentHeights,
38071
+ remeasureParagraph: (block, maxWidth, firstLineIndent) => remeasureParagraph(block, maxWidth, firstLineIndent)
38072
+ });
38073
+ ({ columns: pageColumns, idsByColumn } = resolveFootnoteAssignments(layout));
38074
+ ({ measuresById } = await measureFootnoteBlocks(collectFootnoteIdsByColumn(idsByColumn)));
38075
+ plan = computeFootnoteLayoutPlan(layout, idsByColumn, measuresById, reserves, pageColumns);
38076
+ reserves = plan.reserves;
38077
+ layout = layoutDocument(currentBlocks, currentMeasures, {
38078
+ ...options,
38079
+ footnoteReservedByPageIndex: reserves,
38080
+ headerContentHeights,
38081
+ footerContentHeights,
38082
+ remeasureParagraph: (block, maxWidth, firstLineIndent) => remeasureParagraph(block, maxWidth, firstLineIndent)
38083
+ });
38084
+ let { columns: finalPageColumns, idsByColumn: finalIdsByColumn } = resolveFootnoteAssignments(layout);
38085
+ let { blocks: finalBlocks, measuresById: finalMeasuresById } = await measureFootnoteBlocks(
38086
+ collectFootnoteIdsByColumn(finalIdsByColumn)
38087
+ );
38088
+ let finalPlan = computeFootnoteLayoutPlan(
38089
+ layout,
38090
+ finalIdsByColumn,
38091
+ finalMeasuresById,
38092
+ reserves,
38093
+ finalPageColumns
38094
+ );
38095
+ const finalReserves = finalPlan.reserves;
38096
+ let reservesAppliedToLayout = reserves;
38097
+ const reservesDiffer = finalReserves.length !== reserves.length || finalReserves.some((h2, i) => (reserves[i] ?? 0) !== h2) || reserves.some((h2, i) => (finalReserves[i] ?? 0) !== h2);
38098
+ if (reservesDiffer) {
38099
+ layout = layoutDocument(currentBlocks, currentMeasures, {
38100
+ ...options,
38101
+ footnoteReservedByPageIndex: finalReserves,
38102
+ headerContentHeights,
38103
+ footerContentHeights,
38104
+ remeasureParagraph: (block, maxWidth, firstLineIndent) => remeasureParagraph(block, maxWidth, firstLineIndent)
38105
+ });
38106
+ reservesAppliedToLayout = finalReserves;
38107
+ ({ columns: finalPageColumns, idsByColumn: finalIdsByColumn } = resolveFootnoteAssignments(layout));
38108
+ ({ blocks: finalBlocks, measuresById: finalMeasuresById } = await measureFootnoteBlocks(
38109
+ collectFootnoteIdsByColumn(finalIdsByColumn)
38110
+ ));
38111
+ finalPlan = computeFootnoteLayoutPlan(
38112
+ layout,
38113
+ finalIdsByColumn,
38114
+ finalMeasuresById,
38115
+ reservesAppliedToLayout,
38116
+ finalPageColumns
38117
+ );
38118
+ }
38119
+ const blockById = /* @__PURE__ */ new Map();
38120
+ finalBlocks.forEach((block) => {
38121
+ blockById.set(block.id, block);
38122
+ });
38123
+ const injected = injectFragments(
38124
+ layout,
38125
+ finalPlan,
38126
+ finalMeasuresById,
38127
+ reservesAppliedToLayout,
38128
+ blockById,
38129
+ finalPageColumns
38130
+ );
38131
+ const alignedBlocks = [];
38132
+ const alignedMeasures = [];
38133
+ finalBlocks.forEach((block) => {
38134
+ const measure = finalMeasuresById.get(block.id);
38135
+ if (!measure) return;
38136
+ alignedBlocks.push(block);
38137
+ alignedMeasures.push(measure);
38138
+ });
38139
+ extraBlocks = injected ? alignedBlocks.concat(injected.decorativeBlocks) : alignedBlocks;
38140
+ extraMeasures = injected ? alignedMeasures.concat(injected.decorativeMeasures) : alignedMeasures;
38141
+ }
38142
+ }
38143
+ }
37226
38144
  let headers;
37227
38145
  let footers;
37228
38146
  if (headerFooter?.constraints && (headerFooter.headerBlocks || headerFooter.footerBlocks)) {
@@ -37283,7 +38201,9 @@ async function incrementalLayout(previousBlocks, _previousLayout, nextBlocks, op
37283
38201
  measures: currentMeasures,
37284
38202
  dirty,
37285
38203
  headers,
37286
- footers
38204
+ footers,
38205
+ extraBlocks,
38206
+ extraMeasures
37287
38207
  };
37288
38208
  }
37289
38209
  const DEFAULT_PAGE_SIZE$1 = { w: 612, h: 792 };
@@ -41322,6 +42242,7 @@ const ATOMIC_INLINE_TYPES = /* @__PURE__ */ new Set([
41322
42242
  "lineBreak",
41323
42243
  "page-number",
41324
42244
  "total-page-number",
42245
+ "footnoteReference",
41325
42246
  "passthroughInline",
41326
42247
  "bookmarkEnd"
41327
42248
  ]);
@@ -46346,6 +47267,28 @@ function paragraphToFlowBlocks$1(para, nextBlockId, positions, defaultFont, defa
46346
47267
  let partIndex = 0;
46347
47268
  let tabOrdinal = 0;
46348
47269
  let suppressedByVanish = false;
47270
+ const toSuperscriptDigits2 = (value) => {
47271
+ const map3 = {
47272
+ "0": "⁰",
47273
+ "1": "¹",
47274
+ "2": "²",
47275
+ "3": "³",
47276
+ "4": "⁴",
47277
+ "5": "⁵",
47278
+ "6": "⁶",
47279
+ "7": "⁷",
47280
+ "8": "⁸",
47281
+ "9": "⁹"
47282
+ };
47283
+ return String(value ?? "").split("").map((ch) => map3[ch] ?? ch).join("");
47284
+ };
47285
+ const resolveFootnoteDisplayNumber2 = (id) => {
47286
+ const key2 = id == null ? null : String(id);
47287
+ if (!key2) return null;
47288
+ const mapping = converterContext?.footnoteNumberById;
47289
+ const mapped = mapping && typeof mapping === "object" ? mapping[key2] : void 0;
47290
+ return typeof mapped === "number" && Number.isFinite(mapped) && mapped > 0 ? mapped : null;
47291
+ };
46349
47292
  const nextId = () => partIndex === 0 ? baseBlockId : `${baseBlockId}-${partIndex}`;
46350
47293
  const attachAnchorParagraphId = (block, anchorParagraphId) => {
46351
47294
  const applicableKinds = /* @__PURE__ */ new Set(["drawing", "image", "table"]);
@@ -46391,6 +47334,34 @@ function paragraphToFlowBlocks$1(para, nextBlockId, positions, defaultFont, defa
46391
47334
  });
46392
47335
  };
46393
47336
  const visitNode = (node, inheritedMarks = [], activeSdt, activeRunStyleId = null, activeRunProperties, activeHidden = false) => {
47337
+ if (node.type === "footnoteReference") {
47338
+ const mergedMarks = [...node.marks ?? [], ...inheritedMarks ?? []];
47339
+ const refPos = positions.get(node);
47340
+ const id = node.attrs?.id;
47341
+ const displayId = resolveFootnoteDisplayNumber2(id) ?? id ?? "*";
47342
+ const displayText = toSuperscriptDigits2(displayId);
47343
+ const run = textNodeToRun(
47344
+ { type: "text", text: displayText },
47345
+ positions,
47346
+ defaultFont,
47347
+ defaultSize,
47348
+ [],
47349
+ // marks applied after linked styles/base defaults
47350
+ activeSdt,
47351
+ hyperlinkConfig,
47352
+ themeColors
47353
+ );
47354
+ const inlineStyleId = getInlineStyleId(mergedMarks);
47355
+ applyRunStyles2(run, inlineStyleId, activeRunStyleId);
47356
+ applyBaseRunDefaults(run, baseRunDefaults, defaultFont, defaultSize);
47357
+ applyMarksToRun(run, mergedMarks, hyperlinkConfig, themeColors);
47358
+ if (refPos) {
47359
+ run.pmStart = refPos.start;
47360
+ run.pmEnd = refPos.end;
47361
+ }
47362
+ currentRuns.push(run);
47363
+ return;
47364
+ }
46394
47365
  if (activeHidden && node.type !== "run") {
46395
47366
  suppressedByVanish = true;
46396
47367
  return;
@@ -54791,12 +55762,36 @@ class PresentationEditor extends EventEmitter {
54791
55762
  const sectionMetadata = [];
54792
55763
  let blocks;
54793
55764
  let bookmarks = /* @__PURE__ */ new Map();
55765
+ let converterContext = void 0;
54794
55766
  try {
54795
55767
  const converter2 = this.#editor.converter;
54796
- const converterContext = converter2 ? {
55768
+ const footnoteNumberById = {};
55769
+ try {
55770
+ const seen = /* @__PURE__ */ new Set();
55771
+ let counter = 1;
55772
+ this.#editor?.state?.doc?.descendants?.((node) => {
55773
+ if (node?.type?.name !== "footnoteReference") return;
55774
+ const rawId = node?.attrs?.id;
55775
+ if (rawId == null) return;
55776
+ const key2 = String(rawId);
55777
+ if (!key2 || seen.has(key2)) return;
55778
+ seen.add(key2);
55779
+ footnoteNumberById[key2] = counter;
55780
+ counter += 1;
55781
+ });
55782
+ } catch {
55783
+ }
55784
+ try {
55785
+ if (converter2 && typeof converter2 === "object") {
55786
+ converter2["footnoteNumberById"] = footnoteNumberById;
55787
+ }
55788
+ } catch {
55789
+ }
55790
+ converterContext = converter2 ? {
54797
55791
  docx: converter2.convertedXml,
54798
55792
  numbering: converter2.numbering,
54799
- linkedStyles: converter2.linkedStyles
55793
+ linkedStyles: converter2.linkedStyles,
55794
+ ...Object.keys(footnoteNumberById).length ? { footnoteNumberById } : {}
54800
55795
  } : void 0;
54801
55796
  const atomNodeTypes = getAtomNodeTypes(this.#editor?.schema ?? null);
54802
55797
  const positionMap = this.#editor?.state?.doc && docJson ? buildPositionMapFromPmDoc(this.#editor.state.doc, docJson) : null;
@@ -54824,13 +55819,20 @@ class PresentationEditor extends EventEmitter {
54824
55819
  this.#handleLayoutError("render", new Error("toFlowBlocks returned undefined blocks"));
54825
55820
  return;
54826
55821
  }
54827
- const layoutOptions = this.#resolveLayoutOptions(blocks, sectionMetadata);
55822
+ const baseLayoutOptions = this.#resolveLayoutOptions(blocks, sectionMetadata);
55823
+ const footnotesLayoutInput = this.#buildFootnotesLayoutInput({
55824
+ converterContext,
55825
+ themeColors: this.#editor?.converter?.themeColors ?? void 0
55826
+ });
55827
+ const layoutOptions = footnotesLayoutInput ? { ...baseLayoutOptions, footnotes: footnotesLayoutInput } : baseLayoutOptions;
54828
55828
  const previousBlocks = this.#layoutState.blocks;
54829
55829
  const previousLayout = this.#layoutState.layout;
54830
55830
  let layout;
54831
55831
  let measures;
54832
55832
  let headerLayouts;
54833
55833
  let footerLayouts;
55834
+ let extraBlocks;
55835
+ let extraMeasures;
54834
55836
  const headerFooterInput = this.#buildHeaderFooterInput();
54835
55837
  try {
54836
55838
  const result = await incrementalLayout(
@@ -54854,6 +55856,8 @@ class PresentationEditor extends EventEmitter {
54854
55856
  return;
54855
55857
  }
54856
55858
  ({ layout, measures } = result);
55859
+ extraBlocks = Array.isArray(result.extraBlocks) ? result.extraBlocks : void 0;
55860
+ extraMeasures = Array.isArray(result.extraMeasures) ? result.extraMeasures : void 0;
54857
55861
  layout.pageGap = this.#getEffectivePageGap();
54858
55862
  layout.layoutEpoch = layoutEpoch;
54859
55863
  headerLayouts = result.headers;
@@ -54913,6 +55917,10 @@ class PresentationEditor extends EventEmitter {
54913
55917
  footerBlocks.push(...rIdResult.blocks);
54914
55918
  footerMeasures.push(...rIdResult.measures);
54915
55919
  }
55920
+ if (extraBlocks && extraMeasures && extraBlocks.length === extraMeasures.length && extraBlocks.length > 0) {
55921
+ footerBlocks.push(...extraBlocks);
55922
+ footerMeasures.push(...extraMeasures);
55923
+ }
54916
55924
  painter.setData?.(
54917
55925
  blocks,
54918
55926
  measures,
@@ -55199,6 +56207,123 @@ class PresentationEditor extends EventEmitter {
55199
56207
  sectionMetadata
55200
56208
  };
55201
56209
  }
56210
+ #buildFootnotesLayoutInput({
56211
+ converterContext,
56212
+ themeColors
56213
+ }) {
56214
+ const footnoteNumberById = converterContext?.footnoteNumberById;
56215
+ const toSuperscriptDigits2 = (value) => {
56216
+ const map3 = {
56217
+ "0": "⁰",
56218
+ "1": "¹",
56219
+ "2": "²",
56220
+ "3": "³",
56221
+ "4": "⁴",
56222
+ "5": "⁵",
56223
+ "6": "⁶",
56224
+ "7": "⁷",
56225
+ "8": "⁸",
56226
+ "9": "⁹"
56227
+ };
56228
+ const str = String(value ?? "");
56229
+ return str.split("").map((ch) => map3[ch] ?? ch).join("");
56230
+ };
56231
+ const ensureFootnoteMarker = (blocks, id) => {
56232
+ const displayNumberRaw = footnoteNumberById && typeof footnoteNumberById === "object" ? footnoteNumberById[id] : void 0;
56233
+ const displayNumber = typeof displayNumberRaw === "number" && Number.isFinite(displayNumberRaw) && displayNumberRaw > 0 ? displayNumberRaw : 1;
56234
+ const firstParagraph = blocks.find((b2) => b2?.kind === "paragraph");
56235
+ if (!firstParagraph) return;
56236
+ const runs = Array.isArray(firstParagraph.runs) ? firstParagraph.runs : [];
56237
+ const markerText = toSuperscriptDigits2(displayNumber);
56238
+ const baseRun = runs.find((r2) => {
56239
+ const dataAttrs = r2.dataAttrs;
56240
+ if (dataAttrs?.["data-sd-footnote-number"]) return false;
56241
+ const pmStart = r2.pmStart;
56242
+ const pmEnd = r2.pmEnd;
56243
+ return typeof pmStart === "number" && Number.isFinite(pmStart) && typeof pmEnd === "number" && Number.isFinite(pmEnd);
56244
+ });
56245
+ const markerPmStart = baseRun?.pmStart ?? null;
56246
+ const markerPmEnd = markerPmStart != null ? baseRun?.pmEnd != null ? Math.max(markerPmStart, Math.min(baseRun.pmEnd, markerPmStart + markerText.length)) : markerPmStart + markerText.length : null;
56247
+ const alreadyHasMarker = runs.some((r2) => {
56248
+ const dataAttrs = r2.dataAttrs;
56249
+ return Boolean(dataAttrs?.["data-sd-footnote-number"]);
56250
+ });
56251
+ if (alreadyHasMarker) {
56252
+ if (markerPmStart != null && markerPmEnd != null) {
56253
+ const markerRun2 = runs.find((r2) => {
56254
+ const dataAttrs = r2.dataAttrs;
56255
+ return Boolean(dataAttrs?.["data-sd-footnote-number"]);
56256
+ });
56257
+ if (markerRun2) {
56258
+ if (markerRun2.pmStart == null) markerRun2.pmStart = markerPmStart;
56259
+ if (markerRun2.pmEnd == null) markerRun2.pmEnd = markerPmEnd;
56260
+ }
56261
+ }
56262
+ return;
56263
+ }
56264
+ const firstTextRun = runs.find((r2) => typeof r2.text === "string");
56265
+ const markerRun = {
56266
+ kind: "text",
56267
+ text: markerText,
56268
+ dataAttrs: {
56269
+ "data-sd-footnote-number": "true"
56270
+ },
56271
+ ...markerPmStart != null ? { pmStart: markerPmStart } : {},
56272
+ ...markerPmEnd != null ? { pmEnd: markerPmEnd } : {}
56273
+ };
56274
+ markerRun.fontFamily = typeof firstTextRun?.fontFamily === "string" ? firstTextRun.fontFamily : "Arial";
56275
+ markerRun.fontSize = typeof firstTextRun?.fontSize === "number" && Number.isFinite(firstTextRun.fontSize) ? firstTextRun.fontSize : 12;
56276
+ if (firstTextRun?.color != null) markerRun.color = firstTextRun.color;
56277
+ runs.unshift(markerRun);
56278
+ firstParagraph.runs = runs;
56279
+ };
56280
+ const state = this.#editor?.state;
56281
+ if (!state) return null;
56282
+ const converter = this.#editor?.converter;
56283
+ const importedFootnotes = Array.isArray(converter?.footnotes) ? converter.footnotes : [];
56284
+ if (importedFootnotes.length === 0) return null;
56285
+ const refs = [];
56286
+ const idsInUse = /* @__PURE__ */ new Set();
56287
+ state.doc.descendants((node, pos) => {
56288
+ if (node.type?.name !== "footnoteReference") return;
56289
+ const id = node.attrs?.id;
56290
+ if (id == null) return;
56291
+ const key2 = String(id);
56292
+ const insidePos = Math.min(pos + 1, state.doc.content.size);
56293
+ refs.push({ id: key2, pos: insidePos });
56294
+ idsInUse.add(key2);
56295
+ });
56296
+ if (refs.length === 0) return null;
56297
+ const blocksById = /* @__PURE__ */ new Map();
56298
+ idsInUse.forEach((id) => {
56299
+ const entry = importedFootnotes.find((f) => String(f?.id) === id);
56300
+ const content = entry?.content;
56301
+ if (!Array.isArray(content) || content.length === 0) return;
56302
+ try {
56303
+ const clonedContent = JSON.parse(JSON.stringify(content));
56304
+ const footnoteDoc = { type: "doc", content: clonedContent };
56305
+ const result = toFlowBlocks(footnoteDoc, {
56306
+ blockIdPrefix: `footnote-${id}-`,
56307
+ enableRichHyperlinks: true,
56308
+ themeColors,
56309
+ converterContext
56310
+ });
56311
+ if (result?.blocks?.length) {
56312
+ ensureFootnoteMarker(result.blocks, id);
56313
+ blocksById.set(id, result.blocks);
56314
+ }
56315
+ } catch {
56316
+ }
56317
+ });
56318
+ if (blocksById.size === 0) return null;
56319
+ return {
56320
+ refs,
56321
+ blocksById,
56322
+ gap: 2,
56323
+ topPadding: 4,
56324
+ dividerHeight: 1
56325
+ };
56326
+ }
55202
56327
  #buildHeaderFooterInput() {
55203
56328
  if (!this.#headerFooterAdapter) {
55204
56329
  return null;
@@ -55388,7 +56513,8 @@ class PresentationEditor extends EventEmitter {
55388
56513
  const fragments2 = slotPage2.fragments ?? [];
55389
56514
  const pageHeight2 = page?.size?.h ?? layout.pageSize?.h ?? this.#layoutOptions.pageSize?.h ?? DEFAULT_PAGE_SIZE.h;
55390
56515
  const margins2 = pageMargins ?? layout.pages[0]?.margins ?? this.#layoutOptions.margins ?? DEFAULT_MARGINS;
55391
- const box2 = this.#computeDecorationBox(kind, margins2, pageHeight2);
56516
+ const decorationMargins2 = kind === "footer" ? this.#stripFootnoteReserveFromBottomMargin(margins2, page ?? null) : margins2;
56517
+ const box2 = this.#computeDecorationBox(kind, decorationMargins2, pageHeight2);
55392
56518
  const rawLayoutHeight2 = rIdLayout.layout.height ?? 0;
55393
56519
  const metrics2 = this.#computeHeaderFooterMetrics(
55394
56520
  kind,
@@ -55439,7 +56565,8 @@ class PresentationEditor extends EventEmitter {
55439
56565
  const fragments = slotPage.fragments ?? [];
55440
56566
  const pageHeight = page?.size?.h ?? layout.pageSize?.h ?? this.#layoutOptions.pageSize?.h ?? DEFAULT_PAGE_SIZE.h;
55441
56567
  const margins = pageMargins ?? layout.pages[0]?.margins ?? this.#layoutOptions.margins ?? DEFAULT_MARGINS;
55442
- const box = this.#computeDecorationBox(kind, margins, pageHeight);
56568
+ const decorationMargins = kind === "footer" ? this.#stripFootnoteReserveFromBottomMargin(margins, page ?? null) : margins;
56569
+ const box = this.#computeDecorationBox(kind, decorationMargins, pageHeight);
55443
56570
  const rawLayoutHeight = variant.layout.height ?? 0;
55444
56571
  const metrics = this.#computeHeaderFooterMetrics(kind, rawLayoutHeight, box, pageHeight, margins.footer ?? 0);
55445
56572
  const fallbackId = this.#headerFooterManager?.getVariantId(kind, headerFooterType);
@@ -55526,6 +56653,16 @@ class PresentationEditor extends EventEmitter {
55526
56653
  return { x: left2, width, height, offset: offset2 };
55527
56654
  }
55528
56655
  }
56656
+ #stripFootnoteReserveFromBottomMargin(pageMargins, page) {
56657
+ const reserveRaw = page?.footnoteReserved;
56658
+ const reserve = typeof reserveRaw === "number" && Number.isFinite(reserveRaw) && reserveRaw > 0 ? reserveRaw : 0;
56659
+ if (!reserve) return pageMargins;
56660
+ const bottomRaw = pageMargins.bottom;
56661
+ const bottom2 = typeof bottomRaw === "number" && Number.isFinite(bottomRaw) ? bottomRaw : 0;
56662
+ const nextBottom = Math.max(0, bottom2 - reserve);
56663
+ if (nextBottom === bottom2) return pageMargins;
56664
+ return { ...pageMargins, bottom: nextBottom };
56665
+ }
55529
56666
  /**
55530
56667
  * Computes the expected header/footer section type for a page based on document configuration.
55531
56668
  *
@@ -55590,7 +56727,8 @@ class PresentationEditor extends EventEmitter {
55590
56727
  height: headerPayload?.hitRegion?.height ?? headerBox.height
55591
56728
  });
55592
56729
  const footerPayload = this.#footerDecorationProvider?.(page.number, margins, page);
55593
- const footerBox = this.#computeDecorationBox("footer", margins, actualPageHeight);
56730
+ const footerBoxMargins = this.#stripFootnoteReserveFromBottomMargin(margins, page);
56731
+ const footerBox = this.#computeDecorationBox("footer", footerBoxMargins, actualPageHeight);
55594
56732
  this.#footerRegions.set(pageIndex, {
55595
56733
  kind: "footer",
55596
56734
  headerId: footerPayload?.headerId,
@@ -61491,6 +62629,109 @@ const CommentsMark = Mark.create({
61491
62629
  return [CommentMarkName$1, Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes)];
61492
62630
  }
61493
62631
  });
62632
+ const toSuperscriptDigits = (value) => {
62633
+ const map3 = {
62634
+ 0: "⁰",
62635
+ 1: "¹",
62636
+ 2: "²",
62637
+ 3: "³",
62638
+ 4: "⁴",
62639
+ 5: "⁵",
62640
+ 6: "⁶",
62641
+ 7: "⁷",
62642
+ 8: "⁸",
62643
+ 9: "⁹"
62644
+ };
62645
+ return String(value ?? "").split("").map((ch) => map3[ch] ?? ch).join("");
62646
+ };
62647
+ const resolveFootnoteDisplayNumber = (editor, id) => {
62648
+ const key2 = id == null ? null : String(id);
62649
+ if (!key2) return null;
62650
+ const map3 = editor?.converter?.footnoteNumberById;
62651
+ const mapped = map3 && typeof map3 === "object" ? map3[key2] : void 0;
62652
+ return typeof mapped === "number" && Number.isFinite(mapped) && mapped > 0 ? mapped : null;
62653
+ };
62654
+ class FootnoteReferenceNodeView {
62655
+ constructor(node, getPos, decorations, editor, htmlAttributes = {}) {
62656
+ this.node = node;
62657
+ this.getPos = getPos;
62658
+ this.editor = editor;
62659
+ this.dom = this.#renderDom(node, htmlAttributes);
62660
+ }
62661
+ #renderDom(node, htmlAttributes) {
62662
+ const el = document.createElement("sup");
62663
+ el.className = "sd-footnote-ref";
62664
+ el.setAttribute("contenteditable", "false");
62665
+ el.setAttribute("aria-label", "Footnote reference");
62666
+ Object.entries(htmlAttributes).forEach(([key2, value]) => {
62667
+ if (value != null && value !== false) {
62668
+ el.setAttribute(key2, String(value));
62669
+ }
62670
+ });
62671
+ const id = node?.attrs?.id;
62672
+ if (id != null) {
62673
+ el.setAttribute("data-footnote-id", String(id));
62674
+ const display = resolveFootnoteDisplayNumber(this.editor, id) ?? id;
62675
+ el.textContent = toSuperscriptDigits(display);
62676
+ } else {
62677
+ el.textContent = "*";
62678
+ }
62679
+ return el;
62680
+ }
62681
+ update(node) {
62682
+ const incomingType = node?.type?.name;
62683
+ const currentType = this.node?.type?.name;
62684
+ if (!incomingType || incomingType !== currentType) return false;
62685
+ this.node = node;
62686
+ const id = node?.attrs?.id;
62687
+ if (id != null) {
62688
+ this.dom.setAttribute("data-footnote-id", String(id));
62689
+ const display = resolveFootnoteDisplayNumber(this.editor, id) ?? id;
62690
+ this.dom.textContent = toSuperscriptDigits(display);
62691
+ } else {
62692
+ this.dom.removeAttribute("data-footnote-id");
62693
+ this.dom.textContent = "*";
62694
+ }
62695
+ return true;
62696
+ }
62697
+ }
62698
+ const FootnoteReference = Node$1.create({
62699
+ name: "footnoteReference",
62700
+ group: "inline",
62701
+ inline: true,
62702
+ atom: true,
62703
+ selectable: false,
62704
+ draggable: false,
62705
+ addOptions() {
62706
+ return {
62707
+ htmlAttributes: {
62708
+ "data-footnote-ref": "true"
62709
+ }
62710
+ };
62711
+ },
62712
+ addAttributes() {
62713
+ return {
62714
+ id: {
62715
+ default: null
62716
+ },
62717
+ customMarkFollows: {
62718
+ default: null
62719
+ }
62720
+ };
62721
+ },
62722
+ addNodeView() {
62723
+ return ({ node, editor, getPos, decorations }) => {
62724
+ const htmlAttributes = this.options.htmlAttributes;
62725
+ return new FootnoteReferenceNodeView(node, getPos, decorations, editor, htmlAttributes);
62726
+ };
62727
+ },
62728
+ parseDOM() {
62729
+ return [{ tag: "sup[data-footnote-id]" }];
62730
+ },
62731
+ renderDOM({ htmlAttributes }) {
62732
+ return ["sup", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes)];
62733
+ }
62734
+ });
61494
62735
  let cache$1 = /* @__PURE__ */ new WeakMap();
61495
62736
  function getParagraphContext(paragraph, startPos, helpers2, revision, compute) {
61496
62737
  const cached = cache$1.get(paragraph);
@@ -75102,6 +76343,7 @@ const getStarterExtensions = () => {
75102
76343
  CommentRangeStart,
75103
76344
  CommentRangeEnd,
75104
76345
  CommentReference,
76346
+ FootnoteReference,
75105
76347
  Document,
75106
76348
  FontFamily,
75107
76349
  FontSize,