@pdfme/schemas 6.1.13 → 6.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/barcodes/helper.d.ts +12 -2
  2. package/dist/{builtins-8En4EIAG.js → builtins-C-2wyB_3.js} +140 -83
  3. package/dist/builtins-C-2wyB_3.js.map +1 -0
  4. package/dist/builtins.js +1 -1
  5. package/dist/dynamicLayout.js +4 -4
  6. package/dist/{dynamicTemplate-DQF7c6NR.js → dynamicTemplate-CcnCXHfM.js} +2 -2
  7. package/dist/{dynamicTemplate-DQF7c6NR.js.map → dynamicTemplate-CcnCXHfM.js.map} +1 -1
  8. package/dist/{dynamicTemplate-DFk_de1Y.js → dynamicTemplate-DEwOv_DE.js} +7 -6
  9. package/dist/dynamicTemplate-DEwOv_DE.js.map +1 -0
  10. package/dist/{dynamicTemplate-CnK0nlnj.js → dynamicTemplate-DkTVIMNr.js} +2 -2
  11. package/dist/{dynamicTemplate-CnK0nlnj.js.map → dynamicTemplate-DkTVIMNr.js.map} +1 -1
  12. package/dist/graphics/image.d.ts +26 -1
  13. package/dist/graphics/orientation.d.ts +31 -0
  14. package/dist/{helper-VroN19Ci.js → helper-BR4_UZA5.js} +26 -3
  15. package/dist/helper-BR4_UZA5.js.map +1 -0
  16. package/dist/index.js +639 -140
  17. package/dist/index.js.map +1 -1
  18. package/dist/lists.js +2 -2
  19. package/dist/{measure-BC7tjAtT.js → measure-BarlRI1L.js} +71 -227
  20. package/dist/measure-BarlRI1L.js.map +1 -0
  21. package/dist/multiVariableText/helper.d.ts +8 -0
  22. package/dist/multiVariableText/types.d.ts +2 -0
  23. package/dist/pdfFont.d.ts +8 -0
  24. package/dist/splitRange-B6311Noc.js +10854 -0
  25. package/dist/splitRange-B6311Noc.js.map +1 -0
  26. package/dist/tables.js +2 -2
  27. package/dist/text/helper.d.ts +33 -1
  28. package/dist/text/lineBreak.d.ts +69 -0
  29. package/dist/text/lineBreakClasses.generated.d.ts +11 -0
  30. package/dist/text/richText.d.ts +2 -0
  31. package/dist/text/uiRender.d.ts +3 -1
  32. package/dist/text/wrap.d.ts +67 -0
  33. package/dist/texts.js +3 -3
  34. package/dist/{utils-D_sFXK0U.js → utils-CPYF2FY_.js} +18 -15
  35. package/dist/utils-CPYF2FY_.js.map +1 -0
  36. package/dist/utils.d.ts +3 -2
  37. package/dist/utils.js +3 -3
  38. package/package.json +3 -1
  39. package/dist/builtins-8En4EIAG.js.map +0 -1
  40. package/dist/dynamicTemplate-DFk_de1Y.js.map +0 -1
  41. package/dist/helper-VroN19Ci.js.map +0 -1
  42. package/dist/measure-BC7tjAtT.js.map +0 -1
  43. package/dist/splitRange-BHV0vb0Q.js +0 -504
  44. package/dist/splitRange-BHV0vb0Q.js.map +0 -1
  45. package/dist/utils-D_sFXK0U.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,16 +1,18 @@
