@windoc/core 0.3.26 → 0.3.28

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.26";
2
+ var version = "0.3.28";
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];
@@ -19111,10 +19112,11 @@ var ListParticle = class {
19111
19112
  if (!cycle) return "\u25CF";
19112
19113
  return cycle[level % cycle.length];
19113
19114
  }
19114
- drawListStyle(ctx, row, position) {
19115
- const { elementList, offsetX, listIndex, ascent } = row;
19115
+ // Marker x for a list row's first line (shared by canvas drawing and export)
19116
+ getListMarkerX(row, position) {
19117
+ const { elementList, offsetX } = row;
19116
19118
  const startElement = elementList[0];
19117
- if (startElement.value !== ZERO || startElement.listWrap) return;
19119
+ if (startElement.value !== ZERO || startElement.listWrap) return null;
19118
19120
  let tabWidth = 0;
19119
19121
  const { defaultTabWidth, scale } = this.options;
19120
19122
  for (let i = 1; i < elementList.length; i++) {
@@ -19124,12 +19126,30 @@ var ListParticle = class {
19124
19126
  }
19125
19127
  const {
19126
19128
  coordinate: {
19127
- leftTop: [startX, startY]
19129
+ leftTop: [startX]
19128
19130
  }
19129
19131
  } = position;
19130
- const indentWidth = this.getListIndentWidth(startElement);
19131
- const x = startX - offsetX + tabWidth + indentWidth;
19132
- const y = startY + ascent;
19132
+ const level = startElement.listLevel ?? 0;
19133
+ const markerWidth = offsetX / (level + 1);
19134
+ return startX - markerWidth + tabWidth;
19135
+ }
19136
+ getListMarker(row) {
19137
+ const startElement = row.elementList[0];
19138
+ if (startElement.listStyle === "checkbox" /* CHECKBOX */) return null;
19139
+ const text = this._getListMarkerText(startElement, row.listIndex);
19140
+ if (!text) return null;
19141
+ return {
19142
+ text,
19143
+ styleElement: this._getListMarkerStyleElement(row.elementList)
19144
+ };
19145
+ }
19146
+ drawListStyle(ctx, row, position) {
19147
+ const { elementList, ascent } = row;
19148
+ const startElement = elementList[0];
19149
+ const x = this.getListMarkerX(row, position);
19150
+ if (x === null) return;
19151
+ const { scale } = this.options;
19152
+ const y = position.coordinate.leftTop[1] + ascent;
19133
19153
  if (startElement.listStyle === "checkbox" /* CHECKBOX */) {
19134
19154
  const { width, height, gap } = this.options.checkbox;
19135
19155
  const checkboxRowElement = {
@@ -19154,12 +19174,11 @@ var ListParticle = class {
19154
19174
  }
19155
19175
  });
19156
19176
  } else {
19157
- const text = this._getListMarkerText(startElement, listIndex);
19158
- if (!text) return;
19159
- const markerStyleElement = this._getListMarkerStyleElement(elementList);
19177
+ const marker = this.getListMarker(row);
19178
+ if (!marker) return;
19160
19179
  ctx.save();
19161
- ctx.font = this.draw.getElementFont(markerStyleElement, scale);
19162
- ctx.fillText(text, x, y);
19180
+ ctx.font = this.draw.getElementFont(marker.styleElement, scale);
19181
+ ctx.fillText(marker.text, x, y);
19163
19182
  ctx.restore();
19164
19183
  }
19165
19184
  }
@@ -22395,8 +22414,9 @@ var Draw = class {
22395
22414
  boundingBoxAscent: 0,
22396
22415
  boundingBoxDescent: 0
22397
22416
  };
22398
- const listIndentWidth = element.listId ? this.listParticle.getListIndentWidth(element) : 0;
22399
- 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;
22400
22420
  const availableWidth = innerWidth - offsetX;
22401
22421
  const isStartElement = curRow.elementList.length === 1;
22402
22422
  x += isStartElement ? offsetX : 0;
@@ -23144,7 +23164,10 @@ var Draw = class {
23144
23164
  if (element.listId) {
23145
23165
  row.isList = true;
23146
23166
  const baseListOffset = listStyleMap.get(element.listId) || 0;
23147
- const rowListIndent = this.listParticle.getListIndentWidth(element);
23167
+ const rowListIndent = this.listParticle.getListIndentWidth(
23168
+ element,
23169
+ baseListOffset
23170
+ );
23148
23171
  row.offsetX = baseListOffset + rowListIndent;
23149
23172
  row.listIndex = listState.listIndex;
23150
23173
  }
@@ -24347,1273 +24370,1039 @@ var Command = class {
24347
24370
  }
24348
24371
  };
24349
24372
 
24350
- // src/utils/docx.ts
24373
+ // src/utils/docxLayout.ts
24351
24374
  import {
24352
- AlignmentType,
24353
24375
  BorderStyle,
24354
- ColumnBreak,
24355
24376
  Document as Document2,
24356
24377
  ExternalHyperlink,
24357
24378
  Footer as Footer2,
24358
24379
  Header as Header2,
24359
- HeadingLevel,
24380
+ HeightRule,
24360
24381
  ImageRun,
24361
- LevelFormat,
24362
24382
  LineRuleType,
24363
24383
  Packer,
24364
- PageBreak,
24365
- PageNumber as PageNumber2,
24366
24384
  PageOrientation,
24367
24385
  Paragraph,
24368
24386
  ShadingType,
24387
+ Tab,
24388
+ TabStopType,
24369
24389
  Table,
24370
24390
  TableCell,
24371
24391
  TableLayoutType,
24372
24392
  TableRow,
24373
24393
  TextRun,
24374
- UnderlineType,
24375
24394
  VerticalAlign as DocxVerticalAlign,
24376
- WidthType
24395
+ WidthType,
24396
+ XmlAttributeComponent,
24397
+ XmlComponent
24377
24398
  } from "docx";
24399
+
24400
+ // src/utils/docx.ts
24401
+ import { UnderlineType } from "docx";
24378
24402
  var DEFAULT_DOCX_FILE_NAME = "document.docx";
24379
24403
  var DEFAULT_IMAGE_WIDTH = 320;
24380
24404
  var DEFAULT_IMAGE_HEIGHT = 180;
24381
- var DEFAULT_LINE_HEIGHT = 1.15;
24382
- var DOCX_FOOTER_TEXT_COLOR = "#000000";
24383
- var docxBulletCharMap = {
24384
- ["disc" /* DISC */]: "\u2022",
24385
- ["circle" /* CIRCLE */]: "\u25E6",
24386
- ["square" /* SQUARE */]: "\u25A0",
24387
- ["checkbox" /* CHECKBOX */]: "\u2611"
24388
- };
24389
- var titleHeadingMap = {
24390
- ["first" /* FIRST */]: HeadingLevel.HEADING_1,
24391
- ["second" /* SECOND */]: HeadingLevel.HEADING_2,
24392
- ["third" /* THIRD */]: HeadingLevel.HEADING_3,
24393
- ["fourth" /* FOURTH */]: HeadingLevel.HEADING_4,
24394
- ["fifth" /* FIFTH */]: HeadingLevel.HEADING_5,
24395
- ["sixth" /* SIXTH */]: HeadingLevel.HEADING_6
24396
- };
24397
- var orderedStyleFormatMap = {
24398
- ["decimal" /* DECIMAL */]: LevelFormat.DECIMAL,
24399
- ["lowerAlpha" /* LOWER_ALPHA */]: LevelFormat.LOWER_LETTER,
24400
- ["upperAlpha" /* UPPER_ALPHA */]: LevelFormat.UPPER_LETTER,
24401
- ["lowerRoman" /* LOWER_ROMAN */]: LevelFormat.LOWER_ROMAN,
24402
- ["upperRoman" /* UPPER_ROMAN */]: LevelFormat.UPPER_ROMAN,
24403
- ["decimalParen" /* DECIMAL_PAREN */]: LevelFormat.DECIMAL,
24404
- decimalZero: LevelFormat.DECIMAL_ZERO,
24405
- lowerAlphaParen: LevelFormat.LOWER_LETTER,
24406
- lowerRomanParen: LevelFormat.LOWER_ROMAN,
24407
- outline: LevelFormat.DECIMAL
24408
- };
24409
- async function exportEditorDataToDocx(payload, options = {}) {
24410
- const fileName = normalizeDocxFileName(options.fileName);
24411
- const file = await createDocxDocument(payload);
24412
- const blob = await Packer.toBlob(file);
24405
+ async function loadImagePayload(src) {
24406
+ const source = src.trim();
24407
+ if (source.startsWith("data:")) {
24408
+ return getImagePayloadFromDataUrl(source);
24409
+ }
24410
+ const response = await fetch(source);
24411
+ if (!response.ok) {
24412
+ throw new Error(`Image request failed: ${response.status}`);
24413
+ }
24414
+ const contentType = response.headers.get("content-type") || "";
24415
+ const data2 = await response.arrayBuffer();
24416
+ return normalizeImagePayload(data2, contentType);
24417
+ }
24418
+ async function getImagePayloadFromDataUrl(src) {
24419
+ const [, meta = "", encoded = ""] = src.match(/^data:([^,]*),(.*)$/) || [];
24420
+ const mime = meta.split(";")[0] || "";
24421
+ const isBase64 = meta.includes(";base64");
24422
+ const text = isBase64 ? atob(encoded) : decodeURIComponent(encoded);
24423
+ const bytes = new Uint8Array(text.length);
24424
+ for (let i = 0; i < text.length; i++) {
24425
+ bytes[i] = text.charCodeAt(i);
24426
+ }
24427
+ return normalizeImagePayload(bytes.buffer, mime);
24428
+ }
24429
+ async function normalizeImagePayload(data2, mime) {
24430
+ const type = getRegularImageType(mime);
24431
+ if (type) {
24432
+ return {
24433
+ type,
24434
+ data: data2
24435
+ };
24436
+ }
24437
+ if (mime.includes("svg")) {
24438
+ return {
24439
+ type: "svg",
24440
+ data: data2,
24441
+ fallbackPng: await rasterizeSvgToPng(data2)
24442
+ };
24443
+ }
24444
+ const fallbackPng = await rasterizeImageToPng(data2, mime);
24413
24445
  return {
24414
- blob,
24415
- fileName
24446
+ type: "png",
24447
+ data: fallbackPng
24416
24448
  };
24417
24449
  }
24418
- async function createDocxDocument(payload) {
24419
- const { data: data2, options } = payload;
24420
- const editorOptions = options;
24421
- const context = createSerializeContext();
24422
- const headerChildren = editorOptions.header.disabled ? [] : await serializeElementList(data2.header || [], editorOptions, context);
24423
- const docxFooterElementList = forceDocxFooterTextColor(data2.footer || []);
24424
- const footerChildren = editorOptions.footer.disabled ? [] : await serializeElementList(
24425
- docxFooterElementList,
24426
- editorOptions,
24427
- context
24428
- );
24429
- const pageNumberParagraph = createPageNumberParagraph(
24430
- {
24431
- ...editorOptions.pageNumber,
24432
- color: DOCX_FOOTER_TEXT_COLOR
24450
+ async function rasterizeSvgToPng(data2) {
24451
+ return rasterizeImageToPng(data2, "image/svg+xml");
24452
+ }
24453
+ async function rasterizeImageToPng(data2, mime) {
24454
+ const blob = new Blob([data2], {
24455
+ type: mime || "image/png"
24456
+ });
24457
+ const url = URL.createObjectURL(blob);
24458
+ try {
24459
+ const image = await loadHtmlImage(url);
24460
+ const canvas = document.createElement("canvas");
24461
+ canvas.width = image.naturalWidth || DEFAULT_IMAGE_WIDTH;
24462
+ canvas.height = image.naturalHeight || DEFAULT_IMAGE_HEIGHT;
24463
+ const context = canvas.getContext("2d");
24464
+ if (!context) {
24465
+ throw new Error("Canvas context is unavailable");
24433
24466
  }
24434
- );
24435
- if (pageNumberParagraph) {
24436
- footerChildren.push(pageNumberParagraph);
24467
+ context.drawImage(image, 0, 0, canvas.width, canvas.height);
24468
+ const pngBlob = await new Promise((resolve, reject) => {
24469
+ canvas.toBlob((output) => {
24470
+ if (output) {
24471
+ resolve(output);
24472
+ } else {
24473
+ reject(new Error("Failed to create PNG blob"));
24474
+ }
24475
+ }, "image/png");
24476
+ });
24477
+ return await pngBlob.arrayBuffer();
24478
+ } finally {
24479
+ URL.revokeObjectURL(url);
24437
24480
  }
24438
- const sectionChildren = await serializeElementList(
24439
- data2.main,
24440
- editorOptions,
24441
- context
24442
- );
24443
- const { width, height, paperDirection } = editorOptions;
24444
- const pageWidth = paperDirection === "horizontal" /* HORIZONTAL */ ? height : width;
24445
- const pageHeight = paperDirection === "horizontal" /* HORIZONTAL */ ? width : height;
24481
+ }
24482
+ function loadHtmlImage(src) {
24483
+ return new Promise((resolve, reject) => {
24484
+ const image = new Image();
24485
+ image.onload = () => resolve(image);
24486
+ image.onerror = () => reject(new Error("Failed to load image"));
24487
+ image.src = src;
24488
+ });
24489
+ }
24490
+ function mapUnderlineType(style) {
24491
+ switch (style) {
24492
+ case "double" /* DOUBLE */:
24493
+ return UnderlineType.DOUBLE;
24494
+ case "dashed" /* DASHED */:
24495
+ return UnderlineType.DASH;
24496
+ case "dotted" /* DOTTED */:
24497
+ return UnderlineType.DOTTED;
24498
+ case "wavy" /* WAVY */:
24499
+ return UnderlineType.WAVE;
24500
+ default:
24501
+ return UnderlineType.SINGLE;
24502
+ }
24503
+ }
24504
+ function normalizeHexColor(color, fallback) {
24505
+ const normalized = normalizeHexLiteral(color) || normalizeRgbColor(color) || normalizeBrowserColor(color);
24506
+ if (normalized) {
24507
+ return normalized;
24508
+ }
24509
+ return fallback ? normalizeHexLiteral(fallback) || void 0 : void 0;
24510
+ }
24511
+ function normalizeHexLiteral(color) {
24512
+ const value = color.trim().replace(/^#/, "");
24513
+ if (/^[\da-f]{3}$/i.test(value)) {
24514
+ return value.split("").map((part) => `${part}${part}`).join("").toUpperCase();
24515
+ }
24516
+ if (/^[\da-f]{4}$/i.test(value)) {
24517
+ return value.slice(0, 3).split("").map((part) => `${part}${part}`).join("").toUpperCase();
24518
+ }
24519
+ if (/^[\da-f]{6}$/i.test(value)) {
24520
+ return value.toUpperCase();
24521
+ }
24522
+ if (/^[\da-f]{8}$/i.test(value)) {
24523
+ return value.slice(0, 6).toUpperCase();
24524
+ }
24525
+ return void 0;
24526
+ }
24527
+ function normalizeRgbColor(color) {
24528
+ const match = color.trim().match(/^rgba?\((.+)\)$/i);
24529
+ if (!match) {
24530
+ return void 0;
24531
+ }
24532
+ const parts = match[1].replace(/\s*\/\s*/g, ",").split(/\s*,\s*|\s+/).filter(Boolean);
24533
+ if (parts.length < 3) {
24534
+ return void 0;
24535
+ }
24536
+ const red = parseRgbChannel(parts[0]);
24537
+ const green = parseRgbChannel(parts[1]);
24538
+ const blue = parseRgbChannel(parts[2]);
24539
+ if (red === null || green === null || blue === null) {
24540
+ return void 0;
24541
+ }
24542
+ const alpha = parts[3] !== void 0 ? parseAlphaChannel(parts[3]) : 1;
24543
+ if (alpha === null || alpha <= 0) {
24544
+ return void 0;
24545
+ }
24546
+ const flattened = alpha >= 1 ? [red, green, blue] : [
24547
+ flattenChannel(red, alpha),
24548
+ flattenChannel(green, alpha),
24549
+ flattenChannel(blue, alpha)
24550
+ ];
24551
+ return flattened.map(toHexChannel).join("");
24552
+ }
24553
+ function normalizeBrowserColor(color) {
24554
+ if (typeof document === "undefined") {
24555
+ return void 0;
24556
+ }
24557
+ const canvas = document.createElement("canvas");
24558
+ const context = canvas.getContext("2d");
24559
+ if (!context) {
24560
+ return void 0;
24561
+ }
24562
+ context.fillStyle = "#000000";
24563
+ context.fillStyle = color;
24564
+ const normalized = context.fillStyle;
24565
+ if (typeof normalized !== "string") {
24566
+ return void 0;
24567
+ }
24568
+ return normalizeHexLiteral(normalized) || normalizeRgbColor(normalized);
24569
+ }
24570
+ function parseRgbChannel(value) {
24571
+ if (value.endsWith("%")) {
24572
+ const percent = Number(value.slice(0, -1));
24573
+ if (!Number.isFinite(percent)) {
24574
+ return null;
24575
+ }
24576
+ return clampColorChannel(Math.round(percent / 100 * 255));
24577
+ }
24578
+ const channel = Number(value);
24579
+ if (!Number.isFinite(channel)) {
24580
+ return null;
24581
+ }
24582
+ return clampColorChannel(Math.round(channel));
24583
+ }
24584
+ function parseAlphaChannel(value) {
24585
+ if (value.endsWith("%")) {
24586
+ const percent = Number(value.slice(0, -1));
24587
+ if (!Number.isFinite(percent)) {
24588
+ return null;
24589
+ }
24590
+ return Math.min(Math.max(percent / 100, 0), 1);
24591
+ }
24592
+ const alpha = Number(value);
24593
+ if (!Number.isFinite(alpha)) {
24594
+ return null;
24595
+ }
24596
+ return Math.min(Math.max(alpha, 0), 1);
24597
+ }
24598
+ function clampColorChannel(value) {
24599
+ return Math.min(Math.max(value, 0), 255);
24600
+ }
24601
+ function flattenChannel(value, alpha) {
24602
+ return clampColorChannel(Math.round(255 * (1 - alpha) + value * alpha));
24603
+ }
24604
+ function toHexChannel(value) {
24605
+ return value.toString(16).padStart(2, "0").toUpperCase();
24606
+ }
24607
+ function getRegularImageType(mime) {
24608
+ if (mime.includes("png")) return "png";
24609
+ if (mime.includes("jpeg") || mime.includes("jpg")) return "jpg";
24610
+ if (mime.includes("gif")) return "gif";
24611
+ if (mime.includes("bmp")) return "bmp";
24612
+ return null;
24613
+ }
24614
+ function normalizeDocxFileName(fileName) {
24615
+ const trimmed = fileName?.trim();
24616
+ if (!trimmed) {
24617
+ return DEFAULT_DOCX_FILE_NAME;
24618
+ }
24619
+ return trimmed.toLowerCase().endsWith(".docx") ? trimmed : `${trimmed}.docx`;
24620
+ }
24621
+
24622
+ // src/utils/docxLayout.ts
24623
+ var PX_TO_TWIP = 15;
24624
+ var ANCHOR_EPS = 0.5;
24625
+ var RIGHT_SLACK_TWIP = -14400;
24626
+ var TINY_TWIP = 1;
24627
+ var tw = (ctx, layoutPx) => Math.round(layoutPx / ctx.scale * PX_TO_TWIP);
24628
+ var pxTw = (px) => Math.round(px * PX_TO_TWIP);
24629
+ async function exportDrawToDocx(draw, options = {}) {
24630
+ const canvas = document.createElement("canvas");
24631
+ const measureCtx = canvas.getContext("2d");
24632
+ const ctx = {
24633
+ draw,
24634
+ scale: draw.getOptions().scale,
24635
+ measureCtx
24636
+ };
24637
+ const file = await createDocument(ctx);
24638
+ const blob = await Packer.toBlob(file);
24639
+ return { blob, fileName: normalizeDocxFileName(options.fileName) };
24640
+ }
24641
+ async function createDocument(ctx) {
24642
+ const { draw, scale } = ctx;
24643
+ const opts = draw.getOptions();
24644
+ const width = draw.getOriginalWidth();
24645
+ const height = draw.getOriginalHeight();
24646
+ const margins = draw.getOriginalMargins();
24647
+ const header = draw.getHeader();
24648
+ const footer = draw.getFooter();
24649
+ const headerTop = header.getHeaderTop() / scale;
24650
+ const headerExtra = header.getExtraHeight() / scale;
24651
+ const footerExtra = footer.getExtraHeight() / scale;
24652
+ const layoutMargins = draw.getMargins();
24653
+ const body = await serializeMain(ctx, layoutMargins[3]);
24654
+ const headerChildren = opts.header.disabled ? [] : await serializeRows(ctx, header.getRowList(), {
24655
+ startX: layoutMargins[3],
24656
+ width: draw.getInnerWidth(),
24657
+ positionList: header.getPositionList()
24658
+ });
24659
+ const footerChildren = opts.footer.disabled ? [] : await serializeFooter(ctx, width, height, margins);
24660
+ const { pageNumber } = draw.getOptions();
24661
+ endWithParagraph(headerChildren);
24662
+ endWithParagraph(footerChildren);
24446
24663
  return new Document2({
24447
- features: {
24448
- updateFields: true
24449
- },
24450
- numbering: {
24451
- config: createNumberingConfig()
24664
+ compatabilityModeVersion: 15,
24665
+ // LibreOffice formats PAGE/NUMPAGES results from the paragraph style
24666
+ styles: {
24667
+ paragraphStyles: [
24668
+ {
24669
+ id: PAGE_NUMBER_STYLE,
24670
+ name: "Windoc Page Number",
24671
+ run: pageNumberRunStyle(pageNumber)
24672
+ }
24673
+ ]
24452
24674
  },
24675
+ defaultTabStop: pxTw(width),
24453
24676
  sections: [
24454
24677
  {
24455
24678
  properties: {
24456
24679
  page: {
24457
24680
  size: {
24458
- width: pxToTwips(pageWidth),
24459
- height: pxToTwips(pageHeight),
24460
- orientation: paperDirection === "horizontal" /* HORIZONTAL */ ? PageOrientation.LANDSCAPE : PageOrientation.PORTRAIT
24681
+ width: pxTw(width),
24682
+ height: pxTw(height),
24683
+ orientation: opts.paperDirection === "horizontal" /* HORIZONTAL */ ? PageOrientation.LANDSCAPE : PageOrientation.PORTRAIT
24461
24684
  },
24462
24685
  margin: {
24463
- top: pxToTwips(editorOptions.margins[0]),
24464
- right: pxToTwips(editorOptions.margins[1]),
24465
- bottom: pxToTwips(editorOptions.margins[2]),
24466
- left: pxToTwips(editorOptions.margins[3]),
24467
- header: pxToTwips(editorOptions.header.top),
24468
- footer: pxToTwips(editorOptions.footer.bottom)
24469
- },
24470
- pageNumbers: !editorOptions.pageNumber.disabled && editorOptions.pageNumber.fromPageNo === 0 ? {
24471
- start: editorOptions.pageNumber.startPageNo
24472
- } : void 0
24686
+ top: pxTw(margins[0] + headerExtra),
24687
+ right: pxTw(margins[1]),
24688
+ bottom: pxTw(margins[2] + footerExtra),
24689
+ left: pxTw(margins[3]),
24690
+ header: pxTw(headerTop),
24691
+ footer: pxTw(footer.getFooterBottom() / scale),
24692
+ gutter: 0
24693
+ }
24473
24694
  }
24474
24695
  },
24475
- headers: headerChildren.length > 0 ? {
24476
- default: new Header2({
24477
- children: headerChildren
24478
- })
24479
- } : void 0,
24480
- footers: footerChildren.length > 0 ? {
24481
- default: new Footer2({
24482
- children: footerChildren
24483
- })
24484
- } : void 0,
24485
- children: sectionChildren.length ? sectionChildren : [new Paragraph({})]
24696
+ headers: headerChildren.length ? { default: new Header2({ children: headerChildren }) } : void 0,
24697
+ footers: footerChildren.length ? { default: new Footer2({ children: footerChildren }) } : void 0,
24698
+ children: body.length ? body : [new Paragraph({})]
24486
24699
  }
24487
24700
  ]
24488
24701
  });
24489
24702
  }
24490
- async function serializeElementList(elementList, options, context) {
24491
- const blocks = [];
24492
- let inlineBuffer = [];
24493
- const flushInlineBuffer = async () => {
24494
- if (!inlineBuffer.length) return;
24495
- blocks.push(
24496
- ...await serializeInlineElements(inlineBuffer, options, context)
24497
- );
24498
- inlineBuffer = [];
24703
+ async function serializeMain(ctx, startX) {
24704
+ const { draw } = ctx;
24705
+ const zone = {
24706
+ startX,
24707
+ width: draw.getInnerWidth(),
24708
+ positionList: draw.getPosition().getOriginalMainPositionList()
24499
24709
  };
24500
- for (let i = 0; i < elementList.length; i++) {
24501
- const element = elementList[i];
24502
- if (element.type === "area" /* AREA */ && element.valueList?.length) {
24503
- await flushInlineBuffer();
24504
- blocks.push(
24505
- ...await serializeElementList(element.valueList, options, context)
24506
- );
24507
- continue;
24508
- }
24509
- if (element.type === "title" /* TITLE */ && element.valueList?.length) {
24510
- await flushInlineBuffer();
24511
- blocks.push(
24512
- ...await serializeInlineElements(element.valueList, options, context, {
24513
- heading: titleHeadingMap[element.level || "first" /* FIRST */]
24514
- })
24515
- );
24516
- continue;
24517
- }
24518
- if (element.type === "list" /* LIST */ && element.valueList?.length) {
24519
- await flushInlineBuffer();
24520
- blocks.push(...await serializeListElement(element, options, context));
24521
- continue;
24522
- }
24523
- if (element.type === "table" /* TABLE */ && element.trList?.length) {
24524
- await flushInlineBuffer();
24525
- blocks.push(await serializeTableElement(element, options, context));
24526
- continue;
24527
- }
24528
- if (element.type === "pageBreak" /* PAGE_BREAK */) {
24529
- await flushInlineBuffer();
24530
- blocks.push(
24531
- new Paragraph({
24532
- children: [new PageBreak()]
24533
- })
24534
- );
24535
- continue;
24536
- }
24537
- if (element.type === "separator" /* SEPARATOR */) {
24538
- await flushInlineBuffer();
24539
- blocks.push(
24540
- new Paragraph({
24541
- thematicBreak: true
24542
- })
24543
- );
24544
- continue;
24545
- }
24546
- if (element.type === "image" /* IMAGE */ && element.imgDisplay && element.imgDisplay !== "inline") {
24547
- await flushInlineBuffer();
24548
- blocks.push(...await serializeInlineElements([element], options));
24549
- continue;
24550
- }
24551
- if (element.type === "block" /* BLOCK */) {
24552
- await flushInlineBuffer();
24553
- const blockText = getBlockFallbackText(element);
24554
- if (blockText) {
24555
- blocks.push(
24556
- ...await serializeInlineElements(
24557
- [{ value: blockText }],
24558
- options,
24559
- context
24710
+ const children = [];
24711
+ const pageRowList = draw.getPageRowList();
24712
+ for (let p = 0; p < pageRowList.length; p++) {
24713
+ children.push(
24714
+ ...await serializeRows(ctx, pageRowList[p], zone, {
24715
+ pageBreakBefore: p > 0
24716
+ })
24717
+ );
24718
+ }
24719
+ return children;
24720
+ }
24721
+ async function serializeRows(ctx, rowList, zone, options = {}) {
24722
+ const { pageBreakBefore = false, padTop = 0, padBottom = 0 } = options;
24723
+ const children = [];
24724
+ for (let r = 0; r < rowList.length; r++) {
24725
+ const row = rowList[r];
24726
+ const isFirst = r === 0;
24727
+ const isLast = r === rowList.length - 1;
24728
+ const table = row.elementList.find((el) => el.type === "table" /* TABLE */);
24729
+ if (table) {
24730
+ if (isFirst && (pageBreakBefore || padTop) || row.offsetY) {
24731
+ children.push(
24732
+ createSpacerParagraph(
24733
+ isFirst && pageBreakBefore,
24734
+ (isFirst ? padTop : 0) + (row.offsetY || 0) / ctx.scale
24560
24735
  )
24561
24736
  );
24562
24737
  }
24738
+ const position = zone.positionList[row.startIndex + row.elementList.indexOf(table)];
24739
+ children.push(await serializeTable(ctx, table, position, zone));
24740
+ const rest = (row.height + (row.spaceBelow || 0)) / ctx.scale - table.height + (isLast ? padBottom : 0);
24741
+ if (rest * PX_TO_TWIP >= 1) {
24742
+ children.push(createSpacerParagraph(false, rest));
24743
+ }
24563
24744
  continue;
24564
24745
  }
24565
- inlineBuffer.push(element);
24746
+ const rowOptions = {
24747
+ pageBreakBefore: isFirst && pageBreakBefore,
24748
+ padTop: isFirst ? padTop : 0,
24749
+ padBottom: isLast ? padBottom : 0
24750
+ };
24751
+ if (!isFirst && isTextRow(rowList[r - 1])) rowOptions.skipLead = true;
24752
+ rowOptions.rowY = rowY(zone, row);
24753
+ if (!isLast && isTextRow(rowList[r + 1])) {
24754
+ rowOptions.extraAfter = rowLead(ctx, rowList[r + 1], {
24755
+ rowY: rowY(zone, rowList[r + 1])
24756
+ });
24757
+ }
24758
+ const lead = rowOptions.pageBreakBefore ? rowLead(ctx, row, rowOptions) : 0;
24759
+ if (lead) {
24760
+ children.push(createSpacerParagraph(true, lead / PX_TO_TWIP));
24761
+ rowOptions.pageBreakBefore = false;
24762
+ rowOptions.skipLead = true;
24763
+ }
24764
+ children.push(await serializeRow(ctx, row, zone, rowOptions));
24566
24765
  }
24567
- await flushInlineBuffer();
24568
- return blocks;
24766
+ return children;
24569
24767
  }
24570
- async function serializeInlineElements(elementList, options, _context, overrides = {}) {
24571
- const paragraphs = [];
24572
- let current = null;
24573
- const getParagraphContext = (element) => ({
24574
- heading: overrides.heading,
24575
- numbering: overrides.numbering,
24576
- alignment: overrides.alignment !== void 0 ? overrides.alignment : mapRowFlexToAlignment(element?.rowFlex),
24577
- rowMargin: overrides.rowMargin !== void 0 ? overrides.rowMargin : element?.rowMargin
24578
- });
24579
- const flushParagraph = (context) => {
24580
- if (!current) {
24581
- if (!context) return;
24582
- paragraphs.push(createParagraph([], context, options.defaultRowMargin));
24768
+ function rowY(zone, row) {
24769
+ return zone.positionList[row.startIndex]?.coordinate.leftTop[1];
24770
+ }
24771
+ function isTextRow(row) {
24772
+ return !row.elementList.some((el) => el.type === "table" /* TABLE */);
24773
+ }
24774
+ async function serializeRow(ctx, row, zone, options, suffix) {
24775
+ const { pageBreakBefore = false } = options;
24776
+ const { draw, scale } = ctx;
24777
+ const positions = zone.positionList;
24778
+ const children = [];
24779
+ const tabStops = [];
24780
+ const firstPosition = positions[row.startIndex];
24781
+ let pen = 0;
24782
+ let indent = firstPosition ? (firstPosition.coordinate.leftTop[0] - zone.startX) / scale : 0;
24783
+ let started = false;
24784
+ const anchorTo = (layoutX) => {
24785
+ const target = (layoutX - zone.startX) / scale;
24786
+ if (!started) {
24787
+ indent = target;
24788
+ pen = target;
24789
+ started = true;
24583
24790
  return;
24584
24791
  }
24585
- paragraphs.push(
24586
- createParagraph(
24587
- current.children,
24588
- current.context,
24589
- options.defaultRowMargin
24590
- )
24591
- );
24592
- current = null;
24593
- };
24594
- const ensureParagraph = (element) => {
24595
- if (!current) {
24596
- current = {
24597
- children: [],
24598
- context: getParagraphContext(element)
24599
- };
24792
+ if (target - pen > ANCHOR_EPS) {
24793
+ tabStops.push(pxTw(target));
24794
+ children.push(new TextRun({ children: [new Tab()] }));
24795
+ pen = target;
24600
24796
  }
24601
- return current;
24602
24797
  };
24603
- const appendText = (element, text, childFactory, preserveTrailingBreak = false) => {
24604
- const normalized = normalizeText(text);
24605
- const parts = normalized.split("\n");
24606
- let lastSeparatorFlushedContent = false;
24607
- for (let i = 0; i < parts.length; i++) {
24608
- const segment = parts[i];
24609
- if (segment) {
24610
- ensureParagraph(element).children.push(childFactory(segment));
24611
- }
24612
- if (i < parts.length - 1) {
24613
- if (!current) {
24614
- paragraphs.push(
24615
- createParagraph(
24616
- [],
24617
- getParagraphContext(element),
24618
- options.defaultRowMargin
24619
- )
24620
- );
24621
- lastSeparatorFlushedContent = false;
24622
- } else {
24623
- flushParagraph();
24624
- lastSeparatorFlushedContent = true;
24625
- }
24626
- }
24627
- }
24628
- if (preserveTrailingBreak && normalized.endsWith("\n") && lastSeparatorFlushedContent) {
24629
- paragraphs.push(
24630
- createParagraph(
24631
- [],
24632
- getParagraphContext(element),
24633
- options.defaultRowMargin
24634
- )
24798
+ if (row.isList && firstPosition) {
24799
+ const listParticle = draw.getListParticle();
24800
+ const markerX = listParticle.getListMarkerX(row, firstPosition);
24801
+ const marker = markerX === null ? null : listParticle.getListMarker(row);
24802
+ if (markerX !== null && marker) {
24803
+ anchorTo(markerX);
24804
+ children.push(createTextRun(ctx, marker.text, marker.styleElement));
24805
+ pen += measure(ctx, marker.text, marker.styleElement);
24806
+ }
24807
+ }
24808
+ let run = null;
24809
+ const flush = () => {
24810
+ if (!run) return;
24811
+ children.push(createTextRun(ctx, run.text, run.element));
24812
+ run = null;
24813
+ };
24814
+ for (let j = 0; j < row.elementList.length; j++) {
24815
+ const element = row.elementList[j];
24816
+ const position = positions[row.startIndex + j];
24817
+ if (!position) continue;
24818
+ if (element.hide || element.control?.hide || element.area?.hide) continue;
24819
+ if (element.type === "separator" /* SEPARATOR */) {
24820
+ flush();
24821
+ return createSeparatorParagraph(
24822
+ ctx,
24823
+ row,
24824
+ element,
24825
+ position.coordinate.leftTop[0],
24826
+ position.coordinate.leftTop[1],
24827
+ zone,
24828
+ options
24635
24829
  );
24636
24830
  }
24637
- };
24638
- for (let i = 0; i < elementList.length; i++) {
24639
- const element = elementList[i];
24640
- if (element.type === "hyperlink" /* HYPERLINK */ && element.valueList?.length) {
24641
- const hyperlink = await createHyperlinkChild(element, options);
24642
- if (hyperlink) {
24643
- ensureParagraph(element).children.push(hyperlink);
24644
- }
24831
+ if (element.value === ZERO || element.type === "tab" /* TAB */ || element.type === "pageBreak" /* PAGE_BREAK */ || element.type === "columnBreak" /* COLUMN_BREAK */) {
24645
24832
  continue;
24646
24833
  }
24834
+ const x = position.coordinate.leftTop[0];
24835
+ const layoutWidth = element.metrics.width / scale;
24647
24836
  if (element.type === "image" /* IMAGE */) {
24648
- const imageChild = await createImageChild(element);
24649
- if (!imageChild) {
24650
- appendText(
24651
- element,
24652
- "[Image unavailable]",
24653
- (segment) => createTextRun(
24654
- segment,
24655
- element,
24656
- options.defaultSize,
24657
- options.defaultFont
24658
- ),
24659
- i === elementList.length - 1
24660
- );
24661
- continue;
24662
- }
24663
- ensureParagraph(element).children.push(imageChild);
24664
- continue;
24665
- }
24666
- if (element.type === "columnBreak" /* COLUMN_BREAK */) {
24667
- ensureParagraph(element).children.push(new ColumnBreak());
24837
+ flush();
24838
+ anchorTo(x);
24839
+ const image = await createImageRun(element);
24840
+ if (image) children.push(image);
24841
+ pen += layoutWidth;
24668
24842
  continue;
24669
24843
  }
24670
- if (element.type === "tab" /* TAB */) {
24671
- appendText(
24672
- element,
24673
- " ",
24674
- (segment) => createTextRun(
24675
- segment,
24676
- element,
24677
- options.defaultSize,
24678
- options.defaultFont
24679
- ),
24680
- i === elementList.length - 1
24844
+ if (element.type === "label" /* LABEL */) {
24845
+ flush();
24846
+ const padding = element.label?.padding || draw.getOptions().label.defaultPadding;
24847
+ anchorTo(x + padding[3] * scale);
24848
+ children.push(
24849
+ createTextRun(ctx, element.value, element, labelShading(ctx, element))
24681
24850
  );
24851
+ pen += measure(ctx, element.value, element);
24682
24852
  continue;
24683
24853
  }
24684
- const textValue = getInlineElementText(element);
24685
- if (!textValue) {
24686
- continue;
24687
- }
24688
- appendText(
24689
- element,
24690
- textValue,
24691
- (segment) => createTextRun(
24692
- segment,
24693
- element,
24694
- options.defaultSize,
24695
- options.defaultFont
24696
- ),
24697
- i === elementList.length - 1
24698
- );
24699
- }
24700
- if (current) {
24701
- flushParagraph();
24702
- }
24703
- return paragraphs.length ? paragraphs : [createParagraph([], overrides, options.defaultRowMargin)];
24704
- }
24705
- async function serializeListElement(element, options, context) {
24706
- const zipList = zipElementList(element.valueList || []);
24707
- const listItemMap = splitListElement(zipList);
24708
- const paragraphs = [];
24709
- const listInstance = createListInstance(context);
24710
- for (const listItemElements of listItemMap.values()) {
24711
- const numbering = getListNumbering(
24712
- getListItemNumberingElement(listItemElements, element),
24713
- listInstance
24714
- );
24715
- paragraphs.push(
24716
- ...await serializeInlineElements(listItemElements, options, context, {
24717
- numbering
24718
- })
24719
- );
24720
- }
24721
- return paragraphs;
24722
- }
24723
- async function serializeTableElement(element, options, context) {
24724
- const rows = [];
24725
- const trList = element.trList || [];
24726
- const tableContext = createTableSerializeContext(element);
24727
- for (let i = 0; i < trList.length; i++) {
24728
- rows.push(
24729
- await serializeTableRow(trList[i], options, context, tableContext)
24730
- );
24731
- }
24732
- const tableWidth = getTableWidthFromColgroup(element.colgroup);
24733
- return new Table({
24734
- rows,
24735
- width: tableWidth ? {
24736
- size: pxToTwips(tableWidth),
24737
- type: WidthType.DXA
24738
- } : {
24739
- size: 100,
24740
- type: WidthType.PERCENTAGE
24741
- },
24742
- columnWidths: element.colgroup?.map((col) => pxToTwips(col.width)),
24743
- layout: TableLayoutType.FIXED,
24744
- borders: createTableBorders(element)
24745
- });
24746
- }
24747
- async function serializeTableRow(tr, options, context, tableContext) {
24748
- const children = [];
24749
- for (let i = 0; i < tr.tdList.length; i++) {
24750
- children.push(
24751
- await serializeTableCell(tr.tdList[i], options, context, tableContext)
24752
- );
24753
- }
24754
- return new TableRow({
24755
- children
24756
- });
24757
- }
24758
- async function serializeTableCell(td, options, context, tableContext) {
24759
- const children = await serializeElementList(td.value, options, context);
24760
- const shadingFill = td.backgroundColor ? normalizeHexColor(td.backgroundColor) : void 0;
24761
- const cellWidth = tableContext.cellLayoutMap.get(td)?.width || td.width;
24762
- return new TableCell({
24763
- children: children.length ? children : [new Paragraph({})],
24764
- columnSpan: td.colspan > 1 ? td.colspan : void 0,
24765
- rowSpan: td.rowspan > 1 ? td.rowspan : void 0,
24766
- verticalAlign: mapCellVerticalAlign(td.verticalAlign),
24767
- width: cellWidth ? {
24768
- size: pxToTwips(cellWidth),
24769
- type: WidthType.DXA
24770
- } : void 0,
24771
- shading: shadingFill ? {
24772
- fill: shadingFill,
24773
- type: ShadingType.CLEAR
24774
- } : void 0,
24775
- margins: mapPaddingToTableMargins(td.padding),
24776
- borders: createTableCellBorders(
24777
- td,
24778
- tableContext.borderColor,
24779
- tableContext.borderWidth
24780
- )
24781
- });
24782
- }
24783
- function createParagraph(children, context, defaultRowMargin) {
24784
- return new Paragraph({
24785
- children,
24786
- heading: context.heading,
24787
- numbering: context.numbering,
24788
- alignment: context.alignment,
24789
- spacing: {
24790
- line: Math.round(
24791
- 240 * (context.rowMargin || defaultRowMargin || DEFAULT_LINE_HEIGHT)
24792
- ),
24793
- lineRule: LineRuleType.AUTO
24794
- }
24795
- });
24796
- }
24797
- function createTextRun(text, element, defaultSize, defaultFont) {
24798
- const color = element.color ? normalizeHexColor(element.color, "000000") : void 0;
24799
- const highlightFill = element.highlight ? normalizeHexColor(element.highlight) : void 0;
24800
- return new TextRun({
24801
- text,
24802
- bold: !!element.bold,
24803
- italics: !!element.italic,
24804
- strike: !!element.strikeout,
24805
- subScript: element.type === "subscript" /* SUBSCRIPT */,
24806
- superScript: element.type === "superscript" /* SUPERSCRIPT */,
24807
- color,
24808
- shading: highlightFill ? {
24809
- fill: highlightFill,
24810
- type: ShadingType.CLEAR
24811
- } : void 0,
24812
- underline: element.underline ? {
24813
- type: mapUnderlineType(element.textDecoration?.style)
24814
- } : void 0,
24815
- font: element.font || defaultFont,
24816
- size: Math.round((element.size || defaultSize) * 2)
24817
- });
24818
- }
24819
- async function createHyperlinkChild(element, options) {
24820
- if (!element.url) {
24821
- const fallbackText = getTextFromElementList(element.valueList || []);
24822
- return fallbackText ? createTextRun(
24823
- fallbackText,
24824
- element.valueList?.[0] || element,
24825
- options.defaultSize,
24826
- options.defaultFont
24827
- ) : null;
24828
- }
24829
- const children = await createHyperlinkRuns(
24830
- element.valueList || [],
24831
- options.defaultSize,
24832
- options.defaultFont
24833
- );
24834
- if (!children.length) {
24835
- return null;
24836
- }
24837
- return new ExternalHyperlink({
24838
- link: element.url,
24839
- children
24840
- });
24841
- }
24842
- async function createHyperlinkRuns(valueList, defaultSize, defaultFont) {
24843
- const children = [];
24844
- for (let i = 0; i < valueList.length; i++) {
24845
- const element = valueList[i];
24846
- if (element.type === "tab" /* TAB */) {
24847
- children.push(createTextRun(" ", element, defaultSize, defaultFont));
24848
- continue;
24849
- }
24850
- const text = normalizeText(getInlineElementText(element)).replace(
24851
- /\n/g,
24852
- " "
24853
- );
24854
- if (text) {
24855
- children.push(createTextRun(text, element, defaultSize, defaultFont));
24856
- }
24857
- }
24858
- return children;
24859
- }
24860
- async function createImageChild(element) {
24861
- try {
24862
- const image = await loadImagePayload(element.value);
24863
- const width = Math.max(1, Math.round(element.width || DEFAULT_IMAGE_WIDTH));
24864
- const height = Math.max(
24865
- 1,
24866
- Math.round(element.height || DEFAULT_IMAGE_HEIGHT)
24867
- );
24868
- if (image.type === "svg" && image.fallbackPng) {
24869
- return new ImageRun({
24870
- type: "svg",
24871
- data: image.data,
24872
- fallback: {
24873
- type: "png",
24874
- data: image.fallbackPng
24875
- },
24876
- transformation: {
24877
- width,
24878
- height
24879
- }
24880
- });
24881
- }
24882
- return new ImageRun({
24883
- type: image.type,
24884
- data: image.data,
24885
- transformation: {
24886
- width,
24887
- height
24854
+ const text = getElementText(element);
24855
+ if (!text) continue;
24856
+ const natural = measure(ctx, text, element);
24857
+ const target = (x - zone.startX) / scale;
24858
+ const isDrift = !started || target - pen > ANCHOR_EPS;
24859
+ const extra = layoutWidth - natural;
24860
+ const needsSpacing = Math.abs(extra) * PX_TO_TWIP >= 1;
24861
+ const canJoin = run && !isDrift && !needsSpacing && isSameRunStyle(run.element, element);
24862
+ if (canJoin) {
24863
+ run.text += text;
24864
+ } else {
24865
+ flush();
24866
+ anchorTo(x);
24867
+ if (needsSpacing) {
24868
+ children.push(
24869
+ createTextRun(ctx, text, element, void 0, pxTw(extra))
24870
+ );
24871
+ } else {
24872
+ run = { text, element };
24888
24873
  }
24889
- });
24890
- } catch {
24891
- return null;
24892
- }
24893
- }
24894
- async function loadImagePayload(src) {
24895
- const source = src.trim();
24896
- if (source.startsWith("data:")) {
24897
- return getImagePayloadFromDataUrl(source);
24898
- }
24899
- const response = await fetch(source);
24900
- if (!response.ok) {
24901
- throw new Error(`Image request failed: ${response.status}`);
24902
- }
24903
- const contentType = response.headers.get("content-type") || "";
24904
- const data2 = await response.arrayBuffer();
24905
- return normalizeImagePayload(data2, contentType);
24906
- }
24907
- async function getImagePayloadFromDataUrl(src) {
24908
- const [, meta = "", encoded = ""] = src.match(/^data:([^,]*),(.*)$/) || [];
24909
- const mime = meta.split(";")[0] || "";
24910
- const isBase64 = meta.includes(";base64");
24911
- const text = isBase64 ? atob(encoded) : decodeURIComponent(encoded);
24912
- const bytes = new Uint8Array(text.length);
24913
- for (let i = 0; i < text.length; i++) {
24914
- bytes[i] = text.charCodeAt(i);
24915
- }
24916
- return normalizeImagePayload(bytes.buffer, mime);
24917
- }
24918
- async function normalizeImagePayload(data2, mime) {
24919
- const type = getRegularImageType(mime);
24920
- if (type) {
24921
- return {
24922
- type,
24923
- data: data2
24924
- };
24925
- }
24926
- if (mime.includes("svg")) {
24927
- return {
24928
- type: "svg",
24929
- data: data2,
24930
- fallbackPng: await rasterizeSvgToPng(data2)
24931
- };
24932
- }
24933
- const fallbackPng = await rasterizeImageToPng(data2, mime);
24934
- return {
24935
- type: "png",
24936
- data: fallbackPng
24937
- };
24938
- }
24939
- async function rasterizeSvgToPng(data2) {
24940
- return rasterizeImageToPng(data2, "image/svg+xml");
24941
- }
24942
- async function rasterizeImageToPng(data2, mime) {
24943
- const blob = new Blob([data2], {
24944
- type: mime || "image/png"
24945
- });
24946
- const url = URL.createObjectURL(blob);
24947
- try {
24948
- const image = await loadHtmlImage(url);
24949
- const canvas = document.createElement("canvas");
24950
- canvas.width = image.naturalWidth || DEFAULT_IMAGE_WIDTH;
24951
- canvas.height = image.naturalHeight || DEFAULT_IMAGE_HEIGHT;
24952
- const context = canvas.getContext("2d");
24953
- if (!context) {
24954
- throw new Error("Canvas context is unavailable");
24955
24874
  }
24956
- context.drawImage(image, 0, 0, canvas.width, canvas.height);
24957
- const pngBlob = await new Promise((resolve, reject) => {
24958
- canvas.toBlob((output) => {
24959
- if (output) {
24960
- resolve(output);
24961
- } else {
24962
- reject(new Error("Failed to create PNG blob"));
24963
- }
24964
- }, "image/png");
24965
- });
24966
- return await pngBlob.arrayBuffer();
24967
- } finally {
24968
- URL.revokeObjectURL(url);
24875
+ pen = target + layoutWidth;
24969
24876
  }
24970
- }
24971
- function loadHtmlImage(src) {
24972
- return new Promise((resolve, reject) => {
24973
- const image = new Image();
24974
- image.onload = () => resolve(image);
24975
- image.onerror = () => reject(new Error("Failed to load image"));
24976
- image.src = src;
24977
- });
24978
- }
24979
- function createPageNumberParagraph(pageNumber) {
24980
- if (pageNumber.disabled || pageNumber.fromPageNo > 0) {
24981
- return null;
24877
+ flush();
24878
+ if (!children.length && !suffix) {
24879
+ const total = (options.skipLead ? 0 : rowLead(ctx, row, options)) + rowLine(ctx, row).lineTw + rowSpacing(ctx, row, options).after;
24880
+ return createSpacerParagraph(pageBreakBefore, total / PX_TO_TWIP);
24982
24881
  }
24983
- const children = [];
24984
- const format = pageNumber.format || FORMAT_PLACEHOLDER.PAGE_NO;
24985
- const tokens = format.split(/(\{pageNo\}|\{pageCount\})/);
24986
- for (let i = 0; i < tokens.length; i++) {
24987
- const token = tokens[i];
24988
- if (!token) continue;
24989
- const runChildren = token === FORMAT_PLACEHOLDER.PAGE_NO ? [PageNumber2.CURRENT] : token === FORMAT_PLACEHOLDER.PAGE_COUNT ? [PageNumber2.TOTAL_PAGES] : void 0;
24990
- children.push(
24991
- new TextRun({
24992
- text: runChildren ? void 0 : token,
24993
- children: runChildren,
24994
- color: normalizeHexColor(pageNumber.color || "#000000", "000000"),
24995
- font: pageNumber.font,
24996
- size: Math.round((pageNumber.size || 12) * 2)
24997
- })
24998
- );
24882
+ const stops = tabStops.map((position) => ({ type: TabStopType.LEFT, position }));
24883
+ if (suffix) {
24884
+ stops.push({ type: suffix.tabType, position: suffix.tabStop });
24885
+ children.push(new TextRun({ children: [new Tab()] }), ...suffix.children);
24999
24886
  }
25000
24887
  return new Paragraph({
25001
24888
  children,
25002
- alignment: mapRowFlexToAlignment(pageNumber.rowFlex)
24889
+ run: hasInlineImage(row) ? { size: IMAGE_MARK_HALF_POINTS } : void 0,
24890
+ style: suffix?.style,
24891
+ pageBreakBefore,
24892
+ widowControl: false,
24893
+ tabStops: stops,
24894
+ indent: { left: pxTw(indent), right: RIGHT_SLACK_TWIP },
24895
+ spacing: rowSpacing(ctx, row, options)
25003
24896
  });
25004
24897
  }
25005
- function forceDocxFooterTextColor(elementList) {
25006
- const footerElementList = deepClone(elementList);
25007
- forceColorValue(footerElementList);
25008
- return footerElementList;
25009
- }
25010
- function forceColorValue(value) {
25011
- if (!value || typeof value !== "object") {
25012
- return;
25013
- }
25014
- if (Array.isArray(value)) {
25015
- for (let i = 0; i < value.length; i++) {
25016
- forceColorValue(value[i]);
25017
- }
25018
- return;
25019
- }
25020
- const record = value;
25021
- if ("color" in record) {
25022
- record.color = DOCX_FOOTER_TEXT_COLOR;
25023
- }
25024
- const keys = Object.keys(record);
25025
- for (let i = 0; i < keys.length; i++) {
25026
- forceColorValue(record[keys[i]]);
25027
- }
25028
- }
25029
- function createNumberingConfig() {
25030
- const config = [
25031
- createOrderedReference("windoc-ol-decimal", Array(9).fill("decimal" /* DECIMAL */)),
25032
- createOrderedReference(
25033
- "windoc-ol-lower-alpha",
25034
- Array(9).fill("lowerAlpha" /* LOWER_ALPHA */)
25035
- ),
25036
- createOrderedReference(
25037
- "windoc-ol-upper-alpha",
25038
- Array(9).fill("upperAlpha" /* UPPER_ALPHA */)
25039
- ),
25040
- createOrderedReference(
25041
- "windoc-ol-lower-roman",
25042
- Array(9).fill("lowerRoman" /* LOWER_ROMAN */)
25043
- ),
25044
- createOrderedReference(
25045
- "windoc-ol-upper-roman",
25046
- Array(9).fill("upperRoman" /* UPPER_ROMAN */)
25047
- ),
25048
- createOrderedReference(
25049
- "windoc-ol-decimal-paren",
25050
- Array(9).fill("decimalParen" /* DECIMAL_PAREN */)
25051
- ),
25052
- createOrderedReference(
25053
- `windoc-ol-${"olDefault" /* DEFAULT */}`,
25054
- createCycleList(olPresetCycles["olDefault" /* DEFAULT */], 9)
25055
- ),
25056
- createOrderedReference(
25057
- `windoc-ol-${"olParen" /* PARENTHESIS */}`,
25058
- createCycleList(olPresetCycles["olParen" /* PARENTHESIS */], 9)
25059
- ),
25060
- createOrderedReference(
25061
- `windoc-ol-${"olOutline" /* OUTLINE */}`,
25062
- createCycleList(olPresetCycles["olOutline" /* OUTLINE */], 9)
25063
- ),
25064
- createOrderedReference(
25065
- `windoc-ol-${"olUpperA" /* UPPER_ALPHA */}`,
25066
- createCycleList(olPresetCycles["olUpperA" /* UPPER_ALPHA */], 9)
25067
- ),
25068
- createOrderedReference(
25069
- `windoc-ol-${"olRoman" /* ROMAN */}`,
25070
- createCycleList(olPresetCycles["olRoman" /* ROMAN */], 9)
25071
- ),
25072
- createOrderedReference(
25073
- `windoc-ol-${"olZeroPad" /* ZERO_PAD */}`,
25074
- createCycleList(olPresetCycles["olZeroPad" /* ZERO_PAD */], 9)
25075
- ),
25076
- createBulletReference(
25077
- "windoc-ul-disc",
25078
- Array(9).fill(docxBulletCharMap["disc" /* DISC */])
25079
- ),
25080
- createBulletReference(
25081
- "windoc-ul-circle",
25082
- Array(9).fill(docxBulletCharMap["circle" /* CIRCLE */])
25083
- ),
25084
- createBulletReference(
25085
- "windoc-ul-square",
25086
- Array(9).fill(docxBulletCharMap["square" /* SQUARE */])
25087
- ),
25088
- createBulletReference(
25089
- "windoc-ul-checkbox",
25090
- Array(9).fill(docxBulletCharMap["checkbox" /* CHECKBOX */])
25091
- ),
25092
- createBulletReference(
25093
- `windoc-ul-${"ulDefault" /* DEFAULT */}`,
25094
- createCycleList(ulPresetCycles["ulDefault" /* DEFAULT */], 9)
25095
- ),
25096
- createBulletReference(
25097
- `windoc-ul-${"ulDiamond" /* DIAMOND */}`,
25098
- createCycleList(ulPresetCycles["ulDiamond" /* DIAMOND */], 9)
25099
- ),
25100
- createBulletReference(
25101
- `windoc-ul-${"ulHollowSq" /* HOLLOW_SQ */}`,
25102
- createCycleList(ulPresetCycles["ulHollowSq" /* HOLLOW_SQ */], 9)
25103
- ),
25104
- createBulletReference(
25105
- `windoc-ul-${"ulArrow" /* ARROW */}`,
25106
- createCycleList(ulPresetCycles["ulArrow" /* ARROW */], 9)
25107
- ),
25108
- createBulletReference(
25109
- `windoc-ul-${"ulStar" /* STAR */}`,
25110
- createCycleList(ulPresetCycles["ulStar" /* STAR */], 9)
25111
- ),
25112
- createBulletReference(
25113
- `windoc-ul-${"ulCheckArr" /* CHECK_ARROW */}`,
25114
- createCycleList(ulPresetCycles["ulCheckArr" /* CHECK_ARROW */], 9)
25115
- )
25116
- ];
25117
- return config;
25118
- }
25119
- function createOrderedReference(reference, styles) {
25120
- return {
25121
- reference,
25122
- levels: styles.map((style, level) => ({
25123
- level,
25124
- format: orderedStyleFormatMap[style] || LevelFormat.DECIMAL,
25125
- text: getOrderedLevelText(style, level),
25126
- alignment: AlignmentType.START,
25127
- style: {
25128
- paragraph: {
25129
- indent: {
25130
- left: 720 * (level + 1),
25131
- hanging: 360
25132
- }
25133
- }
25134
- }
25135
- }))
25136
- };
25137
- }
25138
- function createBulletReference(reference, bullets) {
25139
- return {
25140
- reference,
25141
- levels: bullets.map((bullet, level) => ({
25142
- level,
25143
- format: LevelFormat.BULLET,
25144
- text: bullet,
25145
- alignment: AlignmentType.START,
25146
- style: {
25147
- paragraph: {
25148
- indent: {
25149
- left: 720 * (level + 1),
25150
- hanging: 360
25151
- }
25152
- }
25153
- }
25154
- }))
25155
- };
25156
- }
25157
- function getOrderedLevelText(style, level) {
25158
- const placeholder = `%${level + 1}`;
25159
- if (style === "outline") {
25160
- return Array.from({ length: level + 1 }, (_, index) => `%${index + 1}`).join(
25161
- "."
25162
- ) + ".";
25163
- }
25164
- if (style.endsWith("Paren") || style === "decimalParen" /* DECIMAL_PAREN */) {
25165
- return `${placeholder})`;
25166
- }
25167
- return `${placeholder}.`;
25168
- }
25169
- function createCycleList(source = [], size) {
25170
- const result = [];
25171
- for (let i = 0; i < size; i++) {
25172
- result.push(source[i % source.length]);
25173
- }
25174
- return result;
25175
- }
25176
- function getListNumbering(element, instance) {
25177
- const level = Math.max(0, Math.min(element.listLevel || 0, 8));
25178
- if (element.listType === "ul" /* UL */) {
25179
- const reference2 = element.listPreset ? `windoc-ul-${element.listPreset}` : `windoc-ul-${element.listStyle || "disc" /* DISC */}`;
24898
+ var EXACT_BASELINE_RATIO = 0.8;
24899
+ var IMAGE_MARK_HALF_POINTS = 2;
24900
+ var IMAGE_MARK_DESCENT_PX = 0.3;
24901
+ function hasInlineImage(row) {
24902
+ return row.elementList.some((el) => el.type === "image" /* IMAGE */);
24903
+ }
24904
+ function rowLine(ctx, row, baseline = row.ascent / ctx.scale) {
24905
+ const { scale } = ctx;
24906
+ const height = row.height / scale;
24907
+ if (hasInlineImage(row)) {
24908
+ const lineTw2 = pxTw(Math.min(baseline, height) + IMAGE_MARK_DESCENT_PX);
25180
24909
  return {
25181
- reference: reference2,
25182
- level,
25183
- instance
24910
+ lineTw: lineTw2,
24911
+ beforeTw: 0,
24912
+ afterTw: Math.max(0, pxTw(height) - lineTw2),
24913
+ atLeast: true
25184
24914
  };
25185
24915
  }
25186
- const reference = element.listPreset ? `windoc-ol-${element.listPreset}` : getOrderedReferenceFromStyle(element.listStyle);
25187
- return {
25188
- reference,
25189
- level,
25190
- instance
25191
- };
25192
- }
25193
- function getOrderedReferenceFromStyle(style) {
25194
- switch (style) {
25195
- case "lowerAlpha" /* LOWER_ALPHA */:
25196
- return "windoc-ol-lower-alpha";
25197
- case "upperAlpha" /* UPPER_ALPHA */:
25198
- return "windoc-ol-upper-alpha";
25199
- case "lowerRoman" /* LOWER_ROMAN */:
25200
- return "windoc-ol-lower-roman";
25201
- case "upperRoman" /* UPPER_ROMAN */:
25202
- return "windoc-ol-upper-roman";
25203
- case "decimalParen" /* DECIMAL_PAREN */:
25204
- return "windoc-ol-decimal-paren";
25205
- default:
25206
- return "windoc-ol-decimal";
25207
- }
25208
- }
25209
- function createTableBorders(element) {
25210
- const outerBorder = createBorder(
25211
- element.borderColor,
25212
- BorderStyle.SINGLE,
25213
- element.borderExternalWidth || element.borderWidth
24916
+ const ascent = Math.min(baseline, height);
24917
+ const line = Math.min(
24918
+ ascent / EXACT_BASELINE_RATIO,
24919
+ (height - ascent) / (1 - EXACT_BASELINE_RATIO)
25214
24920
  );
25215
- const innerBorder = createBorder(
25216
- element.borderColor,
25217
- BorderStyle.SINGLE,
25218
- element.borderWidth
24921
+ const lineTw = Math.max(TINY_TWIP, pxTw(line));
24922
+ const beforeTw = Math.max(
24923
+ 0,
24924
+ pxTw(ascent) - Math.round(lineTw * EXACT_BASELINE_RATIO)
25219
24925
  );
25220
- switch (element.borderType) {
25221
- case "empty" /* EMPTY */:
25222
- return {
25223
- top: noneBorder(),
25224
- bottom: noneBorder(),
25225
- left: noneBorder(),
25226
- right: noneBorder(),
25227
- insideHorizontal: noneBorder(),
25228
- insideVertical: noneBorder()
25229
- };
25230
- case "external" /* EXTERNAL */:
25231
- return {
25232
- top: outerBorder,
25233
- bottom: outerBorder,
25234
- left: outerBorder,
25235
- right: outerBorder,
25236
- insideHorizontal: noneBorder(),
25237
- insideVertical: noneBorder()
25238
- };
25239
- case "internal" /* INTERNAL */:
25240
- return {
25241
- top: noneBorder(),
25242
- bottom: noneBorder(),
25243
- left: noneBorder(),
25244
- right: noneBorder(),
25245
- insideHorizontal: innerBorder,
25246
- insideVertical: innerBorder
25247
- };
25248
- case "dash" /* DASH */:
25249
- return {
25250
- top: createBorder(
25251
- element.borderColor,
25252
- BorderStyle.DASHED,
25253
- element.borderExternalWidth || element.borderWidth
25254
- ),
25255
- bottom: createBorder(
25256
- element.borderColor,
25257
- BorderStyle.DASHED,
25258
- element.borderExternalWidth || element.borderWidth
25259
- ),
25260
- left: createBorder(
25261
- element.borderColor,
25262
- BorderStyle.DASHED,
25263
- element.borderExternalWidth || element.borderWidth
25264
- ),
25265
- right: createBorder(
25266
- element.borderColor,
25267
- BorderStyle.DASHED,
25268
- element.borderExternalWidth || element.borderWidth
25269
- ),
25270
- insideHorizontal: createBorder(
25271
- element.borderColor,
25272
- BorderStyle.DASHED,
25273
- element.borderWidth
25274
- ),
25275
- insideVertical: createBorder(
25276
- element.borderColor,
25277
- BorderStyle.DASHED,
25278
- element.borderWidth
25279
- )
25280
- };
25281
- default:
25282
- return {
25283
- top: outerBorder,
25284
- bottom: outerBorder,
25285
- left: outerBorder,
25286
- right: outerBorder,
25287
- insideHorizontal: innerBorder,
25288
- insideVertical: innerBorder
25289
- };
25290
- }
25291
- }
25292
- function createTableCellBorders(td, inheritedColor, inheritedWidth) {
25293
- if (!td.borderTypes?.length && !td.borderColor) {
25294
- return void 0;
25295
- }
25296
- const color = td.borderColor || inheritedColor;
25297
- const createCellBorder = () => createBorder(color, BorderStyle.SINGLE, inheritedWidth);
25298
- if (td.borderColor && !td.borderTypes?.length) {
25299
- return {
25300
- top: createCellBorder(),
25301
- right: createCellBorder(),
25302
- bottom: createCellBorder(),
25303
- left: createCellBorder()
25304
- };
25305
- }
25306
- const top = td.borderTypes?.includes("top" /* TOP */) ? createCellBorder() : noneBorder();
25307
- const right2 = td.borderTypes?.includes("right" /* RIGHT */) ? createCellBorder() : noneBorder();
25308
- const bottom = td.borderTypes?.includes("bottom" /* BOTTOM */) ? createCellBorder() : noneBorder();
25309
- const left2 = td.borderTypes?.includes("left" /* LEFT */) ? createCellBorder() : noneBorder();
25310
- return {
25311
- top,
25312
- right: right2,
25313
- bottom,
25314
- left: left2
25315
- };
25316
- }
25317
- function createBorder(color = "#000000", style = BorderStyle.SINGLE, width = 1) {
25318
- return {
25319
- style,
25320
- color: normalizeHexColor(color, "000000") || "000000",
25321
- size: pxBorderToDocxSize(width)
25322
- };
25323
- }
25324
- function noneBorder() {
25325
- return {
25326
- style: BorderStyle.NONE,
25327
- size: 0,
25328
- color: "FFFFFF"
25329
- };
25330
- }
25331
- function mapPaddingToTableMargins(padding) {
25332
- if (!padding) return void 0;
25333
- return {
25334
- top: pxToTwips(padding[0]),
25335
- right: pxToTwips(padding[1]),
25336
- bottom: pxToTwips(padding[2]),
25337
- left: pxToTwips(padding[3]),
25338
- marginUnitType: WidthType.DXA
25339
- };
25340
- }
25341
- function mapCellVerticalAlign(verticalAlign) {
25342
- switch (verticalAlign) {
25343
- case VerticalAlign.CENTER:
25344
- return DocxVerticalAlign.CENTER;
25345
- case "bottom" /* BOTTOM */:
25346
- return DocxVerticalAlign.BOTTOM;
25347
- default:
25348
- return DocxVerticalAlign.TOP;
25349
- }
25350
- }
25351
- function mapRowFlexToAlignment(rowFlex) {
25352
- switch (rowFlex) {
25353
- case "center" /* CENTER */:
25354
- return AlignmentType.CENTER;
25355
- case "right" /* RIGHT */:
25356
- return AlignmentType.RIGHT;
25357
- case "alignment" /* ALIGNMENT */:
25358
- case "justify" /* JUSTIFY */:
25359
- return AlignmentType.JUSTIFIED;
25360
- default:
25361
- return AlignmentType.LEFT;
25362
- }
25363
- }
25364
- function mapUnderlineType(style) {
25365
- switch (style) {
25366
- case "double" /* DOUBLE */:
25367
- return UnderlineType.DOUBLE;
25368
- case "dashed" /* DASHED */:
25369
- return UnderlineType.DASH;
25370
- case "dotted" /* DOTTED */:
25371
- return UnderlineType.DOTTED;
25372
- case "wavy" /* WAVY */:
25373
- return UnderlineType.WAVE;
25374
- default:
25375
- return UnderlineType.SINGLE;
25376
- }
25377
- }
25378
- function getInlineElementText(element) {
25379
- switch (element.type) {
25380
- case "control" /* CONTROL */:
25381
- return getControlText(element.control);
25382
- case "date" /* DATE */:
25383
- return element.valueList?.length ? getTextFromElementList(element.valueList) : element.value;
25384
- case "checkbox" /* CHECKBOX */:
25385
- return element.checkbox?.value ? "\u2611" : "\u2610";
25386
- case "radio" /* RADIO */:
25387
- return element.radio?.value ? "\u25C9" : "\u25CB";
25388
- case "label" /* LABEL */:
25389
- case "latex" /* LATEX */:
25390
- case "subscript" /* SUBSCRIPT */:
25391
- case "superscript" /* SUPERSCRIPT */:
25392
- return element.value;
25393
- default:
25394
- return element.value;
25395
- }
25396
- }
25397
- function getControlText(control) {
25398
- if (!control) return "";
25399
- if (control.type === "text" /* TEXT */ || control.type === "date" /* DATE */ || control.type === "number" /* NUMBER */) {
25400
- const value = control.value?.length ? getTextFromElementList(control.value) : "";
25401
- const content = value || control.placeholder || "";
25402
- return `${control.preText || ""}${content}${control.postText || ""}`;
25403
- }
25404
- if (control.type === "select" /* SELECT */) {
25405
- const labels = getSelectedControlLabels(control.code, control.valueSets);
25406
- const content = labels.length ? labels.join(control.multiSelectDelimiter || ", ") : control.placeholder || "";
25407
- return `${control.preText || ""}${content}${control.postText || ""}`;
25408
- }
25409
- if (control.type === "checkbox" /* CHECKBOX */ || control.type === "radio" /* RADIO */) {
25410
- const isRadio = control.type === "radio" /* RADIO */;
25411
- return (control.valueSets || []).map((valueSet) => {
25412
- const checked = control.code?.split(",").includes(valueSet.code) || false;
25413
- return `${checked ? isRadio ? "\u25C9" : "\u2611" : isRadio ? "\u25CB" : "\u2610"} ${valueSet.value}`;
25414
- }).join(" ");
25415
- }
25416
- return "";
25417
- }
25418
- function getSelectedControlLabels(code, valueSets) {
25419
- if (!code || !valueSets?.length) return [];
25420
- return code.split(",").map(
25421
- (selectCode) => valueSets.find((valueSet) => valueSet.code === selectCode)?.value
25422
- ).filter((value) => !!value);
25423
- }
25424
- function getBlockFallbackText(element) {
25425
- if (element.block?.iframeBlock?.src) {
25426
- return element.block.iframeBlock.src;
25427
- }
25428
- if (element.block?.videoBlock?.src) {
25429
- return element.block.videoBlock.src;
25430
- }
25431
- return "";
25432
- }
25433
- function normalizeText(text = "") {
25434
- return text.replace(new RegExp(ZERO, "g"), "").replace(/\r\n/g, "\n");
24926
+ const afterTw = Math.max(0, pxTw(height) - lineTw - beforeTw);
24927
+ return { lineTw, beforeTw, afterTw, atLeast: false };
25435
24928
  }
25436
- function createSerializeContext() {
24929
+ function rowLead(ctx, row, options) {
24930
+ const separator = row.elementList.find(
24931
+ (el) => el.type === "separator" /* SEPARATOR */
24932
+ );
24933
+ const baseline = separator && options.rowY !== void 0 ? separatorBaseline(
24934
+ ctx,
24935
+ row,
24936
+ options.rowY,
24937
+ separator.lineWidth || ctx.draw.getOptions().separator.lineWidth
24938
+ ) : void 0;
24939
+ return rowLine(ctx, row, baseline).beforeTw + tw(ctx, (row.offsetY || 0) + (row.spaceAbove || 0)) + pxTw(options.padTop || 0);
24940
+ }
24941
+ function rowSpacing(ctx, row, options) {
24942
+ const { lineTw, afterTw, atLeast } = rowLine(ctx, row);
25437
24943
  return {
25438
- nextListInstance: 1
24944
+ before: options.skipLead ? 0 : rowLead(ctx, row, options),
24945
+ after: afterTw + tw(ctx, row.spaceBelow || 0) + pxTw(options.padBottom || 0) + (options.extraAfter || 0),
24946
+ line: lineTw,
24947
+ lineRule: atLeast ? LineRuleType.AT_LEAST : LineRuleType.EXACT
25439
24948
  };
25440
24949
  }
25441
- function getListItemNumberingElement(listItemElements, fallback) {
25442
- return listItemElements.find(
25443
- (listItem) => listItem.listId || listItem.listLevel !== void 0 || !!listItem.listPreset || !!listItem.listStyle || !!listItem.listType
25444
- ) || fallback;
25445
- }
25446
- function createListInstance(context) {
25447
- return context.nextListInstance++;
24950
+ function createSeparatorParagraph(ctx, row, element, x, rowY2, zone, options) {
24951
+ const { scale } = ctx;
24952
+ const opts = ctx.draw.getOptions();
24953
+ const lineWidth = element.lineWidth || opts.separator.lineWidth;
24954
+ const sideGap = 4;
24955
+ const left2 = (x - zone.startX) / scale + sideGap;
24956
+ const width = Math.max(1, (element.width || 0) - sideGap * 2);
24957
+ const color = element.color || opts.separator.strokeStyle;
24958
+ const line = rowLine(ctx, row, separatorBaseline(ctx, row, rowY2, lineWidth));
24959
+ return new Paragraph({
24960
+ pageBreakBefore: options.pageBreakBefore,
24961
+ widowControl: false,
24962
+ indent: { left: pxTw(left2), right: RIGHT_SLACK_TWIP },
24963
+ children: [
24964
+ new ImageRun({
24965
+ type: "png",
24966
+ data: createLineImage(width, lineWidth, color, element.dashArray),
24967
+ transformation: { width, height: lineWidth }
24968
+ })
24969
+ ],
24970
+ spacing: {
24971
+ before: options.skipLead ? 0 : rowLead(ctx, row, options),
24972
+ after: line.afterTw + tw(ctx, row.spaceBelow || 0) + pxTw(options.padBottom || 0) + (options.extraAfter || 0),
24973
+ line: line.lineTw,
24974
+ lineRule: LineRuleType.EXACT
24975
+ }
24976
+ });
25448
24977
  }
25449
- function createTableSerializeContext(element) {
25450
- return {
25451
- cellLayoutMap: computeTableCellLayout(
25452
- element.trList || [],
25453
- element.colgroup || []
25454
- ),
25455
- borderColor: element.borderColor,
25456
- borderWidth: element.borderWidth,
25457
- borderExternalWidth: element.borderExternalWidth
25458
- };
24978
+ function separatorBaseline(ctx, row, rowY2, lineWidth) {
24979
+ return (Math.round(rowY2 + row.ascent) - rowY2) / ctx.scale + lineWidth;
25459
24980
  }
25460
- function computeTableCellLayout(trList, colgroup) {
25461
- const cellLayoutMap = /* @__PURE__ */ new WeakMap();
25462
- if (!trList.length || !colgroup.length) {
25463
- return cellLayoutMap;
24981
+ function createLineImage(width, height, color, dashArray) {
24982
+ const ratio = 4;
24983
+ const canvas = document.createElement("canvas");
24984
+ canvas.width = Math.max(1, Math.round(width * ratio));
24985
+ canvas.height = Math.max(1, Math.round(height * ratio));
24986
+ const ctx = canvas.getContext("2d");
24987
+ ctx.strokeStyle = color;
24988
+ ctx.lineWidth = canvas.height;
24989
+ if (dashArray?.length) ctx.setLineDash(dashArray.map((v) => v * ratio));
24990
+ ctx.beginPath();
24991
+ ctx.moveTo(0, canvas.height / 2);
24992
+ ctx.lineTo(canvas.width, canvas.height / 2);
24993
+ ctx.stroke();
24994
+ const binary = atob(canvas.toDataURL("image/png").split(",")[1]);
24995
+ const bytes = new Uint8Array(binary.length);
24996
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
24997
+ return bytes;
24998
+ }
24999
+ function endWithParagraph(children) {
25000
+ if (children[children.length - 1] instanceof Table) {
25001
+ children.push(createSpacerParagraph(false));
25464
25002
  }
25465
- const occupiedUntilRow = Array(colgroup.length).fill(0);
25466
- for (let rowIndex = 0; rowIndex < trList.length; rowIndex++) {
25467
- const tr = trList[rowIndex];
25468
- let colIndex = 0;
25469
- for (let tdIndex = 0; tdIndex < tr.tdList.length; tdIndex++) {
25470
- while (colIndex < colgroup.length && occupiedUntilRow[colIndex] > rowIndex) {
25471
- colIndex += 1;
25472
- }
25473
- const td = tr.tdList[tdIndex];
25474
- let width = 0;
25475
- for (let currentCol = colIndex; currentCol < colIndex + td.colspan && currentCol < colgroup.length; currentCol++) {
25476
- width += colgroup[currentCol].width;
25477
- occupiedUntilRow[currentCol] = Math.max(
25478
- occupiedUntilRow[currentCol],
25479
- rowIndex + td.rowspan
25480
- );
25481
- }
25482
- cellLayoutMap.set(td, { width });
25483
- colIndex += td.colspan;
25003
+ return children;
25004
+ }
25005
+ function createSpacerParagraph(pageBreakBefore, height = 0) {
25006
+ return new Paragraph({
25007
+ pageBreakBefore,
25008
+ widowControl: false,
25009
+ children: [new TextRun({ text: "", size: 2 })],
25010
+ spacing: {
25011
+ before: 0,
25012
+ after: 0,
25013
+ line: Math.max(TINY_TWIP, pxTw(height)),
25014
+ lineRule: LineRuleType.EXACT
25484
25015
  }
25485
- }
25486
- return cellLayoutMap;
25016
+ });
25487
25017
  }
25488
- function getTableWidthFromColgroup(colgroup) {
25489
- if (!colgroup?.length) {
25490
- return void 0;
25018
+ async function serializeTable(ctx, element, position, zone) {
25019
+ const { draw, scale } = ctx;
25020
+ const opts = draw.getOptions();
25021
+ const tableX = position.coordinate.leftTop[0];
25022
+ const trList = element.trList || [];
25023
+ const colgroup = element.colgroup || [];
25024
+ const rows = [];
25025
+ for (let t = 0; t < trList.length; t++) {
25026
+ const tr = trList[t];
25027
+ const cells = [];
25028
+ let rowTopBorder = 0;
25029
+ for (let d = 0; d < tr.tdList.length; d++) {
25030
+ const td = tr.tdList[d];
25031
+ const padding = td.padding || opts.table.tdPadding;
25032
+ const borders = createCellBorders(ctx, element, td);
25033
+ const topBorder = t === 0 ? borderPx(borders.top) : 0;
25034
+ rowTopBorder = Math.max(rowTopBorder, topBorder);
25035
+ const cellChildren = await serializeRows(
25036
+ ctx,
25037
+ td.rowList || [],
25038
+ {
25039
+ startX: (td.x + padding[3]) * scale + tableX,
25040
+ width: (td.width - padding[1] - padding[3]) * scale,
25041
+ positionList: td.positionList || []
25042
+ },
25043
+ { padTop: Math.max(0, padding[0] - topBorder), padBottom: padding[2] }
25044
+ );
25045
+ cells.push(
25046
+ new TableCell({
25047
+ children: cellChildren.length ? endWithParagraph(cellChildren) : [createSpacerParagraph(false)],
25048
+ columnSpan: td.colspan > 1 ? td.colspan : void 0,
25049
+ rowSpan: td.rowspan > 1 ? td.rowspan : void 0,
25050
+ width: { size: pxTw(td.width), type: WidthType.DXA },
25051
+ verticalAlign: mapVerticalAlign(td.verticalAlign),
25052
+ margins: {
25053
+ top: 0,
25054
+ right: pxTw(padding[1]),
25055
+ bottom: 0,
25056
+ left: pxTw(Math.max(0, padding[3] - borderPx(borders.left) / 2)),
25057
+ marginUnitType: WidthType.DXA
25058
+ },
25059
+ shading: td.backgroundColor ? {
25060
+ fill: normalizeHexColor(td.backgroundColor, "FFFFFF"),
25061
+ type: ShadingType.CLEAR,
25062
+ color: "auto"
25063
+ } : void 0,
25064
+ borders
25065
+ })
25066
+ );
25067
+ }
25068
+ rows.push(
25069
+ new TableRow({
25070
+ children: cells,
25071
+ cantSplit: true,
25072
+ height: {
25073
+ value: pxTw(tr.height - rowTopBorder),
25074
+ rule: HeightRule.EXACT
25075
+ }
25076
+ })
25077
+ );
25491
25078
  }
25492
- return colgroup.reduce((sum, col) => sum + col.width, 0);
25079
+ return new Table({
25080
+ rows,
25081
+ layout: TableLayoutType.FIXED,
25082
+ width: { size: pxTw(element.width), type: WidthType.DXA },
25083
+ columnWidths: colgroup.map((col) => pxTw(col.width)),
25084
+ indent: { size: tw(ctx, tableX - zone.startX), type: WidthType.DXA },
25085
+ borders: noTableBorders()
25086
+ });
25493
25087
  }
25494
- function normalizeHexColor(color, fallback) {
25495
- const normalized = normalizeHexLiteral(color) || normalizeRgbColor(color) || normalizeBrowserColor(color);
25496
- if (normalized) {
25497
- return normalized;
25498
- }
25499
- return fallback ? normalizeHexLiteral(fallback) || void 0 : void 0;
25088
+ function createCellBorders(ctx, table, td) {
25089
+ const opts = ctx.draw.getOptions();
25090
+ const color = normalizeHexColor(
25091
+ td.borderColor || table.borderColor || opts.table.defaultBorderColor,
25092
+ "000000"
25093
+ );
25094
+ const width = table.borderWidth || 1;
25095
+ const externalWidth = table.borderExternalWidth || width;
25096
+ const isDash = table.borderType === "dash" /* DASH */;
25097
+ const line = (w) => ({
25098
+ style: isDash ? BorderStyle.DASHED : BorderStyle.SINGLE,
25099
+ size: Math.max(2, Math.round(w * 6)),
25100
+ color
25101
+ });
25102
+ const none = { style: BorderStyle.NONE, size: 0, color: "auto" };
25103
+ const isTop = td.rowIndex === 0;
25104
+ const isLeft = td.colIndex === 0;
25105
+ const isBottom = td.rowIndex + td.rowspan === table.trList.length;
25106
+ const isRight = td.colIndex + td.colspan === table.colgroup.length;
25107
+ const edge = (outer) => outer ? line(externalWidth) : line(width);
25108
+ let top = none;
25109
+ let right2 = none;
25110
+ let bottom = none;
25111
+ let left2 = none;
25112
+ switch (table.borderType) {
25113
+ case "empty" /* EMPTY */:
25114
+ break;
25115
+ case "external" /* EXTERNAL */:
25116
+ if (isTop) top = edge(true);
25117
+ if (isBottom) bottom = edge(true);
25118
+ if (isLeft) left2 = edge(true);
25119
+ if (isRight) right2 = edge(true);
25120
+ break;
25121
+ case "internal" /* INTERNAL */:
25122
+ if (!isTop) top = edge(false);
25123
+ if (!isBottom) bottom = edge(false);
25124
+ if (!isLeft) left2 = edge(false);
25125
+ if (!isRight) right2 = edge(false);
25126
+ break;
25127
+ default:
25128
+ top = edge(isTop);
25129
+ bottom = edge(isBottom);
25130
+ left2 = edge(isLeft);
25131
+ right2 = edge(isRight);
25132
+ }
25133
+ if (td.borderTypes?.includes("top" /* TOP */)) top = line(width);
25134
+ if (td.borderTypes?.includes("right" /* RIGHT */)) right2 = line(width);
25135
+ if (td.borderTypes?.includes("bottom" /* BOTTOM */)) bottom = line(width);
25136
+ if (td.borderTypes?.includes("left" /* LEFT */)) left2 = line(width);
25137
+ return { top, right: right2, bottom, left: left2 };
25138
+ }
25139
+ function borderPx(border) {
25140
+ return border.style === BorderStyle.NONE ? 0 : border.size / 8 * (4 / 3);
25141
+ }
25142
+ function noTableBorders() {
25143
+ const none = { style: BorderStyle.NONE, size: 0, color: "auto" };
25144
+ return {
25145
+ top: none,
25146
+ bottom: none,
25147
+ left: none,
25148
+ right: none,
25149
+ insideHorizontal: none,
25150
+ insideVertical: none
25151
+ };
25500
25152
  }
25501
- function normalizeHexLiteral(color) {
25502
- const value = color.trim().replace(/^#/, "");
25503
- if (/^[\da-f]{3}$/i.test(value)) {
25504
- return value.split("").map((part) => `${part}${part}`).join("").toUpperCase();
25505
- }
25506
- if (/^[\da-f]{4}$/i.test(value)) {
25507
- return value.slice(0, 3).split("").map((part) => `${part}${part}`).join("").toUpperCase();
25153
+ function mapVerticalAlign(verticalAlign) {
25154
+ switch (verticalAlign) {
25155
+ case "middle" /* MIDDLE */:
25156
+ return DocxVerticalAlign.CENTER;
25157
+ case "bottom" /* BOTTOM */:
25158
+ return DocxVerticalAlign.BOTTOM;
25159
+ default:
25160
+ return DocxVerticalAlign.TOP;
25508
25161
  }
25509
- if (/^[\da-f]{6}$/i.test(value)) {
25510
- return value.toUpperCase();
25162
+ }
25163
+ async function serializeFooter(ctx, pageWidth, pageHeight, margins) {
25164
+ const { draw, scale } = ctx;
25165
+ const footer = draw.getFooter();
25166
+ const opts = draw.getOptions();
25167
+ const barHeight = footer.getHeight() / scale;
25168
+ const contentHeight = footer.getRowHeight() / scale;
25169
+ const paddingTop = Math.floor(Math.max(0, (barHeight - contentHeight) / 2) * scale) / scale;
25170
+ const zone = {
25171
+ startX: draw.getMargins()[3],
25172
+ width: draw.getInnerWidth(),
25173
+ positionList: footer.getPositionList()
25174
+ };
25175
+ const rowList = footer.getRowList();
25176
+ const textWidth = pageWidth - margins[1] - margins[3];
25177
+ const pageNumber = createPageNumberRuns(ctx, pageHeight, textWidth);
25178
+ let pageNumberRow = -1;
25179
+ if (pageNumber) {
25180
+ pageNumberRow = rowList.length - 1;
25181
+ for (let r = 0; r < rowList.length; r++) {
25182
+ const top = zone.positionList[rowList[r].startIndex].coordinate.leftTop[1] / scale;
25183
+ if (pageNumber.baseline <= top + rowList[r].height / scale) {
25184
+ pageNumberRow = r;
25185
+ break;
25186
+ }
25187
+ }
25511
25188
  }
25512
- if (/^[\da-f]{8}$/i.test(value)) {
25513
- return value.slice(0, 6).toUpperCase();
25189
+ const children = [];
25190
+ for (let r = 0; r < rowList.length; r++) {
25191
+ const row = rowList[r];
25192
+ const rowOptions = { padTop: r === 0 ? paddingTop : 0 };
25193
+ if (r !== pageNumberRow) {
25194
+ children.push(...await serializeRows(ctx, [row], zone, rowOptions));
25195
+ continue;
25196
+ }
25197
+ const top = zone.positionList[row.startIndex].coordinate.leftTop[1] / scale;
25198
+ const shift = pageNumber.baseline - (top + row.ascent / scale);
25199
+ children.push(
25200
+ await serializeRow(ctx, row, zone, rowOptions, {
25201
+ tabStop: pageNumber.tabStop,
25202
+ tabType: pageNumber.tabType,
25203
+ children: pageNumber.runs(shift),
25204
+ style: PAGE_NUMBER_STYLE
25205
+ })
25206
+ );
25514
25207
  }
25515
- return void 0;
25208
+ const fill = opts.footer.backgroundColor ? normalizeHexColor(opts.footer.backgroundColor, "FFFFFF") : void 0;
25209
+ const none = { style: BorderStyle.NONE, size: 0, color: "auto" };
25210
+ const band = new Table({
25211
+ layout: TableLayoutType.FIXED,
25212
+ width: { size: pxTw(pageWidth), type: WidthType.DXA },
25213
+ columnWidths: [pxTw(pageWidth)],
25214
+ indent: { size: -pxTw(margins[3]), type: WidthType.DXA },
25215
+ borders: noTableBorders(),
25216
+ rows: [
25217
+ new TableRow({
25218
+ cantSplit: true,
25219
+ height: { value: pxTw(barHeight), rule: HeightRule.EXACT },
25220
+ children: [
25221
+ new TableCell({
25222
+ width: { size: pxTw(pageWidth), type: WidthType.DXA },
25223
+ margins: {
25224
+ top: 0,
25225
+ bottom: 0,
25226
+ left: pxTw(margins[3]),
25227
+ right: pxTw(margins[1]),
25228
+ marginUnitType: WidthType.DXA
25229
+ },
25230
+ shading: fill ? { fill, type: ShadingType.CLEAR, color: "auto" } : void 0,
25231
+ borders: { top: none, right: none, bottom: none, left: none },
25232
+ children: children.length ? children : [createSpacerParagraph(false)]
25233
+ })
25234
+ ]
25235
+ })
25236
+ ]
25237
+ });
25238
+ return [band];
25516
25239
  }
25517
- function normalizeRgbColor(color) {
25518
- const match = color.trim().match(/^rgba?\((.+)\)$/i);
25519
- if (!match) {
25520
- return void 0;
25240
+ var PAGE_NUMBER_STYLE = "WindocPageNumber";
25241
+ var FieldCharAttributes = class extends XmlAttributeComponent {
25242
+ constructor() {
25243
+ super(...arguments);
25244
+ this.xmlKeys = { type: "w:fldCharType" };
25521
25245
  }
25522
- const parts = match[1].replace(/\s*\/\s*/g, ",").split(/\s*,\s*|\s+/).filter(Boolean);
25523
- if (parts.length < 3) {
25524
- return void 0;
25246
+ };
25247
+ var FieldChar = class extends XmlComponent {
25248
+ constructor(type) {
25249
+ super("w:fldChar");
25250
+ this.root.push(new FieldCharAttributes({ type }));
25525
25251
  }
25526
- const red = parseRgbChannel(parts[0]);
25527
- const green = parseRgbChannel(parts[1]);
25528
- const blue = parseRgbChannel(parts[2]);
25529
- if (red === null || green === null || blue === null) {
25530
- return void 0;
25252
+ };
25253
+ var InstrTextAttributes = class extends XmlAttributeComponent {
25254
+ constructor() {
25255
+ super(...arguments);
25256
+ this.xmlKeys = { space: "xml:space" };
25531
25257
  }
25532
- const alpha = parts[3] !== void 0 ? parseAlphaChannel(parts[3]) : 1;
25533
- if (alpha === null || alpha <= 0) {
25534
- return void 0;
25258
+ };
25259
+ var InstrText = class extends XmlComponent {
25260
+ constructor(instruction) {
25261
+ super("w:instrText");
25262
+ this.root.push(new InstrTextAttributes({ space: "preserve" }));
25263
+ this.root.push(` ${instruction} `);
25535
25264
  }
25536
- const flattened = alpha >= 1 ? [red, green, blue] : [
25537
- flattenChannel(red, alpha),
25538
- flattenChannel(green, alpha),
25539
- flattenChannel(blue, alpha)
25265
+ };
25266
+ function createFieldRuns(instruction, cached, style) {
25267
+ return [
25268
+ new TextRun({ ...style, children: [new FieldChar("begin")] }),
25269
+ new TextRun({ ...style, children: [new InstrText(instruction)] }),
25270
+ new TextRun({ ...style, children: [new FieldChar("separate")] }),
25271
+ new TextRun({ ...style, text: cached }),
25272
+ new TextRun({ ...style, children: [new FieldChar("end")] })
25540
25273
  ];
25541
- return flattened.map(toHexChannel).join("");
25542
25274
  }
25543
- function normalizeBrowserColor(color) {
25544
- if (typeof document === "undefined") {
25545
- return void 0;
25546
- }
25547
- const canvas = document.createElement("canvas");
25548
- const context = canvas.getContext("2d");
25549
- if (!context) {
25550
- return void 0;
25551
- }
25552
- context.fillStyle = "#000000";
25553
- context.fillStyle = color;
25554
- const normalized = context.fillStyle;
25555
- if (typeof normalized !== "string") {
25556
- return void 0;
25557
- }
25558
- return normalizeHexLiteral(normalized) || normalizeRgbColor(normalized);
25275
+ function pageNumberRunStyle(pageNumber) {
25276
+ return {
25277
+ font: mapFont(pageNumber?.font || "sans-serif"),
25278
+ // PageNumber draws `${size}px`; half-points = px * 0.75 * 2
25279
+ size: Math.round((pageNumber?.size || 12) * 1.5),
25280
+ color: normalizeHexColor(pageNumber?.color || "#000000", "000000")
25281
+ };
25559
25282
  }
25560
- function parseRgbChannel(value) {
25561
- if (value.endsWith("%")) {
25562
- const percent = Number(value.slice(0, -1));
25563
- if (!Number.isFinite(percent)) {
25564
- return null;
25565
- }
25566
- return clampColorChannel(Math.round(percent / 100 * 255));
25567
- }
25568
- const channel = Number(value);
25569
- if (!Number.isFinite(channel)) {
25570
- return null;
25283
+ function createPageNumberRuns(ctx, pageHeight, textWidth) {
25284
+ const { pageNumber } = ctx.draw.getOptions();
25285
+ if (pageNumber.disabled || pageNumber.fromPageNo > 0) return null;
25286
+ const pageCount = ctx.draw.getPageCount();
25287
+ const tokens = (pageNumber.format || FORMAT_PLACEHOLDER.PAGE_NO).split(
25288
+ /(\{pageNo\}|\{pageCount\})/
25289
+ );
25290
+ const tabType = pageNumber.rowFlex === "center" /* CENTER */ ? TabStopType.CENTER : pageNumber.rowFlex === "right" /* RIGHT */ ? TabStopType.RIGHT : TabStopType.LEFT;
25291
+ const tabStop = tabType === TabStopType.CENTER ? pxTw(textWidth / 2) : tabType === TabStopType.RIGHT ? pxTw(textWidth) : 0;
25292
+ const position = (shift) => {
25293
+ const halfPoints = Math.round(-shift * 0.75 * 2);
25294
+ return halfPoints ? `${halfPoints}` : void 0;
25295
+ };
25296
+ return {
25297
+ baseline: pageHeight - pageNumber.bottom,
25298
+ tabType,
25299
+ tabStop,
25300
+ // shift > 0 means the number sits lower than the row baseline
25301
+ runs: (shift) => tokens.filter(Boolean).map((token) => {
25302
+ const style = {
25303
+ ...pageNumberRunStyle(pageNumber),
25304
+ position: position(shift)
25305
+ };
25306
+ const field = token === FORMAT_PLACEHOLDER.PAGE_NO ? { instruction: "PAGE", cached: "1" } : token === FORMAT_PLACEHOLDER.PAGE_COUNT ? { instruction: "NUMPAGES", cached: `${pageCount}` } : null;
25307
+ if (!field) return [new TextRun({ text: token, ...style })];
25308
+ return createFieldRuns(field.instruction, field.cached, style);
25309
+ }).flat()
25310
+ };
25311
+ }
25312
+ function createTextRun(ctx, text, element, shading, characterSpacing) {
25313
+ const opts = ctx.draw.getOptions();
25314
+ const isLink = element.type === "hyperlink" /* HYPERLINK */;
25315
+ const color = normalizeHexColor(
25316
+ element.color || (isLink ? opts.defaultHyperlinkColor : opts.defaultColor),
25317
+ "000000"
25318
+ );
25319
+ const highlight = element.highlight ? normalizeHexColor(element.highlight) : void 0;
25320
+ const textRun = new TextRun({
25321
+ text,
25322
+ font: mapFont(element.font || opts.defaultFont),
25323
+ size: Math.round(getSize(ctx, element) * 2),
25324
+ bold: !!element.bold,
25325
+ italics: !!element.italic,
25326
+ strike: !!element.strikeout,
25327
+ superScript: element.type === "superscript" /* SUPERSCRIPT */,
25328
+ subScript: element.type === "subscript" /* SUBSCRIPT */,
25329
+ underline: element.underline || isLink ? { type: mapUnderlineType(element.textDecoration?.style) } : void 0,
25330
+ color,
25331
+ shading: shading || (highlight ? { fill: highlight, type: ShadingType.CLEAR, color: "auto" } : void 0),
25332
+ characterSpacing
25333
+ });
25334
+ if (isLink && element.url) {
25335
+ return new ExternalHyperlink({ link: element.url, children: [textRun] });
25571
25336
  }
25572
- return clampColorChannel(Math.round(channel));
25337
+ return textRun;
25573
25338
  }
25574
- function parseAlphaChannel(value) {
25575
- if (value.endsWith("%")) {
25576
- const percent = Number(value.slice(0, -1));
25577
- if (!Number.isFinite(percent)) {
25578
- return null;
25339
+ function labelShading(ctx, element) {
25340
+ const { defaultBackgroundColor } = ctx.draw.getOptions().label;
25341
+ return {
25342
+ fill: normalizeHexColor(
25343
+ element.label?.backgroundColor || defaultBackgroundColor,
25344
+ "FFFFFF"
25345
+ ),
25346
+ type: ShadingType.CLEAR,
25347
+ color: "auto"
25348
+ };
25349
+ }
25350
+ async function createImageRun(element) {
25351
+ try {
25352
+ const image = await loadImagePayload(element.value);
25353
+ const transformation = {
25354
+ width: Math.max(1, element.width || 1),
25355
+ height: Math.max(1, element.height || 1)
25356
+ };
25357
+ if (image.type === "svg" && image.fallbackPng) {
25358
+ return new ImageRun({
25359
+ type: "svg",
25360
+ data: image.data,
25361
+ fallback: { type: "png", data: image.fallbackPng },
25362
+ transformation
25363
+ });
25579
25364
  }
25580
- return Math.min(Math.max(percent / 100, 0), 1);
25581
- }
25582
- const alpha = Number(value);
25583
- if (!Number.isFinite(alpha)) {
25365
+ return new ImageRun({
25366
+ type: image.type,
25367
+ data: image.data,
25368
+ transformation
25369
+ });
25370
+ } catch {
25584
25371
  return null;
25585
25372
  }
25586
- return Math.min(Math.max(alpha, 0), 1);
25587
- }
25588
- function clampColorChannel(value) {
25589
- return Math.min(Math.max(value, 0), 255);
25590
25373
  }
25591
- function flattenChannel(value, alpha) {
25592
- return clampColorChannel(Math.round(255 * (1 - alpha) + value * alpha));
25374
+ function getElementText(element) {
25375
+ switch (element.type) {
25376
+ case "checkbox" /* CHECKBOX */:
25377
+ return element.checkbox?.value ? "\u2611" : "\u2610";
25378
+ case "radio" /* RADIO */:
25379
+ return element.radio?.value ? "\u25C9" : "\u25CB";
25380
+ default:
25381
+ return (element.value || "").replace(new RegExp(ZERO, "g"), "");
25382
+ }
25593
25383
  }
25594
- function toHexChannel(value) {
25595
- return value.toString(16).padStart(2, "0").toUpperCase();
25384
+ function getSize(ctx, element) {
25385
+ return element.actualSize || element.size || ctx.draw.getOptions().defaultSize;
25596
25386
  }
25597
- function pxBorderToDocxSize(width) {
25598
- const safeWidth = Number.isFinite(width) && width > 0 ? width : 1;
25599
- return Math.max(2, Math.round(safeWidth * 6));
25387
+ function measure(ctx, text, element) {
25388
+ ctx.measureCtx.font = ctx.draw.getElementFont(element, 1);
25389
+ return ctx.measureCtx.measureText(text).width;
25600
25390
  }
25601
- function getRegularImageType(mime) {
25602
- if (mime.includes("png")) return "png";
25603
- if (mime.includes("jpeg") || mime.includes("jpg")) return "jpg";
25604
- if (mime.includes("gif")) return "gif";
25605
- if (mime.includes("bmp")) return "bmp";
25606
- return null;
25391
+ function isSameRunStyle(a, b) {
25392
+ return a.type === b.type && a.font === b.font && a.size === b.size && a.actualSize === b.actualSize && !!a.bold === !!b.bold && !!a.italic === !!b.italic && !!a.underline === !!b.underline && !!a.strikeout === !!b.strikeout && a.color === b.color && a.highlight === b.highlight && a.url === b.url;
25607
25393
  }
25608
- function normalizeDocxFileName(fileName) {
25609
- const trimmed = fileName?.trim();
25610
- if (!trimmed) {
25611
- return DEFAULT_DOCX_FILE_NAME;
25394
+ function mapFont(font) {
25395
+ const name = font.trim().replace(/^['"]|['"]$/g, "");
25396
+ switch (name.toLowerCase()) {
25397
+ case "sans-serif":
25398
+ return "Arial";
25399
+ case "serif":
25400
+ return "Times New Roman";
25401
+ case "monospace":
25402
+ return "Courier New";
25403
+ default:
25404
+ return name;
25612
25405
  }
25613
- return trimmed.toLowerCase().endsWith(".docx") ? trimmed : `${trimmed}.docx`;
25614
- }
25615
- function pxToTwips(px) {
25616
- return Math.round(px * 15);
25617
25406
  }
25618
25407
 
25619
25408
  // src/utils/pdf.ts
@@ -25625,7 +25414,7 @@ import {
25625
25414
  var DEFAULT_PDF_FILE_NAME = "document.pdf";
25626
25415
  var DEFAULT_IMAGE_WIDTH2 = 320;
25627
25416
  var DEFAULT_IMAGE_HEIGHT2 = 180;
25628
- var DEFAULT_LINE_HEIGHT2 = 1.15;
25417
+ var DEFAULT_LINE_HEIGHT = 1.15;
25629
25418
  var PAGE_REGION_GAP = 12;
25630
25419
  var SEPARATOR_HEIGHT = 12;
25631
25420
  var LIST_GAP = 8;
@@ -25820,7 +25609,7 @@ async function parseElementList(elementList, context, options) {
25820
25609
  }
25821
25610
  if (element.type === "block" /* BLOCK */) {
25822
25611
  await flushInlineBuffer();
25823
- const blockText = getBlockFallbackText2(element);
25612
+ const blockText = getBlockFallbackText(element);
25824
25613
  if (blockText) {
25825
25614
  blocks.push(
25826
25615
  ...await parseInlineElements(
@@ -25872,7 +25661,7 @@ async function parseInlineElements(elementList, context, options, overrides = {}
25872
25661
  current = null;
25873
25662
  };
25874
25663
  const appendText = (element, text, factory, preserveTrailingBreak = false) => {
25875
- const normalized = normalizeText2(text);
25664
+ const normalized = normalizeText(text);
25876
25665
  const parts = normalized.split("\n");
25877
25666
  let lastSeparatorFlushedContent = false;
25878
25667
  for (let i = 0; i < parts.length; i++) {
@@ -25908,7 +25697,7 @@ async function parseInlineElements(elementList, context, options, overrides = {}
25908
25697
  const children = element.valueList || [];
25909
25698
  for (let childIndex = 0; childIndex < children.length; childIndex++) {
25910
25699
  const child = children[childIndex];
25911
- const text = normalizeText2(getInlineElementText2(child));
25700
+ const text = normalizeText(getInlineElementText(child));
25912
25701
  if (!text) continue;
25913
25702
  appendText(
25914
25703
  element,
@@ -25957,7 +25746,7 @@ async function parseInlineElements(elementList, context, options, overrides = {}
25957
25746
  ensureParagraph(element).runs.push(badgeRun);
25958
25747
  continue;
25959
25748
  }
25960
- const textValue = getInlineElementText2(element);
25749
+ const textValue = getInlineElementText(element);
25961
25750
  if (!textValue) {
25962
25751
  continue;
25963
25752
  }
@@ -25985,7 +25774,7 @@ async function parseListBlock(element, context, options) {
25985
25774
  const blocks = [];
25986
25775
  const orderedCounts = Array(9).fill(0);
25987
25776
  for (const listItemElements of listItemMap.values()) {
25988
- const itemElement = getListItemNumberingElement2(listItemElements, element);
25777
+ const itemElement = getListItemNumberingElement(listItemElements, element);
25989
25778
  const marker = getListMarker(itemElement, orderedCounts);
25990
25779
  blocks.push(
25991
25780
  ...await parseInlineElements(listItemElements, context, options, {
@@ -26544,10 +26333,11 @@ function layoutParagraph(block, width, context) {
26544
26333
  }
26545
26334
  if (token.width > availableWidth && token.kind === "text" && token.run.type === "text") {
26546
26335
  const segments = splitTextToken(token, availableWidth, context);
26547
- for (let segmentIndex = 0; segmentIndex < segments.length; segmentIndex++) {
26548
- tokens.splice(i + segmentIndex, 0, segments[segmentIndex]);
26336
+ if (segments.length > 1) {
26337
+ tokens.splice(i, 1, ...segments);
26338
+ i--;
26339
+ continue;
26549
26340
  }
26550
- continue;
26551
26341
  }
26552
26342
  if (line.width + token.width > availableWidth && line.fragments.length) {
26553
26343
  commitLine();
@@ -26906,7 +26696,7 @@ function getEmptyLineHeight(block, context) {
26906
26696
  block.context.rowMargin || context.options.defaultRowMargin
26907
26697
  );
26908
26698
  }
26909
- function getRunHeight(run, rowMargin = DEFAULT_LINE_HEIGHT2) {
26699
+ function getRunHeight(run, rowMargin = DEFAULT_LINE_HEIGHT) {
26910
26700
  return run.size * rowMargin;
26911
26701
  }
26912
26702
  function resolveTextMetrics(run, context) {
@@ -27115,10 +26905,10 @@ function loadHtmlImage2(src) {
27115
26905
  image.src = src;
27116
26906
  });
27117
26907
  }
27118
- function getInlineElementText2(element) {
26908
+ function getInlineElementText(element) {
27119
26909
  switch (element.type) {
27120
26910
  case "control" /* CONTROL */:
27121
- return getControlText2(element.control);
26911
+ return getControlText(element.control);
27122
26912
  case "date" /* DATE */:
27123
26913
  return element.valueList?.length ? getTextFromElementList(element.valueList) : element.value;
27124
26914
  case "checkbox" /* CHECKBOX */:
@@ -27134,7 +26924,7 @@ function getInlineElementText2(element) {
27134
26924
  return element.value;
27135
26925
  }
27136
26926
  }
27137
- function getControlText2(control) {
26927
+ function getControlText(control) {
27138
26928
  if (!control) return "";
27139
26929
  if (control.type === "text" /* TEXT */ || control.type === "date" /* DATE */ || control.type === "number" /* NUMBER */) {
27140
26930
  const value = control.value?.length ? getTextFromElementList(control.value) : "";
@@ -27142,7 +26932,7 @@ function getControlText2(control) {
27142
26932
  return `${control.preText || ""}${content}${control.postText || ""}`;
27143
26933
  }
27144
26934
  if (control.type === "select" /* SELECT */) {
27145
- const labels = getSelectedControlLabels2(control.code, control.valueSets);
26935
+ const labels = getSelectedControlLabels(control.code, control.valueSets);
27146
26936
  const content = labels.length ? labels.join(control.multiSelectDelimiter || ", ") : control.placeholder || "";
27147
26937
  return `${control.preText || ""}${content}${control.postText || ""}`;
27148
26938
  }
@@ -27155,13 +26945,13 @@ function getControlText2(control) {
27155
26945
  }
27156
26946
  return "";
27157
26947
  }
27158
- function getSelectedControlLabels2(code, valueSets) {
26948
+ function getSelectedControlLabels(code, valueSets) {
27159
26949
  if (!code || !valueSets?.length) return [];
27160
26950
  return code.split(",").map(
27161
26951
  (selectCode) => valueSets.find((valueSet) => valueSet.code === selectCode)?.value
27162
26952
  ).filter((value) => !!value);
27163
26953
  }
27164
- function getBlockFallbackText2(element) {
26954
+ function getBlockFallbackText(element) {
27165
26955
  if (element.block?.iframeBlock?.src) {
27166
26956
  return element.block.iframeBlock.src;
27167
26957
  }
@@ -27170,7 +26960,7 @@ function getBlockFallbackText2(element) {
27170
26960
  }
27171
26961
  return "";
27172
26962
  }
27173
- function getListItemNumberingElement2(listItemElements, fallback) {
26963
+ function getListItemNumberingElement(listItemElements, fallback) {
27174
26964
  return listItemElements.find(
27175
26965
  (listItem) => listItem.listId || listItem.listLevel !== void 0 || !!listItem.listPreset || !!listItem.listStyle || !!listItem.listType
27176
26966
  ) || fallback;
@@ -27392,7 +27182,7 @@ function getReservedPageNumberBottom(pageNumber, pageNumberHeight) {
27392
27182
  }
27393
27183
  return pxToPt(pageNumber.bottom + 4) + pageNumberHeight;
27394
27184
  }
27395
- function normalizeText2(text = "") {
27185
+ function normalizeText(text = "") {
27396
27186
  return text.replace(new RegExp(ZERO, "g"), "").replace(/\r\n/g, "\n");
27397
27187
  }
27398
27188
  function normalizePdfFileName(fileName) {
@@ -28732,8 +28522,7 @@ var CommandAdapt = class {
28732
28522
  }
28733
28523
  }
28734
28524
  async exportDocx(payload = {}) {
28735
- const result = this.draw.getValue();
28736
- const { blob, fileName } = await exportEditorDataToDocx(result, payload);
28525
+ const { blob, fileName } = await exportDrawToDocx(this.draw, payload);
28737
28526
  downloadBlob(blob, fileName);
28738
28527
  }
28739
28528
  async exportPdf(payload = {}) {