@pdfme/schemas
Version:
TypeScript base PDF generator and React base UI. Open source, developed by the community, and completely free to use under the MIT license!
1,504 lines • 185 kB
JavaScript
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-DmVDtmzO.js";
import { a as measureTextLines, d as isInlineMarkdownTextSchema, g as parseInlineMarkdown, p as resolveFontVariant } from "./measure-L5diay3k.js";
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-BB2DHceW.js";
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-B4GCNLF9.js";
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-zDZkqBnX.js";
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-BwzF9C1L.js";
import { n as substituteVariablesAsInlineMarkdownLiterals, r as validateVariables, t as substituteVariables } from "./helper-CEme39Uo.js";
import "./tables.js";
import "./lists.js";
import { DEFAULT_FONT_NAME, ZOOM, b64toUint8Array, getDefaultFont, getFallbackFontName, getInternalLinkTarget, mm2pt, normalizeLinkHref, px2mm } from "@pdfme/common";
import { Buffer as Buffer$1 } from "buffer";
import { toRadians } from "@pdfme/pdf-lib";
import { Barcode, Calendar, CalendarClock, ChevronDown, Circle, CircleDot, Clock, Image, List, Minus, QrCode, Route, Square, SquareCheck, Table, Type } from "lucide";
import DOMPurify from "dompurify";
import bwipjs from "bwip-js";
import AirDatepicker from "air-datepicker";
import * as dateFns from "date-fns/locale";
import { format } from "date-fns";
//#region \0rolldown/runtime.js
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
key = keys[i];
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
get: ((k) => from[k]).bind(null, key),
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
});
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
value: mod,
enumerable: true
}) : target, mod));
//#endregion
//#region src/multiVariableText/pdfRender.ts
var pdfRender$3 = async (arg) => {
const { value, schema, ...rest } = arg;
if (schema.readOnly) {
await pdfRender$4({
value,
schema,
...rest
});
return;
}
if (!validateVariables(value, schema)) return;
await pdfRender$4({
value: isInlineMarkdownTextSchema(schema) ? substituteVariablesAsInlineMarkdownLiterals(schema.text || "", value) : substituteVariables(schema.text || "", value),
schema,
...rest
});
};
//#endregion
//#region src/multiVariableText/propPanel.ts
var mapDynamicVariables = (props) => {
const { rootElement, changeSchemas, activeSchema, i18n, options } = props;
const mvtSchema = activeSchema;
const text = mvtSchema.text || "";
let variables = {};
try {
const parsed = JSON.parse(mvtSchema.content || "{}");
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) variables = parsed;
} catch {}
const variablesChanged = updateVariablesFromText(text, variables);
const varNames = Object.keys(variables);
if (variablesChanged) changeSchemas([
{
key: "content",
value: JSON.stringify(variables),
schemaId: activeSchema.id
},
{
key: "variables",
value: varNames,
schemaId: activeSchema.id
},
{
key: "readOnly",
value: varNames.length === 0,
schemaId: activeSchema.id
}
]);
const placeholderRowEl = document.getElementById("placeholder-dynamic-var")?.closest(".ant-form-item");
if (!placeholderRowEl) throw new Error("Failed to find Ant form placeholder row to create dynamic variables inputs.");
placeholderRowEl.style.display = "none";
rootElement.parentElement.style.display = "block";
if (varNames.length > 0) for (let variableName of varNames) {
const varRow = placeholderRowEl.cloneNode(true);
const textarea = varRow.querySelector("textarea");
textarea.id = "dynamic-var-" + variableName;
textarea.value = variables[variableName];
textarea.addEventListener("change", (e) => {
if (variableName in variables) {
variables[variableName] = e.target.value;
changeSchemas([{
key: "content",
value: JSON.stringify(variables),
schemaId: activeSchema.id
}]);
}
});
const label = varRow.querySelector("label");
label.innerText = variableName;
varRow.style.display = "block";
rootElement.appendChild(varRow);
}
else {
const para = document.createElement("p");
const colorValue = options?.theme?.token?.colorPrimary || "#168fe3";
const safeColorValue = /^#[0-9A-F]{6}$/i.test(colorValue) || /^(rgb|hsl)a?\(\s*([+-]?\d+%?\s*,\s*){2,3}[+-]?\d+%?\s*\)$/i.test(colorValue) ? colorValue : "#168fe3";
const typingInstructions = i18n("schemas.mvt.typingInstructions");
const sampleField = i18n("schemas.mvt.sampleField");
para.appendChild(document.createTextNode(typingInstructions + " "));
const codeEl = document.createElement("code");
codeEl.style.color = safeColorValue;
codeEl.style.fontWeight = "bold";
codeEl.textContent = `{${sampleField}}`;
para.appendChild(codeEl);
rootElement.appendChild(para);
}
};
var propPanel$2 = {
schema: (propPanelProps) => {
if (typeof propPanel$3.schema !== "function") throw new Error("Oops, is text schema no longer a function?");
const parentSchema = typeof propPanel$3.schema === "function" ? propPanel$3.schema(propPanelProps) : {};
const i18n = propPanelProps.i18n;
return {
...parentSchema,
"-------": {
type: "void",
widget: "Divider"
},
dynamicVarContainer: {
title: i18n("schemas.mvt.variablesSampleData"),
type: "string",
widget: "Card",
span: 24,
properties: {
dynamicVariables: {
type: "object",
widget: "mapDynamicVariables",
bind: false,
span: 24
},
placeholderDynamicVar: {
title: i18n("schemas.mvt.placeholderDynamicVariable"),
type: "string",
format: "textarea",
props: {
id: "placeholder-dynamic-var",
autoSize: {
minRows: 2,
maxRows: 5
}
},
span: 24
}
}
}
};
},
widgets: {
...propPanel$3.widgets,
mapDynamicVariables
},
defaultSchema: {
...propPanel$3.defaultSchema,
readOnly: false,
type: "multiVariableText",
text: "Add text here using {} for variables ",
width: 50,
height: 15,
content: "{}",
variables: []
}
};
var updateVariablesFromText = (text, variables) => {
const matches = getVariableNames(text);
let changed = false;
if (matches.length > 0) {
const uniqueMatches = new Set(matches);
for (const variableName of uniqueMatches) if (!(variableName in variables)) {
variables[variableName] = variableName.toUpperCase();
changed = true;
}
Object.keys(variables).forEach((variableName) => {
if (!uniqueMatches.has(variableName)) {
delete variables[variableName];
changed = true;
}
});
} else Object.keys(variables).forEach((variableName) => {
delete variables[variableName];
changed = true;
});
return changed;
};
//#endregion
//#region src/multiVariableText/uiRender.ts
var uiRender$3 = async (arg) => {
const { value, schema, rootElement, mode, onChange, ...rest } = arg;
let text = schema.text;
let numVariables = schema.variables.length;
const renderResolvedValue = schema.readOnly === true && mode !== "designer";
const renderValue = renderResolvedValue ? value : isInlineMarkdownTextSchema(schema) ? substituteVariablesAsInlineMarkdownLiterals(text, value) : substituteVariables(text, value);
if (mode === "form" && numVariables > 0 && !renderResolvedValue) {
await formUiRender(arg);
return;
}
await uiRender$4({
value: isEditable(mode, schema) ? text : renderValue,
schema,
mode: mode === "form" ? "viewer" : mode,
rootElement,
onChange: (arg) => {
if (!Array.isArray(arg)) {
if (onChange) onChange({
key: "text",
value: arg.value
});
} else throw new Error("onChange is not an array, the parent text plugin has changed...");
},
...rest
});
const textBlock = rootElement.querySelector("#text-" + String(schema.id));
if (!textBlock) throw new Error("Text block not found. Ensure the text block has an id of \"text-\" + schema.id");
if (mode === "designer") textBlock.addEventListener("keyup", (event) => {
text = textBlock.textContent || "";
if (keyPressShouldBeChecked(event)) {
const newNumVariables = countUniqueVariableNames(text);
if (numVariables !== newNumVariables) {
if (onChange) onChange({
key: "text",
value: text
});
numVariables = newNumVariables;
}
}
});
};
var formUiRender = async (arg) => {
const { value, schema, rootElement, onChange, stopEditing, theme, _cache, options } = arg;
const rawText = schema.text;
if (rootElement.parentElement) rootElement.parentElement.style.outline = "";
let variables = {};
if (value) try {
const parsed = JSON.parse(value);
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) variables = parsed;
} catch {}
const substitutedText = substituteVariables(rawText, variables);
const inlineMarkdownRuns = isInlineMarkdownTextSchema(schema) ? parseInlineMarkdown(rawText) : void 0;
const font = options?.font || getDefaultFont();
const textBlock = buildStyledTextContainer(arg, await getFontKitFont(schema.fontName, font, _cache), inlineMarkdownRuns ? getInlineMarkdownFormDisplayText(inlineMarkdownRuns, variables) : substitutedText);
if (getTextLineRange(schema)) {
const { lines } = await measureTextLines({
value: inlineMarkdownRuns ? substituteVariablesAsInlineMarkdownLiterals(rawText, variables) : substitutedText,
schema,
font,
_cache,
ignoreDynamicFontSize: true
});
renderSplitVariableSpans({
textBlock,
lines,
runs: inlineMarkdownRuns,
rawText,
variables,
schema,
font,
theme,
onChange,
stopEditing
});
return;
}
if (inlineMarkdownRuns) {
renderInlineMarkdownVariableSpans({
runs: inlineMarkdownRuns,
variables,
textBlock,
schema,
font,
theme,
onChange,
stopEditing
});
return;
}
const variableIndices = /* @__PURE__ */ new Map();
visitVariables(rawText, ({ name, startIndex }) => {
variableIndices.set(startIndex, name);
});
let inVarString = false;
for (let i = 0; i < rawText.length; i++) {
const variableName = variableIndices.get(i);
if (variableName) {
inVarString = true;
let span = document.createElement("span");
span.style.outline = `${theme.colorPrimary} dashed 1px`;
makeElementPlainTextContentEditable(span);
span.textContent = variables[variableName];
span.addEventListener("blur", (e) => {
const newValue = e.target.textContent || "";
if (newValue !== variables[variableName]) {
variables[variableName] = newValue;
if (onChange) onChange({
key: "content",
value: JSON.stringify(variables)
});
if (stopEditing) stopEditing();
}
});
textBlock.appendChild(span);
} else if (inVarString) {
if (rawText[i] === "}") inVarString = false;
} else {
let span = document.createElement("span");
span.style.letterSpacing = rawText.length === i + 1 ? "0" : "inherit";
span.textContent = rawText[i];
textBlock.appendChild(span);
}
}
};
var renderSplitVariableSpans = (arg) => {
const { textBlock, lines, runs, rawText, variables, schema, font, theme, onChange, stopEditing } = arg;
const lineRange = getTextLineRange(schema);
const lineSegments = getSplitLineSegments({
lines,
runs,
rawText,
variables,
start: lineRange?.start ?? 0,
end: lineRange?.end ?? lines.length
});
textBlock.innerHTML = "";
lineSegments.forEach((segments, lineIndex) => {
segments.forEach((segment) => {
if (segment.variableName) {
appendRangedVariableSpan({
textBlock,
segment,
variables,
schema,
font,
theme,
onChange,
stopEditing
});
return;
}
const span = segment.run ? createStaticInlineMarkdownElement(segment.run) : document.createElement("span");
span.style.letterSpacing = lineIndex === lineSegments.length - 1 ? "0" : "inherit";
span.textContent = segment.text;
if (segment.run) applyInlineMarkdownStyle({
element: span,
run: segment.run,
schema,
font
});
textBlock.appendChild(span);
});
if (lineIndex < lineSegments.length - 1) textBlock.appendChild(document.createElement("br"));
});
};
var getSplitLineSegments = (arg) => {
const { lines, runs, rawText, variables, start, end } = arg;
return consumeMeasuredLineSegments(lines, runs ? buildResolvedInlineMarkdownChars(runs, variables) : buildResolvedPlainChars(rawText, variables), { dropUnmappedTargets: Boolean(runs) }).slice(start, end);
};
var buildResolvedPlainChars = (rawText, variables) => {
const chars = [];
let lastIndex = 0;
visitVariables(rawText, ({ name, startIndex, endIndex }) => {
appendTextChars(chars, rawText.slice(lastIndex, startIndex));
const value = variables[name] ?? "";
for (let i = 0; i < value.length; i += 1) chars.push({
char: value[i],
variableName: name,
variableOffset: i
});
lastIndex = endIndex + 1;
});
appendTextChars(chars, rawText.slice(lastIndex));
return chars;
};
var buildResolvedInlineMarkdownChars = (runs, variables) => {
const chars = [];
runs.forEach((run) => {
let lastIndex = 0;
visitVariables(run.text, ({ name, startIndex, endIndex }) => {
appendTextChars(chars, run.text.slice(lastIndex, startIndex), run);
const value = variables[name] ?? "";
for (let i = 0; i < value.length; i += 1) chars.push({
char: value[i],
variableName: name,
variableOffset: i,
run
});
lastIndex = endIndex + 1;
});
appendTextChars(chars, run.text.slice(lastIndex), run);
});
return chars;
};
var appendTextChars = (chars, text, run) => {
for (let i = 0; i < text.length; i += 1) chars.push({
char: text[i],
run
});
};
var consumeMeasuredLineSegments = (lines, resolvedChars, options = {}) => {
const lineSegments = [];
let cursor = 0;
lines.forEach((line) => {
const segments = [];
const lineText = stripTrailingLineBreaks(line);
for (let i = 0; i < lineText.length; i += 1) {
const target = lineText[i];
while (cursor < resolvedChars.length && resolvedChars[cursor].char !== target && isWhitespaceChar(resolvedChars[cursor].char) && !isWhitespaceChar(target)) cursor += 1;
if (cursor >= resolvedChars.length) {
if (options.dropUnmappedTargets) continue;
appendSegment(segments, { char: target });
continue;
}
const sourceChar = resolvedChars[cursor];
if (sourceChar.char === target) {
appendSegment(segments, sourceChar);
cursor += 1;
} else {
if (options.dropUnmappedTargets) continue;
appendSegment(segments, { char: target });
}
}
cursor = absorbHiddenTrailingWhitespace(segments, resolvedChars, cursor);
if (line.endsWith("\r\n") || line.endsWith("\n") || line.endsWith("\r")) {
if (resolvedChars[cursor]?.char === "\r" && resolvedChars[cursor + 1]?.char === "\n") cursor += 2;
else if (resolvedChars[cursor]?.char === "\n" || resolvedChars[cursor]?.char === "\r") cursor += 1;
}
lineSegments.push(segments);
});
return lineSegments;
};
var absorbHiddenTrailingWhitespace = (segments, resolvedChars, cursor) => {
let nextCursor = cursor;
while (nextCursor < resolvedChars.length && isHorizontalWhitespaceChar(resolvedChars[nextCursor].char)) {
const sourceChar = resolvedChars[nextCursor];
const lastSegment = segments.at(-1);
if (lastSegment && lastSegment.variableName === sourceChar.variableName && lastSegment.variableEnd === sourceChar.variableOffset && lastSegment.run === sourceChar.run && sourceChar.variableOffset !== void 0) lastSegment.variableEnd = sourceChar.variableOffset + 1;
nextCursor += 1;
}
return nextCursor;
};
var stripTrailingLineBreaks = (value) => {
let end = value.length;
while (end > 0) {
const char = value[end - 1];
if (char !== "\n" && char !== "\r") break;
end -= 1;
}
return value.slice(0, end);
};
var isWhitespaceChar = (value) => value === " " || value === " " || value === "\n" || value === "\r" || value === "\f" || value === "\v";
var isHorizontalWhitespaceChar = (value) => value === " " || value === " " || value === "\f" || value === "\v";
var appendSegment = (segments, sourceChar) => {
const lastSegment = segments.at(-1);
if (lastSegment && lastSegment.variableName === sourceChar.variableName && lastSegment.variableEnd === sourceChar.variableOffset && lastSegment.run === sourceChar.run) {
lastSegment.text += sourceChar.char;
if (sourceChar.variableOffset !== void 0) lastSegment.variableEnd = sourceChar.variableOffset + 1;
return;
}
segments.push({
text: sourceChar.char,
variableName: sourceChar.variableName,
variableStart: sourceChar.variableOffset,
variableEnd: sourceChar.variableOffset === void 0 ? void 0 : sourceChar.variableOffset + 1,
run: sourceChar.run
});
};
var appendRangedVariableSpan = (arg) => {
const { textBlock, segment, variables, schema, font, theme, onChange, stopEditing } = arg;
if (!segment.variableName) return;
const span = document.createElement("span");
span.style.outline = `${theme.colorPrimary} dashed 1px`;
if (segment.run) applyInlineMarkdownStyle({
element: span,
run: segment.run,
schema,
font
});
makeElementPlainTextContentEditable(span);
span.textContent = segment.text;
span.addEventListener("blur", (e) => {
const variableName = segment.variableName;
if (!variableName) return;
const newValue = e.target.textContent || "";
if (newValue === segment.text) return;
const currentValue = variables[variableName] ?? "";
const start = Math.min(segment.variableStart ?? 0, currentValue.length);
const end = Math.min(segment.variableEnd ?? currentValue.length, currentValue.length);
variables[variableName] = currentValue.slice(0, start) + newValue + currentValue.slice(end);
if (onChange) onChange({
key: "content",
value: JSON.stringify(variables)
});
if (stopEditing) stopEditing();
});
textBlock.appendChild(span);
};
var getInlineMarkdownFormDisplayText = (runs, variables) => runs.map((run) => substituteVariables(run.text, variables)).join("");
var applyInlineMarkdownStyle = (arg) => {
const { element, run, schema, font } = arg;
const resolvedFont = resolveFontVariant(run, schema, font);
if (resolvedFont.fontName) element.style.fontFamily = `'${resolvedFont.fontName}'`;
if (resolvedFont.syntheticBold) {
element.style.fontWeight = "800";
element.style.textShadow = SYNTHETIC_BOLD_CSS_TEXT_SHADOW;
}
if (resolvedFont.syntheticItalic) element.style.fontStyle = "italic";
const textDecorations = [];
if (run.href) textDecorations.push("underline");
if (run.strikethrough) textDecorations.push("line-through");
if (textDecorations.length > 0) element.style.textDecoration = textDecorations.join(" ");
if (run.code) {
element.style.backgroundColor = CODE_BACKGROUND_COLOR;
element.style.borderRadius = "2px";
element.style.padding = "0 0.15em";
if (!schema.fontVariants?.code || !font[schema.fontVariants.code]) element.style.fontFamily = resolvedFont.fontName ? `'${resolvedFont.fontName}', monospace` : "monospace";
}
};
var createStaticInlineMarkdownElement = (run) => {
const href = run.href ? normalizeLinkHref(run.href) : void 0;
if (!href) return document.createElement("span");
const anchor = document.createElement("a");
anchor.href = href;
if (!getInternalLinkTarget(href)) {
anchor.target = "_blank";
anchor.rel = "noopener noreferrer";
}
return anchor;
};
var appendTextSpan = (arg) => {
const { textBlock, text, run, schema, font } = arg;
if (!text) return;
const span = createStaticInlineMarkdownElement(run);
span.textContent = text;
applyInlineMarkdownStyle({
element: span,
run,
schema,
font
});
textBlock.appendChild(span);
};
var appendVariableSpan = (arg) => {
const { textBlock, variableName, variables, run, schema, font, theme, onChange, stopEditing } = arg;
const span = document.createElement("span");
span.style.outline = `${theme.colorPrimary} dashed 1px`;
applyInlineMarkdownStyle({
element: span,
run,
schema,
font
});
makeElementPlainTextContentEditable(span);
span.textContent = variables[variableName] ?? "";
span.addEventListener("blur", (e) => {
const newValue = e.target.textContent || "";
if (newValue !== variables[variableName]) {
variables[variableName] = newValue;
if (onChange) onChange({
key: "content",
value: JSON.stringify(variables)
});
if (stopEditing) stopEditing();
}
});
textBlock.appendChild(span);
};
var renderInlineMarkdownVariableSpans = (arg) => {
const { runs, variables, textBlock, schema, font, theme, onChange, stopEditing } = arg;
textBlock.innerHTML = "";
runs.forEach((run) => {
let lastIndex = 0;
visitVariables(run.text, ({ name, startIndex, endIndex }) => {
appendTextSpan({
textBlock,
text: run.text.slice(lastIndex, startIndex),
run,
schema,
font
});
appendVariableSpan({
textBlock,
variableName: name,
variables,
run,
schema,
font,
theme,
onChange,
stopEditing
});
lastIndex = endIndex + 1;
});
appendTextSpan({
textBlock,
text: run.text.slice(lastIndex),
run,
schema,
font
});
});
};
/**
* An optimisation to try to minimise jank while typing.
* Only check whether variables were modified based on certain key presses.
* Regex would otherwise be performed on every key press (which isn't terrible, but this code helps).
*/
var keyPressShouldBeChecked = (event) => {
if (event.key === "ArrowUp" || event.key === "ArrowDown" || event.key === "ArrowLeft" || event.key === "ArrowRight") return false;
const selection = window.getSelection();
const contenteditable = event.target;
if (selection?.focusOffset === contenteditable?.textContent?.length) return event.key === "}" || event.key === "Backspace" || event.key === "Delete";
if (selection?.anchorOffset === 0) return event.key === "{" || event.key === "Backspace" || event.key === "Delete";
return true;
};
//#endregion
//#region src/multiVariableText/index.ts
var schema$1 = {
pdf: pdfRender$3,
ui: uiRender$3,
propPanel: propPanel$2,
icon: createSvgStr(Type),
uninterruptedEditMode: true
};
//#endregion
//#region src/shapes/rectAndEllipse.ts
var shape = {
ui: (arg) => {
const { schema, rootElement } = arg;
const div = document.createElement("div");
div.style.width = "100%";
div.style.height = "100%";
div.style.boxSizing = "border-box";
if (schema.type === "ellipse") div.style.borderRadius = "50%";
else if (schema.radius && schema.radius > 0) div.style.borderRadius = `${schema.radius}mm`;
div.style.borderWidth = `${schema.borderWidth ?? 0}mm`;
div.style.borderStyle = schema.borderWidth && schema.borderColor ? "solid" : "none";
div.style.borderColor = schema.borderColor ?? "transparent";
div.style.backgroundColor = schema.color ?? "transparent";
rootElement.appendChild(div);
},
pdf: (arg) => {
const { schema, page, options } = arg;
if (!schema.color && !schema.borderColor) return;
const { colorType } = options;
const cArg = {
schema,
pageHeight: page.getHeight()
};
const { position, width, height, rotate, opacity } = convertForPdfLayoutProps(cArg);
const { position: { x: x4Ellipse, y: y4Ellipse } } = convertForPdfLayoutProps({
...cArg,
applyRotateTranslate: false
});
const borderWidth = schema.borderWidth ? mm2pt(schema.borderWidth) : 0;
const drawOptions = {
rotate,
borderWidth,
borderColor: hex2PrintingColor(schema.borderColor, colorType),
color: hex2PrintingColor(schema.color, colorType),
opacity,
borderOpacity: opacity
};
if (schema.type === "ellipse") page.drawEllipse({
x: x4Ellipse + width / 2,
y: y4Ellipse + height / 2,
xScale: width / 2 - borderWidth / 2,
yScale: height / 2 - borderWidth / 2,
...drawOptions
});
else if (schema.type === "rectangle") {
const radius = schema.radius ?? 0;
page.drawRectangle({
x: position.x + borderWidth * ((1 - Math.sin(toRadians(rotate))) / 2) + Math.tan(toRadians(rotate)) * Math.PI ** 2,
y: position.y + borderWidth * ((1 + Math.sin(toRadians(rotate))) / 2) + Math.tan(toRadians(rotate)) * Math.PI ** 2,
width: width - borderWidth,
height: height - borderWidth,
...radius ? { radius: mm2pt(radius) } : {},
...drawOptions
});
}
},
propPanel: {
schema: ({ i18n }) => ({
borderWidth: {
title: i18n("schemas.borderWidth"),
type: "number",
widget: "inputNumber",
props: {
min: 0,
step: 1
},
span: 12
},
borderColor: {
title: i18n("schemas.borderColor"),
type: "string",
widget: "color",
props: { disabledAlpha: true },
rules: [{
pattern: HEX_COLOR_PATTERN,
message: i18n("validation.hexColor")
}],
span: 12
},
color: {
title: i18n("schemas.color"),
type: "string",
widget: "color",
props: { disabledAlpha: true },
rules: [{
pattern: HEX_COLOR_PATTERN,
message: i18n("validation.hexColor")
}]
},
radius: {
title: i18n("schemas.radius"),
type: "number",
widget: "inputNumber",
props: {
min: 0,
step: 1
},
span: 12
}
}),
defaultSchema: {
name: "",
type: "rectangle",
position: {
x: 0,
y: 0
},
width: 62.5,
height: 37.5,
rotate: 0,
opacity: 1,
borderWidth: 1,
borderColor: "#000000",
color: "",
readOnly: true,
radius: 0
}
}
};
var getPropPanelSchema = (type) => ({
...shape.propPanel,
defaultSchema: {
...shape.propPanel.defaultSchema,
type
}
});
var rectangle = {
...shape,
propPanel: getPropPanelSchema("rectangle"),
icon: createSvgStr(Square)
};
var ellipse = {
...shape,
propPanel: getPropPanelSchema("ellipse"),
icon: createSvgStr(Circle)
};
//#endregion
//#region src/list/pdfRender.ts
var rectanglePdfRender$2 = rectangle.pdf;
var pdfRender$2 = async (arg) => {
const { schema, value } = arg;
const items = normalizeListItems(value);
const range = getListItemRange(schema) ?? {
start: 0,
end: items.length
};
const visibleItems = items.slice(range.start, range.end);
if (visibleItems.length === 0) return;
const layout = await calculateListLayout({
schema,
items: visibleItems,
markerItems: items,
startIndex: range.start,
options: arg.options,
_cache: arg._cache
});
if (schema.backgroundColor) await rectanglePdfRender$2({
...arg,
schema: {
...schema,
type: "rectangle",
borderWidth: 0,
borderColor: "",
color: schema.backgroundColor
}
});
let y = schema.position.y;
for (const item of layout.items) {
await pdfRender$4({
...arg,
value: item.marker,
schema: {
...schema,
type: "text",
position: {
x: schema.position.x + item.markerX,
y
},
width: layout.markerWidth,
height: item.height,
backgroundColor: "",
alignment: "right",
verticalAlignment: "top",
dynamicFontSize: void 0
}
});
await pdfRender$4({
...arg,
value: item.item,
schema: {
...schema,
type: "text",
position: {
x: schema.position.x + item.bodyX,
y
},
width: item.bodyWidth,
height: item.height,
backgroundColor: "",
verticalAlignment: "top",
dynamicFontSize: void 0
}
});
y += item.height;
}
};
//#endregion
//#region src/list/propPanel.ts
var propPanel$1 = {
schema: (propPanelProps) => {
if (typeof propPanel$3.schema !== "function") throw new Error("Oops, is text schema no longer a function?");
const parentSchema = propPanel$3.schema(propPanelProps);
const i18n = propPanelProps.i18n;
const listSchema = { ...parentSchema };
delete listSchema.useDynamicFontSize;
delete listSchema.dynamicFontSize;
return {
...listSchema,
"-------": {
type: "void",
widget: "Divider"
},
listStyle: {
title: i18n("schemas.list.listStyle"),
type: "string",
widget: "select",
props: { options: [{
label: i18n("schemas.list.bullet"),
value: LIST_STYLE_BULLET
}, {
label: i18n("schemas.list.ordered"),
value: LIST_STYLE_ORDERED
}] },
span: 24
},
markerWidth: {
title: i18n("schemas.list.markerWidth"),
type: "number",
widget: "inputNumber",
props: { min: 0 },
span: 6
},
markerGap: {
title: i18n("schemas.list.markerGap"),
type: "number",
widget: "inputNumber",
props: { min: 0 },
span: 6
},
indentSize: {
title: i18n("schemas.list.indentSize"),
type: "number",
widget: "inputNumber",
props: { min: 0 },
span: 6
},
itemSpacing: {
title: i18n("schemas.list.itemSpacing"),
type: "number",
widget: "inputNumber",
props: { min: 0 },
span: 6
}
};
},
widgets: propPanel$3.widgets,
defaultSchema: {
...propPanel$3.defaultSchema,
type: "list",
content: JSON.stringify(["First item", "Second item"]),
width: 80,
height: 20,
listStyle: DEFAULT_LIST_STYLE,
markerWidth: 6,
markerGap: 2,
indentSize: 6,
itemSpacing: 1,
dynamicFontSize: void 0,
verticalAlignment: "top"
}
};
//#endregion
//#region src/list/uiRender.ts
var focusDataKey = "pdfmeListFocusIndex";
var actionDataKey = "pdfmeListAction";
var internalFocusDataKey = "pdfmeListInternalFocus";
var caretMarker = "";
var pendingFocusIndexes = /* @__PURE__ */ new Map();
var getListFocusKey = (schema) => schema.id || schema.name;
var isComposingKeyboardEvent = (event) => event.isComposing || event.keyCode === 229;
var getText = (element) => {
const rawText = element.innerText;
const hasCaretMarker = rawText.includes(caretMarker);
let text = rawText.replace(/\u200B/g, "");
if (!hasCaretMarker && text.endsWith("\n")) text = text.slice(0, -1);
return text;
};
var setStyles = (element, styles) => {
Object.assign(element.style, styles);
};
var focusBody = (body) => {
body.focus();
const selection = window.getSelection();
const range = document.createRange();
if (selection && range) {
range.selectNodeContents(body);
range.collapse(false);
selection.removeAllRanges();
selection.addRange(range);
}
};
var getCaretRangeFromPoint = (x, y) => {
const documentWithCaret = document;
if (documentWithCaret.caretRangeFromPoint) return documentWithCaret.caretRangeFromPoint(x, y);
const caretPosition = documentWithCaret.caretPositionFromPoint?.(x, y);
if (!caretPosition) return null;
const range = document.createRange();
range.setStart(caretPosition.offsetNode, caretPosition.offset);
range.collapse(true);
return range;
};
var focusBodyFromMouseEvent = (body, event) => {
body.focus();
const range = getCaretRangeFromPoint(event.clientX, event.clientY);
if (!range || !body.contains(range.startContainer)) return;
const selection = window.getSelection();
if (!selection) return;
selection.removeAllRanges();
selection.addRange(range);
};
var getBodyEditor = (body) => body.querySelector("[contenteditable], [tabindex]");
var insertLineBreakAtSelection = (element) => {
const fallbackText = getText(element);
const selection = window.getSelection();
if (!selection?.rangeCount) {
element.innerText = `${fallbackText}\n${caretMarker}`;
focusBody(element);
return true;
}
const range = selection.getRangeAt(0);
if (!element.contains(range.commonAncestorContainer)) {
element.innerText = `${fallbackText}\n${caretMarker}`;
focusBody(element);
return true;
}
selection.deleteFromDocument();
const fragment = document.createDocumentFragment();
const lineBreak = document.createElement("br");
const marker = document.createTextNode(caretMarker);
fragment.append(lineBreak, marker);
range.insertNode(fragment);
range.setStart(marker, marker.length);
range.collapse(true);
selection.removeAllRanges();
selection.addRange(range);
if (!element.innerText.includes(caretMarker)) {
element.innerText = `${fallbackText}\n${caretMarker}`;
focusBody(element);
}
return true;
};
var createActionButton = (arg) => {
const button = document.createElement("button");
button.type = "button";
button.innerText = arg.label;
button.setAttribute("aria-label", arg.ariaLabel);
button.disabled = Boolean(arg.disabled);
setStyles(button, {
width: "18px",
height: "18px",
display: "inline-flex",
alignItems: "center",
justifyContent: "center",
padding: "0",
border: "1px solid #d9d9d9",
borderRadius: "3px",
background: "#ffffff",
color: "#333333",
fontSize: "11px",
lineHeight: "1",
cursor: arg.disabled ? "not-allowed" : "pointer",
opacity: arg.disabled ? .45 : 1
});
button.addEventListener("pointerdown", (event) => {
arg.onPressStart?.();
event.stopPropagation();
});
button.addEventListener("mousedown", (event) => {
arg.onPressStart?.();
event.preventDefault();
event.stopPropagation();
});
button.addEventListener("click", (event) => {
event.preventDefault();
event.stopPropagation();
if (!arg.disabled) arg.onClick();
});
return button;
};
var uiRender$2 = async (arg) => {
const { rootElement, schema, value, mode, onChange, stopEditing, tabIndex, placeholder } = arg;
const focusKey = getListFocusKey(schema);
const editable = isEditable(mode, schema);
const showControls = editable && (mode === "form" || mode === "designer");
const usePlaceholder = editable && !value && Boolean(placeholder);
const items = normalizeListItems(usePlaceholder ? placeholder || "" : value);
const originalItems = normalizeListItemEntries(value);
const range = getListItemRange(schema) ?? {
start: 0,
end: items.length
};
const visibleItems = items.slice(range.start, range.end);
const renderItems = visibleItems;
rootElement.innerHTML = "";
setStyles(rootElement, {
position: "relative",
width: "100%",
height: "100%",
backgroundColor: schema.backgroundColor || "transparent",
overflow: "visible"
});
const layout = await calculateListLayout({
schema,
items: renderItems,
markerItems: items,
startIndex: range.start,
options: arg.options,
_cache: arg._cache
});
const bodyElements = [];
const getEditedItems = () => layout.items.map((item, index) => ({
level: item.level,
text: getText(bodyElements[index])
}));
const getNextItems = () => {
const editedItems = getEditedItems();
if (usePlaceholder) return editedItems;
const nextItems = [...originalItems];
nextItems.splice(range.start, visibleItems.length, ...editedItems);
return nextItems;
};
const commitItems = (nextItems, focusIndex) => {
if (!onChange) return;
if (focusIndex !== void 0) {
rootElement.dataset[focusDataKey] = String(focusIndex);
pendingFocusIndexes.set(focusKey, focusIndex);
}
onChange({
key: "content",
value: serializeListItems(nextItems)
});
};
const commitHeight = async (focusIndex) => {
if (!onChange) return;
if (focusIndex !== void 0) {
rootElement.dataset[focusDataKey] = String(focusIndex);
pendingFocusIndexes.set(focusKey, focusIndex);
}
const rawItems = normalizeListItems(serializeListItems(getNextItems()));
const nextLayout = await calculateListLayout({
schema,
items: rawItems.slice(range.start, range.end),
markerItems: rawItems,
startIndex: range.start,
options: arg.options,
_cache: arg._cache
});
if (schema.height !== nextLayout.totalHeight) onChange({
key: "height",
value: nextLayout.totalHeight
});
};
const preserveEditingForAction = () => {
rootElement.dataset[actionDataKey] = "true";
};
const updateItems = (rowIndex, mutate) => {
const nextItems = getNextItems();
if (nextItems.length === 0) nextItems.push({
level: 0,
text: ""
});
const focusIndex = mutate(nextItems, Math.min(Math.max(range.start + rowIndex, 0), nextItems.length - 1));
preserveEditingForAction();
commitItems(nextItems, focusIndex);
};
const preserveEditingForInternalFocus = () => {
rootElement.dataset[internalFocusDataKey] = "true";
};
const preserveEditingForKeyboardCommit = () => {
preserveEditingForInternalFocus();
setTimeout(() => {
if (rootElement.dataset[internalFocusDataKey] === "true") delete rootElement.dataset[internalFocusDataKey];
});
};
const handleInternalFocusPointer = (event) => {
preserveEditingForInternalFocus();
event.stopPropagation();
};
const handleBodyMouseDown = (body, event) => {
handleInternalFocusPointer(event);
focusBodyFromMouseEvent(body, event);
};
const appendEmptyListControls = () => {
const controls = document.createElement("div");
controls.addEventListener("pointerdown", preserveEditingForAction);
controls.addEventListener("mousedown", preserveEditingForAction);
setStyles(controls, {
position: "absolute",
top: "0mm",
right: "-20px",
display: "flex",
gap: "2px"
});
controls.appendChild(createActionButton({
label: "+",
ariaLabel: arg.i18n("schemas.list.addItem"),
onPressStart: preserveEditingForAction,
onClick: () => {
const nextItems = [...originalItems];
nextItems.splice(range.start, 0, {
level: 0,
text: ""
});
commitItems(nextItems, range.start);
}
}));
rootElement.appendChild(controls);
};
let offsetY = 0;
for (let index = 0; index < layout.items.length; index += 1) {
const item = layout.items[index];
const row = document.createElement("div");
setStyles(row, {
position: "absolute",
top: `${offsetY}mm`,
left: "0mm",
width: `${schema.width}mm`,
height: `${item.height}mm`
});
const marker = document.createElement("div");
setStyles(marker, {
position: "absolute",
top: "0mm",
left: `${item.markerX}mm`,
width: `${layout.markerWidth}mm`,
height: "100%",
backgroundColor: "transparent",
cursor: "default"
});
const body = document.createElement("div");
setStyles(body, {
position: "absolute",
top: "0mm",
left: `${item.bodyX}mm`,
width: `${item.bodyWidth}mm`,
height: `${item.height}mm`,
backgroundColor: "transparent",
cursor: editable ? "text" : "default"
});
const schemaForUI = schema;
const textSchema = {
...schema,
id: `${schemaForUI.id || schema.name}-list-item-${item.itemIndex}`,
name: `${schema.name}-list-item-${item.itemIndex}`,
type: "text",
content: item.item,
position: {
x: 0,
y: 0
},
width: item.bodyWidth,
height: item.height,
alignment: schema.alignment ?? "left",
fontSize: schema.fontSize ?? 13,
lineHeight: schema.lineHeight ?? 1,
characterSpacing: schema.characterSpacing ?? 0,
fontColor: usePlaceholder ? PLACEHOLDER_FONT_COLOR : schema.fontColor || "#000000",
backgroundColor: ""
};
const markerTextSchema = {
...textSchema,
id: `${schemaForUI.id || schema.name}-list-marker-${item.itemIndex}`,
name: `${schema.name}-list-marker-${item.itemIndex}`,
content: item.marker,
width: layout.markerWidth,
height: item.height,
alignment: "right",
fontColor: schema.fontColor || "#000000"
};
await uiRender$4({
...arg,
rootElement: marker,
schema: markerTextSchema,
value: item.marker,
mode: "viewer",
placeholder: "",
onChange: void 0,
stopEditing: void 0
});
await uiRender$4({
...arg,
rootElement: body,
schema: textSchema,
value: item.item,
placeholder: "",
onChange: void 0,
stopEditing: void 0
});
if (editable) {
const editor = getBodyEditor(body);
if (!editor) throw new Error("Unable to find list item text editor");
editor.tabIndex = tabIndex || 0;
body.addEventListener("pointerdown", handleInternalFocusPointer);
body.addEventListener("mousedown", (event) => {
handleBodyMouseDown(editor, event);
});
body.addEventListener("click", (event) => {
event.stopPropagation();
focusBodyFromMouseEvent(editor, event);
});
editor.addEventListener("focus", () => {
if (usePlaceholder) {
editor.innerText = "";
editor.style.color = schema.fontColor || "#000000";
}
});
body.addEventListener("blur", (event) => {
const isListAction = rootElement.dataset[actionDataKey] === "true";
const relatedTarget = event.relatedTarget;
const isInternalFocus = rootElement.dataset[internalFocusDataKey] === "true" || relatedTarget instanceof Node && rootElement.contains(relatedTarget);
delete rootElement.dataset[internalFocusDataKey];
if (isListAction || isInternalFocus) return;
if (!onChange) return;
commitItems(getNextItems());
if (stopEditing) stopEditing();
}, true);
editor.addEventListener("keydown", (event) => {
if (event.key === "Enter") {
if (isComposingKeyboardEvent(event)) return;
event.preventDefault();
if (insertLineBreakAtSelection(editor)) {
preserveEditingForKeyboardCommit();
if (mode === "form") commitHeight(range.start + index);
else commitItems(getNextItems(), range.start + index);
}
} else if (event.key === "Tab") {
event.preventDefault();
updateItems(index, (nextItems, itemIndex) => {
const itemToUpdate = nextItems[itemIndex];
itemToUpdate.level = event.shiftKey ? Math.max(itemToUpdate.level - 1, 0) : Math.min(itemToUpdate.level + 1, 8);
return itemIndex;
});
} else if (event.key === "Backspace" && getText(editor) === "") {
event.preventDefault();
updateItems(index, (nextItems, itemIndex) => {
if (nextItems.length <= 1) {
nextItems.splice(0);
return;
}
nextItems.splice(itemIndex, 1);
return Math.min(itemIndex, nextItems.length - 1);
});
}
});
bodyElements.push(editor);
}
row.appendChild(marker);
row.appendChild(body);
if (showControls) {
const controls = document.createElement("div");
controls.addEventListener("pointerdown", preserveEditingForAction);
controls.addEventListener("mousedown", preserveEditingForAction);
setStyles(controls, {
position: "absolute",
top: "0mm",
right: "-82px",
display: "flex",
gap: "2px"
});
controls.appendChild(createActionButton({
label: "+",
ariaLabel: arg.i18n("schemas.list.addItem"),
onPressStart: preserveEditingForAction,
onClick: () => {
updateItems(index, (nextItems, itemIndex) => {
nextItems.splice(itemIndex + 1, 0, {
level: nextItems[itemIndex]?.level ?? 0,
text: ""
});
return itemIndex + 1;
});
}
}));
controls.appendChild(createActionButton({
label: "-",
ariaLabel: arg.i18n("schemas.list.removeItem"),
onPressStart: preserveEditingForAction,
onClick: () => {
updateItems(index, (nextItems, itemIndex) => {
if (nextItems.length <= 1) {
nextItems.splice(0);
return;
}
nextItems.splice(itemIndex, 1);
return Math.min(itemIndex, nextItems.length - 1);
});
}
}));
controls.appendChild(createActionButton({
label: "<",
ariaLabel: arg.i18n("schemas.list.outdentItem"),
disabled: item.level === 0,
onPressStart: preserveEditingForAction,
onClick: () => {
updateItems(index, (nextItems, itemIndex) => {
nextItems[itemIndex].level = Math.max(nextItems[itemIndex].level - 1, 0);
return itemIndex;
});
}
}));
controls.appendChild(createActionButton({
label: ">",
ariaLabel: arg.i18n("schemas.list.indentItem"),
disabled: item.level >= 8,
onPressStart: preserveEditingForAction,
onClick: () => {
updateItems(index, (nextItems, itemIndex) => {
nextItems[itemIndex].level = Math.min(nextItems[itemIndex].level + 1, 8);
return itemIndex;
});
}
}));
row.appendChild(controls);
}
rootElement.appendChild(row);
offsetY += item.height;
}
if (showControls && visibleItems.length === 0) appendEmptyListControls();
const pendingFocusIndex = pendingFocusIndexes.get(focusKey);
if (pendingFocusIndex !== void 0) pendingFocusIndexes.delete(focusKey);
const requestedFocusIndex = Number(rootElement.dataset[focusDataKey] ?? pendingFocusIndex);
delete rootElement.dataset[focusDataKey];
delete rootElement.dataset[actionDataKey];
delete rootElement.dataset[internalFocusDataKey];
const relativeFocusIndex = requestedFocusIndex - range.start;
if (editable && Number.isFinite(requestedFocusIndex) && bodyElements[relativeFocusIndex]) setTimeout(() => focusBody(bodyElements[relativeFocusIndex]));
else if (editable && mode === "designer" && bodyElements[0]) setTimeout(() => {
if (!rootElement.contains(document.activeElement)) focusBody(bodyElements[0]);
});
if (schema.height !== layout.totalHeight && onChange) onChange({
key: "height",
value: layout.totalHeight
});
};
//#endregion
//#region src/list/index.ts
var listSchema = {
pdf: pdfRender$2,
ui: uiRender$2,
propPanel: propPanel$1,
icon: createSvgStr(List)
};
//#endregion
//#region src/graphics/imagehelper.ts
var decoder = new TextDecoder();
var toUTF8String = (input, start = 0, end = input.length) => decoder.decode(input.slice(start, end));
var toHexString = (input, start = 0, end = input.length) => input.slice(start, end).reduce((memo, i) => memo + ("0" + i.toString(16)).slice(-2), "");
var readUInt16BE = (input, offset = 0) => input[offset] * 2 ** 8 + input[offset + 1];
var readUInt32BE = (input, offset = 0) => input[offset] * 2 ** 24 + input[offset + 1] * 2 ** 16 + input[offset + 2] * 2 ** 8 + input[offset + 3];
var extractSize = (input, index) => {
return {
height: readUInt16BE(input, index),
width: readUInt16BE(input, index + 2)
};
};
var validateInput = (input, index) => {
if (index > input.length) throw new TypeError("Corrupt JPG, exceeded buffer limits");
if (input[index] !== 255) throw new TypeError("Invalid JPG, marker table corrupted");
};
var JPG = {
validate: (input) => toHexString(input, 0, 2) === "ffd8",
calculate(input) {
input = input.slice(4);
let next;
while (input.length) {
const i = readUInt16BE(input, 0);
validateInput(input, i);
next = input[i + 1];
if (next === 192 || next === 193 || next === 194) return extractSize(input, i + 5);
input = input.slice(i + 2);
}
throw new TypeError("Invalid JPG, no size found");
}
};
var pngSignature = "PNG\r\n\n";
var pngImageHeaderChunkName = "IHDR";
var pngFriedChunkName = "CgBI";
var typeHandlers = {
jpg: JPG,
png: {
validate(input) {
if (pngSignature === toUTF8String(input, 1, 8)) {
let chunkName = toUTF8String(input, 12, 16);
if (chunkName === pngFriedChunkName) chunkName = toUTF8String(input, 28, 32);
if (chunkName !== pngImageHeaderChunkName) throw new TypeError("Invalid PNG");
return true;
}
return false;
},
calculate(input) {
if (toUTF8String(input, 12, 16) === pngFriedChunkName) return {
height: readUInt32BE(input, 36),
width: readUInt32BE(input, 32)
};
return {
height: readUInt32BE(input, 20),
width: readUInt32BE(input, 16)
};
}
}
};
function detector(input) {
const firstBytes = {
137: "png",
255: "jpg"
};
const byte = input[0];
if (byte in firstBytes) {
const type = firstBytes[byte];
if (type && typeHandlers[type].validate(input)) return type;
}
return Object.keys(typeHandlers).find((key) => typeHandlers[key].validate(input));
}
var getImageDimension = (value) => {
const idx = value.indexOf(";base64,");
const imgBase64 = value.substring(idx + 8, value.length);
return imageSize(Buffer$1.from(imgBase64, "base64"));
};
var imageSize = (imgBuffer) => {
const type = detector(imgBuffer);
if (typeof type !== "undefined" && type in typeHandlers) {
const size = typeHandlers[type].calculate(imgBuffer);
if (size !== void 0) return size;
}
throw new TypeError("[@pdfme/schemas/images] Unsupported file type: " + (type === void 0 ? "undefined" : type));
};
//#endregion
//#region src/graphics/image.ts
/**
* Build a short fingerprint for a potentially-large base64 image string.
* Previously `${schema.type}${input}` was used, pinning multi-MB base64
* strings in the cache Map forever — every unique image input created a
* permanent Map key whose byte length matched the image itself.
*
* The fingerprint is an FNV-1a 32-bit hash over the full input, combined
* with the schema type and input byte length. An earlier revision sampled
* three 16-char regions (first + middle + last) instead of hashing, but
* the first-16 slice is a constant data-URI prefix for any image of the
* same MIME type (`data:image/png;b…` / `data:image/jpeg…`), contributing
* no entropy. Hashing every byte removes that weakness at the same O(n)
* cost, without