docxodus 9.7.0 → 9.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +0 -1
- package/dist/editor.bundle.js +454 -99
- package/dist/editor.d.ts +4 -4
- package/dist/editor.d.ts.map +1 -1
- package/dist/editor.js +55 -8
- package/dist/editor.js.map +1 -1
- package/dist/embed.bundle.js +454 -99
- package/dist/embed.iife.js +454 -99
- package/dist/page-geometry.d.ts +50 -3
- package/dist/page-geometry.d.ts.map +1 -1
- package/dist/page-geometry.js +63 -6
- package/dist/page-geometry.js.map +1 -1
- package/dist/pagination.bundle.js +416 -91
- package/dist/pagination.d.ts +61 -8
- package/dist/pagination.d.ts.map +1 -1
- package/dist/pagination.js +353 -105
- package/dist/pagination.js.map +1 -1
- package/dist/ribbon-chrome.d.ts +1 -1
- package/dist/ribbon-chrome.d.ts.map +1 -1
- package/dist/ribbon-chrome.js +1 -0
- package/dist/ribbon-chrome.js.map +1 -1
- package/dist/wasm/_framework/Docxodus.wasm +0 -0
- package/dist/wasm/_framework/Docxodus.wasm.br +0 -0
- package/dist/wasm/_framework/DocxodusWasm.wasm +0 -0
- package/dist/wasm/_framework/DocxodusWasm.wasm.br +0 -0
- package/dist/wasm/_framework/System.Collections.Concurrent.wasm +0 -0
- package/dist/wasm/_framework/System.Collections.Concurrent.wasm.br +0 -0
- package/dist/wasm/_framework/System.Collections.Immutable.wasm +0 -0
- package/dist/wasm/_framework/System.Collections.Immutable.wasm.br +0 -0
- package/dist/wasm/_framework/System.Collections.NonGeneric.wasm +0 -0
- package/dist/wasm/_framework/System.Collections.NonGeneric.wasm.br +0 -0
- package/dist/wasm/_framework/System.Collections.Specialized.wasm +0 -0
- package/dist/wasm/_framework/System.Collections.Specialized.wasm.br +0 -0
- package/dist/wasm/_framework/System.Collections.wasm +0 -0
- package/dist/wasm/_framework/System.Collections.wasm.br +0 -0
- package/dist/wasm/_framework/System.ComponentModel.Primitives.wasm +0 -0
- package/dist/wasm/_framework/System.ComponentModel.Primitives.wasm.br +0 -0
- package/dist/wasm/_framework/System.ComponentModel.TypeConverter.wasm +0 -0
- package/dist/wasm/_framework/System.ComponentModel.TypeConverter.wasm.br +0 -0
- package/dist/wasm/_framework/System.ComponentModel.wasm +0 -0
- package/dist/wasm/_framework/System.ComponentModel.wasm.br +0 -0
- package/dist/wasm/_framework/System.Console.wasm +0 -0
- package/dist/wasm/_framework/System.Console.wasm.br +0 -0
- package/dist/wasm/_framework/System.Diagnostics.Process.wasm +0 -0
- package/dist/wasm/_framework/System.Diagnostics.Process.wasm.br +0 -0
- package/dist/wasm/_framework/System.IO.Compression.wasm +0 -0
- package/dist/wasm/_framework/System.IO.Compression.wasm.br +0 -0
- package/dist/wasm/_framework/System.IO.Pipelines.wasm +0 -0
- package/dist/wasm/_framework/System.IO.Pipelines.wasm.br +0 -0
- package/dist/wasm/_framework/System.Linq.Expressions.wasm +0 -0
- package/dist/wasm/_framework/System.Linq.Expressions.wasm.br +0 -0
- package/dist/wasm/_framework/System.Linq.wasm +0 -0
- package/dist/wasm/_framework/System.Linq.wasm.br +0 -0
- package/dist/wasm/_framework/System.Memory.wasm +0 -0
- package/dist/wasm/_framework/System.Memory.wasm.br +0 -0
- package/dist/wasm/_framework/System.Net.Http.wasm +0 -0
- package/dist/wasm/_framework/System.Net.Http.wasm.br +0 -0
- package/dist/wasm/_framework/System.Net.Primitives.wasm +0 -0
- package/dist/wasm/_framework/System.Net.Primitives.wasm.br +0 -0
- package/dist/wasm/_framework/System.ObjectModel.wasm +0 -0
- package/dist/wasm/_framework/System.ObjectModel.wasm.br +0 -0
- package/dist/wasm/_framework/System.Private.CoreLib.wasm +0 -0
- package/dist/wasm/_framework/System.Private.CoreLib.wasm.br +0 -0
- package/dist/wasm/_framework/System.Private.Uri.wasm +0 -0
- package/dist/wasm/_framework/System.Private.Uri.wasm.br +0 -0
- package/dist/wasm/_framework/System.Private.Xml.Linq.wasm +0 -0
- package/dist/wasm/_framework/System.Private.Xml.Linq.wasm.br +0 -0
- package/dist/wasm/_framework/System.Private.Xml.wasm +0 -0
- package/dist/wasm/_framework/System.Private.Xml.wasm.br +0 -0
- package/dist/wasm/_framework/System.Runtime.InteropServices.JavaScript.wasm +0 -0
- package/dist/wasm/_framework/System.Runtime.InteropServices.JavaScript.wasm.br +0 -0
- package/dist/wasm/_framework/System.Runtime.wasm +0 -0
- package/dist/wasm/_framework/System.Runtime.wasm.br +0 -0
- package/dist/wasm/_framework/System.Security.Cryptography.wasm +0 -0
- package/dist/wasm/_framework/System.Security.Cryptography.wasm.br +0 -0
- package/dist/wasm/_framework/System.Text.Encodings.Web.wasm +0 -0
- package/dist/wasm/_framework/System.Text.Encodings.Web.wasm.br +0 -0
- package/dist/wasm/_framework/System.Text.Json.wasm +0 -0
- package/dist/wasm/_framework/System.Text.Json.wasm.br +0 -0
- package/dist/wasm/_framework/System.Text.RegularExpressions.wasm +0 -0
- package/dist/wasm/_framework/System.Text.RegularExpressions.wasm.br +0 -0
- package/dist/wasm/_framework/System.Xml.Linq.wasm +0 -0
- package/dist/wasm/_framework/System.Xml.Linq.wasm.br +0 -0
- package/dist/wasm/_framework/System.Xml.XDocument.wasm +0 -0
- package/dist/wasm/_framework/System.Xml.XDocument.wasm.br +0 -0
- package/dist/wasm/_framework/System.wasm +0 -0
- package/dist/wasm/_framework/System.wasm.br +0 -0
- package/dist/wasm/_framework/dotnet.boot.js +35 -35
- package/dist/wasm/_framework/dotnet.boot.js.br +0 -0
- package/dist/wasm/_framework/dotnet.js +1 -1
- package/dist/wasm/_framework/dotnet.js.br +0 -0
- package/dist/wasm/_framework/dotnet.native.js +3 -3
- package/dist/wasm/_framework/dotnet.native.js.br +0 -0
- package/dist/wasm/_framework/dotnet.native.wasm +0 -0
- package/dist/wasm/_framework/dotnet.native.wasm.br +0 -0
- package/dist/wasm/_framework/dotnet.runtime.js +1 -1
- package/dist/wasm/_framework/dotnet.runtime.js.br +0 -0
- package/package.json +14 -5
- package/dist/wasm/index.html +0 -238
- package/dist/wasm/main.js +0 -26
package/dist/editor.bundle.js
CHANGED
|
@@ -165,7 +165,7 @@ var DocxodusEditor = (() => {
|
|
|
165
165
|
var DEFAULT_PAGE_WIDTH = 612;
|
|
166
166
|
var DEFAULT_PAGE_HEIGHT = 792;
|
|
167
167
|
var DEFAULT_MARGIN = 72;
|
|
168
|
-
var
|
|
168
|
+
var DEFAULT_HEADER_FOOTER_DISTANCE = 36;
|
|
169
169
|
function pxToPt(px) {
|
|
170
170
|
return px * 0.75;
|
|
171
171
|
}
|
|
@@ -181,8 +181,8 @@ var DocxodusEditor = (() => {
|
|
|
181
181
|
const marginRight = parseFloat(section.dataset.marginRight || "") || DEFAULT_MARGIN;
|
|
182
182
|
const marginBottom = parseFloat(section.dataset.marginBottom || "") || DEFAULT_MARGIN;
|
|
183
183
|
const marginLeft = parseFloat(section.dataset.marginLeft || "") || DEFAULT_MARGIN;
|
|
184
|
-
const
|
|
185
|
-
const
|
|
184
|
+
const headerDistance = parseFloat(section.dataset.headerHeight || "") || DEFAULT_HEADER_FOOTER_DISTANCE;
|
|
185
|
+
const footerDistance = parseFloat(section.dataset.footerHeight || "") || DEFAULT_HEADER_FOOTER_DISTANCE;
|
|
186
186
|
return {
|
|
187
187
|
pageWidth,
|
|
188
188
|
pageHeight,
|
|
@@ -192,8 +192,37 @@ var DocxodusEditor = (() => {
|
|
|
192
192
|
marginRight,
|
|
193
193
|
marginBottom,
|
|
194
194
|
marginLeft,
|
|
195
|
-
|
|
196
|
-
|
|
195
|
+
headerDistance,
|
|
196
|
+
footerDistance,
|
|
197
|
+
headerHeight: headerDistance,
|
|
198
|
+
footerHeight: footerDistance
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
var MIN_BODY_BAND_PT = 12;
|
|
202
|
+
function resolvePageBands(dims, headerContentHeight, footerContentHeight) {
|
|
203
|
+
const headerTop = dims.headerDistance;
|
|
204
|
+
const footerBottom = dims.pageHeight - dims.footerDistance;
|
|
205
|
+
const headerReach = headerContentHeight > 0 ? headerTop + headerContentHeight : 0;
|
|
206
|
+
const footerReach = footerContentHeight > 0 ? footerBottom - footerContentHeight : dims.pageHeight;
|
|
207
|
+
let bodyTop = Math.max(dims.marginTop, headerReach);
|
|
208
|
+
let bodyBottom = Math.min(dims.pageHeight - dims.marginBottom, footerReach);
|
|
209
|
+
const shortfall = MIN_BODY_BAND_PT - (bodyBottom - bodyTop);
|
|
210
|
+
if (shortfall > 0) {
|
|
211
|
+
const headerGrowth = bodyTop - dims.marginTop;
|
|
212
|
+
const footerGrowth = dims.pageHeight - dims.marginBottom - bodyBottom;
|
|
213
|
+
const growth = headerGrowth + footerGrowth;
|
|
214
|
+
if (growth > 0) {
|
|
215
|
+
bodyTop -= shortfall * headerGrowth / growth;
|
|
216
|
+
bodyBottom += shortfall * footerGrowth / growth;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return {
|
|
220
|
+
headerTop,
|
|
221
|
+
headerHeight: Math.max(0, Math.min(headerContentHeight, bodyTop - headerTop)),
|
|
222
|
+
bodyTop,
|
|
223
|
+
bodyHeight: Math.max(0, bodyBottom - bodyTop),
|
|
224
|
+
footerTop: footerBottom - Math.max(0, Math.min(footerContentHeight, footerBottom - bodyBottom)),
|
|
225
|
+
footerHeight: Math.max(0, Math.min(footerContentHeight, footerBottom - bodyBottom))
|
|
197
226
|
};
|
|
198
227
|
}
|
|
199
228
|
function sectionWrappers(root) {
|
|
@@ -230,6 +259,9 @@ var DocxodusEditor = (() => {
|
|
|
230
259
|
}
|
|
231
260
|
|
|
232
261
|
// src/pagination.ts
|
|
262
|
+
function samePageBox(a, b) {
|
|
263
|
+
return a.pageWidth === b.pageWidth && a.pageHeight === b.pageHeight && a.marginTop === b.marginTop && a.marginRight === b.marginRight && a.marginBottom === b.marginBottom && a.marginLeft === b.marginLeft;
|
|
264
|
+
}
|
|
233
265
|
var MAX_FOOTNOTE_AREA_RATIO = 0.6;
|
|
234
266
|
var MIN_BODY_CONTENT_HEIGHT = 72;
|
|
235
267
|
var PaginationEngine = class {
|
|
@@ -275,16 +307,43 @@ var DocxodusEditor = (() => {
|
|
|
275
307
|
);
|
|
276
308
|
this.pageNumbering = this.parsePageNumbering(sections);
|
|
277
309
|
const sectionsToProcess = sections.length > 0 ? Array.from(sections) : [this.stagingElement];
|
|
310
|
+
const runs = [];
|
|
278
311
|
for (const section of sectionsToProcess) {
|
|
279
|
-
const sectionIndex = parseInt(section.dataset.sectionIndex || "0", 10);
|
|
280
312
|
const dims = parseSectionDimensions(section);
|
|
313
|
+
const previous = runs[runs.length - 1];
|
|
314
|
+
if (previous && section.dataset.sectionType === "continuous" && samePageBox(previous.dims, dims)) {
|
|
315
|
+
previous.sections.push(section);
|
|
316
|
+
} else {
|
|
317
|
+
runs.push({
|
|
318
|
+
sections: [section],
|
|
319
|
+
sectionIndex: parseInt(section.dataset.sectionIndex || "0", 10),
|
|
320
|
+
dims
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
for (const run of runs) {
|
|
281
325
|
this.stagingElement.style.visibility = "hidden";
|
|
282
326
|
this.stagingElement.style.position = "absolute";
|
|
283
327
|
this.stagingElement.style.left = "-9999px";
|
|
284
328
|
this.stagingElement.style.display = "block";
|
|
285
|
-
|
|
286
|
-
const
|
|
287
|
-
|
|
329
|
+
const blocks = [];
|
|
330
|
+
for (const section of run.sections) {
|
|
331
|
+
section.style.width = `${run.dims.contentWidth}pt`;
|
|
332
|
+
const columnCount = parseInt(section.dataset.cols || "1", 10);
|
|
333
|
+
if (columnCount > 1) {
|
|
334
|
+
const gap = parseFloat(section.dataset.colGap || "");
|
|
335
|
+
blocks.push(...this.buildColumnBlocks(
|
|
336
|
+
section,
|
|
337
|
+
run.dims,
|
|
338
|
+
run.sectionIndex,
|
|
339
|
+
columnCount,
|
|
340
|
+
Number.isFinite(gap) ? gap : 36
|
|
341
|
+
));
|
|
342
|
+
} else {
|
|
343
|
+
blocks.push(...this.measureBlocks(section, run.dims));
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
const sectionPages = this.flowToPages(blocks, run.dims, pageNumber, run.sectionIndex);
|
|
288
347
|
pages.push(...sectionPages);
|
|
289
348
|
pageNumber += sectionPages.length;
|
|
290
349
|
}
|
|
@@ -373,11 +432,70 @@ var DocxodusEditor = (() => {
|
|
|
373
432
|
keepWithNext: child.dataset.keepWithNext === "true",
|
|
374
433
|
keepLines: child.dataset.keepLines === "true",
|
|
375
434
|
pageBreakBefore: child.dataset.pageBreakBefore === "true",
|
|
376
|
-
isPageBreak
|
|
435
|
+
isPageBreak,
|
|
436
|
+
isWordParagraph: this.isWordParagraphElement(child)
|
|
377
437
|
});
|
|
378
438
|
}
|
|
379
439
|
return blocks;
|
|
380
440
|
}
|
|
441
|
+
/**
|
|
442
|
+
* Flows a multi-column (`w:cols`) section's children into CSS-multicol container
|
|
443
|
+
* blocks. Word lays such a section out as N columns inside the same body extent;
|
|
444
|
+
* a balanced `column-count` container reproduces that geometry, and the paginator
|
|
445
|
+
* then places each container as one ordinary measured block. A container grows
|
|
446
|
+
* greedily until its balanced height would exceed the smallest page body available
|
|
447
|
+
* to the section, so a long columned section still splits across pages at block
|
|
448
|
+
* boundaries. Each child lands in exactly one container, so anchors never
|
|
449
|
+
* duplicate. An explicit page break child passes through as its own block, which
|
|
450
|
+
* ends the current container and lets the normal flow logic turn the page.
|
|
451
|
+
*/
|
|
452
|
+
buildColumnBlocks(section, dims, sectionIndex, columnCount, columnGapPt) {
|
|
453
|
+
const children = Array.from(section.children);
|
|
454
|
+
const blocks = [];
|
|
455
|
+
const maxFragmentHeight = this.smallestEffectiveContentHeight(dims, sectionIndex);
|
|
456
|
+
const isBreak = (child) => child.dataset.pageBreak === "true" || child.classList.contains(`${this.cssPrefix}break`);
|
|
457
|
+
const makeContainer = (slice) => {
|
|
458
|
+
const container = document.createElement("div");
|
|
459
|
+
container.style.columnCount = String(columnCount);
|
|
460
|
+
container.style.columnGap = `${columnGapPt}pt`;
|
|
461
|
+
for (const child of slice) {
|
|
462
|
+
container.appendChild(child.cloneNode(true));
|
|
463
|
+
}
|
|
464
|
+
return container;
|
|
465
|
+
};
|
|
466
|
+
let start2 = 0;
|
|
467
|
+
while (start2 < children.length) {
|
|
468
|
+
if (isBreak(children[start2])) {
|
|
469
|
+
blocks.push(this.measureElement(children[start2], dims));
|
|
470
|
+
start2++;
|
|
471
|
+
continue;
|
|
472
|
+
}
|
|
473
|
+
let end = start2 + 1;
|
|
474
|
+
let container = makeContainer(children.slice(start2, end));
|
|
475
|
+
let measured = this.measureElement(container, dims);
|
|
476
|
+
while (end < children.length && !isBreak(children[end])) {
|
|
477
|
+
const candidate = makeContainer(children.slice(start2, end + 1));
|
|
478
|
+
const candidateMeasured = this.measureElement(candidate, dims);
|
|
479
|
+
if (candidateMeasured.heightPt > maxFragmentHeight) break;
|
|
480
|
+
container = candidate;
|
|
481
|
+
measured = candidateMeasured;
|
|
482
|
+
end++;
|
|
483
|
+
}
|
|
484
|
+
blocks.push({
|
|
485
|
+
element: container,
|
|
486
|
+
heightPt: measured.heightPt,
|
|
487
|
+
marginTopPt: measured.marginTopPt,
|
|
488
|
+
marginBottomPt: measured.marginBottomPt,
|
|
489
|
+
keepWithNext: false,
|
|
490
|
+
keepLines: false,
|
|
491
|
+
pageBreakBefore: false,
|
|
492
|
+
isPageBreak: false,
|
|
493
|
+
isWordParagraph: false
|
|
494
|
+
});
|
|
495
|
+
start2 = end;
|
|
496
|
+
}
|
|
497
|
+
return blocks;
|
|
498
|
+
}
|
|
381
499
|
/**
|
|
382
500
|
* Measures one element in the same hidden staging context used for the source blocks.
|
|
383
501
|
* This is intentionally DOM-based: table row heights cannot be inferred from individual
|
|
@@ -402,7 +520,8 @@ var DocxodusEditor = (() => {
|
|
|
402
520
|
keepWithNext: element.dataset.keepWithNext === "true",
|
|
403
521
|
keepLines: element.dataset.keepLines === "true",
|
|
404
522
|
pageBreakBefore: element.dataset.pageBreakBefore === "true",
|
|
405
|
-
isPageBreak: element.dataset.pageBreak === "true" || element.classList.contains(`${this.cssPrefix}break`)
|
|
523
|
+
isPageBreak: element.dataset.pageBreak === "true" || element.classList.contains(`${this.cssPrefix}break`),
|
|
524
|
+
isWordParagraph: this.isWordParagraphElement(element)
|
|
406
525
|
};
|
|
407
526
|
this.stagingElement.removeChild(measurementHost);
|
|
408
527
|
return measured;
|
|
@@ -434,10 +553,15 @@ var DocxodusEditor = (() => {
|
|
|
434
553
|
* collapsed-margin rules as normal block placement. The trailing margin is
|
|
435
554
|
* intentionally excluded, matching the individual block fit check.
|
|
436
555
|
*/
|
|
437
|
-
measureKeepWithNextChainBodyHeight(chain, previousMarginBottomPt, isFirstOnPage) {
|
|
556
|
+
measureKeepWithNextChainBodyHeight(chain, previousMarginBottomPt, isFirstOnPage, pageInSection) {
|
|
438
557
|
const firstBlock = chain[0];
|
|
439
558
|
if (!firstBlock) return 0;
|
|
440
|
-
const firstMarginTop =
|
|
559
|
+
const firstMarginTop = this.effectiveBlockMarginTop(
|
|
560
|
+
firstBlock,
|
|
561
|
+
previousMarginBottomPt,
|
|
562
|
+
isFirstOnPage,
|
|
563
|
+
pageInSection
|
|
564
|
+
);
|
|
441
565
|
let bodyHeight = firstMarginTop + firstBlock.heightPt;
|
|
442
566
|
for (let index = 1; index < chain.length; index++) {
|
|
443
567
|
const previousBlock = chain[index - 1];
|
|
@@ -446,6 +570,35 @@ var DocxodusEditor = (() => {
|
|
|
446
570
|
}
|
|
447
571
|
return bodyHeight;
|
|
448
572
|
}
|
|
573
|
+
/** Word paragraphs render as `p`, or as `h1`–`h6` when their style has an outline level. */
|
|
574
|
+
isWordParagraphElement(element) {
|
|
575
|
+
return element.tagName === "P" || /^H[1-6]$/.test(element.tagName);
|
|
576
|
+
}
|
|
577
|
+
shouldSuppressPageTopSpacing(block, isFirstOnPage, pageInSection) {
|
|
578
|
+
return isFirstOnPage && pageInSection > 1 && block.isWordParagraph === true;
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* Resolves the part of a block's top margin that consumes the current page.
|
|
582
|
+
*
|
|
583
|
+
* In Word's native DOCX layout, paragraph space-before is suppressed when a paragraph is the
|
|
584
|
+
* first body block on a later page of the SAME section. The first page of a document/section is
|
|
585
|
+
* the exception and keeps its spacing. Tables and other block margins are not paragraph spacing,
|
|
586
|
+
* so they continue to use the ordinary CSS collapsing rule.
|
|
587
|
+
*/
|
|
588
|
+
effectiveBlockMarginTop(block, previousMarginBottomPt, isFirstOnPage, pageInSection) {
|
|
589
|
+
if (this.shouldSuppressPageTopSpacing(block, isFirstOnPage, pageInSection)) {
|
|
590
|
+
return 0;
|
|
591
|
+
}
|
|
592
|
+
return isFirstOnPage ? block.marginTopPt : Math.max(block.marginTopPt, previousMarginBottomPt) - previousMarginBottomPt;
|
|
593
|
+
}
|
|
594
|
+
/** Clone a source block with the same page-top spacing decision used by the height budget. */
|
|
595
|
+
cloneBlockForPage(block, isFirstOnPage, pageInSection) {
|
|
596
|
+
const clone = block.element.cloneNode(true);
|
|
597
|
+
if (this.shouldSuppressPageTopSpacing(block, isFirstOnPage, pageInSection)) {
|
|
598
|
+
clone.style.setProperty("margin-top", "0", "important");
|
|
599
|
+
}
|
|
600
|
+
return clone;
|
|
601
|
+
}
|
|
449
602
|
/**
|
|
450
603
|
* Finds footnote references introduced by a sequence of blocks, preserving
|
|
451
604
|
* document order and excluding references already assigned to the page.
|
|
@@ -470,9 +623,9 @@ var DocxodusEditor = (() => {
|
|
|
470
623
|
*/
|
|
471
624
|
smallestEffectiveContentHeight(dims, sectionIndex) {
|
|
472
625
|
return Math.min(
|
|
473
|
-
this.
|
|
474
|
-
this.
|
|
475
|
-
this.
|
|
626
|
+
this.getPageBands(dims, sectionIndex, 1, 1).bodyHeight,
|
|
627
|
+
this.getPageBands(dims, sectionIndex, 2, 1).bodyHeight,
|
|
628
|
+
this.getPageBands(dims, sectionIndex, 2, 2).bodyHeight
|
|
476
629
|
);
|
|
477
630
|
}
|
|
478
631
|
/**
|
|
@@ -996,7 +1149,7 @@ var DocxodusEditor = (() => {
|
|
|
996
1149
|
/**
|
|
997
1150
|
* Adds footnotes to a page container, including continuation content.
|
|
998
1151
|
*/
|
|
999
|
-
addPageFootnotes(pageBox, footnoteIds, dims, footnoteHeight, continuation, partialFootnotes) {
|
|
1152
|
+
addPageFootnotes(pageBox, footnoteIds, dims, bands, footnoteHeight, continuation, partialFootnotes) {
|
|
1000
1153
|
const hasContinuation = continuation && continuation.remainingElements.length > 0;
|
|
1001
1154
|
if (footnoteIds.length === 0 && !hasContinuation) {
|
|
1002
1155
|
return;
|
|
@@ -1007,12 +1160,12 @@ var DocxodusEditor = (() => {
|
|
|
1007
1160
|
const partialFootnoteIds = new Set(partialFootnotes?.map((p) => p.footnoteId) || []);
|
|
1008
1161
|
const maxFootnoteHeight = Math.min(
|
|
1009
1162
|
footnoteHeight,
|
|
1010
|
-
|
|
1163
|
+
bands.bodyHeight * MAX_FOOTNOTE_AREA_RATIO
|
|
1011
1164
|
);
|
|
1012
1165
|
const footnotesDiv = document.createElement("div");
|
|
1013
1166
|
footnotesDiv.className = `${this.cssPrefix}footnotes`;
|
|
1014
1167
|
footnotesDiv.style.position = "absolute";
|
|
1015
|
-
footnotesDiv.style.bottom = `${dims.
|
|
1168
|
+
footnotesDiv.style.bottom = `${dims.pageHeight - (bands.bodyTop + bands.bodyHeight)}pt`;
|
|
1016
1169
|
footnotesDiv.style.left = `${dims.marginLeft}pt`;
|
|
1017
1170
|
footnotesDiv.style.width = `${dims.contentWidth}pt`;
|
|
1018
1171
|
footnotesDiv.style.boxSizing = "border-box";
|
|
@@ -1086,50 +1239,51 @@ var DocxodusEditor = (() => {
|
|
|
1086
1239
|
return sectionHf.footerDefault;
|
|
1087
1240
|
}
|
|
1088
1241
|
/**
|
|
1089
|
-
*
|
|
1090
|
-
*
|
|
1091
|
-
*
|
|
1092
|
-
*
|
|
1093
|
-
*
|
|
1242
|
+
* The header, body, and footer bands for one page position.
|
|
1243
|
+
*
|
|
1244
|
+
* The single owner of "where does anything sit vertically on this page" — placement in
|
|
1245
|
+
* {@link createPage}, the body budget the flow loop spends, and the note area's anchor all
|
|
1246
|
+
* read it, so those three cannot disagree about where the body ends.
|
|
1247
|
+
*
|
|
1248
|
+
* Deterministic: it depends only on the section's page setup and the registry's pre-measured
|
|
1249
|
+
* story heights, never on the page's content, which is what keeps it lazy-loading compatible.
|
|
1094
1250
|
*/
|
|
1095
|
-
|
|
1251
|
+
getPageBands(dims, sectionIndex, pageInSection, globalPageNumber) {
|
|
1096
1252
|
const sectionHf = this.hfRegistry.get(sectionIndex);
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
}
|
|
1124
|
-
}
|
|
1125
|
-
const headerExpansion = headerHeight - dims.marginTop;
|
|
1126
|
-
const footerExpansion = footerHeight - dims.marginBottom;
|
|
1127
|
-
const contentHeight = dims.contentHeight - headerExpansion - footerExpansion;
|
|
1128
|
-
return { headerHeight, footerHeight, contentHeight };
|
|
1253
|
+
return resolvePageBands(
|
|
1254
|
+
dims,
|
|
1255
|
+
this.selectStoryHeight(
|
|
1256
|
+
sectionHf?.headerFirstHeight,
|
|
1257
|
+
sectionHf?.headerEvenHeight,
|
|
1258
|
+
sectionHf?.headerDefaultHeight,
|
|
1259
|
+
pageInSection,
|
|
1260
|
+
globalPageNumber
|
|
1261
|
+
),
|
|
1262
|
+
this.selectStoryHeight(
|
|
1263
|
+
sectionHf?.footerFirstHeight,
|
|
1264
|
+
sectionHf?.footerEvenHeight,
|
|
1265
|
+
sectionHf?.footerDefaultHeight,
|
|
1266
|
+
pageInSection,
|
|
1267
|
+
globalPageNumber
|
|
1268
|
+
)
|
|
1269
|
+
);
|
|
1270
|
+
}
|
|
1271
|
+
/**
|
|
1272
|
+
* The measured height of the running story this page position selects, mirroring
|
|
1273
|
+
* {@link selectHeader}/{@link selectFooter}. Zero when the page has no such story.
|
|
1274
|
+
*/
|
|
1275
|
+
selectStoryHeight(first, even, fallback, pageInSection, globalPageNumber) {
|
|
1276
|
+
if (pageInSection === 1 && first != null) return first;
|
|
1277
|
+
if (globalPageNumber % 2 === 0 && even != null) return even;
|
|
1278
|
+
return fallback ?? 0;
|
|
1129
1279
|
}
|
|
1130
1280
|
/**
|
|
1131
1281
|
* Measures the content height of a header or footer element.
|
|
1132
|
-
*
|
|
1282
|
+
*
|
|
1283
|
+
* This is what tells {@link resolvePageBands} whether the story stays inside its margin or
|
|
1284
|
+
* pushes the body, so it must measure the story ALONE — any padding added here would have to
|
|
1285
|
+
* be added to the rendered band too, and the two drifting apart is exactly how a header
|
|
1286
|
+
* silently starts overlapping body text.
|
|
1133
1287
|
*/
|
|
1134
1288
|
measureHeaderFooterHeight(source, contentWidth) {
|
|
1135
1289
|
const measureContainer = document.createElement("div");
|
|
@@ -1137,7 +1291,6 @@ var DocxodusEditor = (() => {
|
|
|
1137
1291
|
measureContainer.style.visibility = "hidden";
|
|
1138
1292
|
measureContainer.style.width = `${contentWidth}pt`;
|
|
1139
1293
|
measureContainer.style.left = "-9999px";
|
|
1140
|
-
measureContainer.style.paddingBottom = "4pt";
|
|
1141
1294
|
for (const child of Array.from(source.childNodes)) {
|
|
1142
1295
|
measureContainer.appendChild(child.cloneNode(true));
|
|
1143
1296
|
}
|
|
@@ -1157,7 +1310,7 @@ var DocxodusEditor = (() => {
|
|
|
1157
1310
|
let currentContent = [];
|
|
1158
1311
|
let pageNumber = startPageNumber;
|
|
1159
1312
|
let pageInSection = 1;
|
|
1160
|
-
let {
|
|
1313
|
+
let { bodyHeight: effectiveContentHeight } = this.getPageBands(
|
|
1161
1314
|
dims,
|
|
1162
1315
|
sectionIndex,
|
|
1163
1316
|
pageInSection,
|
|
@@ -1192,8 +1345,8 @@ var DocxodusEditor = (() => {
|
|
|
1192
1345
|
pageNumber++;
|
|
1193
1346
|
pageInSection++;
|
|
1194
1347
|
currentContent = [];
|
|
1195
|
-
const
|
|
1196
|
-
effectiveContentHeight =
|
|
1348
|
+
const newBands = this.getPageBands(dims, sectionIndex, pageInSection, pageNumber);
|
|
1349
|
+
effectiveContentHeight = newBands.bodyHeight;
|
|
1197
1350
|
remainingHeight = effectiveContentHeight;
|
|
1198
1351
|
prevMarginBottomPt = 0;
|
|
1199
1352
|
currentFootnoteIds = [];
|
|
@@ -1251,11 +1404,12 @@ var DocxodusEditor = (() => {
|
|
|
1251
1404
|
const currentChainHeight = this.measureKeepWithNextChainBodyHeight(
|
|
1252
1405
|
keepChain,
|
|
1253
1406
|
prevMarginBottomPt,
|
|
1254
|
-
currentContent.length === 0
|
|
1407
|
+
currentContent.length === 0,
|
|
1408
|
+
pageInSection
|
|
1255
1409
|
) + additionalChainFootnoteHeight;
|
|
1256
1410
|
const currentAvailableHeight = remainingHeight - currentFootnoteHeight;
|
|
1257
1411
|
if (currentChainHeight > currentAvailableHeight) {
|
|
1258
|
-
const
|
|
1412
|
+
const nextPageBands = this.getPageBands(
|
|
1259
1413
|
dims,
|
|
1260
1414
|
sectionIndex,
|
|
1261
1415
|
pageInSection + 1,
|
|
@@ -1264,14 +1418,15 @@ var DocxodusEditor = (() => {
|
|
|
1264
1418
|
const freshChainBodyHeight = this.measureKeepWithNextChainBodyHeight(
|
|
1265
1419
|
keepChain,
|
|
1266
1420
|
0,
|
|
1267
|
-
true
|
|
1421
|
+
true,
|
|
1422
|
+
pageInSection + 1
|
|
1268
1423
|
);
|
|
1269
1424
|
const freshChainFootnoteHeight = this.measureFootnotesHeight(
|
|
1270
1425
|
newChainFootnoteIds,
|
|
1271
1426
|
dims.contentWidth,
|
|
1272
1427
|
nextPageContinuation
|
|
1273
1428
|
);
|
|
1274
|
-
if (freshChainBodyHeight + freshChainFootnoteHeight <=
|
|
1429
|
+
if (freshChainBodyHeight + freshChainFootnoteHeight <= nextPageBands.bodyHeight) {
|
|
1275
1430
|
finishPage();
|
|
1276
1431
|
}
|
|
1277
1432
|
}
|
|
@@ -1290,10 +1445,12 @@ var DocxodusEditor = (() => {
|
|
|
1290
1445
|
additionalFootnoteHeight = totalFootnoteHeight - currentFootnoteHeight;
|
|
1291
1446
|
}
|
|
1292
1447
|
const isFirstOnPage = currentContent.length === 0;
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1448
|
+
const effectiveMarginTop = this.effectiveBlockMarginTop(
|
|
1449
|
+
block,
|
|
1450
|
+
prevMarginBottomPt,
|
|
1451
|
+
isFirstOnPage,
|
|
1452
|
+
pageInSection
|
|
1453
|
+
);
|
|
1297
1454
|
const blockSpace = effectiveMarginTop + block.heightPt + additionalFootnoteHeight;
|
|
1298
1455
|
const effectiveRemainingHeight = remainingHeight - currentFootnoteHeight;
|
|
1299
1456
|
const bodyContentUsed = effectiveContentHeight - remainingHeight;
|
|
@@ -1313,19 +1470,19 @@ var DocxodusEditor = (() => {
|
|
|
1313
1470
|
}
|
|
1314
1471
|
}
|
|
1315
1472
|
if (blockSpace <= effectiveRemainingHeight) {
|
|
1316
|
-
currentContent.push(
|
|
1473
|
+
currentContent.push(this.cloneBlockForPage(block, isFirstOnPage, pageInSection));
|
|
1317
1474
|
remainingHeight -= effectiveMarginTop + block.heightPt + block.marginBottomPt;
|
|
1318
1475
|
prevMarginBottomPt = block.marginBottomPt;
|
|
1319
1476
|
if (newFootnoteIds.length > 0) {
|
|
1320
1477
|
currentFootnoteIds.push(...newFootnoteIds);
|
|
1321
1478
|
currentFootnoteHeight += additionalFootnoteHeight;
|
|
1322
1479
|
}
|
|
1323
|
-
} else if (block.heightPt + block
|
|
1480
|
+
} else if (block.heightPt + this.effectiveBlockMarginTop(block, 0, true, pageInSection + 1) <= effectiveContentHeight) {
|
|
1324
1481
|
const blockSpaceWithoutFootnotes = effectiveMarginTop + block.heightPt;
|
|
1325
1482
|
const minBodySpaceNeeded = bodyContentUsed + blockSpaceWithoutFootnotes + MIN_BODY_CONTENT_HEIGHT;
|
|
1326
1483
|
const canExpandFootnotes = minBodySpaceNeeded <= effectiveContentHeight;
|
|
1327
1484
|
if (newFootnoteIds.length > 0 && blockSpaceWithoutFootnotes <= effectiveRemainingHeight) {
|
|
1328
|
-
currentContent.push(
|
|
1485
|
+
currentContent.push(this.cloneBlockForPage(block, isFirstOnPage, pageInSection));
|
|
1329
1486
|
remainingHeight -= effectiveMarginTop + block.heightPt + block.marginBottomPt;
|
|
1330
1487
|
prevMarginBottomPt = block.marginBottomPt;
|
|
1331
1488
|
const availableForFootnotes = Math.min(
|
|
@@ -1372,7 +1529,7 @@ var DocxodusEditor = (() => {
|
|
|
1372
1529
|
const expandedFootnoteSpace = Math.min(maxFootnoteArea, additionalFootnoteHeight + currentFootnoteHeight);
|
|
1373
1530
|
const bodySpaceAfterExpansion = effectiveContentHeight - expandedFootnoteSpace;
|
|
1374
1531
|
if (blockSpaceWithoutFootnotes <= bodySpaceAfterExpansion - bodyContentUsed) {
|
|
1375
|
-
currentContent.push(
|
|
1532
|
+
currentContent.push(this.cloneBlockForPage(block, isFirstOnPage, pageInSection));
|
|
1376
1533
|
remainingHeight = bodySpaceAfterExpansion - bodyContentUsed - blockSpaceWithoutFootnotes;
|
|
1377
1534
|
prevMarginBottomPt = block.marginBottomPt;
|
|
1378
1535
|
currentFootnoteIds.push(...newFootnoteIds);
|
|
@@ -1380,8 +1537,14 @@ var DocxodusEditor = (() => {
|
|
|
1380
1537
|
} else {
|
|
1381
1538
|
finishPage();
|
|
1382
1539
|
const newPageFootnoteHeight = allBlockFootnoteIds.length > 0 ? this.measureFootnotesHeight(allBlockFootnoteIds, dims.contentWidth, currentContinuation) : currentContinuation ? this.measureContinuationHeight(currentContinuation, dims.contentWidth) : 0;
|
|
1383
|
-
const
|
|
1384
|
-
|
|
1540
|
+
const newPageMarginTop = this.effectiveBlockMarginTop(
|
|
1541
|
+
block,
|
|
1542
|
+
0,
|
|
1543
|
+
true,
|
|
1544
|
+
pageInSection
|
|
1545
|
+
);
|
|
1546
|
+
const newPageSpace = newPageMarginTop + block.heightPt + block.marginBottomPt;
|
|
1547
|
+
currentContent.push(this.cloneBlockForPage(block, true, pageInSection));
|
|
1385
1548
|
remainingHeight = effectiveContentHeight - newPageSpace;
|
|
1386
1549
|
prevMarginBottomPt = block.marginBottomPt;
|
|
1387
1550
|
currentFootnoteIds = [...currentFootnoteIds, ...allBlockFootnoteIds];
|
|
@@ -1390,8 +1553,14 @@ var DocxodusEditor = (() => {
|
|
|
1390
1553
|
} else {
|
|
1391
1554
|
finishPage();
|
|
1392
1555
|
const newPageFootnoteHeight = allBlockFootnoteIds.length > 0 ? this.measureFootnotesHeight(allBlockFootnoteIds, dims.contentWidth, currentContinuation) : currentContinuation ? this.measureContinuationHeight(currentContinuation, dims.contentWidth) : 0;
|
|
1393
|
-
const
|
|
1394
|
-
|
|
1556
|
+
const newPageMarginTop = this.effectiveBlockMarginTop(
|
|
1557
|
+
block,
|
|
1558
|
+
0,
|
|
1559
|
+
true,
|
|
1560
|
+
pageInSection
|
|
1561
|
+
);
|
|
1562
|
+
const newPageSpace = newPageMarginTop + block.heightPt + block.marginBottomPt;
|
|
1563
|
+
currentContent.push(this.cloneBlockForPage(block, true, pageInSection));
|
|
1395
1564
|
remainingHeight = effectiveContentHeight - newPageSpace;
|
|
1396
1565
|
prevMarginBottomPt = block.marginBottomPt;
|
|
1397
1566
|
currentFootnoteIds = [...currentFootnoteIds, ...allBlockFootnoteIds];
|
|
@@ -1410,7 +1579,7 @@ var DocxodusEditor = (() => {
|
|
|
1410
1579
|
if (currentContent.length > 0) {
|
|
1411
1580
|
finishPage();
|
|
1412
1581
|
}
|
|
1413
|
-
currentContent.push(
|
|
1582
|
+
currentContent.push(this.cloneBlockForPage(block, true, pageInSection));
|
|
1414
1583
|
currentFootnoteIds = [...currentFootnoteIds, ...allBlockFootnoteIds];
|
|
1415
1584
|
finishPage();
|
|
1416
1585
|
}
|
|
@@ -1440,6 +1609,161 @@ var DocxodusEditor = (() => {
|
|
|
1440
1609
|
if (el.getAttribute("contenteditable") !== null) el.setAttribute("contenteditable", "false");
|
|
1441
1610
|
}
|
|
1442
1611
|
}
|
|
1612
|
+
/**
|
|
1613
|
+
* Resolves floating DrawingML objects after their anchor paragraphs have landed on a page.
|
|
1614
|
+
*
|
|
1615
|
+
* The converter deliberately carries the OOXML bases and offsets as data instead of flattening
|
|
1616
|
+
* them into CSS: page/margin/column bases belong to the page, while paragraph/line/character
|
|
1617
|
+
* bases belong to the laid-out anchor. Once both coordinate systems exist, promote the object
|
|
1618
|
+
* from the clipped text column into the page box and position it in one shared point space.
|
|
1619
|
+
*/
|
|
1620
|
+
positionDrawingAnchors(pageBox, contentArea, dims, pageNumber) {
|
|
1621
|
+
const anchors = Array.from(
|
|
1622
|
+
contentArea.querySelectorAll('[data-docx-drawing-anchor="true"]')
|
|
1623
|
+
);
|
|
1624
|
+
if (anchors.length === 0) return;
|
|
1625
|
+
const pageRect = pageBox.getBoundingClientRect();
|
|
1626
|
+
const pixelsPerPoint = pageRect.width / dims.pageWidth;
|
|
1627
|
+
if (!(pixelsPerPoint > 0)) return;
|
|
1628
|
+
for (const anchor of anchors) {
|
|
1629
|
+
const staticRect = anchor.getBoundingClientRect();
|
|
1630
|
+
const paragraph = anchor.closest("p, h1, h2, h3, h4, h5, h6");
|
|
1631
|
+
const paragraphRect = paragraph?.getBoundingClientRect() ?? staticRect;
|
|
1632
|
+
const toPageX = (x) => (x - pageRect.left) / pixelsPerPoint;
|
|
1633
|
+
const toPageY = (y) => (y - pageRect.top) / pixelsPerPoint;
|
|
1634
|
+
const context = {
|
|
1635
|
+
staticLeft: toPageX(staticRect.left),
|
|
1636
|
+
staticTop: toPageY(staticRect.top),
|
|
1637
|
+
lineHeight: paragraph ? (parseFloat(getComputedStyle(paragraph).lineHeight) || staticRect.height) / pixelsPerPoint : staticRect.height / pixelsPerPoint,
|
|
1638
|
+
paragraphLeft: toPageX(paragraphRect.left),
|
|
1639
|
+
paragraphTop: toPageY(paragraphRect.top),
|
|
1640
|
+
paragraphWidth: paragraphRect.width / pixelsPerPoint,
|
|
1641
|
+
paragraphHeight: paragraphRect.height / pixelsPerPoint
|
|
1642
|
+
};
|
|
1643
|
+
const widthReference = this.horizontalAnchorReference(
|
|
1644
|
+
anchor.getAttribute("data-docx-anchor-width-relative") ?? "margin",
|
|
1645
|
+
dims,
|
|
1646
|
+
pageNumber,
|
|
1647
|
+
context
|
|
1648
|
+
);
|
|
1649
|
+
const widthPercent = this.anchorNumber(anchor, "width-percent");
|
|
1650
|
+
if (widthPercent !== void 0) {
|
|
1651
|
+
anchor.style.width = `${widthReference.size * widthPercent / 100}pt`;
|
|
1652
|
+
}
|
|
1653
|
+
const heightReference = this.verticalAnchorReference(
|
|
1654
|
+
anchor.getAttribute("data-docx-anchor-height-relative") ?? "margin",
|
|
1655
|
+
dims,
|
|
1656
|
+
pageNumber,
|
|
1657
|
+
context
|
|
1658
|
+
);
|
|
1659
|
+
const heightPercent = this.anchorNumber(anchor, "height-percent");
|
|
1660
|
+
if (heightPercent !== void 0 && anchor.dataset.docxAnchorAutofit !== "true") {
|
|
1661
|
+
anchor.style.height = `${heightReference.size * heightPercent / 100}pt`;
|
|
1662
|
+
}
|
|
1663
|
+
const sizedRect = anchor.getBoundingClientRect();
|
|
1664
|
+
const width = sizedRect.width / pixelsPerPoint;
|
|
1665
|
+
const height = sizedRect.height / pixelsPerPoint;
|
|
1666
|
+
const horizontal2 = this.horizontalAnchorReference(
|
|
1667
|
+
anchor.getAttribute("data-docx-anchor-h-relative") ?? "column",
|
|
1668
|
+
dims,
|
|
1669
|
+
pageNumber,
|
|
1670
|
+
context
|
|
1671
|
+
);
|
|
1672
|
+
const vertical2 = this.verticalAnchorReference(
|
|
1673
|
+
anchor.getAttribute("data-docx-anchor-v-relative") ?? "paragraph",
|
|
1674
|
+
dims,
|
|
1675
|
+
pageNumber,
|
|
1676
|
+
context
|
|
1677
|
+
);
|
|
1678
|
+
const left2 = this.resolveAnchorAxis(
|
|
1679
|
+
horizontal2,
|
|
1680
|
+
width,
|
|
1681
|
+
anchor.getAttribute("data-docx-anchor-h-align"),
|
|
1682
|
+
this.anchorNumber(anchor, "h-offset"),
|
|
1683
|
+
pageNumber
|
|
1684
|
+
);
|
|
1685
|
+
const top2 = this.resolveAnchorAxis(
|
|
1686
|
+
vertical2,
|
|
1687
|
+
height,
|
|
1688
|
+
anchor.getAttribute("data-docx-anchor-v-align"),
|
|
1689
|
+
this.anchorNumber(anchor, "v-offset"),
|
|
1690
|
+
pageNumber
|
|
1691
|
+
);
|
|
1692
|
+
pageBox.appendChild(anchor);
|
|
1693
|
+
anchor.style.position = "absolute";
|
|
1694
|
+
anchor.style.left = `${left2}pt`;
|
|
1695
|
+
anchor.style.top = `${top2}pt`;
|
|
1696
|
+
anchor.style.margin = "0";
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
anchorNumber(anchor, suffix) {
|
|
1700
|
+
const raw = anchor.getAttribute(`data-docx-anchor-${suffix}`);
|
|
1701
|
+
if (raw === null) return void 0;
|
|
1702
|
+
const value = Number(raw);
|
|
1703
|
+
return Number.isFinite(value) ? value : void 0;
|
|
1704
|
+
}
|
|
1705
|
+
horizontalAnchorReference(relativeFrom, dims, pageNumber, context) {
|
|
1706
|
+
const leftMargin = { start: 0, size: dims.marginLeft };
|
|
1707
|
+
const rightMargin = {
|
|
1708
|
+
start: dims.marginLeft + dims.contentWidth,
|
|
1709
|
+
size: dims.marginRight
|
|
1710
|
+
};
|
|
1711
|
+
switch (relativeFrom) {
|
|
1712
|
+
case "page":
|
|
1713
|
+
return { start: 0, size: dims.pageWidth };
|
|
1714
|
+
case "character":
|
|
1715
|
+
return { start: context.staticLeft, size: 0 };
|
|
1716
|
+
case "leftMargin":
|
|
1717
|
+
return leftMargin;
|
|
1718
|
+
case "rightMargin":
|
|
1719
|
+
return rightMargin;
|
|
1720
|
+
case "insideMargin":
|
|
1721
|
+
return pageNumber % 2 === 1 ? leftMargin : rightMargin;
|
|
1722
|
+
case "outsideMargin":
|
|
1723
|
+
return pageNumber % 2 === 1 ? rightMargin : leftMargin;
|
|
1724
|
+
case "paragraph":
|
|
1725
|
+
return { start: context.paragraphLeft, size: context.paragraphWidth };
|
|
1726
|
+
case "column":
|
|
1727
|
+
case "margin":
|
|
1728
|
+
default:
|
|
1729
|
+
return { start: dims.marginLeft, size: dims.contentWidth };
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
verticalAnchorReference(relativeFrom, dims, pageNumber, context) {
|
|
1733
|
+
const topMargin = { start: 0, size: dims.marginTop };
|
|
1734
|
+
const bottomMargin = {
|
|
1735
|
+
start: dims.marginTop + dims.contentHeight,
|
|
1736
|
+
size: dims.marginBottom
|
|
1737
|
+
};
|
|
1738
|
+
switch (relativeFrom) {
|
|
1739
|
+
case "page":
|
|
1740
|
+
return { start: 0, size: dims.pageHeight };
|
|
1741
|
+
case "paragraph":
|
|
1742
|
+
return { start: context.paragraphTop, size: context.paragraphHeight };
|
|
1743
|
+
case "line":
|
|
1744
|
+
return { start: context.staticTop, size: context.lineHeight };
|
|
1745
|
+
case "topMargin":
|
|
1746
|
+
return topMargin;
|
|
1747
|
+
case "bottomMargin":
|
|
1748
|
+
return bottomMargin;
|
|
1749
|
+
case "insideMargin":
|
|
1750
|
+
return pageNumber % 2 === 1 ? topMargin : bottomMargin;
|
|
1751
|
+
case "outsideMargin":
|
|
1752
|
+
return pageNumber % 2 === 1 ? bottomMargin : topMargin;
|
|
1753
|
+
case "margin":
|
|
1754
|
+
default:
|
|
1755
|
+
return { start: dims.marginTop, size: dims.contentHeight };
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
resolveAnchorAxis(reference, objectSize, alignment, offset, pageNumber) {
|
|
1759
|
+
if (offset !== void 0) return reference.start + offset;
|
|
1760
|
+
let fraction = 0;
|
|
1761
|
+
if (alignment === "center") fraction = 0.5;
|
|
1762
|
+
else if (alignment === "right" || alignment === "bottom") fraction = 1;
|
|
1763
|
+
else if (alignment === "inside") fraction = pageNumber % 2 === 1 ? 0 : 1;
|
|
1764
|
+
else if (alignment === "outside") fraction = pageNumber % 2 === 1 ? 1 : 0;
|
|
1765
|
+
return reference.start + (reference.size - objectSize) * fraction;
|
|
1766
|
+
}
|
|
1443
1767
|
/**
|
|
1444
1768
|
* Creates a page container element.
|
|
1445
1769
|
*/
|
|
@@ -1466,22 +1790,22 @@ var DocxodusEditor = (() => {
|
|
|
1466
1790
|
pageBox.dataset.pageNumber = String(pageNumber);
|
|
1467
1791
|
pageBox.dataset.sectionIndex = String(sectionIndex);
|
|
1468
1792
|
pageBox.dataset.pageInSection = String(pageInSection);
|
|
1469
|
-
const
|
|
1793
|
+
const bands = this.getPageBands(dims, sectionIndex, pageInSection, pageNumber);
|
|
1470
1794
|
const headerSource = this.selectHeader(sectionIndex, pageInSection, pageNumber);
|
|
1471
1795
|
if (headerSource) {
|
|
1472
1796
|
const headerDiv = document.createElement("div");
|
|
1473
1797
|
headerDiv.className = `${this.cssPrefix}header`;
|
|
1474
1798
|
headerDiv.style.position = "absolute";
|
|
1475
|
-
headerDiv.style.top =
|
|
1799
|
+
headerDiv.style.top = `${bands.headerTop}pt`;
|
|
1800
|
+
headerDiv.style.bottom = "auto";
|
|
1476
1801
|
headerDiv.style.left = `${dims.marginLeft}pt`;
|
|
1477
1802
|
headerDiv.style.width = `${dims.contentWidth}pt`;
|
|
1478
|
-
headerDiv.style.height = `${
|
|
1803
|
+
headerDiv.style.height = `${bands.headerHeight}pt`;
|
|
1479
1804
|
headerDiv.style.overflow = "hidden";
|
|
1480
1805
|
headerDiv.style.boxSizing = "border-box";
|
|
1481
1806
|
headerDiv.style.display = "flex";
|
|
1482
1807
|
headerDiv.style.flexDirection = "column";
|
|
1483
|
-
headerDiv.style.justifyContent = "flex-
|
|
1484
|
-
headerDiv.style.paddingBottom = "4pt";
|
|
1808
|
+
headerDiv.style.justifyContent = "flex-start";
|
|
1485
1809
|
for (const child of Array.from(headerSource.childNodes)) {
|
|
1486
1810
|
const clonedheaderDiv = child.cloneNode(true);
|
|
1487
1811
|
if (clonedheaderDiv.nodeType === 1) this.makeClonedStoryInert(clonedheaderDiv);
|
|
@@ -1489,8 +1813,8 @@ var DocxodusEditor = (() => {
|
|
|
1489
1813
|
}
|
|
1490
1814
|
pageBox.appendChild(headerDiv);
|
|
1491
1815
|
}
|
|
1492
|
-
const contentAreaTop =
|
|
1493
|
-
const contentAreaHeight =
|
|
1816
|
+
const contentAreaTop = bands.bodyTop;
|
|
1817
|
+
const contentAreaHeight = bands.bodyHeight;
|
|
1494
1818
|
const contentArea = document.createElement("div");
|
|
1495
1819
|
contentArea.className = `${this.cssPrefix}content`;
|
|
1496
1820
|
contentArea.style.position = "absolute";
|
|
@@ -1505,23 +1829,23 @@ var DocxodusEditor = (() => {
|
|
|
1505
1829
|
pageBox.appendChild(contentArea);
|
|
1506
1830
|
const hasContinuation = continuation && continuation.remainingElements.length > 0;
|
|
1507
1831
|
if (footnoteIds.length > 0 || hasContinuation) {
|
|
1508
|
-
this.addPageFootnotes(pageBox, footnoteIds, dims, footnoteHeight, continuation, partialFootnotes);
|
|
1832
|
+
this.addPageFootnotes(pageBox, footnoteIds, dims, bands, footnoteHeight, continuation, partialFootnotes);
|
|
1509
1833
|
}
|
|
1510
1834
|
const footerSource = this.selectFooter(sectionIndex, pageInSection, pageNumber);
|
|
1511
1835
|
if (footerSource) {
|
|
1512
1836
|
const footerDiv = document.createElement("div");
|
|
1513
1837
|
footerDiv.className = `${this.cssPrefix}footer`;
|
|
1514
1838
|
footerDiv.style.position = "absolute";
|
|
1515
|
-
footerDiv.style.
|
|
1839
|
+
footerDiv.style.top = "auto";
|
|
1840
|
+
footerDiv.style.bottom = `${dims.footerDistance}pt`;
|
|
1516
1841
|
footerDiv.style.left = `${dims.marginLeft}pt`;
|
|
1517
1842
|
footerDiv.style.width = `${dims.contentWidth}pt`;
|
|
1518
|
-
footerDiv.style.height = `${
|
|
1843
|
+
footerDiv.style.height = `${bands.footerHeight}pt`;
|
|
1519
1844
|
footerDiv.style.overflow = "hidden";
|
|
1520
1845
|
footerDiv.style.boxSizing = "border-box";
|
|
1521
1846
|
footerDiv.style.display = "flex";
|
|
1522
1847
|
footerDiv.style.flexDirection = "column";
|
|
1523
|
-
footerDiv.style.justifyContent = "flex-
|
|
1524
|
-
footerDiv.style.paddingTop = "4pt";
|
|
1848
|
+
footerDiv.style.justifyContent = "flex-end";
|
|
1525
1849
|
for (const child of Array.from(footerSource.childNodes)) {
|
|
1526
1850
|
const clonedfooterDiv = child.cloneNode(true);
|
|
1527
1851
|
if (clonedfooterDiv.nodeType === 1) this.makeClonedStoryInert(clonedfooterDiv);
|
|
@@ -1536,6 +1860,7 @@ var DocxodusEditor = (() => {
|
|
|
1536
1860
|
pageBox.appendChild(pageNum);
|
|
1537
1861
|
}
|
|
1538
1862
|
this.containerElement.appendChild(pageBox);
|
|
1863
|
+
this.positionDrawingAnchors(pageBox, contentArea, dims, pageNumber);
|
|
1539
1864
|
const notesEl = pageBox.querySelector(`.${this.cssPrefix}footnotes`);
|
|
1540
1865
|
if (notesEl) {
|
|
1541
1866
|
const notesHeightPt = pxToPt(notesEl.getBoundingClientRect().height);
|
|
@@ -4910,6 +5235,19 @@ var DocxodusEditor = (() => {
|
|
|
4910
5235
|
function isListBlock(block) {
|
|
4911
5236
|
return !!block.querySelector(":scope > [data-list-marker]");
|
|
4912
5237
|
}
|
|
5238
|
+
function ensureListMarkerSeparator(block) {
|
|
5239
|
+
const marker = block.querySelector(":scope > [data-list-marker]");
|
|
5240
|
+
if (!marker) return;
|
|
5241
|
+
const next = marker.nextElementSibling;
|
|
5242
|
+
if (next?.hasAttribute("data-editor-list-separator")) return;
|
|
5243
|
+
const separator = block.ownerDocument.createElement("span");
|
|
5244
|
+
separator.setAttribute("data-editor-list-separator", "");
|
|
5245
|
+
separator.setAttribute("data-list-marker", "true");
|
|
5246
|
+
separator.setAttribute("aria-hidden", "true");
|
|
5247
|
+
separator.style.display = "none";
|
|
5248
|
+
separator.textContent = " ";
|
|
5249
|
+
marker.after(separator);
|
|
5250
|
+
}
|
|
4913
5251
|
var GENERATED_CHROME_SELECTOR = '[data-list-marker], a.footnote-ref, a.endnote-ref, a[class$="-backref"]';
|
|
4914
5252
|
function isGeneratedChrome(node) {
|
|
4915
5253
|
return node?.nodeType === 1 && !!node.matches?.(GENERATED_CHROME_SELECTOR);
|
|
@@ -6200,6 +6538,7 @@ var DocxodusEditor = (() => {
|
|
|
6200
6538
|
const unid = el.getAttribute("data-anchor");
|
|
6201
6539
|
if (!unid || !this.anchorIdOf(el)) return;
|
|
6202
6540
|
el.setAttribute("contenteditable", "true");
|
|
6541
|
+
ensureListMarkerSeparator(el);
|
|
6203
6542
|
el.querySelectorAll(GENERATED_CHROME_SELECTOR).forEach((m) => m.setAttribute("contenteditable", "false"));
|
|
6204
6543
|
el.dataset.committedText = blockContentText(el);
|
|
6205
6544
|
el.addEventListener("focus", () => {
|
|
@@ -7062,8 +7401,8 @@ var DocxodusEditor = (() => {
|
|
|
7062
7401
|
* editable `data-anchor` blocks in the notes section, so editing it afterwards needs no new op.
|
|
7063
7402
|
*
|
|
7064
7403
|
* Body blocks only: Word disallows a note reference inside a header/footer story or inside
|
|
7065
|
-
* another note, and the session rejects those with `AnchorWrongKind`.
|
|
7066
|
-
*
|
|
7404
|
+
* another note, and the session rejects those with `AnchorWrongKind`. Reconciliation creates
|
|
7405
|
+
* the first notes section when needed and renumbers every affected citation in place.
|
|
7067
7406
|
*/
|
|
7068
7407
|
insertFootnote(markdown = "New footnote.") {
|
|
7069
7408
|
this.insertNote("footnote", markdown);
|
|
@@ -7455,7 +7794,7 @@ var DocxodusEditor = (() => {
|
|
|
7455
7794
|
* the current node — never a section div (multi-child) or the edit root. */
|
|
7456
7795
|
unitWrapperOf(el) {
|
|
7457
7796
|
let n = el;
|
|
7458
|
-
while (n.parentElement && n.parentElement !== this.editRoot && n.parentElement.tagName === "DIV" && !n.parentElement.hasAttribute("data-anchor") && n.parentElement.childElementCount === 1) {
|
|
7797
|
+
while (n.parentElement && n.parentElement !== this.editRoot && n.parentElement.tagName === "DIV" && !n.parentElement.hasAttribute("data-section-index") && !n.parentElement.hasAttribute("data-anchor") && n.parentElement.childElementCount === 1) {
|
|
7459
7798
|
n = n.parentElement;
|
|
7460
7799
|
}
|
|
7461
7800
|
return n;
|
|
@@ -7491,6 +7830,7 @@ var DocxodusEditor = (() => {
|
|
|
7491
7830
|
const pureRemoved = diff.removed.filter(
|
|
7492
7831
|
(i) => !diff.substituted.some((s) => s.oldIndex === i) && !movedOld.has(i)
|
|
7493
7832
|
);
|
|
7833
|
+
const appliedFresh = /* @__PURE__ */ new Set();
|
|
7494
7834
|
const nodeAt = (j) => {
|
|
7495
7835
|
const oi = diff.keep.get(j);
|
|
7496
7836
|
if (oi !== void 0) return oldNodes[oi];
|
|
@@ -7498,7 +7838,7 @@ var DocxodusEditor = (() => {
|
|
|
7498
7838
|
if (movedOi !== void 0) return oldNodes[movedOi];
|
|
7499
7839
|
if (subOldByNew.has(j)) return fresh.get(j);
|
|
7500
7840
|
const f = fresh.get(j);
|
|
7501
|
-
return f &&
|
|
7841
|
+
return f && appliedFresh.has(j) ? f : null;
|
|
7502
7842
|
};
|
|
7503
7843
|
for (const j of pureAdded) {
|
|
7504
7844
|
const el = fresh.get(j);
|
|
@@ -7513,6 +7853,7 @@ var DocxodusEditor = (() => {
|
|
|
7513
7853
|
if (prevW) prevW.after(el);
|
|
7514
7854
|
else if (nextW) nextW.before(el);
|
|
7515
7855
|
else return "insert with no anchored neighbor";
|
|
7856
|
+
appliedFresh.add(j);
|
|
7516
7857
|
this.wireUnit(el, units[j]);
|
|
7517
7858
|
}
|
|
7518
7859
|
for (const i of pureRemoved) {
|
|
@@ -7551,13 +7892,13 @@ var DocxodusEditor = (() => {
|
|
|
7551
7892
|
if (anchorEl) this.wireBlock(anchorEl);
|
|
7552
7893
|
root.querySelectorAll("[data-anchor]").forEach((b) => this.wireBlock(b));
|
|
7553
7894
|
}
|
|
7554
|
-
/** Old-sequence diff state for one notes section. `null` requests remount
|
|
7555
|
-
* stampable
|
|
7895
|
+
/** Old-sequence diff state for one notes section. `null` requests remount when an
|
|
7896
|
+
* existing list is not stampable; an absent list is the valid first-note case. */
|
|
7556
7897
|
notesDiff(sectionClass, units) {
|
|
7557
7898
|
const ol = this.editRoot.querySelector(`section.${sectionClass} > ol`);
|
|
7558
7899
|
const lis = ol ? Array.from(ol.children).filter((c) => c.tagName === "LI") : [];
|
|
7559
7900
|
if (units.length === 0 && lis.length === 0) return { lis, diff: diffUnits([], []) };
|
|
7560
|
-
if (!ol) return
|
|
7901
|
+
if (!ol) return { lis, diff: diffUnits([], units) };
|
|
7561
7902
|
const tokens = [];
|
|
7562
7903
|
for (const li of lis) {
|
|
7563
7904
|
const unid = li.getAttribute("data-note-anchor");
|
|
@@ -7574,7 +7915,20 @@ var DocxodusEditor = (() => {
|
|
|
7574
7915
|
this.editRoot.querySelector(`section.${sectionClass}`)?.remove();
|
|
7575
7916
|
return;
|
|
7576
7917
|
}
|
|
7577
|
-
|
|
7918
|
+
let section = this.editRoot.querySelector(`:scope > section.${sectionClass}`);
|
|
7919
|
+
let ol = section?.querySelector(`:scope > ol`) ?? null;
|
|
7920
|
+
if (!ol) {
|
|
7921
|
+
if (!section) {
|
|
7922
|
+
section = document.createElement("section");
|
|
7923
|
+
section.className = sectionClass;
|
|
7924
|
+
const following = sectionClass === "footnotes" ? this.editRoot.querySelector(`:scope > section.endnotes`) : null;
|
|
7925
|
+
if (following) following.before(section);
|
|
7926
|
+
else this.editRoot.append(section);
|
|
7927
|
+
}
|
|
7928
|
+
if (!section.querySelector(":scope > hr")) section.prepend(document.createElement("hr"));
|
|
7929
|
+
ol = document.createElement("ol");
|
|
7930
|
+
section.append(ol);
|
|
7931
|
+
}
|
|
7578
7932
|
const prefix = sectionClass === "footnotes" ? "fn" : "en";
|
|
7579
7933
|
const nodes = [];
|
|
7580
7934
|
for (let j = 0; j < units.length; j++) {
|
|
@@ -7755,6 +8109,7 @@ var DocxodusEditor = (() => {
|
|
|
7755
8109
|
color: var(--dxr-ink);
|
|
7756
8110
|
background: var(--dxr-desk);
|
|
7757
8111
|
-webkit-text-size-adjust: 100%;
|
|
8112
|
+
text-size-adjust: 100%;
|
|
7758
8113
|
}
|
|
7759
8114
|
.dxr *, .dxr *::before, .dxr *::after { box-sizing: border-box; }
|
|
7760
8115
|
|