@pdfme/schemas 6.2.3 → 6.2.4-dev.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,18 +1,16 @@
1
1
  import { D as getBoxContentArea, E as createBoxDimension, L as CODE_BACKGROUND_COLOR, V as DEFAULT_FONT_COLOR, X as PLACEHOLDER_FONT_COLOR, Z as SYNTHETIC_BOLD_CSS_TEXT_SHADOW, a as createListItemSplitRange, c as getListItemRange, g as getFontKitFont, i as TEXT_LINE_SPLIT_UNIT, it as VERTICAL_ALIGN_MIDDLE, l as getTableBodyRange, n as LIST_ITEM_SPLIT_UNIT, o as createTableBodySplitRange, r as TABLE_BODY_SPLIT_UNIT, s as createTextLineSplitRange, t as BUILT_IN_DYNAMIC_LAYOUT_SPLIT_UNITS, u as getTextLineRange, z as DEFAULT_ALIGNMENT } from "./splitRange-B6311Noc.js";
2
2
  import { a as measureTextLines, f as resolveFontVariant, h as parseInlineMarkdown, u as isInlineMarkdownTextSchema } from "./measure-BarlRI1L.js";
3
- import { a as mapVerticalAlignToFlex, c as Formatter, d as embedAndGetFont, i as makeElementPlainTextContentEditable, l as getExtraFormatterSchema, n as textSchema, o as uiRender$4, r as buildStyledTextContainer, s as propPanel$3, t as builtInPlugins, u as pdfRender$4 } from "./builtins-BFhn3tdm.js";
4
- import { a as getCellPropPanelSchema, c as HEX_COLOR_PATTERN, i as getBodyWithSchemaRange, l as OPAQUE_HEX_COLOR_PATTERN, n as getDynamicLayoutForTable, o as getColumnStylesPropPanelSchema, r as getBody, s as getDefaultCellStyles, t as getDynamicHeightsForTable, u as createSingleTable } from "./dynamicTemplate-1cxsTQIG.js";
5
- import { a as createSvgStr, d as rgbColorToCmykColor, f as rotatePoint, g as visitVariables, h as getVariableNames, i as createErrorElm, l as isEditable, m as countUniqueVariableNames, n as applyAlphaToOpacity, p as splitHexAlpha, r as convertForPdfLayoutProps, s as hex2PrintingColor, t as addAlphaToHex, u as readFile } from "./utils-DL6a4DC2.js";
3
+ import { _ as embedAndGetFont, a as mapVerticalAlignToFlex, c as Formatter, d as TextAlignLeftIcon, f as TextAlignRightIcon, g as pdfRender$4, h as TextVerticalAlignTopIcon, i as makeElementPlainTextContentEditable, l as getExtraFormatterSchema, m as TextVerticalAlignMiddleIcon, n as textSchema, o as uiRender$4, p as TextVerticalAlignBottomIcon, r as buildStyledTextContainer, s as propPanel$3, t as builtInPlugins, u as TextAlignCenterIcon } from "./builtins-Ch7UBNGU.js";
4
+ import { C as hashImageDataUrl, S as normalizeImageOrientation, _ as TABLE_CELL_TYPES, a as getCellPropPanelSchema, b as TABLE_IMAGE_DATA_URL_PATTERN, c as HEX_COLOR_PATTERN, d as getTableImageObjectPosition, f as normalizeTableCellType, g as warnInvalidTableImageOnce, h as resolveImageDimension, i as getBodyWithSchemaRange, l as OPAQUE_HEX_COLOR_PATTERN, m as resolveFixedTableImageHeight, n as getDynamicLayoutForTable, o as getDefaultCellStyles, p as normalizeTableImageHeightMode, r as getBody, s as remapColumnStylesOnRemove, t as getDynamicHeightsForTable, u as createSingleTable, v as TABLE_IMAGE_HEIGHT_MODES, x as detectImageFormat, y as DEFAULT_TABLE_IMAGE_HEIGHT_MODE } from "./dynamicTemplate-Bnu4jVaz.js";
5
+ import { a as createSvgStr, d as rgbColorToCmykColor, f as rotatePoint, g as visitVariables, h as getVariableNames, i as createErrorElm, l as isEditable, m as countUniqueVariableNames, n as applyAlphaToOpacity, p as splitHexAlpha, r as convertForPdfLayoutProps, s as hex2PrintingColor, t as addAlphaToHex, u as readFile } from "./utils-BfzehhiZ.js";
6
6
  import { a as normalizeListItems, c as LIST_STYLE_BULLET, i as normalizeListItemEntries, l as LIST_STYLE_ORDERED, n as calculateListLayout, o as serializeListItems, s as DEFAULT_LIST_STYLE, t as getDynamicLayoutForList } from "./dynamicTemplate-CcnCXHfM.js";
7
7
  import { i as validateVariables, n as substituteVariables, r as substituteVariablesAsInlineMarkdownLiterals, t as resolveReadOnlyMultiVariableText } from "./helper-BR4_UZA5.js";
8
8
  import "./tables.js";
9
9
  import "./lists.js";
10
- import { DEFAULT_FONT_NAME, ZOOM, b64toUint8Array, getDefaultFont, getFallbackFontName, getInternalLinkTarget, isHexValid, mm2pt, normalizeLinkHref, px2mm } from "@pdfme/common";
10
+ import { DEFAULT_FONT_NAME, ZOOM, b64toUint8Array, cloneDeep, getDefaultFont, getFallbackFontName, getInternalLinkTarget, isHexValid, mm2pt, normalizeLinkHref, px2mm } from "@pdfme/common";
11
11
  import { Buffer as Buffer$1 } from "buffer";
12
12
  import { popGraphicsState, pushGraphicsState, rotateDegrees, translate } from "@pdfme/pdf-lib";
13
13
  import { Barcode, Calendar, CalendarClock, ChevronDown, Circle, CircleDot, Clock, Image, List, Minus, QrCode, Route, Square, SquareCheck, Table, Type } from "lucide";
14
- import jpegJsModule from "jpeg-js";
15
- import UPNGModule from "@pdf-lib/upng";
16
14
  import DOMPurify from "dompurify";
17
15
  import bwipjs from "bwip-js";
18
16
  import AirDatepicker from "air-datepicker";
@@ -1412,210 +1410,6 @@ var listSchema = {
1412
1410
  icon: createSvgStr(List)
1413
1411
  };
1414
1412
  //#endregion