1
- import { $ as VERTICAL_ALIGN_MIDDLE, F as DEFAULT_FONT_COLOR, G as PLACEHOLDER_FONT_COLOR, K as SYNTHETIC_BOLD_CSS_TEXT_SHADOW, N as DEFAULT_ALIGNMENT, a as createListItemSplitRange, b as createBoxDimension, c as getListItemRange, h as getFontKitFont, i as TEXT_LINE_SPLIT_UNIT, j as CODE_BACKGROUND_COLOR, 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, x as getBoxContentArea } from "./splitRange-BHV0vb0Q.js";
2
- import { a as measureTextLines, d as isInlineMarkdownTextSchema, g as parseInlineMarkdown, p as resolveFontVariant } from "./measure-BC7tjAtT.js";
3
- import { a as mapVerticalAlignToFlex, c as Formatter, 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-8En4EIAG.js";
4
- import { a as getCellPropPanelSchema, c as HEX_COLOR_PATTERN, i as getBodyWithSchemaRange, l as createSingleTable, n as getDynamicLayoutForTable, o as getColumnStylesPropPanelSchema, r as getBody, s as getDefaultCellStyles, t as getDynamicHeightsForTable } from "./dynamicTemplate-CnK0nlnj.js";
5
- import { c as isEditable, d as countUniqueVariableNames, f as getVariableNames, i as createSvgStr, l as readFile, n as convertForPdfLayoutProps, o as hex2PrintingColor, p as visitVariables, r as createErrorElm, t as addAlphaToHex, u as rotatePoint } from "./utils-D_sFXK0U.js";
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-DQF7c6NR.js";
7
- import { n as substituteVariablesAsInlineMarkdownLiterals, r as validateVariables, t as substituteVariables } from "./helper-VroN19Ci.js";
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
+ 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-C-2wyB_3.js";
4
+ import { a as getCellPropPanelSchema, c as HEX_COLOR_PATTERN, i as getBodyWithSchemaRange, l as createSingleTable, n as getDynamicLayoutForTable, o as getColumnStylesPropPanelSchema, r as getBody, s as getDefaultCellStyles, t as getDynamicHeightsForTable } from "./dynamicTemplate-DkTVIMNr.js";
5
+ import { c as isEditable, d as rotatePoint, f as countUniqueVariableNames, i as createSvgStr, l as readFile, m as visitVariables, n as convertForPdfLayoutProps, o as hex2PrintingColor, p as getVariableNames, r as createErrorElm, t as addAlphaToHex, u as rgbColorToCmykColor } from "./utils-CPYF2FY_.js";
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
+ 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
10
  import { DEFAULT_FONT_NAME, ZOOM, b64toUint8Array, getDefaultFont, getFallbackFontName, getInternalLinkTarget, mm2pt, normalizeLinkHref, px2mm } from "@pdfme/common";
11
11
  import { Buffer as Buffer$1 } from "buffer";
12
- import { toRadians } from "@pdfme/pdf-lib";
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";
14
16
  import DOMPurify from "dompurify";
15
17
  import bwipjs from "bwip-js";
16
18
  import AirDatepicker from "air-datepicker";
@@ -43,9 +45,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
43
45
  var pdfRender$3 = async (arg) => {
44
46
  const { value, schema, ...rest } = arg;
45
47
  if (schema.readOnly) {
46
- const readOnlyValue = schema.variables.length > 0 ? value : schema.text || "";
47
48
  await pdfRender$4({
48
- value: readOnlyValue,
49
+ value: resolveReadOnlyMultiVariableText(schema, value),
49
50
  schema,
50
51
  ...rest
51
52
  });
@@ -211,7 +212,7 @@ var uiRender$3 = async (arg) => {
211
212
  let text = schema.text;
212
213
  let numVariables = schema.variables.length;
213
214
  const renderResolvedValue = schema.readOnly === true && mode !== "designer";
214
- const renderValue = renderResolvedValue ? numVariables > 0 ? value : text || "" : isInlineMarkdownTextSchema(schema) ? substituteVariablesAsInlineMarkdownLiterals(text, value) : substituteVariables(text, value);
215
+ const renderValue = renderResolvedValue ? resolveReadOnlyMultiVariableText(schema, value) : isInlineMarkdownTextSchema(schema) ? substituteVariablesAsInlineMarkdownLiterals(text, value) : substituteVariables(text, value);
215
216
  if (mode === "form" && numVariables > 0 && !renderResolvedValue) {
216
217
  await formUiRender(arg);
217
218
  return;
@@ -232,7 +233,7 @@ var uiRender$3 = async (arg) => {
232
233
  ...rest
233
234
  });
234
235
  const textBlock = rootElement.querySelector("#text-" + String(schema.id));
235
- if (!textBlock) throw new Error("Text block not found. Ensure the text block has an id of \"text-\" + schema.id");
236
+ if (!textBlock) return;
236
237
  if (mode === "designer") textBlock.addEventListener("keyup", (event) => {
237
238
  text = textBlock.innerText || "";
238
239
  if (keyPressShouldBeChecked(event)) {
@@ -690,9 +691,13 @@ var shape = {
690
691
  });
691
692
  else if (schema.type === "rectangle") {
692
693
  const radius = schema.radius ?? 0;
694
+ const inset = rotatePoint({
695
+ x: position.x + borderWidth / 2,
696
+ y: position.y + borderWidth / 2
697
+ }, position, rotate.angle);
693
698
  page.drawRectangle({
694
- x: position.x + borderWidth * ((1 - Math.sin(toRadians(rotate))) / 2) + Math.tan(toRadians(rotate)) * Math.PI ** 2,
695
- y: position.y + borderWidth * ((1 + Math.sin(toRadians(rotate))) / 2) + Math.tan(toRadians(rotate)) * Math.PI ** 2,
699
+ x: inset.x,
700
+ y: inset.y,
696
701
  width: width - borderWidth,
697
702
  height: height - borderWidth,
698
703
  ...radius ? { radius: mm2pt(radius) } : {},
@@ -1407,88 +1412,208 @@ var listSchema = {
1407
1412
  icon: createSvgStr(List)
1408
1413
  };
1409
1414
  //#endregion
1410
- //#region src/graphics/imagehelper.ts
1411
- var decoder = new TextDecoder();
1412
- var toUTF8String = (input, start = 0, end = input.length) => decoder.decode(input.slice(start, end));
1413
- var toHexString = (input, start = 0, end = input.length) => input.slice(start, end).reduce((memo, i) => memo + ("0" + i.toString(16)).slice(-2), "");
1414
- var readUInt16BE = (input, offset = 0) => input[offset] * 256 + input[offset + 1];
1415
- var readUInt32BE = (input, offset = 0) => input[offset] * 2 ** 24 + input[offset + 1] * 2 ** 16 + input[offset + 2] * 256 + input[offset + 3];
1416
- var extractSize = (input, index) => {
1417
- return {
1418
- height: readUInt16BE(input, index),
1419
- width: readUInt16BE(input, index + 2)
1420
- };
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`);
1421
1425
  };
1422
- var validateInput = (input, index) => {
1423
- if (index > input.length) throw new TypeError("Corrupt JPG, exceeded buffer limits");
1424
- if (input[index] !== 255) throw new TypeError("Invalid JPG, marker table corrupted");
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;
1425
1451
  };
1426
- var JPG = {
1427
- validate: (input) => toHexString(input, 0, 2) === "ffd8",
1428
- calculate(input) {
1429
- input = input.slice(4);
1430
- let next;
1431
- while (input.length) {
1432
- const i = readUInt16BE(input, 0);
1433
- validateInput(input, i);
1434
- next = input[i + 1];
1435
- if (next === 192 || next === 193 || next === 194) return extractSize(input, i + 5);
1436
- input = input.slice(i + 2);
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;
1437
1476
  }
1438
- throw new TypeError("Invalid JPG, no size found");
1477
+ return;
1478
+ } catch {
1479
+ return;
1439
1480
  }
1440
1481
  };
1441
- var pngSignature = "PNG\r\n\n";
1442
- var pngImageHeaderChunkName = "IHDR";
1443
- var pngFriedChunkName = "CgBI";
1444
- var typeHandlers = {
1445
- jpg: JPG,
1446
- png: {
1447
- validate(input) {
1448
- if (pngSignature === toUTF8String(input, 1, 8)) {
1449
- let chunkName = toUTF8String(input, 12, 16);
1450
- if (chunkName === pngFriedChunkName) chunkName = toUTF8String(input, 28, 32);
1451
- if (chunkName !== pngImageHeaderChunkName) throw new TypeError("Invalid PNG");
1452
- return true;
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;
1453
1508
  }
1454
- return false;
1455
- },
1456
- calculate(input) {
1457
- if (toUTF8String(input, 12, 16) === pngFriedChunkName) return {
1458
- height: readUInt32BE(input, 36),
1459
- width: readUInt32BE(input, 32)
1460
- };
1461
- return {
1462
- height: readUInt32BE(input, 20),
1463
- width: readUInt32BE(input, 16)
1464
- };
1465
1509
  }
1510
+ pos += size;
1466
1511
  }
1467
1512
  };
1468
- function detector(input) {
1469
- const firstBytes = {
1470
- 137: "png",
1471
- 255: "jpg"
1472
- };
1473
- const byte = input[0];
1474
- if (byte in firstBytes) {
1475
- const type = firstBytes[byte];
1476
- if (type && typeHandlers[type].validate(input)) return type;
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;
1477
1528
  }
1478
- return Object.keys(typeHandlers).find((key) => typeHandlers[key].validate(input));
1479
- }
1480
- var getImageDimension = (value) => {
1481
- const idx = value.indexOf(";base64,");
1482
- const imgBase64 = value.substring(idx + 8, value.length);
1483
- return imageSize(Buffer$1.from(imgBase64, "base64"));
1484
1529
  };
1485
- var imageSize = (imgBuffer) => {
1486
- const type = detector(imgBuffer);
1487
- if (typeof type !== "undefined" && type in typeHandlers) {
1488
- const size = typeHandlers[type].calculate(imgBuffer);
1489
- if (size !== void 0) return size;
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];
1490
1578
  }
1491
- throw new TypeError("[@pdfme/schemas/images] Unsupported file type: " + (type === void 0 ? "undefined" : type));
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;
1492
1617
  };
1493
1618
  //#endregion
1494
1619
  //#region src/graphics/image.ts
@@ -1516,40 +1641,166 @@ var getCacheKey = (schema, input) => {
1516
1641
  const hex = (hash >>> 0).toString(16).padStart(8, "0");
1517
1642
  return `${schema.type}:${input.length}:${hex}`;
1518
1643
  };
1644
+ var dataUrlToBytes = (value) => {
1645
+ const idx = value.indexOf(";base64,");
1646
+ const base64 = idx >= 0 ? value.slice(idx + 8) : value;
1647
+ return new Uint8Array(Buffer$1.from(base64, "base64"));
1648
+ };
1519
1649
  var fullSize$1 = {
1520
1650
  width: "100%",
1521
1651
  height: "100%"
1522
1652
  };
1653
+ var IMAGE_OBJECT_FITS = ["contain", "cover"];
1654
+ var IMAGE_OBJECT_POSITIONS = [
1655
+ "left top",
1656
+ "center top",
1657
+ "right top",
1658
+ "left center",
1659
+ "center center",
1660
+ "right center",
1661
+ "left bottom",
1662
+ "center bottom",
1663
+ "right bottom"
1664
+ ];
1665
+ var DEFAULT_IMAGE_OBJECT_FIT = "contain";
1666
+ var DEFAULT_IMAGE_OBJECT_POSITION = "center center";
1667
+ var imageObjectFitSet = new Set(IMAGE_OBJECT_FITS);
1668
+ var imageObjectPositionSet = new Set(IMAGE_OBJECT_POSITIONS);
1669
+ var normalizeObjectFit = (value) => typeof value === "string" && imageObjectFitSet.has(value) ? value : DEFAULT_IMAGE_OBJECT_FIT;
1670
+ var normalizeObjectPosition = (value) => typeof value === "string" && imageObjectPositionSet.has(value) ? value : DEFAULT_IMAGE_OBJECT_POSITION;
1671
+ var getEffectiveObjectPosition = (schema) => {
1672
+ return normalizeObjectFit(schema.objectFit) === "cover" ? DEFAULT_IMAGE_OBJECT_POSITION : normalizeObjectPosition(schema.objectPosition);
1673
+ };
1674
+ var alignOffset = (space, position) => {
1675
+ if (position === "right" || position === "bottom") return space;
1676
+ if (position === "center") return space / 2;
1677
+ return 0;
1678
+ };
1679
+ var getImageFitLayout = (arg) => {
1680
+ const { sourceWidth, sourceHeight, boxWidth, boxHeight } = arg;
1681
+ const objectFit = normalizeObjectFit(arg.objectFit);
1682
+ const objectPosition = objectFit === "cover" ? DEFAULT_IMAGE_OBJECT_POSITION : normalizeObjectPosition(arg.objectPosition);
1683
+ const scale = objectFit === "cover" ? Math.max(boxWidth / sourceWidth, boxHeight / sourceHeight) : Math.min(boxWidth / sourceWidth, boxHeight / sourceHeight);
1684
+ const width = sourceWidth * scale;
1685
+ const height = sourceHeight * scale;
1686
+ const [horizontal, vertical] = objectPosition.split(" ");
1687
+ return {
1688
+ objectFit,
1689
+ objectPosition,
1690
+ width,
1691
+ height,
1692
+ offsetX: alignOffset(boxWidth - width, horizontal),
1693
+ offsetY: alignOffset(boxHeight - height, vertical)
1694
+ };
1695
+ };
1696
+ var getClipPathCorners = (arg) => {
1697
+ const { schema, pageHeight } = arg;
1698
+ const clippingBox = convertForPdfLayoutProps({
1699
+ schema,
1700
+ pageHeight,
1701
+ applyRotateTranslate: false
1702
+ });
1703
+ const { x, y } = clippingBox.position;
1704
+ const width = clippingBox.width;
1705
+ const height = clippingBox.height;
1706
+ const rotateDegrees = schema.rotate ? -schema.rotate : 0;
1707
+ const corners = [
1708
+ {
1709
+ x,
1710
+ y
1711
+ },
1712
+ {
1713
+ x,
1714
+ y: y + height
1715
+ },
1716
+ {
1717
+ x: x + width,
1718
+ y: y + height
1719
+ },
1720
+ {
1721
+ x: x + width,
1722
+ y
1723
+ }
1724
+ ];
1725
+ if (!rotateDegrees) return corners;
1726
+ const pivot = {
1727
+ x: x + width / 2,
1728
+ y: y + height / 2
1729
+ };
1730
+ return corners.map((corner) => rotatePoint(corner, pivot, rotateDegrees));
1731
+ };
1523
1732
  var defaultValue = "data:image/png;base64,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";
1733
+ var getImagePropPanelSchema = ({ i18n, activeSchema }) => {
1734
+ const objectFit = normalizeObjectFit(activeSchema.objectFit);
1735
+ const positionOptions = IMAGE_OBJECT_POSITIONS.map((value) => ({
1736
+ value,
1737
+ label: i18n(`schemas.image.position.${value.replace(" ", ".")}`)
1738
+ }));
1739
+ return {
1740
+ objectFit: {
1741
+ title: i18n("schemas.image.objectFit"),
1742
+ type: "string",
1743
+ widget: "select",
1744
+ default: DEFAULT_IMAGE_OBJECT_FIT,
1745
+ props: { options: [{
1746
+ label: i18n("schemas.image.fit.contain"),
1747
+ value: "contain"
1748
+ }, {
1749
+ label: i18n("schemas.image.fit.cover"),
1750
+ value: "cover"
1751
+ }] },
1752
+ span: 8
1753
+ },
1754
+ objectPosition: {
1755
+ title: i18n("schemas.image.objectPosition"),
1756
+ type: "string",
1757
+ widget: "select",
1758
+ default: DEFAULT_IMAGE_OBJECT_POSITION,
1759
+ hidden: objectFit === "cover",
1760
+ props: { options: positionOptions },
1761
+ span: 16
1762
+ }
1763
+ };
1764
+ };
1524
1765
  var imageSchema = {
1525
1766
  pdf: async (arg) => {
1526
- const { value, schema, pdfDoc, page, _cache } = arg;
1767
+ const { value, schema, pdfDoc, page, pdfLib, _cache } = arg;
1527
1768
  if (!value) return;
1528
1769
  const inputImageCacheKey = getCacheKey(schema, value);
1529
1770
  let image = _cache.get(inputImageCacheKey);
1530
1771
  if (!image) {
1531
- image = await (value.startsWith("data:image/png;") ? pdfDoc.embedPng(value) : pdfDoc.embedJpg(value));
1772
+ const originalBytes = dataUrlToBytes(value);
1773
+ const kind = detectImageFormat(originalBytes);
1774
+ let bytes = originalBytes;
1775
+ try {
1776
+ if (kind === "jpeg" || kind === "png") bytes = normalizeImageOrientation(originalBytes);
1777
+ } catch (error) {
1778
+ console.warn("[@pdfme/schemas] EXIF orientation bake failed; embedding original bytes:", error);
1779
+ bytes = originalBytes;
1780
+ }
1781
+ image = await (kind === "png" ? pdfDoc.embedPng(bytes) : pdfDoc.embedJpg(bytes));
1532
1782
  _cache.set(inputImageCacheKey, image);
1533
1783
  }
1534
1784
  const _schema = {
1535
1785
  ...schema,
1536
1786
  position: { ...schema.position }
1537
1787
  };
1538
- const dimension = getImageDimension(value);
1539
- const imageWidth = px2mm(dimension.width);
1540
- const imageHeight = px2mm(dimension.height);
1788
+ const imageWidth = px2mm(image.width);
1789
+ const imageHeight = px2mm(image.height);
1541
1790
  const boxWidth = _schema.width;
1542
1791
  const boxHeight = _schema.height;
1543
- const imageRatio = imageWidth / imageHeight;
1544
- if (imageRatio > boxWidth / boxHeight) {
1545
- _schema.width = boxWidth;
1546
- _schema.height = boxWidth / imageRatio;
1547
- _schema.position.y += (boxHeight - _schema.height) / 2;
1548
- } else {
1549
- _schema.width = boxHeight * imageRatio;
1550
- _schema.height = boxHeight;
1551
- _schema.position.x += (boxWidth - _schema.width) / 2;
1552
- }
1792
+ const fitLayout = getImageFitLayout({
1793
+ sourceWidth: imageWidth,
1794
+ sourceHeight: imageHeight,
1795
+ boxWidth,
1796
+ boxHeight,
1797
+ objectFit: _schema.objectFit,
1798
+ objectPosition: _schema.objectPosition
1799
+ });
1800
+ _schema.width = fitLayout.width;
1801
+ _schema.height = fitLayout.height;
1802
+ _schema.position.x += fitLayout.offsetX;
1803
+ _schema.position.y += fitLayout.offsetY;
1553
1804
  const pageHeight = page.getHeight();
1554
1805
  const { width, height, rotate, position, opacity } = convertForPdfLayoutProps({
1555
1806
  schema: _schema,
@@ -1564,20 +1815,30 @@ var imageSchema = {
1564
1815
  height,
1565
1816
  opacity
1566
1817
  };
1818
+ if (fitLayout.objectFit === "cover") {
1819
+ const [firstCorner, ...corners] = getClipPathCorners({
1820
+ schema,
1821
+ pageHeight
1822
+ });
1823
+ page.pushOperators(pdfLib.pushGraphicsState(), pdfLib.moveTo(firstCorner.x, firstCorner.y), ...corners.map((corner) => pdfLib.lineTo(corner.x, corner.y)), pdfLib.closePath(), pdfLib.clip(), pdfLib.endPath());
1824
+ }
1567
1825
  page.drawImage(image, drawOptions);
1826
+ if (fitLayout.objectFit === "cover") page.pushOperators(pdfLib.popGraphicsState());
1568
1827
  },
1569
1828
  ui: (arg) => {
1570
1829
  const { value, rootElement, mode, onChange, stopEditing, tabIndex, placeholder, theme, schema } = arg;
1571
1830
  const editable = isEditable(mode, schema);
1572
1831
  const isDefault = value === defaultValue;
1832
+ const objectFit = normalizeObjectFit(schema.objectFit);
1833
+ const objectPosition = getEffectiveObjectPosition(schema);
1573
1834
  const container = document.createElement("div");
1574
1835
  const backgroundStyle = placeholder ? `url(${placeholder})` : "none";
1575
1836
  const containerStyle = {
1576
1837
  ...fullSize$1,
1577
1838
  backgroundImage: value ? "none" : backgroundStyle,
1578
- backgroundSize: `contain`,
1839
+ backgroundSize: objectFit,
1579
1840
  backgroundRepeat: "no-repeat",
1580
- backgroundPosition: "center"
1841
+ backgroundPosition: objectPosition
1581
1842
  };
1582
1843
  Object.assign(container.style, containerStyle);
1583
1844
  container.addEventListener("click", (e) => {
@@ -1586,12 +1847,14 @@ var imageSchema = {
1586
1847
  rootElement.appendChild(container);
1587
1848
  if (value) {
1588
1849
  const img = document.createElement("img");
1589
- Object.assign(img.style, {
1850
+ const imgStyle = {
1590
1851
  height: "100%",
1591
1852
  width: "100%",
1592
1853
  borderRadius: 0,
1593
- objectFit: "contain"
1594
- });
1854
+ objectFit,
1855
+ ...objectPosition === "center center" ? {} : { objectPosition }
1856
+ };
1857
+ Object.assign(img.style, imgStyle);
1595
1858
  img.src = value;
1596
1859
  container.appendChild(img);
1597
1860
  }
@@ -1668,7 +1931,10 @@ var imageSchema = {
1668
1931
  }
1669
1932
  },
1670
1933
  propPanel: {
1671
- schema: {},
1934
+ schema: ({ i18n, activeSchema }) => getImagePropPanelSchema({
1935
+ i18n,
1936
+ activeSchema
1937
+ }),
1672
1938
  defaultSchema: {
1673
1939
  name: "",
1674
1940
  type: "image",
@@ -1679,6 +1945,8 @@ var imageSchema = {
1679
1945
  },
1680
1946
  width: 40,
1681
1947
  height: 40,
1948
+ objectFit: DEFAULT_IMAGE_OBJECT_FIT,
1949
+ objectPosition: DEFAULT_IMAGE_OBJECT_POSITION,
1682
1950
  rotate: 0,
1683
1951
  opacity: 1
1684
1952
  }
@@ -1870,6 +2138,145 @@ var isValidSVG = (svgString) => {
1870
2138
  return false;
1871
2139
  }
1872
2140
  };
2141
+ var isWhitespace = (char) => char === " " || char === "\n" || char === "\r" || char === " " || char === "\f";
2142
+ var isAsciiAlpha = (char) => typeof char === "string" && (char >= "A" && char <= "Z" || char >= "a" && char <= "z");
2143
+ var isAttributeNameChar = (char) => typeof char === "string" && (isAsciiAlpha(char) || char >= "0" && char <= "9" || char === "_" || char === ":" || char === "-");
2144
+ var stripImportantSuffix = (value) => {
2145
+ const suffix = "!important";
2146
+ const trimmed = value.trim();
2147
+ return trimmed.toLowerCase().endsWith(suffix) ? trimmed.slice(0, -10).trimEnd() : trimmed;
2148
+ };
2149
+ var parseStyleAttribute = (style) => {
2150
+ if (!style) return {};
2151
+ return style.split(";").reduce((acc, declaration) => {
2152
+ const [key, ...valueParts] = declaration.split(":");
2153
+ const value = valueParts.join(":").trim();
2154
+ if (key && value) acc[key.trim()] = value;
2155
+ return acc;
2156
+ }, {});
2157
+ };
2158
+ var parseAttributes = (tag) => {
2159
+ const attributes = {};
2160
+ let index = 0;
2161
+ while (index < tag.length) {
2162
+ while (index < tag.length && !isAsciiAlpha(tag[index]) && tag[index] !== "_") index += 1;
2163
+ if (index >= tag.length) break;
2164
+ const nameStart = index;
2165
+ while (isAttributeNameChar(tag[index])) index += 1;
2166
+ const name = tag.slice(nameStart, index);
2167
+ while (isWhitespace(tag[index])) index += 1;
2168
+ if (tag[index] !== "=") continue;
2169
+ index += 1;
2170
+ while (isWhitespace(tag[index])) index += 1;
2171
+ const quote = tag[index];
2172
+ if (quote !== "\"" && quote !== "'") {
2173
+ index += 1;
2174
+ continue;
2175
+ }
2176
+ index += 1;
2177
+ const valueStart = index;
2178
+ while (index < tag.length && tag[index] !== quote) index += 1;
2179
+ attributes[name] = tag.slice(valueStart, index);
2180
+ if (tag[index] === quote) index += 1;
2181
+ }
2182
+ return attributes;
2183
+ };
2184
+ var splitFontFamilies = (fontFamily) => {
2185
+ if (!fontFamily) return [];
2186
+ const families = [];
2187
+ let current = "";
2188
+ let quote;
2189
+ for (const char of fontFamily) {
2190
+ if ((char === "\"" || char === "'") && (!quote || quote === char)) {
2191
+ quote = quote ? void 0 : char;
2192
+ continue;
2193
+ }
2194
+ if (char === "," && !quote) {
2195
+ if (current.trim()) families.push(current.trim());
2196
+ current = "";
2197
+ continue;
2198
+ }
2199
+ current += char;
2200
+ }
2201
+ if (current.trim()) families.push(current.trim());
2202
+ return families.map(stripImportantSuffix).filter(Boolean);
2203
+ };
2204
+ var findTagEnd = (svgString, startIndex) => {
2205
+ let index = startIndex;
2206
+ let quote;
2207
+ while (index < svgString.length) {
2208
+ const char = svgString[index];
2209
+ if ((char === "\"" || char === "'") && (!quote || quote === char)) quote = quote ? void 0 : char;
2210
+ else if (char === ">" && !quote) return index;
2211
+ index += 1;
2212
+ }
2213
+ return -1;
2214
+ };
2215
+ var isSelfClosingTag = (tag) => {
2216
+ let index = tag.length - 2;
2217
+ while (index >= 0 && isWhitespace(tag[index])) index -= 1;
2218
+ return tag[index] === "/";
2219
+ };
2220
+ var mergeFontStyle = (base, attributes) => {
2221
+ const style = parseStyleAttribute(attributes.style);
2222
+ return {
2223
+ fontFamily: style["font-family"] || attributes["font-family"] || base.fontFamily,
2224
+ fontStyle: style["font-style"] || attributes["font-style"] || base.fontStyle,
2225
+ fontWeight: style["font-weight"] || attributes["font-weight"] || base.fontWeight
2226
+ };
2227
+ };
2228
+ var getFontCandidates = (fontFamily, { fontStyle, fontWeight }) => {
2229
+ const isBold = fontWeight === "bold" || Number(fontWeight) >= 700;
2230
+ const isItalic = fontStyle === "italic";
2231
+ return Array.from(/* @__PURE__ */ new Set([
2232
+ `${fontFamily}${isBold ? "_bold" : ""}${isItalic ? "_italic" : ""}`,
2233
+ `${fontFamily}${isBold ? "_bold" : ""}`,
2234
+ `${fontFamily}${isItalic ? "_italic" : ""}`,
2235
+ fontFamily
2236
+ ]));
2237
+ };
2238
+ var selectSvgFontNames = (svgString, font) => {
2239
+ const selectedFontNames = /* @__PURE__ */ new Set();
2240
+ const fontNames = Object.keys(font);
2241
+ const styleStack = [{}];
2242
+ let index = 0;
2243
+ while (index < svgString.length) {
2244
+ if (svgString[index] !== "<") {
2245
+ index += 1;
2246
+ continue;
2247
+ }
2248
+ let cursor = index + 1;
2249
+ const isClosingTag = svgString[cursor] === "/";
2250
+ if (isClosingTag) cursor += 1;
2251
+ if (!isAsciiAlpha(svgString[cursor])) {
2252
+ index += 1;
2253
+ continue;
2254
+ }
2255
+ const tagNameStart = cursor;
2256
+ while (isAttributeNameChar(svgString[cursor])) cursor += 1;
2257
+ const tagName = svgString.slice(tagNameStart, cursor).toLowerCase();
2258
+ const tagEnd = findTagEnd(svgString, cursor);
2259
+ if (tagEnd === -1) break;
2260
+ const tag = svgString.slice(index, tagEnd + 1);
2261
+ if (isClosingTag) {
2262
+ if (styleStack.length > 1) styleStack.pop();
2263
+ index = tagEnd + 1;
2264
+ continue;
2265
+ }
2266
+ const currentStyle = mergeFontStyle(styleStack[styleStack.length - 1], parseAttributes(tag));
2267
+ if (tagName === "text") {
2268
+ let selectedFontName;
2269
+ for (const family of splitFontFamilies(currentStyle.fontFamily)) {
2270
+ selectedFontName = getFontCandidates(family, currentStyle).find((fontName) => font[fontName]) || fontNames.find((fontName) => fontName.startsWith(family));
2271
+ if (selectedFontName) break;
2272
+ }
2273
+ if (selectedFontName) selectedFontNames.add(selectedFontName);
2274
+ }
2275
+ if (!isSelfClosingTag(tag)) styleStack.push(currentStyle);
2276
+ index = tagEnd + 1;
2277
+ }
2278
+ return Array.from(selectedFontNames);
2279
+ };
1873
2280
  var svgSchema = {
1874
2281
  ui: (arg) => {
1875
2282
  const { rootElement, value, mode, onChange, theme, schema } = arg;
@@ -1922,20 +2329,45 @@ var svgSchema = {
1922
2329
  }
1923
2330
  },
1924
2331
  pdf: async (arg) => {
1925
- const { page, schema, value } = arg;
2332
+ const { page, pdfDoc, pdfLib, options, schema, value, _cache } = arg;
1926
2333
  if (!value || !isValidSVG(value)) return;
1927
2334
  const pageHeight = page.getHeight();
1928
2335
  const { width, height, position } = convertForPdfLayoutProps({
1929
2336
  schema,
1930
- pageHeight
2337
+ pageHeight,
2338
+ applyRotateTranslate: false
1931
2339
  });
1932
2340
  const { x, y } = position;
1933
- await page.drawSvg(value, {
1934
- x,
1935
- y: y + height,
1936
- width,
1937
- height
1938
- });
2341
+ const font = options.font;
2342
+ const fontEntries = font ? await Promise.all(selectSvgFontNames(value, font).map(async (fontName) => [fontName, await embedAndGetFont({
2343
+ pdfDoc,
2344
+ font,
2345
+ fontName,
2346
+ _cache
2347
+ })])) : [];
2348
+ const fonts = fontEntries.length > 0 ? Object.fromEntries(fontEntries) : void 0;
2349
+ const mapColor = options.colorType?.toLowerCase() === "cmyk" ? ({ parsed }) => ({
2350
+ color: rgbColorToCmykColor(parsed.rgb),
2351
+ alpha: parsed.alpha
2352
+ }) : void 0;
2353
+ const rotateDegrees = schema.rotate ? -schema.rotate : 0;
2354
+ if (rotateDegrees) {
2355
+ const pivotX = x + width / 2;
2356
+ const pivotY = y + height / 2;
2357
+ page.pushOperators(pdfLib.pushGraphicsState(), pdfLib.translate(pivotX, pivotY), pdfLib.rotateDegrees(rotateDegrees), pdfLib.translate(-pivotX, -pivotY));
2358
+ }
2359
+ try {
2360
+ await page.drawSvg(value, {
2361
+ x,
2362
+ y: y + height,
2363
+ width,
2364
+ height,
2365
+ fonts,
2366
+ mapColor
2367
+ });
2368
+ } finally {
2369
+ if (rotateDegrees) page.pushOperators(pdfLib.popGraphicsState());
2370
+ }
1939
2371
  },
1940
2372
  propPanel: {
1941
2373
  schema: {},
@@ -1952,7 +2384,8 @@ var svgSchema = {
1952
2384
  y: 0
1953
2385
  },
1954
2386
  width: 40,
1955
- height: 50
2387
+ height: 50,
2388
+ rotate: 0
1956
2389
  }
1957
2390
  },
1958
2391
  icon: createSvgStr(Route)
@@ -2025,7 +2458,47 @@ var barCodeType2Bcid = (type) => type === "nw7" ? "rationalizedCodabar" : type;
2025
2458
  * Strip hash from the beginning of HTML hex color codes for the bwip.js lib
2026
2459
  */
2027
2460
  var mapHexColorForBwipJsLib = (color, fallback) => color ? color.replace("#", "") : fallback ? fallback.replace("#", "") : "000000";
2028
- var createBarCode = async (arg) => {
2461
+ var getBwipJsRenderer = () => bwipjs;
2462
+ var getDocument = () => globalThis.document;
2463
+ var getOffscreenCanvasCtor = () => globalThis.OffscreenCanvas;
2464
+ /**
2465
+ * Pick a barcode renderer from capabilities, not `typeof window`.
2466
+ * Browser Workers have neither `window` nor Node's `bwip-js.toBuffer()`.
2467
+ * OffscreenCanvas without `toCanvas()` (Node bwip-js export) falls back to `toBuffer()`.
2468
+ */
2469
+ var resolveBarcodeRenderRuntime = () => {
2470
+ const doc = getDocument();
2471
+ const renderer = getBwipJsRenderer();
2472
+ if (doc && typeof doc.createElement === "function") return "document-canvas";
2473
+ if (typeof getOffscreenCanvasCtor() === "function" && typeof renderer.toCanvas === "function") return "offscreencanvas";
2474
+ if (typeof renderer.toBuffer === "function") return "node-buffer";
2475
+ throw new Error("[@pdfme/schemas] Barcode rendering requires a document canvas, OffscreenCanvas, or bwip-js toBuffer().");
2476
+ };
2477
+ var pngBufferFromDataUrl = (dataUrl) => Buffer$1.from(b64toUint8Array(dataUrl).buffer);
2478
+ var pngBufferFromBlob = async (blob) => Buffer$1.from(await blob.arrayBuffer());
2479
+ var renderBarcodeToCanvas = (canvas, options) => {
2480
+ const toCanvas = getBwipJsRenderer().toCanvas;
2481
+ if (typeof toCanvas !== "function") throw new Error("[@pdfme/schemas] bwip-js toCanvas() is not available in this environment.");
2482
+ toCanvas(canvas, options);
2483
+ };
2484
+ var renderBarcodeToDocumentCanvas = (options) => {
2485
+ const canvas = getDocument().createElement("canvas");
2486
+ renderBarcodeToCanvas(canvas, options);
2487
+ return pngBufferFromDataUrl(canvas.toDataURL("image/png"));
2488
+ };
2489
+ var renderBarcodeToOffscreenCanvas = async (options) => {
2490
+ const OffscreenCanvasCtor = getOffscreenCanvasCtor();
2491
+ if (typeof OffscreenCanvasCtor !== "function") throw new Error("[@pdfme/schemas] OffscreenCanvas is not available in this environment.");
2492
+ const canvas = new OffscreenCanvasCtor(1, 1);
2493
+ renderBarcodeToCanvas(canvas, options);
2494
+ return pngBufferFromBlob(await canvas.convertToBlob({ type: "image/png" }));
2495
+ };
2496
+ var renderBarcodeToNodeBuffer = async (options) => {
2497
+ const toBuffer = getBwipJsRenderer().toBuffer;
2498
+ if (typeof toBuffer !== "function") throw new Error("[@pdfme/schemas] bwip-js toBuffer() is not available in this environment.");
2499
+ return toBuffer(options);
2500
+ };
2501
+ var createBwipJsRenderOptions = (arg) => {
2029
2502
  const { type, input, width, height, backgroundColor, barColor, textColor, includetext = true } = arg;
2030
2503
  const bwipjsArg = {
2031
2504
  bcid: barCodeType2Bcid(type),
@@ -2039,47 +2512,73 @@ var createBarCode = async (arg) => {
2039
2512
  if (backgroundColor) bwipjsArg.backgroundcolor = mapHexColorForBwipJsLib(backgroundColor);
2040
2513
  if (barColor) bwipjsArg.barcolor = mapHexColorForBwipJsLib(barColor);
2041
2514
  if (textColor) bwipjsArg.textcolor = mapHexColorForBwipJsLib(textColor);
2042
- let res;
2043
- if (typeof window !== "undefined") {
2044
- const canvas = document.createElement("canvas");
2045
- bwipjs.toCanvas(canvas, bwipjsArg);
2046
- const dataUrl = canvas.toDataURL("image/png");
2047
- res = Buffer$1.from(b64toUint8Array(dataUrl).buffer);
2048
- } else res = await bwipjs.toBuffer(bwipjsArg);
2049
- return res;
2515
+ return bwipjsArg;
2516
+ };
2517
+ var createBarCodeSvg = (arg) => {
2518
+ const toSVG = getBwipJsRenderer().toSVG;
2519
+ if (typeof toSVG !== "function") throw new Error("[@pdfme/schemas] bwip-js toSVG() is not available in this environment.");
2520
+ const defaultFill = `#${mapHexColorForBwipJsLib(arg.barColor)}`;
2521
+ const svg = toSVG(createBwipJsRenderOptions(arg)).replace(/<svg\b(?![^>]*\sfill=)/, `<svg fill="${defaultFill}"`);
2522
+ if (/<image\b/i.test(svg)) throw new Error(`[@pdfme/schemas] bwip-js emitted an embedded image for ${arg.type}; vector barcode PDF rendering requires path-based SVG.`);
2523
+ return svg;
2524
+ };
2525
+ var createBarCode = async (arg) => {
2526
+ const bwipjsArg = createBwipJsRenderOptions(arg);
2527
+ const runtime = resolveBarcodeRenderRuntime();
2528
+ if (runtime === "document-canvas") return renderBarcodeToDocumentCanvas(bwipjsArg);
2529
+ if (runtime === "offscreencanvas") return renderBarcodeToOffscreenCanvas(bwipjsArg);
2530
+ return renderBarcodeToNodeBuffer(bwipjsArg);
2050
2531
  };
2051
2532
  //#endregion
2052
2533
  //#region src/barcodes/pdfRender.ts
2053
2534
  var getBarcodeCacheKey = (schema, value) => {
2054
- return `${schema.type}${schema.backgroundColor}${schema.barColor}${schema.textColor}${value}${schema.includetext}`;
2535
+ return `svg:${schema.type}:${schema.width}:${schema.height}:${schema.backgroundColor}:${schema.barColor}:${schema.textColor}:${schema.includetext}:${value}`;
2536
+ };
2537
+ var addSvgOpacity = (svg, opacity) => {
2538
+ return opacity === void 0 ? svg : svg.replace(/<svg\b/, `<svg opacity="${opacity}"`);
2539
+ };
2540
+ var getSvgColorMapper = (colorType = "") => {
2541
+ return colorType.toLowerCase() === "cmyk" ? ({ parsed }) => ({
2542
+ color: rgbColorToCmykColor(parsed.rgb),
2543
+ alpha: parsed.alpha
2544
+ }) : void 0;
2055
2545
  };
2056
2546
  var pdfRender$1 = async (arg) => {
2057
- const { value, schema, pdfDoc, page, _cache } = arg;
2547
+ const { value, schema, page, options, _cache } = arg;
2058
2548
  if (!validateBarcodeInput(schema.type, value)) return;
2059
2549
  const inputBarcodeCacheKey = getBarcodeCacheKey(schema, value);
2060
- let image = _cache.get(inputBarcodeCacheKey);
2061
- if (!image) {
2062
- const imageBuf = await createBarCode({
2550
+ let svg = _cache.get(inputBarcodeCacheKey);
2551
+ if (!svg) {
2552
+ svg = createBarCodeSvg({
2063
2553
  ...schema,
2064
2554
  type: schema.type,
2065
2555
  input: value
2066
2556
  });
2067
- image = await pdfDoc.embedPng(imageBuf);
2068
- _cache.set(inputBarcodeCacheKey, image);
2557
+ _cache.set(inputBarcodeCacheKey, svg);
2069
2558
  }
2070
2559
  const pageHeight = page.getHeight();
2071
- const { width, height, rotate, position: { x, y }, opacity } = convertForPdfLayoutProps({
2560
+ const { width, height, position: { x, y }, opacity } = convertForPdfLayoutProps({
2072
2561
  schema,
2073
- pageHeight
2074
- });
2075
- page.drawImage(image, {
2076
- x,
2077
- y,
2078
- rotate,
2079
- width,
2080
- height,
2081
- opacity
2562
+ pageHeight,
2563
+ applyRotateTranslate: false
2082
2564
  });
2565
+ const pivot = {
2566
+ x: x + width / 2,
2567
+ y: y + height / 2
2568
+ };
2569
+ const rotate = schema.rotate ? -schema.rotate : 0;
2570
+ if (rotate) page.pushOperators(pushGraphicsState(), translate(pivot.x, pivot.y), rotateDegrees(rotate), translate(-pivot.x, -pivot.y));
2571
+ try {
2572
+ await page.drawSvg(addSvgOpacity(svg, opacity), {
2573
+ x,
2574
+ y: y + height,
2575
+ width,
2576
+ height,
2577
+ mapColor: getSvgColorMapper(options.colorType)
2578
+ });
2579
+ } finally {
2580
+ if (rotate) page.pushOperators(popGraphicsState());
2581
+ }
2083
2582
  };
2084
2583
  //#endregion
2085
2584
  //#region src/barcodes/propPanel.ts