@windoc/core 0.3.27 → 0.3.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/version.ts
2
- var version = "0.3.27";
2
+ var version = "0.3.29";
3
3
 
4
4
  // src/dataset/enum/Common.ts
5
5
  var MaxHeightRatio = /* @__PURE__ */ ((MaxHeightRatio2) => {
@@ -18995,10 +18995,11 @@ var ListParticle = class {
18995
18995
  const curIndex = isSetCursor ? endIndex : startIndex;
18996
18996
  this.draw.render({ curIndex, isSetCursor });
18997
18997
  }
18998
- getListIndentWidth(element) {
18999
- const { scale } = this.options;
18998
+ // Each level indents by the list's marker column, so a nested marker starts
18999
+ // exactly where its parent's text starts
19000
+ getListIndentWidth(element, markerWidth) {
19000
19001
  const level = element.listLevel ?? 0;
19001
- return level * INDENT_PER_LEVEL * scale;
19002
+ return level * markerWidth;
19002
19003
  }
19003
19004
  _getListMarkerStyleElement(elementList, startIndex = 0) {
19004
19005
  const startElement = elementList[startIndex];
@@ -19128,8 +19129,9 @@ var ListParticle = class {
19128
19129
  leftTop: [startX]
19129
19130
  }
19130
19131
  } = position;
19131
- const indentWidth = this.getListIndentWidth(startElement);
19132
- return startX - offsetX + tabWidth + indentWidth;
19132
+ const level = startElement.listLevel ?? 0;
19133
+ const markerWidth = offsetX / (level + 1);
19134
+ return startX - markerWidth + tabWidth;
19133
19135
  }
19134
19136
  getListMarker(row) {
19135
19137
  const startElement = row.elementList[0];
@@ -22412,8 +22414,9 @@ var Draw = class {
22412
22414
  boundingBoxAscent: 0,
22413
22415
  boundingBoxDescent: 0
22414
22416
  };
22415
- const listIndentWidth = element.listId ? this.listParticle.getListIndentWidth(element) : 0;
22416
- const offsetX = curRow.offsetX || (element.listId && listStyleMap.get(element.listId) || 0) + listIndentWidth || 0;
22417
+ const listMarkerWidth = element.listId && listStyleMap.get(element.listId) || 0;
22418
+ const listIndentWidth = element.listId ? this.listParticle.getListIndentWidth(element, listMarkerWidth) : 0;
22419
+ const offsetX = curRow.offsetX || listMarkerWidth + listIndentWidth || 0;
22417
22420
  const availableWidth = innerWidth - offsetX;
22418
22421
  const isStartElement = curRow.elementList.length === 1;
22419
22422
  x += isStartElement ? offsetX : 0;
@@ -23161,7 +23164,10 @@ var Draw = class {
23161
23164
  if (element.listId) {
23162
23165
  row.isList = true;
23163
23166
  const baseListOffset = listStyleMap.get(element.listId) || 0;
23164
- const rowListIndent = this.listParticle.getListIndentWidth(element);
23167
+ const rowListIndent = this.listParticle.getListIndentWidth(
23168
+ element,
23169
+ baseListOffset
23170
+ );
23165
23171
  row.offsetX = baseListOffset + rowListIndent;
23166
23172
  row.listIndex = listState.listIndex;
23167
23173
  }
@@ -24366,13 +24372,16 @@ var Command = class {
24366
24372
 
24367
24373
  // src/utils/docxLayout.ts
24368
24374
  import {
24375
+ AlignmentType,
24369
24376
  BorderStyle,
24370
24377
  Document as Document2,
24371
24378
  ExternalHyperlink,
24372
24379
  Footer as Footer2,
24373
24380
  Header as Header2,
24374
24381
  HeightRule,
24382
+ HorizontalPositionRelativeFrom,
24375
24383
  ImageRun,
24384
+ LevelFormat,
24376
24385
  LineRuleType,
24377
24386
  Packer,
24378
24387
  PageOrientation,
@@ -24385,7 +24394,9 @@ import {
24385
24394
  TableLayoutType,
24386
24395
  TableRow,
24387
24396
  TextRun,
24397
+ TextWrappingType,
24388
24398
  VerticalAlign as DocxVerticalAlign,
24399
+ VerticalPositionRelativeFrom,
24389
24400
  WidthType,
24390
24401
  XmlAttributeComponent,
24391
24402
  XmlComponent
@@ -24626,7 +24637,8 @@ async function exportDrawToDocx(draw, options = {}) {
24626
24637
  const ctx = {
24627
24638
  draw,
24628
24639
  scale: draw.getOptions().scale,
24629
- measureCtx
24640
+ measureCtx,
24641
+ lists: /* @__PURE__ */ new Map()
24630
24642
  };
24631
24643
  const file = await createDocument(ctx);
24632
24644
  const blob = await Packer.toBlob(file);
@@ -24644,16 +24656,21 @@ async function createDocument(ctx) {
24644
24656
  const headerExtra = header.getExtraHeight() / scale;
24645
24657
  const footerExtra = footer.getExtraHeight() / scale;
24646
24658
  const layoutMargins = draw.getMargins();
24659
+ planLists(ctx);
24647
24660
  const body = await serializeMain(ctx, layoutMargins[3]);
24648
24661
  const headerChildren = opts.header.disabled ? [] : await serializeRows(ctx, header.getRowList(), {
24649
24662
  startX: layoutMargins[3],
24650
24663
  width: draw.getInnerWidth(),
24651
24664
  positionList: header.getPositionList()
24652
24665
  });
24653
- const footerChildren = opts.footer.disabled ? [] : await serializeFooter(ctx, width, height, margins);
24666
+ const footerResult = opts.footer.disabled ? { children: [], distance: footer.getFooterBottom() / scale } : await serializeFooter(ctx, width, height, margins);
24667
+ const footerChildren = footerResult.children;
24654
24668
  const { pageNumber } = draw.getOptions();
24669
+ endWithParagraph(headerChildren);
24670
+ endWithParagraph(footerChildren);
24655
24671
  return new Document2({
24656
24672
  compatabilityModeVersion: 15,
24673
+ numbering: { config: createNumberingConfig(ctx) },
24657
24674
  // LibreOffice formats PAGE/NUMPAGES results from the paragraph style
24658
24675
  styles: {
24659
24676
  paragraphStyles: [
@@ -24680,7 +24697,7 @@ async function createDocument(ctx) {
24680
24697
  bottom: pxTw(margins[2] + footerExtra),
24681
24698
  left: pxTw(margins[3]),
24682
24699
  header: pxTw(headerTop),
24683
- footer: pxTw(footer.getFooterBottom() / scale),
24700
+ footer: pxTw(footerResult.distance),
24684
24701
  gutter: 0
24685
24702
  }
24686
24703
  }
@@ -24712,13 +24729,14 @@ async function serializeMain(ctx, startX) {
24712
24729
  }
24713
24730
  async function serializeRows(ctx, rowList, zone, options = {}) {
24714
24731
  const { pageBreakBefore = false, padTop = 0, padBottom = 0 } = options;
24732
+ const units = groupUnits(ctx, rowList, zone);
24715
24733
  const children = [];
24716
- for (let r = 0; r < rowList.length; r++) {
24717
- const row = rowList[r];
24718
- const isFirst = r === 0;
24719
- const isLast = r === rowList.length - 1;
24720
- const table = row.elementList.find((el) => el.type === "table" /* TABLE */);
24721
- if (table) {
24734
+ for (let u = 0; u < units.length; u++) {
24735
+ const unit = units[u];
24736
+ const isFirst = u === 0;
24737
+ const isLast = u === units.length - 1;
24738
+ if (unit.kind === "table") {
24739
+ const { row, table } = unit;
24722
24740
  if (isFirst && (pageBreakBefore || padTop) || row.offsetY) {
24723
24741
  children.push(
24724
24742
  createSpacerParagraph(
@@ -24735,72 +24753,92 @@ async function serializeRows(ctx, rowList, zone, options = {}) {
24735
24753
  }
24736
24754
  continue;
24737
24755
  }
24756
+ const first = unitFirstRow(unit);
24738
24757
  const rowOptions = {
24739
24758
  pageBreakBefore: isFirst && pageBreakBefore,
24740
24759
  padTop: isFirst ? padTop : 0,
24741
- padBottom: isLast ? padBottom : 0
24760
+ padBottom: isLast ? padBottom : 0,
24761
+ rowY: rowY(zone, first)
24742
24762
  };
24743
- if (!isFirst && isTextRow(rowList[r - 1])) rowOptions.skipLead = true;
24744
- rowOptions.rowY = rowY(zone, row);
24745
- if (!isLast && isTextRow(rowList[r + 1])) {
24746
- rowOptions.extraAfter = rowLead(ctx, rowList[r + 1], {
24747
- rowY: rowY(zone, rowList[r + 1])
24748
- });
24763
+ if (!isFirst && units[u - 1].kind !== "table") rowOptions.skipLead = true;
24764
+ const next = units[u + 1];
24765
+ if (next && next.kind !== "table") {
24766
+ rowOptions.extraAfter = unitLead(ctx, next, zone, {});
24749
24767
  }
24750
- const lead = rowOptions.pageBreakBefore ? rowLead(ctx, row, rowOptions) : 0;
24768
+ const lead = rowOptions.pageBreakBefore ? unitLead(ctx, unit, zone, rowOptions) : 0;
24751
24769
  if (lead) {
24752
24770
  children.push(createSpacerParagraph(true, lead / PX_TO_TWIP));
24753
24771
  rowOptions.pageBreakBefore = false;
24754
24772
  rowOptions.skipLead = true;
24755
24773
  }
24756
- children.push(await serializeRow(ctx, row, zone, rowOptions));
24774
+ children.push(
24775
+ unit.kind === "list" ? await serializeListItem(ctx, unit.rows, zone, rowOptions) : await serializeRow(ctx, unit.row, zone, rowOptions)
24776
+ );
24757
24777
  }
24758
24778
  return children;
24759
24779
  }
24780
+ function unitFirstRow(unit) {
24781
+ return unit.kind === "list" ? unit.rows[0] : unit.row;
24782
+ }
24783
+ function unitLead(ctx, unit, zone, options) {
24784
+ if (unit.kind === "list") return listLead(ctx, unit.rows[0], options);
24785
+ const row = unitFirstRow(unit);
24786
+ return rowLead(ctx, row, { ...options, rowY: rowY(zone, row) });
24787
+ }
24788
+ function groupUnits(ctx, rowList, zone) {
24789
+ const units = [];
24790
+ for (let r = 0; r < rowList.length; r++) {
24791
+ const row = rowList[r];
24792
+ const table = row.elementList.find((el) => el.type === "table" /* TABLE */);
24793
+ if (table) {
24794
+ units.push({ kind: "table", row, table });
24795
+ continue;
24796
+ }
24797
+ if (isNativeListStart(ctx, row, zone)) {
24798
+ const rows = [row];
24799
+ while (r + 1 < rowList.length && isListContinuation(row, rowList[r + 1])) {
24800
+ rows.push(rowList[++r]);
24801
+ }
24802
+ units.push({ kind: "list", rows });
24803
+ continue;
24804
+ }
24805
+ units.push({ kind: "row", row });
24806
+ }
24807
+ return units;
24808
+ }
24760
24809
  function rowY(zone, row) {
24761
24810
  return zone.positionList[row.startIndex]?.coordinate.leftTop[1];
24762
24811
  }
24763
- function isTextRow(row) {
24764
- return !row.elementList.some((el) => el.type === "table" /* TABLE */);
24765
- }
24766
- async function serializeRow(ctx, row, zone, options, suffix) {
24767
- const { pageBreakBefore = false } = options;
24812
+ async function appendRowRuns(ctx, row, zone, line) {
24768
24813
  const { draw, scale } = ctx;
24769
24814
  const positions = zone.positionList;
24770
- const children = [];
24771
- const tabStops = [];
24772
- const firstPosition = positions[row.startIndex];
24773
- let pen = 0;
24774
- let indent = firstPosition ? (firstPosition.coordinate.leftTop[0] - zone.startX) / scale : 0;
24775
- let started = false;
24776
24815
  const anchorTo = (layoutX) => {
24777
24816
  const target = (layoutX - zone.startX) / scale;
24778
- if (!started) {
24779
- indent = target;
24780
- pen = target;
24781
- started = true;
24817
+ if (!line.started) {
24818
+ line.indent = target;
24819
+ line.pen = target;
24820
+ line.started = true;
24782
24821
  return;
24783
24822
  }
24784
- if (target - pen > ANCHOR_EPS) {
24785
- tabStops.push(pxTw(target));
24786
- children.push(new TextRun({ children: [new Tab()] }));
24787
- pen = target;
24823
+ if (target - line.pen > ANCHOR_EPS) {
24824
+ line.tabStops.push(pxTw(target));
24825
+ line.children.push(new TextRun({ children: [new Tab()] }));
24826
+ line.pen = target;
24788
24827
  }
24789
24828
  };
24790
- if (row.isList && firstPosition) {
24791
- const listParticle = draw.getListParticle();
24792
- const markerX = listParticle.getListMarkerX(row, firstPosition);
24793
- const marker = markerX === null ? null : listParticle.getListMarker(row);
24794
- if (markerX !== null && marker) {
24795
- anchorTo(markerX);
24796
- children.push(createTextRun(ctx, marker.text, marker.styleElement));
24797
- pen += measure(ctx, marker.text, marker.styleElement);
24798
- }
24799
- }
24800
24829
  let run = null;
24801
24830
  const flush = () => {
24802
24831
  if (!run) return;
24803
- children.push(createTextRun(ctx, run.text, run.element));
24832
+ line.children.push(
24833
+ createTextRun(
24834
+ ctx,
24835
+ run.text,
24836
+ run.element,
24837
+ void 0,
24838
+ void 0,
24839
+ line.position
24840
+ )
24841
+ );
24804
24842
  run = null;
24805
24843
  };
24806
24844
  for (let j = 0; j < row.elementList.length; j++) {
@@ -24808,19 +24846,7 @@ async function serializeRow(ctx, row, zone, options, suffix) {
24808
24846
  const position = positions[row.startIndex + j];
24809
24847
  if (!position) continue;
24810
24848
  if (element.hide || element.control?.hide || element.area?.hide) continue;
24811
- if (element.type === "separator" /* SEPARATOR */) {
24812
- flush();
24813
- return createSeparatorParagraph(
24814
- ctx,
24815
- row,
24816
- element,
24817
- position.coordinate.leftTop[0],
24818
- position.coordinate.leftTop[1],
24819
- zone,
24820
- options
24821
- );
24822
- }
24823
- if (element.value === ZERO || element.type === "tab" /* TAB */ || element.type === "pageBreak" /* PAGE_BREAK */ || element.type === "columnBreak" /* COLUMN_BREAK */) {
24849
+ if (element.value === ZERO || element.type === "tab" /* TAB */ || element.type === "pageBreak" /* PAGE_BREAK */ || element.type === "columnBreak" /* COLUMN_BREAK */ || element.type === "separator" /* SEPARATOR */) {
24824
24850
  continue;
24825
24851
  }
24826
24852
  const x = position.coordinate.leftTop[0];
@@ -24829,25 +24855,32 @@ async function serializeRow(ctx, row, zone, options, suffix) {
24829
24855
  flush();
24830
24856
  anchorTo(x);
24831
24857
  const image = await createImageRun(element);
24832
- if (image) children.push(image);
24833
- pen += layoutWidth;
24858
+ if (image) line.children.push(image);
24859
+ line.pen += layoutWidth;
24834
24860
  continue;
24835
24861
  }
24836
24862
  if (element.type === "label" /* LABEL */) {
24837
24863
  flush();
24838
24864
  const padding = element.label?.padding || draw.getOptions().label.defaultPadding;
24839
24865
  anchorTo(x + padding[3] * scale);
24840
- children.push(
24841
- createTextRun(ctx, element.value, element, labelShading(ctx, element))
24866
+ line.children.push(
24867
+ createTextRun(
24868
+ ctx,
24869
+ element.value,
24870
+ element,
24871
+ labelShading(ctx, element),
24872
+ void 0,
24873
+ line.position
24874
+ )
24842
24875
  );
24843
- pen += measure(ctx, element.value, element);
24876
+ line.pen += measure(ctx, element.value, element);
24844
24877
  continue;
24845
24878
  }
24846
24879
  const text = getElementText(element);
24847
24880
  if (!text) continue;
24848
24881
  const natural = measure(ctx, text, element);
24849
24882
  const target = (x - zone.startX) / scale;
24850
- const isDrift = !started || target - pen > ANCHOR_EPS;
24883
+ const isDrift = !line.started || target - line.pen > ANCHOR_EPS;
24851
24884
  const extra = layoutWidth - natural;
24852
24885
  const needsSpacing = Math.abs(extra) * PX_TO_TWIP >= 1;
24853
24886
  const canJoin = run && !isDrift && !needsSpacing && isSameRunStyle(run.element, element);
@@ -24857,39 +24890,286 @@ async function serializeRow(ctx, row, zone, options, suffix) {
24857
24890
  flush();
24858
24891
  anchorTo(x);
24859
24892
  if (needsSpacing) {
24860
- children.push(
24861
- createTextRun(ctx, text, element, void 0, pxTw(extra))
24893
+ line.children.push(
24894
+ createTextRun(
24895
+ ctx,
24896
+ text,
24897
+ element,
24898
+ void 0,
24899
+ pxTw(extra),
24900
+ line.position
24901
+ )
24862
24902
  );
24863
24903
  } else {
24864
24904
  run = { text, element };
24865
24905
  }
24866
24906
  }
24867
- pen = target + layoutWidth;
24907
+ line.pen = target + layoutWidth;
24868
24908
  }
24869
24909
  flush();
24870
- if (!children.length && !suffix) {
24910
+ }
24911
+ async function serializeRow(ctx, row, zone, options, suffix, anchor) {
24912
+ const { pageBreakBefore = false } = options;
24913
+ const { draw, scale } = ctx;
24914
+ const firstPosition = zone.positionList[row.startIndex];
24915
+ const separatorIndex = row.elementList.findIndex(
24916
+ (el) => el.type === "separator" /* SEPARATOR */
24917
+ );
24918
+ if (separatorIndex >= 0) {
24919
+ const position = zone.positionList[row.startIndex + separatorIndex];
24920
+ if (position) {
24921
+ return createSeparatorParagraph(
24922
+ ctx,
24923
+ row,
24924
+ row.elementList[separatorIndex],
24925
+ position.coordinate.leftTop[0],
24926
+ position.coordinate.leftTop[1],
24927
+ zone,
24928
+ options
24929
+ );
24930
+ }
24931
+ }
24932
+ const line = {
24933
+ children: [],
24934
+ tabStops: [],
24935
+ pen: 0,
24936
+ started: false,
24937
+ indent: firstPosition ? (firstPosition.coordinate.leftTop[0] - zone.startX) / scale : 0
24938
+ };
24939
+ if (row.isList && firstPosition) {
24940
+ const listParticle = draw.getListParticle();
24941
+ const markerX = listParticle.getListMarkerX(row, firstPosition);
24942
+ const marker = markerX === null ? null : listParticle.getListMarker(row);
24943
+ if (markerX !== null && marker) {
24944
+ line.indent = (markerX - zone.startX) / scale;
24945
+ line.pen = line.indent;
24946
+ line.started = true;
24947
+ line.children.push(createTextRun(ctx, marker.text, marker.styleElement));
24948
+ line.pen += measure(ctx, marker.text, marker.styleElement);
24949
+ }
24950
+ }
24951
+ await appendRowRuns(ctx, row, zone, line);
24952
+ const { children } = line;
24953
+ if (anchor) children.unshift(anchor);
24954
+ if (!line.started && !suffix) {
24871
24955
  const total = (options.skipLead ? 0 : rowLead(ctx, row, options)) + rowLine(ctx, row).lineTw + rowSpacing(ctx, row, options).after;
24872
- return createSpacerParagraph(pageBreakBefore, total / PX_TO_TWIP);
24956
+ return createSpacerParagraph(pageBreakBefore, total / PX_TO_TWIP, anchor);
24873
24957
  }
24874
- const stops = tabStops.map((position) => ({ type: TabStopType.LEFT, position }));
24958
+ const stops = line.tabStops.map((position) => ({ type: TabStopType.LEFT, position }));
24875
24959
  if (suffix) {
24876
24960
  stops.push({ type: suffix.tabType, position: suffix.tabStop });
24877
24961
  children.push(new TextRun({ children: [new Tab()] }), ...suffix.children);
24878
24962
  }
24879
24963
  return new Paragraph({
24880
24964
  children,
24965
+ run: hasInlineImage(row) ? { size: IMAGE_MARK_HALF_POINTS } : void 0,
24881
24966
  style: suffix?.style,
24882
24967
  pageBreakBefore,
24883
24968
  widowControl: false,
24884
24969
  tabStops: stops,
24885
- indent: { left: pxTw(indent), right: RIGHT_SLACK_TWIP },
24970
+ indent: { left: pxTw(line.indent), right: RIGHT_SLACK_TWIP },
24886
24971
  spacing: rowSpacing(ctx, row, options)
24887
24972
  });
24888
24973
  }
24974
+ var olStyleFormat = {
24975
+ decimal: { format: LevelFormat.DECIMAL, suffix: "." },
24976
+ decimalParen: { format: LevelFormat.DECIMAL, suffix: ")" },
24977
+ decimalZero: { format: LevelFormat.DECIMAL_ZERO, suffix: "." },
24978
+ lowerAlpha: { format: LevelFormat.LOWER_LETTER, suffix: "." },
24979
+ lowerAlphaParen: { format: LevelFormat.LOWER_LETTER, suffix: ")" },
24980
+ upperAlpha: { format: LevelFormat.UPPER_LETTER, suffix: "." },
24981
+ lowerRoman: { format: LevelFormat.LOWER_ROMAN, suffix: "." },
24982
+ lowerRomanParen: { format: LevelFormat.LOWER_ROMAN, suffix: ")" },
24983
+ upperRoman: { format: LevelFormat.UPPER_ROMAN, suffix: "." },
24984
+ outline: { format: LevelFormat.DECIMAL, suffix: "." }
24985
+ };
24986
+ function isListItemStart(row) {
24987
+ const first = row.elementList[0];
24988
+ return !!row.isList && first?.value === ZERO && !first.listWrap;
24989
+ }
24990
+ function rowListId(row) {
24991
+ return row.elementList.find((el) => el.listId)?.listId;
24992
+ }
24993
+ function levelSpec(element, markerText) {
24994
+ const level = Math.min(element.listLevel ?? 0, 8);
24995
+ if (element.listType === "ul" /* UL */) {
24996
+ return { level, format: LevelFormat.BULLET, text: markerText };
24997
+ }
24998
+ const cycle = element.listPreset ? olPresetCycles[element.listPreset] : void 0;
24999
+ const style = cycle ? cycle[level % cycle.length] : "decimal";
25000
+ const spec = olStyleFormat[style] || olStyleFormat.decimal;
25001
+ return { level, format: spec.format, text: `%${level + 1}${spec.suffix}` };
25002
+ }
25003
+ function planLists(ctx) {
25004
+ const listParticle = ctx.draw.getListParticle();
25005
+ const counters = /* @__PURE__ */ new Map();
25006
+ const visit = (rowList) => {
25007
+ for (const row of rowList) {
25008
+ const table = row.elementList.find((el) => el.type === "table" /* TABLE */);
25009
+ if (table) {
25010
+ for (const tr of table.trList || []) {
25011
+ for (const td of tr.tdList) visit(td.rowList || []);
25012
+ }
25013
+ continue;
25014
+ }
25015
+ if (!isListItemStart(row)) continue;
25016
+ const listId = rowListId(row);
25017
+ if (!listId) continue;
25018
+ let plan2 = ctx.lists.get(listId);
25019
+ if (!plan2) {
25020
+ plan2 = {
25021
+ reference: `windoc-list-${ctx.lists.size + 1}`,
25022
+ native: true,
25023
+ levels: /* @__PURE__ */ new Map()
25024
+ };
25025
+ ctx.lists.set(listId, plan2);
25026
+ }
25027
+ const marker = listParticle.getListMarker(row);
25028
+ if (!marker || hasInlineImage(row)) {
25029
+ plan2.native = false;
25030
+ continue;
25031
+ }
25032
+ const spec = levelSpec(row.elementList[0], marker.text);
25033
+ const known = plan2.levels.get(spec.level);
25034
+ if (known && (known.format !== spec.format || known.text !== spec.text)) {
25035
+ plan2.native = false;
25036
+ }
25037
+ plan2.levels.set(spec.level, spec);
25038
+ const count = counters.get(listId) || [];
25039
+ counters.set(listId, count);
25040
+ count[spec.level] = (count[spec.level] || 0) + 1;
25041
+ count.length = spec.level + 1;
25042
+ if (spec.format !== LevelFormat.BULLET && count[spec.level] !== (row.listIndex ?? 0) + 1) {
25043
+ plan2.native = false;
25044
+ }
25045
+ }
25046
+ };
25047
+ ctx.draw.getPageRowList().forEach(visit);
25048
+ }
25049
+ function createNumberingConfig(ctx) {
25050
+ return [...ctx.lists.values()].filter((plan2) => plan2.native).map((plan2) => ({
25051
+ reference: plan2.reference,
25052
+ levels: Array.from({ length: 9 }, (_, level) => {
25053
+ const spec = plan2.levels.get(level) || {
25054
+ format: LevelFormat.DECIMAL,
25055
+ text: `%${level + 1}.`
25056
+ };
25057
+ return {
25058
+ level,
25059
+ format: spec.format,
25060
+ text: spec.text,
25061
+ alignment: AlignmentType.LEFT
25062
+ };
25063
+ })
25064
+ }));
25065
+ }
25066
+ function listMarkerGeometry(ctx, row, zone) {
25067
+ const listParticle = ctx.draw.getListParticle();
25068
+ const firstPosition = zone.positionList[row.startIndex];
25069
+ if (!firstPosition) return null;
25070
+ const markerX = listParticle.getListMarkerX(row, firstPosition);
25071
+ const marker = listParticle.getListMarker(row);
25072
+ if (markerX === null || !marker) return null;
25073
+ const textIndex = row.elementList.findIndex(
25074
+ (el) => el.value !== ZERO && el.type !== "tab" /* TAB */
25075
+ );
25076
+ const textPosition = textIndex >= 0 ? zone.positionList[row.startIndex + textIndex] : firstPosition;
25077
+ const textX = textPosition.coordinate.leftTop[0];
25078
+ if (textX - markerX <= 0) return null;
25079
+ return {
25080
+ marker,
25081
+ indent: (textX - zone.startX) / ctx.scale,
25082
+ hanging: (textX - markerX) / ctx.scale
25083
+ };
25084
+ }
25085
+ function isNativeListStart(ctx, row, zone) {
25086
+ if (!isListItemStart(row) || hasInlineImage(row)) return false;
25087
+ const listId = rowListId(row);
25088
+ if (!listId || !ctx.lists.get(listId)?.native) return false;
25089
+ if (row.elementList.some((el) => el.type === "separator" /* SEPARATOR */)) {
25090
+ return false;
25091
+ }
25092
+ return !!listMarkerGeometry(ctx, row, zone);
25093
+ }
25094
+ function isListContinuation(start, row) {
25095
+ return !!row.isList && !isListItemStart(row) && rowListId(row) === rowListId(start) && row.height === start.height && row.ascent === start.ascent && !row.offsetY && !row.spaceAbove && !hasInlineImage(row) && !row.elementList.some(
25096
+ (el) => el.type === "table" /* TABLE */ || el.type === "separator" /* SEPARATOR */
25097
+ );
25098
+ }
25099
+ function listLead(ctx, row, options) {
25100
+ return tw(ctx, (row.offsetY || 0) + (row.spaceAbove || 0)) + pxTw(options.padTop || 0);
25101
+ }
25102
+ async function serializeListItem(ctx, rows, zone, options) {
25103
+ const { scale } = ctx;
25104
+ const first = rows[0];
25105
+ const last = rows[rows.length - 1];
25106
+ const geometry = listMarkerGeometry(ctx, first, zone);
25107
+ const plan2 = ctx.lists.get(rowListId(first));
25108
+ const height = first.height / scale;
25109
+ const raise = Math.round(
25110
+ (EXACT_BASELINE_RATIO * height - first.ascent / scale) * 1.5
25111
+ );
25112
+ const position = raise ? `${raise}` : void 0;
25113
+ const line = {
25114
+ children: [],
25115
+ tabStops: [],
25116
+ pen: geometry.indent,
25117
+ started: true,
25118
+ indent: geometry.indent,
25119
+ position
25120
+ };
25121
+ for (let i = 0; i < rows.length; i++) {
25122
+ if (i > 0) {
25123
+ line.children.push(new TextRun({ break: 1 }));
25124
+ line.pen = geometry.indent;
25125
+ }
25126
+ await appendRowRuns(ctx, rows[i], zone, line);
25127
+ }
25128
+ return new Paragraph({
25129
+ children: line.children,
25130
+ numbering: {
25131
+ reference: plan2.reference,
25132
+ level: Math.min(first.elementList[0].listLevel ?? 0, 8)
25133
+ },
25134
+ // the number is drawn with the paragraph mark's formatting
25135
+ run: { ...runStyle(ctx, geometry.marker.styleElement), position },
25136
+ pageBreakBefore: options.pageBreakBefore,
25137
+ widowControl: false,
25138
+ tabStops: [...new Set(line.tabStops)].map((tabPosition) => ({
25139
+ type: TabStopType.LEFT,
25140
+ position: tabPosition
25141
+ })),
25142
+ indent: {
25143
+ left: pxTw(geometry.indent),
25144
+ hanging: pxTw(geometry.hanging),
25145
+ right: RIGHT_SLACK_TWIP
25146
+ },
25147
+ spacing: {
25148
+ before: options.skipLead ? 0 : listLead(ctx, first, options),
25149
+ after: tw(ctx, last.spaceBelow || 0) + pxTw(options.padBottom || 0) + (options.extraAfter || 0),
25150
+ line: pxTw(height),
25151
+ lineRule: LineRuleType.EXACT
25152
+ }
25153
+ });
25154
+ }
24889
25155
  var EXACT_BASELINE_RATIO = 0.8;
25156
+ var IMAGE_MARK_HALF_POINTS = 2;
25157
+ var IMAGE_MARK_DESCENT_PX = 0.3;
25158
+ function hasInlineImage(row) {
25159
+ return row.elementList.some((el) => el.type === "image" /* IMAGE */);
25160
+ }
24890
25161
  function rowLine(ctx, row, baseline = row.ascent / ctx.scale) {
24891
25162
  const { scale } = ctx;
24892
25163
  const height = row.height / scale;
25164
+ if (hasInlineImage(row)) {
25165
+ const lineTw2 = pxTw(Math.min(baseline, height) + IMAGE_MARK_DESCENT_PX);
25166
+ return {
25167
+ lineTw: lineTw2,
25168
+ beforeTw: 0,
25169
+ afterTw: Math.max(0, pxTw(height) - lineTw2),
25170
+ atLeast: true
25171
+ };
25172
+ }
24893
25173
  const ascent = Math.min(baseline, height);
24894
25174
  const line = Math.min(
24895
25175
  ascent / EXACT_BASELINE_RATIO,
@@ -24901,7 +25181,7 @@ function rowLine(ctx, row, baseline = row.ascent / ctx.scale) {
24901
25181
  pxTw(ascent) - Math.round(lineTw * EXACT_BASELINE_RATIO)
24902
25182
  );
24903
25183
  const afterTw = Math.max(0, pxTw(height) - lineTw - beforeTw);
24904
- return { lineTw, beforeTw, afterTw };
25184
+ return { lineTw, beforeTw, afterTw, atLeast: false };
24905
25185
  }
24906
25186
  function rowLead(ctx, row, options) {
24907
25187
  const separator = row.elementList.find(
@@ -24916,12 +25196,12 @@ function rowLead(ctx, row, options) {
24916
25196
  return rowLine(ctx, row, baseline).beforeTw + tw(ctx, (row.offsetY || 0) + (row.spaceAbove || 0)) + pxTw(options.padTop || 0);
24917
25197
  }
24918
25198
  function rowSpacing(ctx, row, options) {
24919
- const { lineTw, afterTw } = rowLine(ctx, row);
25199
+ const { lineTw, afterTw, atLeast } = rowLine(ctx, row);
24920
25200
  return {
24921
25201
  before: options.skipLead ? 0 : rowLead(ctx, row, options),
24922
25202
  after: afterTw + tw(ctx, row.spaceBelow || 0) + pxTw(options.padBottom || 0) + (options.extraAfter || 0),
24923
25203
  line: lineTw,
24924
- lineRule: LineRuleType.EXACT
25204
+ lineRule: atLeast ? LineRuleType.AT_LEAST : LineRuleType.EXACT
24925
25205
  };
24926
25206
  }
24927
25207
  function createSeparatorParagraph(ctx, row, element, x, rowY2, zone, options) {
@@ -24968,16 +25248,20 @@ function createLineImage(width, height, color, dashArray) {
24968
25248
  ctx.moveTo(0, canvas.height / 2);
24969
25249
  ctx.lineTo(canvas.width, canvas.height / 2);
24970
25250
  ctx.stroke();
24971
- const binary = atob(canvas.toDataURL("image/png").split(",")[1]);
24972
- const bytes = new Uint8Array(binary.length);
24973
- for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
24974
- return bytes;
25251
+ return canvasToPng(canvas);
24975
25252
  }
24976
- function createSpacerParagraph(pageBreakBefore, height = 0) {
25253
+ function endWithParagraph(children) {
25254
+ if (children[children.length - 1] instanceof Table) {
25255
+ children.push(createSpacerParagraph(false));
25256
+ }
25257
+ return children;
25258
+ }
25259
+ function createSpacerParagraph(pageBreakBefore, height = 0, anchor) {
24977
25260
  return new Paragraph({
24978
25261
  pageBreakBefore,
24979
25262
  widowControl: false,
24980
- children: [new TextRun({ text: "", size: 2 })],
25263
+ run: { size: 2 },
25264
+ children: anchor ? [anchor, new TextRun({ text: "", size: 2 })] : [new TextRun({ text: "", size: 2 })],
24981
25265
  spacing: {
24982
25266
  before: 0,
24983
25267
  after: 0,
@@ -25015,7 +25299,7 @@ async function serializeTable(ctx, element, position, zone) {
25015
25299
  );
25016
25300
  cells.push(
25017
25301
  new TableCell({
25018
- children: cellChildren.length ? cellChildren : [createSpacerParagraph(false)],
25302
+ children: cellChildren.length ? endWithParagraph(cellChildren) : [createSpacerParagraph(false)],
25019
25303
  columnSpan: td.colspan > 1 ? td.colspan : void 0,
25020
25304
  rowSpan: td.rowspan > 1 ? td.rowspan : void 0,
25021
25305
  width: { size: pxTw(td.width), type: WidthType.DXA },
@@ -25136,6 +25420,7 @@ async function serializeFooter(ctx, pageWidth, pageHeight, margins) {
25136
25420
  const footer = draw.getFooter();
25137
25421
  const opts = draw.getOptions();
25138
25422
  const barHeight = footer.getHeight() / scale;
25423
+ const footerBottom = footer.getFooterBottom() / scale;
25139
25424
  const contentHeight = footer.getRowHeight() / scale;
25140
25425
  const paddingTop = Math.floor(Math.max(0, (barHeight - contentHeight) / 2) * scale) / scale;
25141
25426
  const zone = {
@@ -25157,56 +25442,73 @@ async function serializeFooter(ctx, pageWidth, pageHeight, margins) {
25157
25442
  }
25158
25443
  }
25159
25444
  }
25445
+ const band = opts.footer.backgroundColor ? createBandImage(
25446
+ pageWidth,
25447
+ pageHeight - footerBottom - barHeight,
25448
+ barHeight,
25449
+ opts.footer.backgroundColor
25450
+ ) : void 0;
25160
25451
  const children = [];
25161
25452
  for (let r = 0; r < rowList.length; r++) {
25162
25453
  const row = rowList[r];
25163
- const rowOptions = { padTop: r === 0 ? paddingTop : 0 };
25164
- if (r !== pageNumberRow) {
25165
- children.push(...await serializeRows(ctx, [row], zone, rowOptions));
25166
- continue;
25167
- }
25168
- const top = zone.positionList[row.startIndex].coordinate.leftTop[1] / scale;
25169
- const shift = pageNumber.baseline - (top + row.ascent / scale);
25170
- children.push(
25171
- await serializeRow(ctx, row, zone, rowOptions, {
25454
+ let suffix;
25455
+ if (r === pageNumberRow) {
25456
+ const top = zone.positionList[row.startIndex].coordinate.leftTop[1] / scale;
25457
+ suffix = {
25172
25458
  tabStop: pageNumber.tabStop,
25173
25459
  tabType: pageNumber.tabType,
25174
- children: pageNumber.runs(shift),
25460
+ children: pageNumber.runs(
25461
+ pageNumber.baseline - (top + row.ascent / scale)
25462
+ ),
25175
25463
  style: PAGE_NUMBER_STYLE
25176
- })
25464
+ };
25465
+ }
25466
+ children.push(
25467
+ await serializeRow(ctx, row, zone, {}, suffix, r === 0 ? band : void 0)
25177
25468
  );
25178
25469
  }
25179
- const fill = opts.footer.backgroundColor ? normalizeHexColor(opts.footer.backgroundColor, "FFFFFF") : void 0;
25180
- const none = { style: BorderStyle.NONE, size: 0, color: "auto" };
25181
- const band = new Table({
25182
- layout: TableLayoutType.FIXED,
25183
- width: { size: pxTw(pageWidth), type: WidthType.DXA },
25184
- columnWidths: [pxTw(pageWidth)],
25185
- indent: { size: -pxTw(margins[3]), type: WidthType.DXA },
25186
- borders: noTableBorders(),
25187
- rows: [
25188
- new TableRow({
25189
- cantSplit: true,
25190
- height: { value: pxTw(barHeight), rule: HeightRule.EXACT },
25191
- children: [
25192
- new TableCell({
25193
- width: { size: pxTw(pageWidth), type: WidthType.DXA },
25194
- margins: {
25195
- top: 0,
25196
- bottom: 0,
25197
- left: pxTw(margins[3]),
25198
- right: pxTw(margins[1]),
25199
- marginUnitType: WidthType.DXA
25200
- },
25201
- shading: fill ? { fill, type: ShadingType.CLEAR, color: "auto" } : void 0,
25202
- borders: { top: none, right: none, bottom: none, left: none },
25203
- children: children.length ? children : [createSpacerParagraph(false)]
25204
- })
25205
- ]
25206
- })
25207
- ]
25470
+ if (!children.length && band) {
25471
+ children.push(createSpacerParagraph(false, 0, band));
25472
+ }
25473
+ const contentTop = pageHeight - footerBottom - barHeight + paddingTop;
25474
+ const distance = Math.max(0, pageHeight - contentTop - contentHeight);
25475
+ return { children, distance };
25476
+ }
25477
+ var EMU_PER_PX = 9525;
25478
+ function createBandImage(width, top, height, color) {
25479
+ return new ImageRun({
25480
+ type: "png",
25481
+ data: createFillImage(color),
25482
+ transformation: { width, height },
25483
+ floating: {
25484
+ horizontalPosition: {
25485
+ relative: HorizontalPositionRelativeFrom.PAGE,
25486
+ offset: 0
25487
+ },
25488
+ verticalPosition: {
25489
+ relative: VerticalPositionRelativeFrom.PAGE,
25490
+ offset: Math.round(top * EMU_PER_PX)
25491
+ },
25492
+ behindDocument: true,
25493
+ allowOverlap: true,
25494
+ wrap: { type: TextWrappingType.NONE }
25495
+ }
25208
25496
  });
25209
- return [band];
25497
+ }
25498
+ function createFillImage(color) {
25499
+ const canvas = document.createElement("canvas");
25500
+ canvas.width = 4;
25501
+ canvas.height = 4;
25502
+ const ctx = canvas.getContext("2d");
25503
+ ctx.fillStyle = color;
25504
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
25505
+ return canvasToPng(canvas);
25506
+ }
25507
+ function canvasToPng(canvas) {
25508
+ const binary = atob(canvas.toDataURL("image/png").split(",")[1]);
25509
+ const bytes = new Uint8Array(binary.length);
25510
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
25511
+ return bytes;
25210
25512
  }
25211
25513
  var PAGE_NUMBER_STYLE = "WindocPageNumber";
25212
25514
  var FieldCharAttributes = class extends XmlAttributeComponent {
@@ -25280,7 +25582,17 @@ function createPageNumberRuns(ctx, pageHeight, textWidth) {
25280
25582
  }).flat()
25281
25583
  };
25282
25584
  }
25283
- function createTextRun(ctx, text, element, shading, characterSpacing) {
25585
+ function runStyle(ctx, element) {
25586
+ const opts = ctx.draw.getOptions();
25587
+ return {
25588
+ font: mapFont(element.font || opts.defaultFont),
25589
+ size: Math.round(getSize(ctx, element) * 2),
25590
+ bold: !!element.bold,
25591
+ italics: !!element.italic,
25592
+ color: normalizeHexColor(element.color || opts.defaultColor, "000000")
25593
+ };
25594
+ }
25595
+ function createTextRun(ctx, text, element, shading, characterSpacing, position) {
25284
25596
  const opts = ctx.draw.getOptions();
25285
25597
  const isLink = element.type === "hyperlink" /* HYPERLINK */;
25286
25598
  const color = normalizeHexColor(
@@ -25300,7 +25612,8 @@ function createTextRun(ctx, text, element, shading, characterSpacing) {
25300
25612
  underline: element.underline || isLink ? { type: mapUnderlineType(element.textDecoration?.style) } : void 0,
25301
25613
  color,
25302
25614
  shading: shading || (highlight ? { fill: highlight, type: ShadingType.CLEAR, color: "auto" } : void 0),
25303
- characterSpacing
25615
+ characterSpacing,
25616
+ position
25304
25617
  });
25305
25618
  if (isLink && element.url) {
25306
25619
  return new ExternalHyperlink({ link: element.url, children: [textRun] });