diff --git a/apps/server/src/ee b/apps/server/src/ee index 05529bcf9..c7b77ffb9 160000 --- a/apps/server/src/ee +++ b/apps/server/src/ee @@ -1 +1 @@ -Subproject commit 05529bcf97919d84f17442a9faf1a93904a5a85a +Subproject commit c7b77ffb9ed6a7bf462a4683de4f84355122e00b diff --git a/packages/editor-ext/src/lib/prosemirror-docx/schema.ts b/packages/editor-ext/src/lib/prosemirror-docx/schema.ts index 0f7550d32..df89cf1d7 100644 --- a/packages/editor-ext/src/lib/prosemirror-docx/schema.ts +++ b/packages/editor-ext/src/lib/prosemirror-docx/schema.ts @@ -1,4 +1,4 @@ -import { HeadingLevel, ShadingType } from 'docx'; +import { FootnoteReferenceRun, HeadingLevel, Paragraph, ShadingType } from 'docx'; import { Node } from 'prosemirror-model'; import { DocxSerializerAsync, @@ -169,15 +169,26 @@ export const defaultAsyncNodes: NodeSerializerAsync = { state.closeBlock(node, { pageBreakBefore: true }); }, footnoteReference(state, node) { - state.addRunOptions({ superScript: true }); - state.text(`[${node.attrs?.referenceNumber ?? ''}]`); + const number = + Number(node.attrs?.referenceNumber) || state.$footnoteCounter + 1; + state.$footnoteCounter = Math.max(state.$footnoteCounter, number); + // seed an empty body so the reference stays valid even if the trailing + // footnotes list is missing; the footnotes node overwrites it with content + if (!state.footnotes[number]) { + state.footnotes[number] = { children: [new Paragraph('')] }; + } + state.current.push(new FootnoteReferenceRun(number)); }, async footnotes(state, node) { - await state.renderList(node, 'numbered'); - }, - async footnote(state, node) { - await state.renderListItem(node); + for (let i = 0; i < node.childCount; i += 1) { + const item = node.child(i); + const number = + Number(String(item.attrs?.id ?? '').replace('fn:', '')) || i + 1; + await state.footnoteDefinition(item, number); + } }, + // items are consumed by the footnotes handler above + footnote() {}, // No usable static export representation: skip without failing. subpages() {}, transclusionReference() {}, diff --git a/packages/editor-ext/src/lib/prosemirror-docx/serializer.ts b/packages/editor-ext/src/lib/prosemirror-docx/serializer.ts index fa62a8cf6..b349ece3e 100644 --- a/packages/editor-ext/src/lib/prosemirror-docx/serializer.ts +++ b/packages/editor-ext/src/lib/prosemirror-docx/serializer.ts @@ -824,6 +824,29 @@ export class DocxSerializerStateAsync { this.current.push(new FootnoteReferenceRun(this.$footnoteCounter)); } + // Fills the footnote body for an already-referenced footnote number from a + // node holding block content (Docmost keeps footnote text in a trailing + // list, separate from the inline reference). + async footnoteDefinition(node: Node, number: number) { + const { current, children, nextRunOpts, nextParentParagraphOpts } = this; + this.current = []; + this.children = []; + delete this.nextRunOpts; + delete this.nextParentParagraphOpts; + + await this.renderContent(node); + this.footnotes[number] = { + children: this.children.filter( + (child): child is Paragraph => child instanceof Paragraph, + ), + }; + + this.current = current; + this.children = children; + this.nextRunOpts = nextRunOpts; + this.nextParentParagraphOpts = nextParentParagraphOpts; + } + closeBlock(node: Node, props?: IParagraphOptions) { const paragraph = new Paragraph({ children: this.current,