1415
- //#region src/graphics/orientation.ts
1416
- var isJpegJsApi = (value) => !!value && typeof value === "object" && typeof value.decode === "function" && typeof value.encode === "function";
1417
- var isUPNGApi = (value) => !!value && typeof value === "object" && typeof value.decode === "function" && typeof value.toRGBA8 === "function" && typeof value.encode === "function";
1418
- var resolveExport = (value, guard, label) => {
1419
- let current = value;
1420
- while (current && typeof current === "object") {
1421
- if (guard(current)) return current;
1422
- current = current.default;
1423
- }
1424
- throw new TypeError(`Failed to resolve ${label} exports`);
1425
- };
1426
- var jpegJs = resolveExport(jpegJsModule, isJpegJsApi, "jpeg-js");
1427
- var UPNG = resolveExport(UPNGModule, isUPNGApi, "@pdf-lib/upng");
1428
- var JPEG_ENCODE_QUALITY = 90;
1429
- var JPEG_MAX_MEMORY_MB = 512;
1430
- var JPEG_SOI_0 = 255;
1431
- var JPEG_SOI_1 = 216;
1432
- var PNG_SIGNATURE = new Uint8Array([
1433
- 137,
1434
- 80,
1435
- 78,
1436
- 71,
1437
- 13,
1438
- 10,
1439
- 26,
1440
- 10
1441
- ]);
1442
- var toArrayBuffer = (bytes) => {
1443
- const copy = new Uint8Array(bytes.byteLength);
1444
- copy.set(bytes);
1445
- return copy.buffer;
1446
- };
1447
- var isPngSignature = (bytes) => {
1448
- if (bytes.length < PNG_SIGNATURE.length) return false;
1449
- for (let i = 0; i < PNG_SIGNATURE.length; i++) if (bytes[i] !== PNG_SIGNATURE[i]) return false;
1450
- return true;
1451
- };
1452
- var detectImageFormat = (bytes) => {
1453
- if (bytes.length >= 2 && bytes[0] === JPEG_SOI_0 && bytes[1] === JPEG_SOI_1) return "jpeg";
1454
- if (isPngSignature(bytes)) return "png";
1455
- return "other";
1456
- };
1457
- /** TIFF/IFD0: returns 1..8 or undefined. Never throws. */
1458
- var parseTiffOrientation = (tiff) => {
1459
- try {
1460
- if (tiff.length < 8) return void 0;
1461
- const le = tiff[0] === 73 && tiff[1] === 73;
1462
- if (!le && !(tiff[0] === 77 && tiff[1] === 77)) return void 0;
1463
- const view = new DataView(tiff.buffer, tiff.byteOffset, tiff.byteLength);
1464
- if (view.getUint16(2, le) !== 42) return void 0;
1465
- const ifd = view.getUint32(4, le);
1466
- if (ifd + 2 > tiff.length) return void 0;
1467
- const n = view.getUint16(ifd, le);
1468
- for (let i = 0; i < n; i++) {
1469
- const entry = ifd + 2 + i * 12;
1470
- if (entry + 12 > tiff.length) return void 0;
1471
- if (view.getUint16(entry, le) !== 274) continue;
1472
- if (view.getUint16(entry + 2, le) !== 3) return void 0;
1473
- if (view.getUint32(entry + 4, le) !== 1) return void 0;
1474
- const val = view.getUint16(entry + 8, le);
1475
- return val >= 1 && val <= 8 ? val : void 0;
1476
- }
1477
- return;
1478
- } catch {
1479
- return;
1480
- }
1481
- };
1482
- var isExifApp1Payload = (segment) => segment.length >= 6 && segment[0] === 69 && segment[1] === 120 && segment[2] === 105 && segment[3] === 102 && segment[4] === 0 && segment[5] === 0;
1483
- /** Standalone JPEG markers have no length field (TEM, RST0–7, SOI). */
1484
- var isStandaloneJpegMarker = (marker) => marker === 1 || marker >= 208 && marker <= 216;
1485
- /**
1486
- * JPEG: scan APP1 'Exif\0\0' segments; stop at SOS/EOI.
1487
- * Skips 0xFF fill/padding and standalone markers. If an Exif APP1 has no
1488
- * valid Orientation, keep scanning for a later one.
1489
- */
1490
- var getJpegOrientation = (bytes) => {
1491
- if (bytes.length < 2 || bytes[0] !== JPEG_SOI_0 || bytes[1] !== JPEG_SOI_1) return void 0;
1492
- let pos = 2;
1493
- while (pos < bytes.length) {
1494
- if (bytes[pos] !== 255) return void 0;
1495
- while (pos < bytes.length && bytes[pos] === 255) pos++;
1496
- if (pos >= bytes.length) return void 0;
1497
- const marker = bytes[pos++];
1498
- if (isStandaloneJpegMarker(marker)) continue;
1499
- if (marker === 218 || marker === 217) return void 0;
1500
- if (pos + 2 > bytes.length) return void 0;
1501
- const size = bytes[pos] << 8 | bytes[pos + 1];
1502
- if (size < 2 || pos + size > bytes.length) return void 0;
1503
- if (marker === 225 && size >= 8) {
1504
- const segment = bytes.subarray(pos + 2, pos + size);
1505
- if (isExifApp1Payload(segment)) {
1506
- const orientation = parseTiffOrientation(segment.subarray(6));
1507
- if (orientation !== void 0) return orientation;
1508
- }
1509
- }
1510
- pos += size;
1511
- }
1512
- };
1513
- /**
1514
- * PNG: scan chunks for eXIf (raw TIFF payload). Skip IDAT and other
1515
- * ancillary chunks; eXIf may appear after image data. Stop at IEND.
1516
- */
1517
- var getPngOrientation = (bytes) => {
1518
- if (!isPngSignature(bytes)) return void 0;
1519
- const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
1520
- let pos = 8;
1521
- while (pos + 12 <= bytes.length) {
1522
- const length = view.getUint32(pos);
1523
- if (pos + 12 + length > bytes.length) return void 0;
1524
- const type = String.fromCharCode(bytes[pos + 4], bytes[pos + 5], bytes[pos + 6], bytes[pos + 7]);
1525
- if (type === "eXIf") return parseTiffOrientation(bytes.subarray(pos + 8, pos + 8 + length));
1526
- if (type === "IEND") return void 0;
1527
- pos += 12 + length;
1528
- }
1529
- };
1530
- var orientationSwapsDimensions = (orientation) => orientation >= 5;
1531
- /**
1532
- * Map source (x, y) into destination (dx, dy) for EXIF orientations 2–8.
1533
- * Orientations 5–8 swap width and height.
1534
- */
1535
- var transformRgba = (src, width, height, orientation) => {
1536
- const swap = orientationSwapsDimensions(orientation);
1537
- const destWidth = swap ? height : width;
1538
- const destHeight = swap ? width : height;
1539
- const dest = new Uint8Array(destWidth * destHeight * 4);
1540
- for (let y = 0; y < height; y++) for (let x = 0; x < width; x++) {
1541
- let dx = x;
1542
- let dy = y;
1543
- switch (orientation) {
1544
- case 2:
1545
- dx = width - 1 - x;
1546
- dy = y;
1547
- break;
1548
- case 3:
1549
- dx = width - 1 - x;
1550
- dy = height - 1 - y;
1551
- break;
1552
- case 4:
1553
- dx = x;
1554
- dy = height - 1 - y;
1555
- break;
1556
- case 5:
1557
- dx = y;
1558
- dy = x;
1559
- break;
1560
- case 6:
1561
- dx = height - 1 - y;
1562
- dy = x;
1563
- break;
1564
- case 7:
1565
- dx = height - 1 - y;
1566
- dy = width - 1 - x;
1567
- break;
1568
- case 8:
1569
- dx = y;
1570
- dy = width - 1 - x;
1571
- }
1572
- const srcIndex = (y * width + x) * 4;
1573
- const destIndex = (dy * destWidth + dx) * 4;
1574
- dest[destIndex] = src[srcIndex];
1575
- dest[destIndex + 1] = src[srcIndex + 1];
1576
- dest[destIndex + 2] = src[srcIndex + 2];
1577
- dest[destIndex + 3] = src[srcIndex + 3];
1578
- }
1579
- return {
1580
- data: dest,
1581
- width: destWidth,
1582
- height: destHeight
1583
- };
1584
- };
1585
- var bakeJpegOrientation = (bytes) => {
1586
- const orientation = getJpegOrientation(bytes);
1587
- if (!orientation || orientation === 1) return bytes;
1588
- const raw = jpegJs.decode(bytes, {
1589
- useTArray: true,
1590
- formatAsRGBA: true,
1591
- maxMemoryUsageInMB: JPEG_MAX_MEMORY_MB
1592
- });
1593
- const transformed = transformRgba(raw.data, raw.width, raw.height, orientation);
1594
- return new Uint8Array(jpegJs.encode({
1595
- data: transformed.data,
1596
- width: transformed.width,
1597
- height: transformed.height
1598
- }, JPEG_ENCODE_QUALITY).data);
1599
- };
1600
- var bakePngOrientation = (bytes) => {
1601
- const orientation = getPngOrientation(bytes);
1602
- if (!orientation || orientation === 1) return bytes;
1603
- const img = UPNG.decode(toArrayBuffer(bytes));
1604
- const transformed = transformRgba(new Uint8Array(UPNG.toRGBA8(img)[0]), img.width, img.height, orientation);
1605
- return new Uint8Array(UPNG.encode([toArrayBuffer(transformed.data)], transformed.width, transformed.height, 0));
1606
- };
1607
- /**
1608
- * One normalize path for JPEG and PNG. Tag absent/1 is a byte-identical
1609
- * passthrough. Tag 2–8 decodes, transforms pixels, and re-encodes without
1610
- * an orientation tag. May throw on decode/encode failure — callers fall back.
1611
- */
1612
- var normalizeImageOrientation = (bytes) => {
1613
- const kind = detectImageFormat(bytes);
1614
- if (kind === "jpeg") return bakeJpegOrientation(bytes);
1615
- if (kind === "png") return bakePngOrientation(bytes);
1616
- return bytes;
1617
- };
1618
- //#endregion
1619
1413
  //#region src/graphics/image.ts
1620
1414
  /**
1621
1415
  * Build a short fingerprint for a potentially-large base64 image string.
@@ -1632,15 +1426,7 @@ var normalizeImageOrientation = (bytes) => {
1632
1426
  * cost, without retaining any slice of the input as a Map key. Keys stay
1633
1427
  * well under ~40 chars regardless of input size.
1634
1428
  */
1635
- var getCacheKey = (schema, input) => {
1636
- let hash = 2166136261;
1637
- for (let i = 0; i < input.length; i++) {
1638
- hash ^= input.charCodeAt(i);
1639
- hash = Math.imul(hash, 16777619);
1640
- }
1641
- const hex = (hash >>> 0).toString(16).padStart(8, "0");
1642
- return `${schema.type}:${input.length}:${hex}`;
1643
- };
1429
+ var getCacheKey = (schema, input) => `${schema.type}:${input.length}:${hashImageDataUrl(input)}`;
1644
1430
  var dataUrlToBytes = (value) => {
1645
1431
  const idx = value.indexOf(";base64,");
1646
1432
  const base64 = idx >= 0 ? value.slice(idx + 8) : value;
@@ -3000,7 +2786,154 @@ var lineSchema = {
3000
2786
  icon: createSvgStr(Minus)
3001
2787
  };
3002
2788
  //#endregion
2789
+ //#region src/tables/imageCellUi.ts
2790
+ var buttonSize$1 = 18;
2791
+ var pendingImagePicker = null;
2792
+ var tableSchemaKey = (schema) => typeof schema.id === "string" && schema.id !== "" ? schema.id : schema.name;
2793
+ var noteRenderingTable = (schema) => {
2794
+ const schemaKey = tableSchemaKey(schema);
2795
+ if (pendingImagePicker && pendingImagePicker.schemaKey !== schemaKey) pendingImagePicker = null;
2796
+ };
2797
+ var clearImagePickerRequest = () => {
2798
+ pendingImagePicker = null;
2799
+ };
2800
+ var requestImagePicker = (request) => {
2801
+ pendingImagePicker = request;
2802
+ };
2803
+ var consumePendingImagePicker = (request) => {
2804
+ const pending = pendingImagePicker;
2805
+ if (!pending || pending.rowIndex !== request.rowIndex || pending.colIndex !== request.colIndex || pending.schemaKey !== request.schemaKey) return false;
2806
+ pendingImagePicker = null;
2807
+ return true;
2808
+ };
2809
+ var createImageButton = (options) => {
2810
+ const button = document.createElement("button");
2811
+ button.type = "button";
2812
+ button.innerText = options.text;
2813
+ button.setAttribute("aria-label", options.ariaLabel);
2814
+ button.title = options.ariaLabel;
2815
+ button.style.width = `${buttonSize$1}px`;
2816
+ button.style.height = `${buttonSize$1}px`;
2817
+ button.style.position = "absolute";
2818
+ button.style.top = "0px";
2819
+ button.style.right = options.right;
2820
+ button.style.display = "inline-flex";
2821
+ button.style.alignItems = "center";
2822
+ button.style.justifyContent = "center";
2823
+ button.style.padding = "0";
2824
+ button.style.border = "1px solid #d9d9d9";
2825
+ button.style.borderRadius = "3px";
2826
+ button.style.background = "#ffffff";
2827
+ button.style.color = "#333333";
2828
+ button.style.fontSize = "11px";
2829
+ button.style.lineHeight = "1";
2830
+ button.style.cursor = "pointer";
2831
+ button.style.zIndex = "20";
2832
+ button.addEventListener("mousedown", (event) => {
2833
+ event.preventDefault();
2834
+ });
2835
+ button.addEventListener("click", (event) => {
2836
+ event.preventDefault();
2837
+ event.stopPropagation();
2838
+ options.onClick();
2839
+ });
2840
+ return button;
2841
+ };
2842
+ var appendFrame = (schema) => {
2843
+ const contentArea = getBoxContentArea(schema);
2844
+ const frame = document.createElement("div");
2845
+ frame.style.position = "absolute";
2846
+ frame.style.zIndex = "1";
2847
+ frame.style.width = `${contentArea.width}mm`;
2848
+ frame.style.height = `${contentArea.height}mm`;
2849
+ frame.style.top = `${contentArea.topInset}mm`;
2850
+ frame.style.left = `${contentArea.leftInset}mm`;
2851
+ frame.style.boxSizing = "border-box";
2852
+ return frame;
2853
+ };
2854
+ var warnUnsupportedImage = (columnIndex) => {
2855
+ console.warn(`[@pdfme/schemas/table] unsupported image in column ${columnIndex ?? 0}; only PNG/JPEG data URL is supported`);
2856
+ };
2857
+ var renderTableImageCellUi = (arg) => {
2858
+ const { schema, rootElement, value, onChange, i18n, _cache } = arg;
2859
+ const dimension = value ? resolveImageDimension(value, _cache) : void 0;
2860
+ const frame = appendFrame(schema);
2861
+ if (dimension && value) {
2862
+ const img = document.createElement("img");
2863
+ img.alt = "";
2864
+ img.src = value;
2865
+ img.style.width = "100%";
2866
+ img.style.height = "100%";
2867
+ img.style.objectFit = "contain";
2868
+ img.style.objectPosition = getTableImageObjectPosition(schema.alignment, schema.verticalAlignment);
2869
+ frame.appendChild(img);
2870
+ } else {
2871
+ frame.textContent = i18n("schemas.table.imageCell.placeholder");
2872
+ frame.style.border = "1px dotted #999";
2873
+ frame.style.color = "#999";
2874
+ frame.style.fontSize = "10px";
2875
+ frame.style.display = "flex";
2876
+ frame.style.alignItems = "center";
2877
+ frame.style.justifyContent = "center";
2878
+ frame.style.textAlign = "center";
2879
+ frame.style.overflow = "hidden";
2880
+ frame.style.padding = "1mm";
2881
+ }
2882
+ rootElement.appendChild(frame);
2883
+ const input = document.createElement("input");
2884
+ input.type = "file";
2885
+ input.accept = "image/png, image/jpeg";
2886
+ input.tabIndex = -1;
2887
+ input.style.display = "none";
2888
+ input.addEventListener("click", (event) => {
2889
+ event.stopPropagation();
2890
+ });
2891
+ input.addEventListener("change", () => {
2892
+ if (!input.files || input.files.length === 0) return;
2893
+ readFile(input.files).then((result) => {
2894
+ if (typeof result === "string" && TABLE_IMAGE_DATA_URL_PATTERN.test(result) && resolveImageDimension(result, _cache)) {
2895
+ onChange?.({
2896
+ key: "content",
2897
+ value: result
2898
+ });
2899
+ return;
2900
+ }
2901
+ warnUnsupportedImage(schema.columnIndex);
2902
+ }).catch(() => {
2903
+ warnUnsupportedImage(schema.columnIndex);
2904
+ });
2905
+ });
2906
+ rootElement.appendChild(input);
2907
+ const hasValue = typeof value === "string" && value !== "";
2908
+ rootElement.appendChild(createImageButton({
2909
+ text: "▣",
2910
+ ariaLabel: i18n("schemas.table.imageCell.select"),
2911
+ right: hasValue ? `20px` : "0px",
2912
+ onClick: () => {
2913
+ input.click();
2914
+ }
2915
+ }));
2916
+ if (hasValue) rootElement.appendChild(createImageButton({
2917
+ text: "×",
2918
+ ariaLabel: i18n("schemas.table.imageCell.remove"),
2919
+ right: "0px",
2920
+ onClick: () => {
2921
+ onChange?.({
2922
+ key: "content",
2923
+ value: ""
2924
+ });
2925
+ }
2926
+ }));
2927
+ const { rowIndex, columnIndex, pickerSchemaKey } = schema;
2928
+ if (typeof rowIndex === "number" && typeof columnIndex === "number" && typeof pickerSchemaKey === "string" && consumePendingImagePicker({
2929
+ rowIndex,
2930
+ colIndex: columnIndex,
2931
+ schemaKey: pickerSchemaKey
2932
+ }) && !hasValue) input.click();
2933
+ };
2934
+ //#endregion
3003
2935
  //#region src/tables/cell.ts
2936
+ var imagePdfRender = imageSchema.pdf;
3004
2937
  var linePdfRender = lineSchema.pdf;
3005
2938
  var rectanglePdfRender$1 = rectangle.pdf;
3006
2939
  var renderLine = async (arg, schema, position, width, height) => linePdfRender({
@@ -3072,6 +3005,32 @@ var cellSchema = {
3072
3005
  y: position.y
3073
3006
  }, borderWidth.left, height)
3074
3007
  ]);
3008
+ if (schema.cellType === "image") {
3009
+ if (arg.value && resolveImageDimension(arg.value, arg._cache)) {
3010
+ const imageSchema = {
3011
+ name: schema.name,
3012
+ type: "image",
3013
+ content: arg.value,
3014
+ position: contentArea.position,
3015
+ width: contentArea.width,
3016
+ height: contentArea.height,
3017
+ rotate: 0,
3018
+ opacity: 1,
3019
+ objectFit: "contain",
3020
+ objectPosition: getTableImageObjectPosition(schema.alignment, schema.verticalAlignment)
3021
+ };
3022
+ try {
3023
+ await imagePdfRender({
3024
+ ...arg,
3025
+ value: arg.value,
3026
+ schema: imageSchema
3027
+ });
3028
+ } catch {
3029
+ warnInvalidTableImageOnce(arg.value, schema.columnIndex ?? 0, arg._cache);
3030
+ }
3031
+ }
3032
+ return;
3033
+ }
3075
3034
  await pdfRender$4({
3076
3035
  ...arg,
3077
3036
  schema: {
@@ -3091,19 +3050,35 @@ var cellSchema = {
3091
3050
  const { schema, rootElement } = arg;
3092
3051
  const { borderWidth, width, height, borderColor, backgroundColor } = schema;
3093
3052
  rootElement.style.backgroundColor = backgroundColor;
3094
- const textDiv = createTextDiv(schema);
3095
- await uiRender$4({
3096
- ...arg,
3097
- schema: {
3098
- ...schema,
3099
- backgroundColor: "",
3100
- borderColor: "",
3101
- borderWidth: createBoxDimension(0),
3102
- padding: createBoxDimension(0)
3103
- },
3104
- rootElement: textDiv
3105
- });
3106
- rootElement.appendChild(textDiv);
3053
+ if (schema.cellType === "image") {
3054
+ if (arg.mode === "designer") renderTableImageCellUi(arg);
3055
+ else if (arg.value ? resolveImageDimension(arg.value, arg._cache) : void 0) {
3056
+ const imageFrame = createTextDiv(schema);
3057
+ const img = document.createElement("img");
3058
+ img.alt = "";
3059
+ img.src = arg.value;
3060
+ img.style.width = "100%";
3061
+ img.style.height = "100%";
3062
+ img.style.objectFit = "contain";
3063
+ img.style.objectPosition = getTableImageObjectPosition(schema.alignment, schema.verticalAlignment);
3064
+ imageFrame.appendChild(img);
3065
+ rootElement.appendChild(imageFrame);
3066
+ }
3067
+ } else {
3068
+ const textDiv = createTextDiv(schema);
3069
+ await uiRender$4({
3070
+ ...arg,
3071
+ schema: {
3072
+ ...schema,
3073
+ backgroundColor: "",
3074
+ borderColor: "",
3075
+ borderWidth: createBoxDimension(0),
3076
+ padding: createBoxDimension(0)
3077
+ },
3078
+ rootElement: textDiv
3079
+ });
3080
+ rootElement.appendChild(textDiv);
3081
+ }
3107
3082
  [
3108
3083
  createLineDiv(`${width}mm`, `${borderWidth.top}mm`, "0mm", null, null, "0mm", borderColor),
3109
3084
  createLineDiv(`${width}mm`, `${borderWidth.bottom}mm`, null, null, "0mm", "0mm", borderColor),
@@ -3143,7 +3118,7 @@ var cellSchema = {
3143
3118
  //#region src/tables/pdfRender.ts
3144
3119
  var rectanglePdfRender = rectangle.pdf;
3145
3120
  var cellPdfRender = cellSchema.pdf;
3146
- async function drawCell(arg, cell) {
3121
+ async function drawCell(arg, cell, columnIndex) {
3147
3122
  await cellPdfRender({
3148
3123
  ...arg,
3149
3124
  value: cell.raw,
@@ -3166,7 +3141,9 @@ async function drawCell(arg, cell) {
3166
3141
  fontColor: cell.styles.textColor,
3167
3142
  borderColor: cell.styles.lineColor,
3168
3143
  borderWidth: cell.styles.lineWidth,
3169
- padding: cell.styles.cellPadding
3144
+ padding: cell.styles.cellPadding,
3145
+ cellType: cell.isImage() ? "image" : "text",
3146
+ columnIndex
3170
3147
  }
3171
3148
  });
3172
3149
  }
@@ -3180,7 +3157,7 @@ async function drawRow(arg, table, row, cursor, columns) {
3180
3157
  }
3181
3158
  cell.x = cursor.x;
3182
3159
  cell.y = cursor.y;
3183
- await drawCell(arg, cell);
3160
+ await drawCell(arg, cell, column.index);
3184
3161
  cursor.x += column.width;
3185
3162
  }
3186
3163
  cursor.y += row.height;
@@ -3294,7 +3271,7 @@ var calcResizedHeadWidthPercentages = (arg) => {
3294
3271
  var setBorder = (div, borderPosition, arg) => {
3295
3272
  div.style[`border${borderPosition}`] = `${String(arg.schema.tableStyles.borderWidth)}mm solid ${arg.schema.tableStyles.borderColor}`;
3296
3273
  };
3297
- var drawBorder = (div, row, colIndex, rowIndex, rowsLength, arg) => {
3274
+ var drawBorder = (div, row, colIndex, rowIndex, rowsLength, arg, bodyLength) => {
3298
3275
  const isFirstColumn = colIndex === 0;
3299
3276
  const isLastColumn = colIndex === Object.values(row.cells).length - 1;
3300
3277
  const isLastRow = rowIndex === rowsLength - 1;
@@ -3302,7 +3279,7 @@ var drawBorder = (div, row, colIndex, rowIndex, rowsLength, arg) => {
3302
3279
  setBorder(div, "Top", arg);
3303
3280
  if (isFirstColumn) setBorder(div, "Left", arg);
3304
3281
  if (isLastColumn) setBorder(div, "Right", arg);
3305
- if (JSON.parse(arg.value || "[]").length === 0) setBorder(div, "Bottom", arg);
3282
+ if (bodyLength === 0) setBorder(div, "Bottom", arg);
3306
3283
  } else if (row.section === "body") {
3307
3284
  if (!arg.schema.showHead && rowIndex === 0) setBorder(div, "Top", arg);
3308
3285
  if (isFirstColumn) setBorder(div, "Left", arg);
@@ -3325,20 +3302,28 @@ var renderRowUi = (args) => {
3325
3302
  div.style.width = `${cell.width}mm`;
3326
3303
  div.style.height = `${cell.height}mm`;
3327
3304
  div.style.boxSizing = "border-box";
3328
- drawBorder(div, row, colIndex, rowIndex, rows.length, arg);
3329
- div.style.cursor = arg.mode === "designer" || arg.mode === "form" && section === "body" ? "text" : "default";
3305
+ drawBorder(div, row, colIndex, rowIndex, rows.length, arg, value.length);
3306
+ const imageCellEditable = arg.mode === "designer" || arg.mode === "form" && !arg.schema.readOnly;
3307
+ div.style.cursor = cell.isImage() ? imageCellEditable && section === "body" ? "pointer" : "default" : arg.mode === "designer" || arg.mode === "form" && section === "body" ? "text" : "default";
3330
3308
  div.addEventListener("click", () => {
3331
3309
  if (arg.mode === "viewer") return;
3310
+ const enteringEmptyImageCell = cell.isImage() && section === "body" && imageCellEditable && cell.raw === "" && (editingPosition.rowIndex !== rowIndex || editingPosition.colIndex !== colIndex);
3332
3311
  onChangeEditingPosition({
3333
3312
  rowIndex,
3334
3313
  colIndex
3335
3314
  });
3315
+ if (enteringEmptyImageCell) requestImagePicker({
3316
+ rowIndex,
3317
+ colIndex,
3318
+ schemaKey: tableSchemaKey(arg.schema)
3319
+ });
3336
3320
  });
3337
3321
  arg.rootElement.appendChild(div);
3338
3322
  const isEditing = editingPosition.rowIndex === rowIndex && editingPosition.colIndex === colIndex;
3339
3323
  let mode = "viewer";
3340
3324
  if (arg.mode === "form") mode = section === "body" && isEditing && !arg.schema.readOnly ? "designer" : "viewer";
3341
3325
  else if (arg.mode === "designer") mode = isEditing ? "designer" : "form";
3326
+ if (cell.isImage() && !isEditing) mode = "viewer";
3342
3327
  cellUiRender({
3343
3328
  ...arg,
3344
3329
  stopEditing: () => {
@@ -3377,7 +3362,11 @@ var renderRowUi = (args) => {
3377
3362
  },
3378
3363
  width: cell.width,
3379
3364
  height: cell.height,
3380
- ...convertToCellStyle(cell.styles)
3365
+ ...convertToCellStyle(cell.styles),
3366
+ cellType: cell.isImage() ? "image" : "text",
3367
+ columnIndex: colIndex,
3368
+ rowIndex,
3369
+ pickerSchemaKey: tableSchemaKey(arg.schema)
3381
3370
  }
3382
3371
  });
3383
3372
  colOffsetX += cell.width;
@@ -3398,9 +3387,11 @@ var resetEditingPosition = () => {
3398
3387
  headEditingPosition.colIndex = -1;
3399
3388
  bodyEditingPosition.rowIndex = -1;
3400
3389
  bodyEditingPosition.colIndex = -1;
3390
+ clearImagePickerRequest();
3401
3391
  };
3402
3392
  var uiRender = async (arg) => {
3403
3393
  const { rootElement, onChange, schema, value, mode, scale } = arg;
3394
+ noteRenderingTable(schema);
3404
3395
  const body = getBody(value);
3405
3396
  const bodyRange = getTableBodyRange(schema);
3406
3397
  const bodyWidthRange = getBodyWithSchemaRange(value, schema, bodyRange);
@@ -3526,6 +3517,10 @@ var uiRender = async (arg) => {
3526
3517
  {
3527
3518
  key: "content",
3528
3519
  value: JSON.stringify(bodyWidthRange.map((row) => row.filter((_, j) => j !== i)))
3520
+ },
3521
+ {
3522
+ key: "columnStyles",
3523
+ value: remapColumnStylesOnRemove(schema.columnStyles ?? {}, i)
3529
3524
  }
3530
3525
  ]);
3531
3526
  }
@@ -3604,15 +3599,678 @@ var uiRender = async (arg) => {
3604
3599
  });
3605
3600
  };
3606
3601
  //#endregion
3602
+ //#region src/tables/columnsWidget.ts
3603
+ var cellTypeWarnCache = /* @__PURE__ */ new Map();
3604
+ /** More than this many columns, and each block can collapse. */
3605
+ var COLLAPSE_WHEN_MORE_THAN = 6;
3606
+ var expandedColumnBlocks = /* @__PURE__ */ new Set();
3607
+ var columnBlockKey = (schemaId, columnIndex) => `${schemaId}:${columnIndex}`;
3608
+ var themeRecord = (theme) => theme ?? void 0;
3609
+ var themeToken = (theme, key, fallback) => {
3610
+ const value = themeRecord(theme)?.[key];
3611
+ return typeof value === "string" && value.length > 0 ? value : fallback;
3612
+ };
3613
+ var themeNumber = (theme, key, fallback) => {
3614
+ const value = themeRecord(theme)?.[key];
3615
+ return typeof value === "number" && Number.isFinite(value) ? value : fallback;
3616
+ };
3617
+ /** antd outlined Select / InputNumber metrics, read from the Designer token when present. */
3618
+ var panelControlMetrics = (theme) => {
3619
+ const lineWidth = themeNumber(theme, "lineWidth", 1);
3620
+ const controlHeight = themeNumber(theme, "controlHeight", 32);
3621
+ const paddingSm = themeNumber(theme, "paddingSM", 12);
3622
+ const controlHeightSm = themeNumber(theme, "controlHeightSM", 24);
3623
+ const fontSize = themeNumber(theme, "fontSize", 14);
3624
+ const outlineWidth = themeNumber(theme, "controlOutlineWidth", 2);
3625
+ return {
3626
+ lineWidth,
3627
+ controlHeight,
3628
+ borderRadius: themeNumber(theme, "borderRadius", 6),
3629
+ fontSize,
3630
+ paddingInline: Math.max(0, paddingSm - lineWidth),
3631
+ handleWidth: Math.max(0, controlHeightSm - lineWidth * 2),
3632
+ handleFontSize: fontSize / 2,
3633
+ outlineWidth,
3634
+ colorBorder: themeToken(theme, "colorBorder", "#d9d9d9"),
3635
+ colorBg: themeToken(theme, "colorBgContainer", "#ffffff"),
3636
+ colorText: themeToken(theme, "colorText", "rgba(0, 0, 0, 0.88)"),
3637
+ colorArrow: themeToken(theme, "colorTextQuaternary", "rgba(0, 0, 0, 0.25)"),
3638
+ colorIcon: themeToken(theme, "colorIcon", "rgba(0, 0, 0, 0.45)"),
3639
+ colorPrimary: themeToken(theme, "colorPrimary", "#1677ff"),
3640
+ colorPrimaryHover: themeToken(theme, "colorPrimaryHover", "#4096ff"),
3641
+ controlOutline: themeToken(theme, "controlOutline", "rgba(5, 145, 255, 0.1)"),
3642
+ motion: themeToken(theme, "motionDurationMid", "0.2s")
3643
+ };
3644
+ };
3645
+ var ANT_DOWN_PATH = "M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z";
3646
+ var ANT_UP_PATH = "M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z";
3647
+ var appendSvgIcon = (parent, path, size) => {
3648
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
3649
+ svg.setAttribute("viewBox", "64 64 896 896");
3650
+ svg.setAttribute("width", String(size));
3651
+ svg.setAttribute("height", String(size));
3652
+ svg.setAttribute("fill", "currentColor");
3653
+ svg.setAttribute("aria-hidden", "true");
3654
+ svg.setAttribute("focusable", "false");
3655
+ const shape = document.createElementNS("http://www.w3.org/2000/svg", "path");
3656
+ shape.setAttribute("d", path);
3657
+ svg.appendChild(shape);
3658
+ parent.appendChild(svg);
3659
+ };
3660
+ var ensurePanelControlStyles = (rootElement, metrics) => {
3661
+ rootElement.style.setProperty("--pdfme-border", metrics.colorBorder);
3662
+ rootElement.style.setProperty("--pdfme-hover-border", metrics.colorPrimaryHover);
3663
+ rootElement.style.setProperty("--pdfme-active-border", metrics.colorPrimary);
3664
+ rootElement.style.setProperty("--pdfme-outline", metrics.controlOutline);
3665
+ rootElement.style.setProperty("--pdfme-outline-width", `${metrics.outlineWidth}px`);
3666
+ rootElement.style.setProperty("--pdfme-handle-width", `${metrics.handleWidth}px`);
3667
+ rootElement.style.setProperty("--pdfme-handle-hover", metrics.colorPrimary);
3668
+ rootElement.style.setProperty("--pdfme-padding-inline", `${metrics.paddingInline}px`);
3669
+ if (rootElement.querySelector("style[data-pdfme-column-controls]")) return;
3670
+ const style = document.createElement("style");
3671
+ style.dataset.pdfmeColumnControls = "true";
3672
+ style.textContent = `
3673
+ .pdfme-column-select,
3674
+ .pdfme-column-number {
3675
+ border-color: var(--pdfme-border);
3676
+ }
3677
+ .pdfme-column-select:hover,
3678
+ .pdfme-column-number:hover {
3679
+ border-color: var(--pdfme-hover-border);
3680
+ }
3681
+ .pdfme-column-select:focus-within,
3682
+ .pdfme-column-number:focus-within {
3683
+ border-color: var(--pdfme-active-border);
3684
+ box-shadow: 0 0 0 var(--pdfme-outline-width) var(--pdfme-outline);
3685
+ outline: 0;
3686
+ }
3687
+ .pdfme-column-select-input,
3688
+ .pdfme-column-number-input {
3689
+ outline: none;
3690
+ }
3691
+ .pdfme-column-select-input {
3692
+ appearance: none;
3693
+ -webkit-appearance: none;
3694
+ }
3695
+ .pdfme-column-number-input {
3696
+ appearance: textfield;
3697
+ -moz-appearance: textfield;
3698
+ padding-left: var(--pdfme-padding-inline);
3699
+ padding-right: var(--pdfme-padding-inline);
3700
+ }
3701
+ .pdfme-column-number:hover .pdfme-column-number-input,
3702
+ .pdfme-column-number:focus-within .pdfme-column-number-input {
3703
+ padding-right: calc(var(--pdfme-handle-width) + var(--pdfme-padding-inline));
3704
+ }
3705
+ .pdfme-column-number-input::-webkit-outer-spin-button,
3706
+ .pdfme-column-number-input::-webkit-inner-spin-button {
3707
+ -webkit-appearance: none;
3708
+ margin: 0;
3709
+ }
3710
+ .pdfme-column-number-handlers {
3711
+ opacity: 0;
3712
+ width: 0;
3713
+ }
3714
+ .pdfme-column-number:hover .pdfme-column-number-handlers,
3715
+ .pdfme-column-number:focus-within .pdfme-column-number-handlers {
3716
+ opacity: 1;
3717
+ width: var(--pdfme-handle-width);
3718
+ }
3719
+ .pdfme-column-number-handler:hover {
3720
+ color: var(--pdfme-handle-hover);
3721
+ height: 60%;
3722
+ }
3723
+ `;
3724
+ rootElement.appendChild(style);
3725
+ };
3726
+ var HORIZONTAL_ALIGNMENTS = [
3727
+ {
3728
+ value: "left",
3729
+ icon: TextAlignLeftIcon,
3730
+ labelKey: "schemas.left"
3731
+ },
3732
+ {
3733
+ value: "center",
3734
+ icon: TextAlignCenterIcon,
3735
+ labelKey: "schemas.center"
3736
+ },
3737
+ {
3738
+ value: "right",
3739
+ icon: TextAlignRightIcon,
3740
+ labelKey: "schemas.right"
3741
+ }
3742
+ ];
3743
+ var VERTICAL_ALIGNMENTS = [
3744
+ {
3745
+ value: "top",
3746
+ icon: TextVerticalAlignTopIcon,
3747
+ labelKey: "schemas.top"
3748
+ },
3749
+ {
3750
+ value: "middle",
3751
+ icon: TextVerticalAlignMiddleIcon,
3752
+ labelKey: "schemas.middle"
3753
+ },
3754
+ {
3755
+ value: "bottom",
3756
+ icon: TextVerticalAlignBottomIcon,
3757
+ labelKey: "schemas.bottom"
3758
+ }
3759
+ ];
3760
+ var isUnset = (value) => value == null;
3761
+ var clearColumnContent = (content, columnIndex) => {
3762
+ let rows;
3763
+ try {
3764
+ rows = JSON.parse(content ?? "[]");
3765
+ } catch {
3766
+ return content ?? "[]";
3767
+ }
3768
+ if (!Array.isArray(rows)) return content ?? "[]";
3769
+ const next = rows.map((row) => {
3770
+ if (!Array.isArray(row)) return row;
3771
+ const copy = row.slice();
3772
+ if (columnIndex < copy.length) copy[columnIndex] = "";
3773
+ return copy;
3774
+ });
3775
+ return JSON.stringify(next);
3776
+ };
3777
+ var dropColumnKey = (map, columnIndex) => {
3778
+ if (!map) return map;
3779
+ const next = { ...map };
3780
+ delete next[columnIndex];
3781
+ return Object.keys(next).length === 0 ? void 0 : next;
3782
+ };
3783
+ var columnLabel = (head, index, i18n) => {
3784
+ const label = head[index];
3785
+ return label ? label : `${i18n("schemas.table.columnLabel")} ${index + 1}`;
3786
+ };
3787
+ var commitStyles = (props, columnStyles, content) => {
3788
+ const schemaId = props.activeSchema.id;
3789
+ const changes = [{
3790
+ key: "columnStyles",
3791
+ value: columnStyles,
3792
+ schemaId
3793
+ }];
3794
+ if (content !== void 0) changes.push({
3795
+ key: "content",
3796
+ value: content,
3797
+ schemaId
3798
+ });
3799
+ props.changeSchemas(changes);
3800
+ };
3801
+ var setCellType = (props, table, columnIndex, nextType) => {
3802
+ const next = cloneDeep(table.columnStyles ?? {});
3803
+ if (nextType === "image") {
3804
+ next.cellType = {
3805
+ ...next.cellType,
3806
+ [columnIndex]: "image"
3807
+ };
3808
+ if (isUnset(next.imageHeightMode?.[columnIndex])) next.imageHeightMode = {
3809
+ ...next.imageHeightMode,
3810
+ [columnIndex]: DEFAULT_TABLE_IMAGE_HEIGHT_MODE
3811
+ };
3812
+ if (isUnset(next.imageHeight?.[columnIndex])) next.imageHeight = {
3813
+ ...next.imageHeight,
3814
+ [columnIndex]: 20
3815
+ };
3816
+ if (isUnset(next.alignment?.[columnIndex])) next.alignment = {
3817
+ ...next.alignment,
3818
+ [columnIndex]: "center"
3819
+ };
3820
+ } else {
3821
+ const cellType = dropColumnKey(next.cellType, columnIndex);
3822
+ const imageHeightMode = dropColumnKey(next.imageHeightMode, columnIndex);
3823
+ const imageHeight = dropColumnKey(next.imageHeight, columnIndex);
3824
+ if (cellType) next.cellType = cellType;
3825
+ else delete next.cellType;
3826
+ if (imageHeightMode) next.imageHeightMode = imageHeightMode;
3827
+ else delete next.imageHeightMode;
3828
+ if (imageHeight) next.imageHeight = imageHeight;
3829
+ else delete next.imageHeight;
3830
+ }
3831
+ commitStyles(props, next, clearColumnContent(table.content, columnIndex));
3832
+ };
3833
+ var setImageHeightMode = (props, table, columnIndex, mode) => {
3834
+ const next = cloneDeep(table.columnStyles ?? {});
3835
+ next.imageHeightMode = {
3836
+ ...next.imageHeightMode,
3837
+ [columnIndex]: mode
3838
+ };
3839
+ commitStyles(props, next);
3840
+ };
3841
+ var setImageHeight = (props, table, columnIndex, height) => {
3842
+ const next = cloneDeep(table.columnStyles ?? {});
3843
+ next.imageHeight = {
3844
+ ...next.imageHeight,
3845
+ [columnIndex]: height
3846
+ };
3847
+ commitStyles(props, next);
3848
+ };
3849
+ var setAlignment = (props, table, columnIndex, value) => {
3850
+ const next = cloneDeep(table.columnStyles ?? {});
3851
+ if (next.alignment?.[columnIndex] === value) return;
3852
+ next.alignment = {
3853
+ ...next.alignment,
3854
+ [columnIndex]: value
3855
+ };
3856
+ commitStyles(props, next);
3857
+ };
3858
+ var setVerticalAlignment = (props, table, columnIndex, value) => {
3859
+ const next = cloneDeep(table.columnStyles ?? {});
3860
+ if (next.verticalAlignment?.[columnIndex] === value) return;
3861
+ next.verticalAlignment = {
3862
+ ...next.verticalAlignment,
3863
+ [columnIndex]: value
3864
+ };
3865
+ commitStyles(props, next);
3866
+ };
3867
+ var iconSrc = (svg, color) => `data:image/svg+xml;utf8,${encodeURIComponent(svg.replace(/currentColor/g, color))}`;
3868
+ var appendIcon = (parent, svg, color, size = 17) => {
3869
+ const img = document.createElement("img");
3870
+ img.alt = "";
3871
+ img.width = size;
3872
+ img.height = size;
3873
+ img.src = iconSrc(svg, color);
3874
+ parent.appendChild(img);
3875
+ return img;
3876
+ };
3877
+ var appendSelect = (parent, options, value, control, columnIndex, metrics, layout, onChange) => {
3878
+ const shell = document.createElement("div");
3879
+ shell.className = "pdfme-column-select";
3880
+ shell.style.position = "relative";
3881
+ shell.style.display = "inline-flex";
3882
+ shell.style.alignItems = "center";
3883
+ shell.style.boxSizing = "border-box";
3884
+ shell.style.height = `${metrics.controlHeight}px`;
3885
+ shell.style.borderWidth = `${metrics.lineWidth}px`;
3886
+ shell.style.borderStyle = "solid";
3887
+ shell.style.borderRadius = `${metrics.borderRadius}px`;
3888
+ shell.style.background = metrics.colorBg;
3889
+ shell.style.overflow = "hidden";
3890
+ shell.style.transition = `all ${metrics.motion}`;
3891
+ shell.style.flexShrink = layout.flex ? "1" : "0";
3892
+ if (layout.width) shell.style.width = layout.width;
3893
+ if (layout.flex) {
3894
+ shell.style.flex = layout.flex;
3895
+ shell.style.minWidth = "0";
3896
+ }
3897
+ const select = document.createElement("select");
3898
+ select.className = "pdfme-column-select-input";
3899
+ select.dataset.control = control;
3900
+ select.dataset.columnIndex = String(columnIndex);
3901
+ select.style.width = "100%";
3902
+ select.style.height = "100%";
3903
+ select.style.boxSizing = "border-box";
3904
+ select.style.margin = "0";
3905
+ select.style.border = "0";
3906
+ select.style.borderRadius = `${metrics.borderRadius}px`;
3907
+ select.style.background = "transparent";
3908
+ select.style.padding = `0 ${metrics.paddingInline + 18}px 0 ${metrics.paddingInline}px`;
3909
+ select.style.font = "inherit";
3910
+ select.style.fontSize = `${metrics.fontSize}px`;
3911
+ select.style.lineHeight = `${metrics.controlHeight - metrics.lineWidth * 2}px`;
3912
+ select.style.color = metrics.colorText;
3913
+ select.style.cursor = "pointer";
3914
+ options.forEach((option) => {
3915
+ const element = document.createElement("option");
3916
+ element.value = option.value;
3917
+ element.textContent = option.label;
3918
+ select.appendChild(element);
3919
+ });
3920
+ select.value = value;
3921
+ select.addEventListener("change", () => {
3922
+ onChange(select.value);
3923
+ });
3924
+ const arrow = document.createElement("span");
3925
+ arrow.className = "pdfme-column-select-arrow";
3926
+ arrow.setAttribute("aria-hidden", "true");
3927
+ arrow.style.position = "absolute";
3928
+ arrow.style.top = "50%";
3929
+ arrow.style.right = `${metrics.paddingInline}px`;
3930
+ arrow.style.transform = "translateY(-50%)";
3931
+ arrow.style.display = "flex";
3932
+ arrow.style.width = "12px";
3933
+ arrow.style.height = "12px";
3934
+ arrow.style.color = metrics.colorArrow;
3935
+ arrow.style.fontSize = "12px";
3936
+ arrow.style.lineHeight = "1";
3937
+ arrow.style.pointerEvents = "none";
3938
+ appendSvgIcon(arrow, ANT_DOWN_PATH, 12);
3939
+ shell.appendChild(select);
3940
+ shell.appendChild(arrow);
3941
+ parent.appendChild(shell);
3942
+ return select;
3943
+ };
3944
+ var appendNumberInput = (parent, value, columnIndex, ariaLabel, metrics, onCommit) => {
3945
+ const shell = document.createElement("div");
3946
+ shell.className = "pdfme-column-number";
3947
+ shell.style.position = "relative";
3948
+ shell.style.display = "inline-flex";
3949
+ shell.style.alignItems = "center";
3950
+ shell.style.boxSizing = "border-box";
3951
+ shell.style.width = "72px";
3952
+ shell.style.height = `${metrics.controlHeight}px`;
3953
+ shell.style.flexShrink = "0";
3954
+ shell.style.borderWidth = `${metrics.lineWidth}px`;
3955
+ shell.style.borderStyle = "solid";
3956
+ shell.style.borderRadius = `${metrics.borderRadius}px`;
3957
+ shell.style.background = metrics.colorBg;
3958
+ shell.style.transition = `all ${metrics.motion}`;
3959
+ shell.style.overflow = "hidden";
3960
+ const input = document.createElement("input");
3961
+ input.type = "number";
3962
+ input.min = "1";
3963
+ input.step = "1";
3964
+ input.className = "pdfme-column-number-input";
3965
+ input.dataset.control = "imageHeight";
3966
+ input.dataset.columnIndex = String(columnIndex);
3967
+ input.setAttribute("aria-label", ariaLabel);
3968
+ input.style.width = "100%";
3969
+ input.style.height = "100%";
3970
+ input.style.boxSizing = "border-box";
3971
+ input.style.margin = "0";
3972
+ input.style.border = "0";
3973
+ input.style.borderRadius = "0";
3974
+ input.style.background = "transparent";
3975
+ input.style.font = "inherit";
3976
+ input.style.fontSize = `${metrics.fontSize}px`;
3977
+ input.style.lineHeight = "22px";
3978
+ input.style.color = metrics.colorText;
3979
+ input.value = value;
3980
+ input.addEventListener("change", () => {
3981
+ onCommit(input);
3982
+ });
3983
+ const handlers = document.createElement("div");
3984
+ handlers.className = "pdfme-column-number-handlers";
3985
+ handlers.style.position = "absolute";
3986
+ handlers.style.top = "0";
3987
+ handlers.style.right = "0";
3988
+ handlers.style.bottom = "0";
3989
+ handlers.style.display = "flex";
3990
+ handlers.style.flexDirection = "column";
3991
+ handlers.style.overflow = "hidden";
3992
+ handlers.style.background = metrics.colorBg;
3993
+ handlers.style.borderRadius = `0 ${metrics.borderRadius}px ${metrics.borderRadius}px 0`;
3994
+ handlers.style.transition = `all ${metrics.motion}`;
3995
+ const step = (delta) => {
3996
+ const current = Number(input.value);
3997
+ const base = Number.isFinite(current) ? current : Number(value);
3998
+ const next = Math.max(1, base + delta);
3999
+ if (next === base) return;
4000
+ input.value = String(next);
4001
+ input.dispatchEvent(new Event("change"));
4002
+ };
4003
+ const addHandler = (direction, path, label) => {
4004
+ const button = document.createElement("button");
4005
+ button.type = "button";
4006
+ button.className = "pdfme-column-number-handler";
4007
+ button.dataset.control = direction === "up" ? "imageHeightUp" : "imageHeightDown";
4008
+ button.setAttribute("aria-label", label);
4009
+ button.style.flex = "1 1 50%";
4010
+ button.style.display = "flex";
4011
+ button.style.alignItems = "center";
4012
+ button.style.justifyContent = "center";
4013
+ button.style.margin = "0";
4014
+ button.style.padding = "0";
4015
+ button.style.border = "0";
4016
+ button.style.borderLeft = `${metrics.lineWidth}px solid ${metrics.colorBorder}`;
4017
+ button.style.background = "transparent";
4018
+ button.style.color = metrics.colorIcon;
4019
+ button.style.cursor = "pointer";
4020
+ button.style.lineHeight = "0";
4021
+ if (direction === "down") button.style.borderTop = `${metrics.lineWidth}px solid ${metrics.colorBorder}`;
4022
+ button.addEventListener("mousedown", (event) => {
4023
+ event.preventDefault();
4024
+ });
4025
+ button.addEventListener("click", () => {
4026
+ step(direction === "up" ? 1 : -1);
4027
+ });
4028
+ appendSvgIcon(button, path, metrics.handleFontSize);
4029
+ handlers.appendChild(button);
4030
+ };
4031
+ addHandler("up", ANT_UP_PATH, "Increase Value");
4032
+ addHandler("down", ANT_DOWN_PATH, "Decrease Value");
4033
+ shell.appendChild(input);
4034
+ shell.appendChild(handlers);
4035
+ parent.appendChild(shell);
4036
+ return input;
4037
+ };
4038
+ var appendButtonGroup = (parent, control, buttons, current, columnIndex, colors, i18n, onSelect) => {
4039
+ const group = document.createElement("div");
4040
+ group.dataset.control = control;
4041
+ group.dataset.columnIndex = String(columnIndex);
4042
+ group.setAttribute("role", "group");
4043
+ group.style.display = "inline-flex";
4044
+ buttons.forEach((button, index) => {
4045
+ const element = document.createElement("button");
4046
+ element.type = "button";
4047
+ element.dataset.value = button.value;
4048
+ const label = i18n(button.labelKey);
4049
+ element.setAttribute("aria-label", label);
4050
+ element.title = label;
4051
+ const active = current === button.value;
4052
+ element.setAttribute("aria-pressed", active ? "true" : "false");
4053
+ element.style.padding = "7px";
4054
+ element.style.lineHeight = "0";
4055
+ element.style.background = "transparent";
4056
+ element.style.cursor = "pointer";
4057
+ element.style.border = `1px solid ${active ? colors.primary : colors.border}`;
4058
+ element.style.marginLeft = index === 0 ? "0" : "-1px";
4059
+ element.style.borderRadius = index === 0 ? "6px 0 0 6px" : index === buttons.length - 1 ? "0 6px 6px 0" : "0";
4060
+ element.style.position = "relative";
4061
+ element.style.zIndex = active ? "1" : "0";
4062
+ appendIcon(element, button.icon, active ? colors.primary : colors.text);
4063
+ element.addEventListener("click", () => {
4064
+ if (current === button.value) return;
4065
+ onSelect(button.value);
4066
+ });
4067
+ group.appendChild(element);
4068
+ });
4069
+ parent.appendChild(group);
4070
+ return group;
4071
+ };
4072
+ var ellipsis = (element) => {
4073
+ element.style.overflow = "hidden";
4074
+ element.style.textOverflow = "ellipsis";
4075
+ element.style.whiteSpace = "nowrap";
4076
+ element.style.minWidth = "0";
4077
+ };
4078
+ var TableColumns = (props) => {
4079
+ const { rootElement, activeSchema, i18n } = props;
4080
+ const table = activeSchema;
4081
+ const head = table.head || [];
4082
+ const columnStyles = table.columnStyles ?? {};
4083
+ const metrics = panelControlMetrics(props.theme);
4084
+ ensurePanelControlStyles(rootElement, metrics);
4085
+ const colors = {
4086
+ primary: themeToken(props.theme, "colorPrimary", "#1677ff"),
4087
+ border: themeToken(props.theme, "colorBorder", "#d9d9d9"),
4088
+ text: themeToken(props.theme, "colorText", "#000000"),
4089
+ split: themeToken(props.theme, "colorSplit", "#f0f0f0")
4090
+ };
4091
+ const collapsible = head.length > COLLAPSE_WHEN_MORE_THAN;
4092
+ head.forEach((_, index) => {
4093
+ const storedType = columnStyles.cellType?.[index];
4094
+ const cellType = normalizeTableCellType(storedType, index, cellTypeWarnCache) ?? "text";
4095
+ const label = columnLabel(head, index, i18n);
4096
+ const alignment = columnStyles.alignment?.[index];
4097
+ const verticalAlignment = columnStyles.verticalAlignment?.[index];
4098
+ const mode = cellType === "image" ? normalizeTableImageHeightMode(columnStyles.imageHeightMode?.[index]) ?? "fixed" : void 0;
4099
+ const block = document.createElement("div");
4100
+ block.dataset.columnIndex = String(index);
4101
+ block.style.marginBottom = index === head.length - 1 ? "0" : "10px";
4102
+ block.style.paddingBottom = index === head.length - 1 ? "0" : "10px";
4103
+ if (index !== head.length - 1) block.style.borderBottom = `1px solid ${colors.split}`;
4104
+ const body = document.createElement("div");
4105
+ body.dataset.control = "column-body";
4106
+ const expanded = collapsible && expandedColumnBlocks.has(columnBlockKey(activeSchema.id, index));
4107
+ if (collapsible) {
4108
+ const summary = document.createElement("button");
4109
+ summary.type = "button";
4110
+ summary.dataset.control = "summary";
4111
+ summary.setAttribute("aria-expanded", expanded ? "true" : "false");
4112
+ summary.setAttribute("aria-label", `Expand ${label}`);
4113
+ summary.title = label;
4114
+ summary.style.display = "flex";
4115
+ summary.style.alignItems = "center";
4116
+ summary.style.gap = "6px";
4117
+ summary.style.width = "100%";
4118
+ summary.style.padding = "0";
4119
+ summary.style.border = "none";
4120
+ summary.style.background = "transparent";
4121
+ summary.style.cursor = "pointer";
4122
+ summary.style.font = "inherit";
4123
+ summary.style.textAlign = "left";
4124
+ const name = document.createElement("span");
4125
+ name.dataset.control = "summary-name";
4126
+ name.textContent = label;
4127
+ name.style.fontWeight = "600";
4128
+ name.style.fontSize = "12px";
4129
+ name.style.flex = "1";
4130
+ ellipsis(name);
4131
+ summary.appendChild(name);
4132
+ const typeLabel = document.createElement("span");
4133
+ typeLabel.dataset.control = "summary-type";
4134
+ typeLabel.textContent = i18n(`schemas.table.cellType.${cellType}`);
4135
+ typeLabel.style.fontSize = "12px";
4136
+ typeLabel.style.flexShrink = "0";
4137
+ summary.appendChild(typeLabel);
4138
+ const alignmentIcon = HORIZONTAL_ALIGNMENTS.find((button) => button.value === alignment);
4139
+ if (alignmentIcon) {
4140
+ const icon = document.createElement("span");
4141
+ icon.dataset.control = "summary-alignment";
4142
+ icon.dataset.value = alignmentIcon.value;
4143
+ icon.style.lineHeight = "0";
4144
+ icon.style.flexShrink = "0";
4145
+ appendIcon(icon, alignmentIcon.icon, colors.text, 14);
4146
+ summary.appendChild(icon);
4147
+ }
4148
+ const verticalIcon = VERTICAL_ALIGNMENTS.find((button) => button.value === verticalAlignment);
4149
+ if (verticalIcon) {
4150
+ const icon = document.createElement("span");
4151
+ icon.dataset.control = "summary-vertical-alignment";
4152
+ icon.dataset.value = verticalIcon.value;
4153
+ icon.style.lineHeight = "0";
4154
+ icon.style.flexShrink = "0";
4155
+ appendIcon(icon, verticalIcon.icon, colors.text, 14);
4156
+ summary.appendChild(icon);
4157
+ }
4158
+ if (cellType === "image" && mode === "fixed") {
4159
+ const height = document.createElement("span");
4160
+ height.dataset.control = "summary-height";
4161
+ height.textContent = `${resolveFixedTableImageHeight(columnStyles.imageHeight?.[index])}mm`;
4162
+ height.style.fontSize = "12px";
4163
+ height.style.flexShrink = "0";
4164
+ summary.appendChild(height);
4165
+ }
4166
+ summary.addEventListener("click", () => {
4167
+ expandedColumnBlocks.add(columnBlockKey(activeSchema.id, index));
4168
+ summary.hidden = true;
4169
+ summary.setAttribute("aria-expanded", "true");
4170
+ body.hidden = false;
4171
+ });
4172
+ summary.hidden = expanded;
4173
+ body.hidden = !expanded;
4174
+ block.appendChild(summary);
4175
+ }
4176
+ const header = document.createElement("div");
4177
+ header.style.display = "flex";
4178
+ header.style.alignItems = "center";
4179
+ header.style.gap = "8px";
4180
+ if (collapsible) {
4181
+ const collapse = document.createElement("button");
4182
+ collapse.type = "button";
4183
+ collapse.dataset.control = "collapse";
4184
+ collapse.setAttribute("aria-expanded", "true");
4185
+ collapse.setAttribute("aria-label", `Collapse ${label}`);
4186
+ collapse.textContent = "▾";
4187
+ collapse.style.border = "none";
4188
+ collapse.style.background = "transparent";
4189
+ collapse.style.cursor = "pointer";
4190
+ collapse.style.padding = "0 2px 0 0";
4191
+ collapse.style.lineHeight = "1";
4192
+ collapse.addEventListener("click", () => {
4193
+ expandedColumnBlocks.delete(columnBlockKey(activeSchema.id, index));
4194
+ const summary = block.querySelector("[data-control=\"summary\"]");
4195
+ if (summary) {
4196
+ summary.hidden = false;
4197
+ summary.setAttribute("aria-expanded", "false");
4198
+ }
4199
+ body.hidden = true;
4200
+ });
4201
+ header.appendChild(collapse);
4202
+ }
4203
+ const title = document.createElement("div");
4204
+ title.dataset.control = "label";
4205
+ title.textContent = label;
4206
+ title.title = label;
4207
+ title.style.fontSize = "12px";
4208
+ title.style.fontWeight = "600";
4209
+ title.style.flex = "1";
4210
+ ellipsis(title);
4211
+ header.appendChild(title);
4212
+ appendSelect(header, TABLE_CELL_TYPES.map((type) => ({
4213
+ value: type,
4214
+ label: i18n(`schemas.table.cellType.${type}`)
4215
+ })), cellType, "cellType", index, metrics, { width: "110px" }, (value) => {
4216
+ if (value !== "text" && value !== "image") return;
4217
+ if (value === cellType) return;
4218
+ setCellType(props, table, index, value);
4219
+ });
4220
+ body.appendChild(header);
4221
+ const alignments = document.createElement("div");
4222
+ alignments.style.display = "flex";
4223
+ alignments.style.justifyContent = "space-between";
4224
+ alignments.style.alignItems = "center";
4225
+ alignments.style.marginTop = "8px";
4226
+ appendButtonGroup(alignments, "alignment", HORIZONTAL_ALIGNMENTS, alignment, index, colors, i18n, (value) => {
4227
+ if (value !== "left" && value !== "center" && value !== "right") return;
4228
+ setAlignment(props, table, index, value);
4229
+ });
4230
+ appendButtonGroup(alignments, "verticalAlignment", VERTICAL_ALIGNMENTS, verticalAlignment, index, colors, i18n, (value) => {
4231
+ if (value !== "top" && value !== "middle" && value !== "bottom") return;
4232
+ setVerticalAlignment(props, table, index, value);
4233
+ });
4234
+ body.appendChild(alignments);
4235
+ if (cellType === "image" && mode) {
4236
+ const imageRow = document.createElement("div");
4237
+ imageRow.style.display = "flex";
4238
+ imageRow.style.alignItems = "center";
4239
+ imageRow.style.gap = "8px";
4240
+ imageRow.style.marginTop = "8px";
4241
+ appendSelect(imageRow, TABLE_IMAGE_HEIGHT_MODES.map((heightMode) => ({
4242
+ value: heightMode,
4243
+ label: i18n(`schemas.table.imageHeightMode.${heightMode}`)
4244
+ })), mode, "imageHeightMode", index, metrics, { flex: "1" }, (value) => {
4245
+ if (value !== "fixed" && value !== "auto") return;
4246
+ if (value === mode) return;
4247
+ setImageHeightMode(props, table, index, value);
4248
+ });
4249
+ if (mode === "fixed") {
4250
+ const displayHeight = resolveFixedTableImageHeight(columnStyles.imageHeight?.[index]);
4251
+ appendNumberInput(imageRow, String(displayHeight), index, i18n("schemas.table.imageHeightMode.fixed"), metrics, (input) => {
4252
+ const parsed = Number(input.value);
4253
+ if (!Number.isFinite(parsed) || parsed <= 0) {
4254
+ input.value = String(displayHeight);
4255
+ return;
4256
+ }
4257
+ setImageHeight(props, table, index, parsed);
4258
+ });
4259
+ }
4260
+ body.appendChild(imageRow);
4261
+ }
4262
+ block.appendChild(body);
4263
+ rootElement.appendChild(block);
4264
+ });
4265
+ };
4266
+ //#endregion
3607
4267
  //#region src/tables/index.ts
3608
4268
  var tableSchema = {
3609
4269
  pdf: pdfRender,
3610
4270
  ui: uiRender,
3611
4271
  propPanel: {
3612
4272
  schema: ({ activeSchema, options, i18n }) => {
3613
- const tableSchema = activeSchema;
3614
- const head = tableSchema.head || [];
3615
- const showHead = tableSchema.showHead || false;
4273
+ const showHead = activeSchema.showHead || false;
3616
4274
  const font = options.font || { [DEFAULT_FONT_NAME]: {
3617
4275
  data: "",
3618
4276
  fallback: true
@@ -3687,18 +4345,22 @@ var tableSchema = {
3687
4345
  isBody: true
3688
4346
  })
3689
4347
  },
3690
- columnStyles: {
4348
+ tableColumns: {
3691
4349
  title: i18n("schemas.table.columnStyle"),
3692
4350
  type: "object",
3693
4351
  widget: "Card",
4352
+ bind: false,
3694
4353
  span: 24,
3695
- properties: getColumnStylesPropPanelSchema({
3696
- head,
3697
- i18n
3698
- })
4354
+ properties: { columns: {
4355
+ type: "void",
4356
+ widget: "TableColumns",
4357
+ bind: false,
4358
+ span: 24
4359
+ } }
3699
4360
  }
3700
4361
  };
3701
4362
  },
4363
+ widgets: { TableColumns },
3702
4364
  defaultSchema: {
3703
4365
  name: "",
3704
4366
  type: "table",