@pdfme/schemas 6.0.6 → 6.1.0-dev.2

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 (38) hide show
  1. package/dist/builtins-CWHhKSVA.js +1389 -0
  2. package/dist/builtins-CWHhKSVA.js.map +1 -0
  3. package/dist/builtins.js +1 -1
  4. package/dist/{dynamicTemplate-D_DHR3-X.js → dynamicTemplate-DmuRoTw4.js} +23 -350
  5. package/dist/dynamicTemplate-DmuRoTw4.js.map +1 -0
  6. package/dist/helper-M_MmV_d5.js +357 -0
  7. package/dist/helper-M_MmV_d5.js.map +1 -0
  8. package/dist/index.d.ts +4 -2
  9. package/dist/index.js +959 -178
  10. package/dist/index.js.map +1 -1
  11. package/dist/list/constants.d.ts +10 -0
  12. package/dist/list/dynamicTemplate.d.ts +2 -0
  13. package/dist/list/helper.d.ts +15 -0
  14. package/dist/list/index.d.ts +4 -0
  15. package/dist/list/pdfRender.d.ts +3 -0
  16. package/dist/list/propPanel.d.ts +3 -0
  17. package/dist/list/types.d.ts +36 -0
  18. package/dist/list/uiRender.d.ts +3 -0
  19. package/dist/lists-B6dmgpkS.js +117 -0
  20. package/dist/lists-B6dmgpkS.js.map +1 -0
  21. package/dist/lists.d.ts +3 -0
  22. package/dist/lists.js +2 -0
  23. package/dist/multiVariableText/helper.d.ts +2 -1
  24. package/dist/tables/dynamicTemplate.d.ts +2 -1
  25. package/dist/tables.d.ts +1 -1
  26. package/dist/tables.js +2 -2
  27. package/dist/text/constants.d.ts +13 -0
  28. package/dist/text/inlineMarkdown.d.ts +4 -0
  29. package/dist/text/richText.d.ts +46 -0
  30. package/dist/text/richTextPdfRender.d.ts +34 -0
  31. package/dist/text/types.d.ts +18 -0
  32. package/dist/text/uiRender.d.ts +1 -1
  33. package/dist/utils.js +2 -2
  34. package/dist/utils.js.map +1 -1
  35. package/package.json +12 -7
  36. package/dist/builtins-CgaZ0UX3.js +0 -613
  37. package/dist/builtins-CgaZ0UX3.js.map +0 -1
  38. package/dist/dynamicTemplate-D_DHR3-X.js.map +0 -1
@@ -0,0 +1,1389 @@
1
+ import { A as TEXT_FORMAT_INLINE_MARKDOWN, C as FONT_SIZE_ADJUSTMENT, D as PLACEHOLDER_FONT_COLOR, E as FONT_VARIANT_FALLBACK_SYNTHETIC, M as VERTICAL_ALIGN_BOTTOM, N as VERTICAL_ALIGN_MIDDLE, O as SYNTHETIC_BOLD_CSS_TEXT_SHADOW, S as DYNAMIC_FIT_VERTICAL, T as FONT_VARIANT_FALLBACK_PLAIN, _ as DEFAULT_DYNAMIC_FIT, a as getFontKitFont, b as DEFAULT_TEXT_FORMAT, c as splitTextToSize, d as ALIGN_JUSTIFY, g as DEFAULT_ALIGNMENT, h as CODE_HORIZONTAL_PADDING, i as getFontDescentInPt, j as TEXT_FORMAT_PLAIN, k as SYNTHETIC_BOLD_OFFSET_RATIO, l as widthOfTextAtSize, m as CODE_BACKGROUND_COLOR, n as fetchRemoteFontData, o as heightOfFontAtSize, p as ALIGN_RIGHT, r as getBrowserVerticalFontAdjustments, s as isFirefox, t as calculateDynamicFontSize, u as ALIGN_CENTER, v as DEFAULT_FONT_COLOR, w as FONT_VARIANT_FALLBACK_ERROR, x as DYNAMIC_FIT_HORIZONTAL, y as DEFAULT_FONT_VARIANT_FALLBACK } from "./helper-M_MmV_d5.js";
2
+ import { c as HEX_COLOR_PATTERN } from "./dynamicTemplate-DmuRoTw4.js";
3
+ import { convertForPdfLayoutProps, createSvgStr, hex2PrintingColor, isEditable, rotatePoint } from "./utils.js";
4
+ import { DEFAULT_FONT_NAME, getDefaultFont, getFallbackFontName, mm2pt, pt2mm } from "@pdfme/common";
5
+ import { AlignCenter, AlignJustify, AlignLeft, AlignRight, ArrowDownToLine, ArrowUpToLine, Strikethrough, TextCursorInput, Underline } from "lucide";
6
+ //#region src/text/inlineMarkdown.ts
7
+ var MARKDOWN_ESCAPABLE_CHARS = new Set([
8
+ "\\",
9
+ "*",
10
+ "~",
11
+ "`"
12
+ ]);
13
+ var MARKDOWN_ESCAPE_PATTERN = /[\\*~`]/g;
14
+ var MARKDOWN_UNESCAPE_PATTERN = /\\([\\*~`])/g;
15
+ var sameStyle = (a, b) => Boolean(a.bold) === Boolean(b.bold) && Boolean(a.italic) === Boolean(b.italic) && Boolean(a.strikethrough) === Boolean(b.strikethrough) && Boolean(a.code) === Boolean(b.code);
16
+ var appendRun = (runs, text, style) => {
17
+ if (!text) return;
18
+ const lastRun = runs.at(-1);
19
+ if (lastRun && sameStyle(lastRun, style)) {
20
+ lastRun.text += text;
21
+ return;
22
+ }
23
+ runs.push({
24
+ text,
25
+ ...style.bold ? { bold: true } : {},
26
+ ...style.italic ? { italic: true } : {},
27
+ ...style.strikethrough ? { strikethrough: true } : {},
28
+ ...style.code ? { code: true } : {}
29
+ });
30
+ };
31
+ var findClosingDelimiter = (value, delimiter, from) => {
32
+ for (let i = from; i < value.length; i++) {
33
+ if (value[i] === "\\") {
34
+ i += 1;
35
+ continue;
36
+ }
37
+ if (delimiter !== "`" && value[i] === "`") {
38
+ const codeEnd = findClosingDelimiter(value, "`", i + 1);
39
+ if (codeEnd === -1) continue;
40
+ i = codeEnd;
41
+ continue;
42
+ }
43
+ if (value.startsWith(delimiter, i)) return i;
44
+ }
45
+ return -1;
46
+ };
47
+ var getDelimiter = (value, index) => {
48
+ if (value[index] === "`") return "`";
49
+ if (value.startsWith("***", index)) return "***";
50
+ if (value.startsWith("**", index)) return "**";
51
+ if (value.startsWith("~~", index)) return "~~";
52
+ if (value[index] === "*") return "*";
53
+ return "";
54
+ };
55
+ var mergeStyle = (style, delimiter) => {
56
+ if (delimiter === "***") return {
57
+ ...style,
58
+ bold: true,
59
+ italic: true
60
+ };
61
+ if (delimiter === "**") return {
62
+ ...style,
63
+ bold: true
64
+ };
65
+ if (delimiter === "*") return {
66
+ ...style,
67
+ italic: true
68
+ };
69
+ if (delimiter === "~~") return {
70
+ ...style,
71
+ strikethrough: true
72
+ };
73
+ return style;
74
+ };
75
+ var parseRange = (value, from, to, style) => {
76
+ const runs = [];
77
+ let buffer = "";
78
+ const flush = () => {
79
+ appendRun(runs, buffer, style);
80
+ buffer = "";
81
+ };
82
+ for (let index = from; index < to; index++) {
83
+ const char = value[index];
84
+ if (char === "\\" && index + 1 < to && MARKDOWN_ESCAPABLE_CHARS.has(value[index + 1])) {
85
+ buffer += value[index + 1];
86
+ index += 1;
87
+ continue;
88
+ }
89
+ const delimiter = getDelimiter(value, index);
90
+ if (!delimiter) {
91
+ buffer += char;
92
+ continue;
93
+ }
94
+ const closingIndex = findClosingDelimiter(value, delimiter, index + delimiter.length);
95
+ if (closingIndex === -1 || closingIndex + delimiter.length > to) {
96
+ buffer += char;
97
+ continue;
98
+ }
99
+ flush();
100
+ if (delimiter === "`") appendRun(runs, value.slice(index + 1, closingIndex).replace(MARKDOWN_UNESCAPE_PATTERN, "$1"), {
101
+ ...style,
102
+ code: true
103
+ });
104
+ else parseRange(value, index + delimiter.length, closingIndex, mergeStyle(style, delimiter)).forEach((run) => appendRun(runs, run.text, run));
105
+ index = closingIndex + delimiter.length - 1;
106
+ }
107
+ flush();
108
+ return runs;
109
+ };
110
+ var parseInlineMarkdown = (value) => {
111
+ if (!value) return [];
112
+ return parseRange(value, 0, value.length, {});
113
+ };
114
+ var escapeInlineMarkdown = (value) => value.replace(MARKDOWN_ESCAPE_PATTERN, (char) => `\\${char}`);
115
+ var stripInlineMarkdown = (value) => parseInlineMarkdown(value).map((run) => run.text).join("");
116
+ //#endregion
117
+ //#region src/text/richText.ts
118
+ var richTextWordSegmenter = new Intl.Segmenter(void 0, { granularity: "word" });
119
+ var richTextGraphemeSegmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
120
+ var getBaseFontName = (schema, font) => schema.fontName && font[schema.fontName] ? schema.fontName : getFallbackFontName(font);
121
+ var getLoadedFontName = (font, fontName) => fontName && font[fontName] ? fontName : void 0;
122
+ var isInlineMarkdownTextSchema = (schema) => schema.textFormat === "inline-markdown" && !(schema.type === "text" && schema.readOnly !== true);
123
+ var resolveFontVariant = (run, schema, font) => {
124
+ const baseFontName = getBaseFontName(schema, font);
125
+ const variants = schema.fontVariants ?? {};
126
+ const fallback = schema.fontVariantFallback ?? "synthetic";
127
+ let fontName = baseFontName;
128
+ let needsBold = Boolean(run.bold);
129
+ let needsItalic = Boolean(run.italic);
130
+ if (run.code) fontName = getLoadedFontName(font, variants.code) ?? baseFontName;
131
+ else if (run.bold && run.italic) {
132
+ const boldItalic = getLoadedFontName(font, variants.boldItalic);
133
+ const italic = getLoadedFontName(font, variants.italic);
134
+ const bold = getLoadedFontName(font, variants.bold);
135
+ if (boldItalic) {
136
+ fontName = boldItalic;
137
+ needsBold = false;
138
+ needsItalic = false;
139
+ } else if (italic) {
140
+ fontName = italic;
141
+ needsItalic = false;
142
+ } else if (bold) {
143
+ fontName = bold;
144
+ needsBold = false;
145
+ }
146
+ } else if (run.bold) {
147
+ const bold = getLoadedFontName(font, variants.bold);
148
+ if (bold) {
149
+ fontName = bold;
150
+ needsBold = false;
151
+ }
152
+ } else if (run.italic) {
153
+ const italic = getLoadedFontName(font, variants.italic);
154
+ if (italic) {
155
+ fontName = italic;
156
+ needsItalic = false;
157
+ }
158
+ }
159
+ if ((needsBold || needsItalic || run.code && !getLoadedFontName(font, variants.code)) && fallback === "error") throw new Error(`[@pdfme/schemas] Missing font variant for markdown text in field "${schema.name}".`);
160
+ return {
161
+ fontName,
162
+ syntheticBold: fallback !== "plain" && needsBold,
163
+ syntheticItalic: fallback !== "plain" && needsItalic
164
+ };
165
+ };
166
+ var resolveRichTextRuns = async (arg) => {
167
+ const { runs, schema, font, _cache } = arg;
168
+ const fontKitCache = /* @__PURE__ */ new Map();
169
+ const getResolvedFontKitFont = async (fontName) => {
170
+ const cached = fontKitCache.get(fontName);
171
+ if (cached) return cached;
172
+ const fontKitFont = await getFontKitFont(fontName, font, _cache);
173
+ fontKitCache.set(fontName, fontKitFont);
174
+ return fontKitFont;
175
+ };
176
+ return Promise.all(runs.map(async (run) => {
177
+ const resolution = resolveFontVariant(run, schema, font);
178
+ return {
179
+ ...run,
180
+ ...resolution,
181
+ fontKitFont: await getResolvedFontKitFont(resolution.fontName)
182
+ };
183
+ }));
184
+ };
185
+ var measureRunText = (run, text, fontSize, characterSpacing) => {
186
+ const syntheticBoldWidth = run.syntheticBold ? fontSize * SYNTHETIC_BOLD_OFFSET_RATIO * 2 : 0;
187
+ const syntheticItalicWidth = run.syntheticItalic ? heightOfFontAtSize(run.fontKitFont, fontSize) * Math.tan(12 * Math.PI / 180) : 0;
188
+ return widthOfTextAtSize(text, run.fontKitFont, fontSize, characterSpacing) + syntheticBoldWidth + syntheticItalicWidth;
189
+ };
190
+ var createLine = () => ({
191
+ runs: [],
192
+ width: 0,
193
+ hardBreak: false
194
+ });
195
+ var pushRunToLine = (line, run, text, fontSize, characterSpacing) => {
196
+ if (!text) return;
197
+ const width = measureRunText(run, text, fontSize, characterSpacing);
198
+ if (line.runs.length > 0) line.width += characterSpacing;
199
+ line.runs.push({
200
+ ...run,
201
+ text,
202
+ width
203
+ });
204
+ line.width += width;
205
+ };
206
+ var measurePiecesWidth = (pieces, fontSize, characterSpacing) => {
207
+ let width = 0;
208
+ let hasText = false;
209
+ pieces.forEach((piece) => {
210
+ if (!piece.text) return;
211
+ if (hasText) width += characterSpacing;
212
+ width += measureRunText(piece.run, piece.text, fontSize, characterSpacing);
213
+ hasText = true;
214
+ });
215
+ return width;
216
+ };
217
+ var sliceRunPieces = (pieces, startIndex, endIndex) => {
218
+ const result = [];
219
+ let offset = 0;
220
+ pieces.forEach((piece) => {
221
+ const pieceStart = offset;
222
+ const pieceEnd = pieceStart + piece.text.length;
223
+ const sliceStart = Math.max(startIndex, pieceStart);
224
+ const sliceEnd = Math.min(endIndex, pieceEnd);
225
+ if (sliceStart < sliceEnd) result.push({
226
+ run: piece.run,
227
+ text: piece.text.slice(sliceStart - pieceStart, sliceEnd - pieceStart)
228
+ });
229
+ offset = pieceEnd;
230
+ });
231
+ return result;
232
+ };
233
+ var segmentRunPiecesByWord = (runs, onSegment, onHardBreak) => {
234
+ let paragraphPieces = [];
235
+ const flushParagraph = () => {
236
+ if (paragraphPieces.length === 0) return;
237
+ const paragraphText = paragraphPieces.map((piece) => piece.text).join("");
238
+ Array.from(richTextWordSegmenter.segment(paragraphText), ({ segment, index }) => {
239
+ const pieces = sliceRunPieces(paragraphPieces, index, index + segment.length);
240
+ if (pieces.length > 0) onSegment(pieces);
241
+ });
242
+ paragraphPieces = [];
243
+ };
244
+ runs.forEach((run) => {
245
+ run.text.split(/(\r\n|\r|\n)/).forEach((part) => {
246
+ if (part === "\r\n" || part === "\r" || part === "\n") {
247
+ flushParagraph();
248
+ onHardBreak();
249
+ return;
250
+ }
251
+ if (part) paragraphPieces.push({
252
+ run,
253
+ text: part
254
+ });
255
+ });
256
+ });
257
+ flushParagraph();
258
+ };
259
+ var splitIntoGraphemes = (value) => Array.from(richTextGraphemeSegmenter.segment(value), ({ segment }) => segment);
260
+ var countRichTextLineGraphemes = (line) => splitIntoGraphemes(line.runs.map((run) => run.text).join("")).length;
261
+ var layoutRichTextLines = (arg) => {
262
+ const { runs, fontSize, characterSpacing, boxWidthInPt } = arg;
263
+ const lines = [];
264
+ let currentLine = createLine();
265
+ const pushCurrentLine = (hardBreak) => {
266
+ currentLine.hardBreak = hardBreak;
267
+ lines.push(currentLine);
268
+ currentLine = createLine();
269
+ };
270
+ const pushPiecesToLine = (pieces) => {
271
+ pieces.forEach((piece) => {
272
+ pushRunToLine(currentLine, piece.run, piece.text, fontSize, characterSpacing);
273
+ });
274
+ };
275
+ const pushOversizedText = (run, text) => {
276
+ let remainingText = text;
277
+ while (remainingText.length > 0) {
278
+ const pendingSpacing = currentLine.runs.length > 0 ? characterSpacing : 0;
279
+ const remainingWidth = Math.max(boxWidthInPt - currentLine.width - pendingSpacing, 0);
280
+ const remainingTextWidth = measureRunText(run, remainingText, fontSize, characterSpacing);
281
+ if (remainingTextWidth <= remainingWidth || currentLine.runs.length === 0 && remainingTextWidth <= boxWidthInPt) {
282
+ pushRunToLine(currentLine, run, remainingText, fontSize, characterSpacing);
283
+ return;
284
+ }
285
+ if (currentLine.runs.length > 0 && remainingTextWidth <= boxWidthInPt) {
286
+ pushCurrentLine(false);
287
+ continue;
288
+ }
289
+ const graphemes = splitIntoGraphemes(remainingText);
290
+ let fittingText = "";
291
+ let fittingLength = 0;
292
+ for (const grapheme of graphemes) {
293
+ const candidate = fittingText + grapheme;
294
+ const candidateWidth = measureRunText(run, candidate, fontSize, characterSpacing);
295
+ const maxWidth = currentLine.runs.length === 0 ? boxWidthInPt : remainingWidth;
296
+ if (candidateWidth > maxWidth) {
297
+ if (fittingText) break;
298
+ if (currentLine.runs.length > 0) break;
299
+ }
300
+ fittingText = candidate;
301
+ fittingLength += grapheme.length;
302
+ if (candidateWidth > maxWidth) break;
303
+ }
304
+ if (!fittingText) {
305
+ pushCurrentLine(false);
306
+ continue;
307
+ }
308
+ pushRunToLine(currentLine, run, fittingText, fontSize, characterSpacing);
309
+ remainingText = remainingText.slice(fittingLength);
310
+ if (remainingText.length > 0) pushCurrentLine(false);
311
+ }
312
+ };
313
+ const pushSegment = (pieces) => {
314
+ const segmentWidth = measurePiecesWidth(pieces, fontSize, characterSpacing);
315
+ const pendingSpacing = currentLine.runs.length > 0 ? characterSpacing : 0;
316
+ if (segmentWidth <= Math.max(boxWidthInPt - currentLine.width - pendingSpacing, 0) || currentLine.runs.length === 0 && segmentWidth <= boxWidthInPt) {
317
+ pushPiecesToLine(pieces);
318
+ return;
319
+ }
320
+ if (currentLine.runs.length > 0) {
321
+ pushCurrentLine(false);
322
+ if (segmentWidth <= boxWidthInPt) {
323
+ pushPiecesToLine(pieces);
324
+ return;
325
+ }
326
+ }
327
+ pieces.forEach((piece) => pushOversizedText(piece.run, piece.text));
328
+ };
329
+ segmentRunPiecesByWord(runs, pushSegment, () => pushCurrentLine(true));
330
+ if (currentLine.runs.length > 0 || lines.length === 0) pushCurrentLine(false);
331
+ return lines;
332
+ };
333
+ var measureParagraphWidths = (runs, fontSize, characterSpacing) => {
334
+ const widths = [];
335
+ let paragraphPieces = [];
336
+ const pushWidth = () => {
337
+ widths.push(measurePiecesWidth(paragraphPieces, fontSize, characterSpacing));
338
+ paragraphPieces = [];
339
+ };
340
+ runs.forEach((run) => {
341
+ run.text.split(/(\r\n|\r|\n)/).forEach((part) => {
342
+ if (part === "\r\n" || part === "\r" || part === "\n") {
343
+ pushWidth();
344
+ return;
345
+ }
346
+ if (part) paragraphPieces.push({
347
+ run,
348
+ text: part
349
+ });
350
+ });
351
+ });
352
+ pushWidth();
353
+ return widths;
354
+ };
355
+ var getLineHeightAtSize = (line, fontSize) => {
356
+ if (line.runs.length === 0) return fontSize;
357
+ return Math.max(...line.runs.map((run) => heightOfFontAtSize(run.fontKitFont, fontSize)));
358
+ };
359
+ var calculateDynamicRichTextFontSize = async (arg) => {
360
+ const { value, schema, font, _cache, startingFontSize } = arg;
361
+ const { fontSize: schemaFontSize, dynamicFontSize: dynamicFontSizeSetting, characterSpacing: schemaCharacterSpacing, width: boxWidth, height: boxHeight, lineHeight = 1 } = schema;
362
+ const fontSize = startingFontSize || schemaFontSize || 13;
363
+ if (!dynamicFontSizeSetting) return fontSize;
364
+ if (dynamicFontSizeSetting.max < dynamicFontSizeSetting.min) return fontSize;
365
+ const resolvedRuns = await resolveRichTextRuns({
366
+ runs: parseInlineMarkdown(value),
367
+ schema,
368
+ font,
369
+ _cache
370
+ });
371
+ const characterSpacing = schemaCharacterSpacing ?? 0;
372
+ const dynamicFontFit = dynamicFontSizeSetting.fit ?? "vertical";
373
+ const boxWidthInPt = mm2pt(boxWidth);
374
+ let dynamicFontSize = fontSize;
375
+ if (dynamicFontSize < dynamicFontSizeSetting.min) dynamicFontSize = dynamicFontSizeSetting.min;
376
+ else if (dynamicFontSize > dynamicFontSizeSetting.max) dynamicFontSize = dynamicFontSizeSetting.max;
377
+ const calculateConstraints = (size) => {
378
+ let totalWidthInMm = 0;
379
+ let totalHeightInMm = 0;
380
+ layoutRichTextLines({
381
+ runs: resolvedRuns,
382
+ fontSize: size,
383
+ characterSpacing,
384
+ boxWidthInPt
385
+ }).forEach((line, lineIndex) => {
386
+ if (dynamicFontFit === "vertical") totalWidthInMm = Math.max(totalWidthInMm, pt2mm(line.width));
387
+ if (lineIndex === 0) totalHeightInMm += pt2mm(getLineHeightAtSize(line, size) * lineHeight);
388
+ else totalHeightInMm += pt2mm(size * lineHeight);
389
+ });
390
+ if (dynamicFontFit === "horizontal") measureParagraphWidths(resolvedRuns, size, characterSpacing).forEach((paragraphWidth) => {
391
+ totalWidthInMm = Math.max(totalWidthInMm, pt2mm(paragraphWidth));
392
+ });
393
+ return {
394
+ totalWidthInMm,
395
+ totalHeightInMm
396
+ };
397
+ };
398
+ const shouldFontGrowToFit = (totalWidthInMm, totalHeightInMm) => {
399
+ if (dynamicFontSize >= dynamicFontSizeSetting.max) return false;
400
+ if (dynamicFontFit === "horizontal") return totalWidthInMm < boxWidth;
401
+ return totalHeightInMm < boxHeight;
402
+ };
403
+ const shouldFontShrinkToFit = (totalWidthInMm, totalHeightInMm) => {
404
+ if (dynamicFontSize <= dynamicFontSizeSetting.min || dynamicFontSize <= 0) return false;
405
+ return totalWidthInMm > boxWidth || totalHeightInMm > boxHeight;
406
+ };
407
+ let { totalWidthInMm, totalHeightInMm } = calculateConstraints(dynamicFontSize);
408
+ while (shouldFontGrowToFit(totalWidthInMm, totalHeightInMm)) {
409
+ dynamicFontSize += FONT_SIZE_ADJUSTMENT;
410
+ const { totalWidthInMm: newWidth, totalHeightInMm: newHeight } = calculateConstraints(dynamicFontSize);
411
+ if (newHeight < boxHeight) {
412
+ totalWidthInMm = newWidth;
413
+ totalHeightInMm = newHeight;
414
+ } else {
415
+ dynamicFontSize -= FONT_SIZE_ADJUSTMENT;
416
+ break;
417
+ }
418
+ }
419
+ while (shouldFontShrinkToFit(totalWidthInMm, totalHeightInMm)) {
420
+ dynamicFontSize -= FONT_SIZE_ADJUSTMENT;
421
+ ({totalWidthInMm, totalHeightInMm} = calculateConstraints(dynamicFontSize));
422
+ }
423
+ return dynamicFontSize;
424
+ };
425
+ //#endregion
426
+ //#region src/text/richTextPdfRender.ts
427
+ var getSyntheticBoldWidth = (run, fontSize) => run.syntheticBold ? fontSize * SYNTHETIC_BOLD_OFFSET_RATIO * 2 : 0;
428
+ var getSyntheticItalicWidth = (run, fontSize) => run.syntheticItalic ? heightOfFontAtSize(run.fontKitFont, fontSize) * Math.tan(12 * Math.PI / 180) : 0;
429
+ var getRunWidth = (run, fontSize, characterSpacing) => widthOfTextAtSize(run.text, run.fontKitFont, fontSize, characterSpacing) + getSyntheticBoldWidth(run, fontSize) + getSyntheticItalicWidth(run, fontSize);
430
+ var getPdfFont = (run, pdfFontObj) => {
431
+ const pdfFont = pdfFontObj[run.fontName];
432
+ if (!pdfFont) throw new Error(`[@pdfme/schemas] Missing embedded font "${run.fontName}".`);
433
+ return pdfFont;
434
+ };
435
+ var drawDecorationLine = (arg) => {
436
+ const { page, x, y, width, rotate, pivotPoint, fontSize, color, opacity } = arg;
437
+ if (width <= 0) return;
438
+ page.drawLine({
439
+ start: rotatePoint({
440
+ x,
441
+ y
442
+ }, pivotPoint, rotate.angle),
443
+ end: rotatePoint({
444
+ x: x + width,
445
+ y
446
+ }, pivotPoint, rotate.angle),
447
+ thickness: 1 / 12 * fontSize,
448
+ color,
449
+ opacity
450
+ });
451
+ };
452
+ var drawRun = (arg) => {
453
+ const { page, pdfLib, run, pdfFont, x, y, rotate, pivotPoint, fontSize, lineHeight, color, opacity, colorType, characterSpacing, strikethrough } = arg;
454
+ const runWidth = getRunWidth(run, fontSize, characterSpacing);
455
+ const textHeight = heightOfFontAtSize(run.fontKitFont, fontSize);
456
+ if (run.code) {
457
+ const padding = CODE_HORIZONTAL_PADDING;
458
+ const bgX = x - padding;
459
+ const bgY = y - textHeight * .2;
460
+ const bgPoint = rotate.angle === 0 ? {
461
+ x: bgX,
462
+ y: bgY
463
+ } : rotatePoint({
464
+ x: bgX,
465
+ y: bgY
466
+ }, pivotPoint, rotate.angle);
467
+ page.drawRectangle({
468
+ x: bgPoint.x,
469
+ y: bgPoint.y,
470
+ width: runWidth + padding * 2,
471
+ height: textHeight * 1.2,
472
+ rotate,
473
+ color: hex2PrintingColor(CODE_BACKGROUND_COLOR, colorType),
474
+ opacity
475
+ });
476
+ }
477
+ if (strikethrough && runWidth > 0) drawDecorationLine({
478
+ page,
479
+ x,
480
+ y: y + textHeight / 3,
481
+ width: runWidth,
482
+ rotate,
483
+ pivotPoint,
484
+ fontSize,
485
+ color,
486
+ opacity
487
+ });
488
+ const drawAt = (drawX) => {
489
+ const point = rotate.angle === 0 ? {
490
+ x: drawX,
491
+ y
492
+ } : rotatePoint({
493
+ x: drawX,
494
+ y
495
+ }, pivotPoint, rotate.angle);
496
+ page.drawText(run.text, {
497
+ x: point.x,
498
+ y: point.y,
499
+ rotate,
500
+ size: fontSize,
501
+ color,
502
+ lineHeight: lineHeight * fontSize,
503
+ font: pdfFont,
504
+ opacity,
505
+ ...run.syntheticItalic ? { ySkew: pdfLib.degrees(12) } : {}
506
+ });
507
+ };
508
+ drawAt(x);
509
+ if (run.syntheticBold) {
510
+ const offset = fontSize * SYNTHETIC_BOLD_OFFSET_RATIO;
511
+ for (let i = 1; i <= 2; i++) drawAt(x + offset * i);
512
+ }
513
+ };
514
+ var renderInlineMarkdownText = async (arg) => {
515
+ const { value, schema, font, pdfFontObj, fontKitFont, page, pdfLib, _cache, colorType, fontSize, color, alignment, verticalAlignment, lineHeight, characterSpacing, x, width, height, pageHeight, pivotPoint, rotate, opacity } = arg;
516
+ const lines = layoutRichTextLines({
517
+ runs: await resolveRichTextRuns({
518
+ runs: parseInlineMarkdown(value),
519
+ schema,
520
+ font,
521
+ _cache
522
+ }),
523
+ fontSize,
524
+ characterSpacing,
525
+ boxWidthInPt: width
526
+ });
527
+ const firstLineTextHeight = heightOfFontAtSize(fontKitFont, fontSize);
528
+ const descent = getFontDescentInPt(fontKitFont, fontSize);
529
+ const halfLineHeightAdjustment = lineHeight === 0 ? 0 : (lineHeight - 1) * fontSize / 2;
530
+ let yOffset = 0;
531
+ if (verticalAlignment === "top") yOffset = firstLineTextHeight + halfLineHeightAdjustment;
532
+ else {
533
+ const otherLinesHeight = lineHeight * fontSize * (lines.length - 1);
534
+ if (verticalAlignment === "bottom") yOffset = height - otherLinesHeight + descent - halfLineHeightAdjustment;
535
+ else if (verticalAlignment === "middle") yOffset = (height - otherLinesHeight - firstLineTextHeight + descent) / 2 + firstLineTextHeight;
536
+ }
537
+ lines.forEach((line, rowIndex) => {
538
+ if (line.runs.length === 0) return;
539
+ let textWidth = line.width;
540
+ let spacing = characterSpacing;
541
+ if (alignment === "justify" && !line.hardBreak && rowIndex < lines.length - 1) {
542
+ const graphemeCount = countRichTextLineGraphemes(line);
543
+ if (graphemeCount > 0) {
544
+ spacing += (width - textWidth) / graphemeCount;
545
+ textWidth = width;
546
+ }
547
+ }
548
+ let xLine = x;
549
+ if (alignment === "center") xLine += (width - textWidth) / 2;
550
+ else if (alignment === "right") xLine += width - textWidth;
551
+ const yLine = pageHeight - mm2pt(schema.position.y) - yOffset - lineHeight * fontSize * rowIndex;
552
+ page.pushOperators(pdfLib.setCharacterSpacing(spacing));
553
+ if (schema.strikethrough || schema.underline) {
554
+ const textHeight = Math.max(...line.runs.map((run) => heightOfFontAtSize(run.fontKitFont, fontSize)));
555
+ if (schema.strikethrough) drawDecorationLine({
556
+ page,
557
+ x: xLine,
558
+ y: yLine + textHeight / 3,
559
+ width: textWidth,
560
+ rotate,
561
+ pivotPoint,
562
+ fontSize,
563
+ color,
564
+ opacity
565
+ });
566
+ if (schema.underline) drawDecorationLine({
567
+ page,
568
+ x: xLine,
569
+ y: yLine - textHeight / 12,
570
+ width: textWidth,
571
+ rotate,
572
+ pivotPoint,
573
+ fontSize,
574
+ color,
575
+ opacity
576
+ });
577
+ }
578
+ line.runs.reduce((currentX, run, runIndex) => {
579
+ const runWidth = getRunWidth(run, fontSize, spacing);
580
+ drawRun({
581
+ page,
582
+ pdfLib,
583
+ run,
584
+ pdfFont: getPdfFont(run, pdfFontObj),
585
+ x: currentX,
586
+ y: yLine,
587
+ rotate,
588
+ pivotPoint,
589
+ fontSize,
590
+ lineHeight,
591
+ color,
592
+ opacity,
593
+ colorType,
594
+ characterSpacing: spacing,
595
+ strikethrough: Boolean(run.strikethrough)
596
+ });
597
+ return currentX + runWidth + (runIndex === line.runs.length - 1 ? 0 : spacing);
598
+ }, xLine);
599
+ });
600
+ };
601
+ //#endregion
602
+ //#region src/text/pdfRender.ts
603
+ var embedAndGetFontObj = async (arg) => {
604
+ const { pdfDoc, font, _cache } = arg;
605
+ if (_cache.has(pdfDoc)) return _cache.get(pdfDoc);
606
+ const fontValues = await Promise.all(Object.values(font).map(async (v) => {
607
+ let fontData = v.data;
608
+ if (typeof fontData === "string" && fontData.startsWith("http")) fontData = await fetchRemoteFontData(fontData);
609
+ return pdfDoc.embedFont(fontData, { subset: typeof v.subset === "undefined" ? true : v.subset });
610
+ }));
611
+ const fontObj = Object.keys(font).reduce((acc, cur, i) => Object.assign(acc, { [cur]: fontValues[i] }), {});
612
+ _cache.set(pdfDoc, fontObj);
613
+ return fontObj;
614
+ };
615
+ var getFontProp = ({ value, fontKitFont, schema, colorType, fontSize: resolvedFontSize }) => {
616
+ const fontSize = resolvedFontSize ?? (schema.dynamicFontSize ? calculateDynamicFontSize({
617
+ textSchema: schema,
618
+ fontKitFont,
619
+ value
620
+ }) : schema.fontSize ?? 13);
621
+ const color = hex2PrintingColor(schema.fontColor || "#000000", colorType);
622
+ return {
623
+ alignment: schema.alignment ?? "left",
624
+ verticalAlignment: schema.verticalAlignment ?? "top",
625
+ lineHeight: schema.lineHeight ?? 1,
626
+ characterSpacing: schema.characterSpacing ?? 0,
627
+ fontSize,
628
+ color
629
+ };
630
+ };
631
+ var pdfRender = async (arg) => {
632
+ const { value, pdfDoc, pdfLib, page, options, schema, _cache } = arg;
633
+ if (!value) return;
634
+ const { font = getDefaultFont(), colorType } = options;
635
+ const [pdfFontObj, fontKitFont] = await Promise.all([embedAndGetFontObj({
636
+ pdfDoc,
637
+ font,
638
+ _cache
639
+ }), getFontKitFont(schema.fontName, font, _cache)]);
640
+ const enableInlineMarkdown = isInlineMarkdownTextSchema(schema);
641
+ const { fontSize, color, alignment, verticalAlignment, lineHeight, characterSpacing } = getFontProp({
642
+ value: enableInlineMarkdown ? stripInlineMarkdown(value) : value,
643
+ fontKitFont,
644
+ schema,
645
+ colorType,
646
+ fontSize: enableInlineMarkdown && schema.dynamicFontSize ? await calculateDynamicRichTextFontSize({
647
+ value,
648
+ schema,
649
+ font,
650
+ _cache
651
+ }) : void 0
652
+ });
653
+ const fontName = schema.fontName ? schema.fontName : getFallbackFontName(font);
654
+ const pdfFontValue = pdfFontObj && pdfFontObj[fontName];
655
+ const pageHeight = page.getHeight();
656
+ const { width, height, rotate, position: { x, y }, opacity } = convertForPdfLayoutProps({
657
+ schema,
658
+ pageHeight,
659
+ applyRotateTranslate: false
660
+ });
661
+ const pivotPoint = {
662
+ x: x + width / 2,
663
+ y: pageHeight - mm2pt(schema.position.y) - height / 2
664
+ };
665
+ if (schema.backgroundColor) {
666
+ const color = hex2PrintingColor(schema.backgroundColor, colorType);
667
+ if (rotate.angle !== 0) {
668
+ const rotatedPoint = rotatePoint({
669
+ x,
670
+ y
671
+ }, pivotPoint, rotate.angle);
672
+ page.drawRectangle({
673
+ x: rotatedPoint.x,
674
+ y: rotatedPoint.y,
675
+ width,
676
+ height,
677
+ rotate,
678
+ color
679
+ });
680
+ } else page.drawRectangle({
681
+ x,
682
+ y,
683
+ width,
684
+ height,
685
+ rotate,
686
+ color
687
+ });
688
+ }
689
+ if (enableInlineMarkdown) {
690
+ await renderInlineMarkdownText({
691
+ value,
692
+ schema,
693
+ font,
694
+ pdfFontObj,
695
+ fontKitFont,
696
+ page,
697
+ pdfLib,
698
+ _cache,
699
+ colorType,
700
+ fontSize,
701
+ color,
702
+ alignment,
703
+ verticalAlignment,
704
+ lineHeight,
705
+ characterSpacing,
706
+ x,
707
+ width,
708
+ height,
709
+ pageHeight,
710
+ pivotPoint,
711
+ rotate,
712
+ opacity
713
+ });
714
+ return;
715
+ }
716
+ const firstLineTextHeight = heightOfFontAtSize(fontKitFont, fontSize);
717
+ const descent = getFontDescentInPt(fontKitFont, fontSize);
718
+ const halfLineHeightAdjustment = lineHeight === 0 ? 0 : (lineHeight - 1) * fontSize / 2;
719
+ const lines = splitTextToSize({
720
+ value,
721
+ characterSpacing,
722
+ fontSize,
723
+ fontKitFont,
724
+ boxWidthInPt: width
725
+ });
726
+ let yOffset = 0;
727
+ if (verticalAlignment === "top") yOffset = firstLineTextHeight + halfLineHeightAdjustment;
728
+ else {
729
+ const otherLinesHeight = lineHeight * fontSize * (lines.length - 1);
730
+ if (verticalAlignment === "bottom") yOffset = height - otherLinesHeight + descent - halfLineHeightAdjustment;
731
+ else if (verticalAlignment === "middle") yOffset = (height - otherLinesHeight - firstLineTextHeight + descent) / 2 + firstLineTextHeight;
732
+ }
733
+ const segmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
734
+ lines.forEach((line, rowIndex) => {
735
+ const trimmed = line.replace("\n", "");
736
+ const textWidth = widthOfTextAtSize(trimmed, fontKitFont, fontSize, characterSpacing);
737
+ const textHeight = heightOfFontAtSize(fontKitFont, fontSize);
738
+ const rowYOffset = lineHeight * fontSize * rowIndex;
739
+ if (line === "") line = "\r\n";
740
+ let xLine = x;
741
+ if (alignment === "center") xLine += (width - textWidth) / 2;
742
+ else if (alignment === "right") xLine += width - textWidth;
743
+ let yLine = pageHeight - mm2pt(schema.position.y) - yOffset - rowYOffset;
744
+ if (schema.strikethrough && textWidth > 0) {
745
+ const _x = xLine + textWidth + 1;
746
+ const _y = yLine + textHeight / 3;
747
+ page.drawLine({
748
+ start: rotatePoint({
749
+ x: xLine,
750
+ y: _y
751
+ }, pivotPoint, rotate.angle),
752
+ end: rotatePoint({
753
+ x: _x,
754
+ y: _y
755
+ }, pivotPoint, rotate.angle),
756
+ thickness: 1 / 12 * fontSize,
757
+ color,
758
+ opacity
759
+ });
760
+ }
761
+ if (schema.underline && textWidth > 0) {
762
+ const _x = xLine + textWidth + 1;
763
+ const _y = yLine - textHeight / 12;
764
+ page.drawLine({
765
+ start: rotatePoint({
766
+ x: xLine,
767
+ y: _y
768
+ }, pivotPoint, rotate.angle),
769
+ end: rotatePoint({
770
+ x: _x,
771
+ y: _y
772
+ }, pivotPoint, rotate.angle),
773
+ thickness: 1 / 12 * fontSize,
774
+ color,
775
+ opacity
776
+ });
777
+ }
778
+ if (rotate.angle !== 0) {
779
+ const rotatedPoint = rotatePoint({
780
+ x: xLine,
781
+ y: yLine
782
+ }, pivotPoint, rotate.angle);
783
+ xLine = rotatedPoint.x;
784
+ yLine = rotatedPoint.y;
785
+ }
786
+ let spacing = characterSpacing;
787
+ if (alignment === "justify" && line.slice(-1) !== "\n") {
788
+ const iterator = segmenter.segment(trimmed)[Symbol.iterator]();
789
+ const len = Array.from(iterator).length;
790
+ spacing += (width - textWidth) / len;
791
+ }
792
+ page.pushOperators(pdfLib.setCharacterSpacing(spacing));
793
+ page.drawText(trimmed, {
794
+ x: xLine,
795
+ y: yLine,
796
+ rotate,
797
+ size: fontSize,
798
+ color,
799
+ lineHeight: lineHeight * fontSize,
800
+ font: pdfFontValue,
801
+ opacity
802
+ });
803
+ });
804
+ };
805
+ //#endregion
806
+ //#region src/text/icons/index.ts
807
+ var TextStrikethroughIcon = createSvgStr(Strikethrough);
808
+ var TextUnderlineIcon = createSvgStr(Underline);
809
+ var TextAlignLeftIcon = createSvgStr(AlignLeft);
810
+ var TextAlignCenterIcon = createSvgStr(AlignCenter);
811
+ var TextAlignRightIcon = createSvgStr(AlignRight);
812
+ var TextAlignJustifyIcon = createSvgStr(AlignJustify);
813
+ var TextVerticalAlignTopIcon = createSvgStr(ArrowUpToLine);
814
+ var TextVerticalAlignMiddleIcon = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24"><path d="M8 19h3v4h2v-4h3l-4-4l-4 4zm8-14h-3V1h-2v4H8l4 4l4-4zM4 11v2h16v-2H4z" fill="currentColor"></path></svg>`;
815
+ var TextVerticalAlignBottomIcon = createSvgStr(ArrowDownToLine);
816
+ //#endregion
817
+ //#region src/text/extraFormatter.ts
818
+ var Formatter = /* @__PURE__ */ function(Formatter) {
819
+ Formatter["STRIKETHROUGH"] = "strikethrough";
820
+ Formatter["UNDERLINE"] = "underline";
821
+ Formatter["ALIGNMENT"] = "alignment";
822
+ Formatter["VERTICAL_ALIGNMENT"] = "verticalAlignment";
823
+ return Formatter;
824
+ }({});
825
+ function getExtraFormatterSchema(i18n) {
826
+ const buttons = [
827
+ {
828
+ key: Formatter.STRIKETHROUGH,
829
+ icon: TextStrikethroughIcon,
830
+ type: "boolean"
831
+ },
832
+ {
833
+ key: Formatter.UNDERLINE,
834
+ icon: TextUnderlineIcon,
835
+ type: "boolean"
836
+ },
837
+ {
838
+ key: Formatter.ALIGNMENT,
839
+ icon: TextAlignLeftIcon,
840
+ type: "select",
841
+ value: DEFAULT_ALIGNMENT
842
+ },
843
+ {
844
+ key: Formatter.ALIGNMENT,
845
+ icon: TextAlignCenterIcon,
846
+ type: "select",
847
+ value: ALIGN_CENTER
848
+ },
849
+ {
850
+ key: Formatter.ALIGNMENT,
851
+ icon: TextAlignRightIcon,
852
+ type: "select",
853
+ value: ALIGN_RIGHT
854
+ },
855
+ {
856
+ key: Formatter.ALIGNMENT,
857
+ icon: TextAlignJustifyIcon,
858
+ type: "select",
859
+ value: ALIGN_JUSTIFY
860
+ },
861
+ {
862
+ key: Formatter.VERTICAL_ALIGNMENT,
863
+ icon: TextVerticalAlignTopIcon,
864
+ type: "select",
865
+ value: "top"
866
+ },
867
+ {
868
+ key: Formatter.VERTICAL_ALIGNMENT,
869
+ icon: TextVerticalAlignMiddleIcon,
870
+ type: "select",
871
+ value: VERTICAL_ALIGN_MIDDLE
872
+ },
873
+ {
874
+ key: Formatter.VERTICAL_ALIGNMENT,
875
+ icon: TextVerticalAlignBottomIcon,
876
+ type: "select",
877
+ value: VERTICAL_ALIGN_BOTTOM
878
+ }
879
+ ];
880
+ return {
881
+ title: i18n("schemas.text.format"),
882
+ widget: "ButtonGroup",
883
+ buttons,
884
+ span: 24
885
+ };
886
+ }
887
+ //#endregion
888
+ //#region src/text/propPanel.ts
889
+ var UseDynamicFontSize = (props) => {
890
+ const { rootElement, changeSchemas, activeSchema, i18n } = props;
891
+ const checkbox = document.createElement("input");
892
+ checkbox.type = "checkbox";
893
+ checkbox.checked = Boolean(activeSchema?.dynamicFontSize);
894
+ checkbox.onchange = (e) => {
895
+ changeSchemas([{
896
+ key: "dynamicFontSize",
897
+ value: e.target.checked ? {
898
+ min: 4,
899
+ max: 72,
900
+ fit: DEFAULT_DYNAMIC_FIT
901
+ } : void 0,
902
+ schemaId: activeSchema.id
903
+ }]);
904
+ };
905
+ const label = document.createElement("label");
906
+ const span = document.createElement("span");
907
+ span.innerText = i18n("schemas.text.dynamicFontSize") || "";
908
+ span.style.cssText = "margin-left: 0.5rem";
909
+ label.style.cssText = "display: flex; width: 100%;";
910
+ label.appendChild(checkbox);
911
+ label.appendChild(span);
912
+ rootElement.appendChild(label);
913
+ };
914
+ var UseInlineMarkdown = (props) => {
915
+ const { rootElement, changeSchemas, activeSchema, i18n } = props;
916
+ const checkbox = document.createElement("input");
917
+ checkbox.type = "checkbox";
918
+ checkbox.checked = activeSchema?.textFormat === TEXT_FORMAT_INLINE_MARKDOWN;
919
+ checkbox.onchange = (e) => {
920
+ changeSchemas([{
921
+ key: "textFormat",
922
+ value: e.target.checked ? TEXT_FORMAT_INLINE_MARKDOWN : TEXT_FORMAT_PLAIN,
923
+ schemaId: activeSchema.id
924
+ }]);
925
+ };
926
+ const label = document.createElement("label");
927
+ const span = document.createElement("span");
928
+ span.innerText = i18n("schemas.text.inlineMarkdown") || "";
929
+ span.style.cssText = "margin-left: 0.5rem";
930
+ label.style.cssText = "display: flex; width: 100%;";
931
+ label.appendChild(checkbox);
932
+ label.appendChild(span);
933
+ rootElement.appendChild(label);
934
+ };
935
+ var propPanel = {
936
+ schema: ({ options, activeSchema, i18n }) => {
937
+ const font = options.font || { [DEFAULT_FONT_NAME]: {
938
+ data: "",
939
+ fallback: true
940
+ } };
941
+ const fontNames = Object.keys(font);
942
+ const fallbackFontName = getFallbackFontName(font);
943
+ const enableDynamicFont = Boolean(activeSchema?.dynamicFontSize);
944
+ const activeTextSchema = activeSchema;
945
+ const hideTextFormat = activeTextSchema.type === "text" && activeTextSchema.readOnly !== true;
946
+ const enableInlineMarkdown = activeTextSchema.textFormat === "inline-markdown" && !hideTextFormat;
947
+ const baseFontName = activeTextSchema.fontName && font[activeTextSchema.fontName] ? activeTextSchema.fontName : fallbackFontName;
948
+ const optionalFontNames = [{
949
+ label: baseFontName,
950
+ value: ""
951
+ }, ...fontNames.filter((name) => name !== baseFontName).map((name) => ({
952
+ label: name,
953
+ value: name
954
+ }))];
955
+ return {
956
+ fontName: {
957
+ title: i18n("schemas.text.fontName"),
958
+ type: "string",
959
+ widget: "select",
960
+ default: fallbackFontName,
961
+ placeholder: fallbackFontName,
962
+ props: { options: fontNames.map((name) => ({
963
+ label: name,
964
+ value: name
965
+ })) },
966
+ span: 12
967
+ },
968
+ fontSize: {
969
+ title: i18n("schemas.text.size"),
970
+ type: "number",
971
+ widget: "inputNumber",
972
+ span: 6,
973
+ disabled: enableDynamicFont,
974
+ props: { min: 0 }
975
+ },
976
+ characterSpacing: {
977
+ title: i18n("schemas.text.spacing"),
978
+ type: "number",
979
+ widget: "inputNumber",
980
+ span: 6,
981
+ props: { min: 0 }
982
+ },
983
+ formatter: getExtraFormatterSchema(i18n),
984
+ lineHeight: {
985
+ title: i18n("schemas.text.lineHeight"),
986
+ type: "number",
987
+ widget: "inputNumber",
988
+ props: {
989
+ step: .1,
990
+ min: 0
991
+ },
992
+ span: 8
993
+ },
994
+ useDynamicFontSize: {
995
+ type: "boolean",
996
+ widget: "UseDynamicFontSize",
997
+ bind: false,
998
+ span: 16
999
+ },
1000
+ dynamicFontSize: {
1001
+ type: "object",
1002
+ widget: "card",
1003
+ column: 3,
1004
+ properties: {
1005
+ min: {
1006
+ title: i18n("schemas.text.min"),
1007
+ type: "number",
1008
+ widget: "inputNumber",
1009
+ hidden: !enableDynamicFont,
1010
+ props: { min: 0 }
1011
+ },
1012
+ max: {
1013
+ title: i18n("schemas.text.max"),
1014
+ type: "number",
1015
+ widget: "inputNumber",
1016
+ hidden: !enableDynamicFont,
1017
+ props: { min: 0 }
1018
+ },
1019
+ fit: {
1020
+ title: i18n("schemas.text.fit"),
1021
+ type: "string",
1022
+ widget: "select",
1023
+ hidden: !enableDynamicFont,
1024
+ props: { options: [{
1025
+ label: i18n("schemas.horizontal"),
1026
+ value: DYNAMIC_FIT_HORIZONTAL
1027
+ }, {
1028
+ label: i18n("schemas.vertical"),
1029
+ value: DYNAMIC_FIT_VERTICAL
1030
+ }] }
1031
+ }
1032
+ }
1033
+ },
1034
+ fontColor: {
1035
+ title: i18n("schemas.textColor"),
1036
+ type: "string",
1037
+ widget: "color",
1038
+ props: { disabledAlpha: true },
1039
+ rules: [{
1040
+ pattern: HEX_COLOR_PATTERN,
1041
+ message: i18n("validation.hexColor")
1042
+ }]
1043
+ },
1044
+ backgroundColor: {
1045
+ title: i18n("schemas.bgColor"),
1046
+ type: "string",
1047
+ widget: "color",
1048
+ props: { disabledAlpha: true },
1049
+ rules: [{
1050
+ pattern: HEX_COLOR_PATTERN,
1051
+ message: i18n("validation.hexColor")
1052
+ }]
1053
+ },
1054
+ useInlineMarkdown: {
1055
+ type: "boolean",
1056
+ widget: "UseInlineMarkdown",
1057
+ bind: false,
1058
+ hidden: hideTextFormat,
1059
+ span: enableInlineMarkdown ? 12 : 24
1060
+ },
1061
+ fontVariantFallback: {
1062
+ title: i18n("schemas.text.variantFallback"),
1063
+ type: "string",
1064
+ widget: "select",
1065
+ default: DEFAULT_FONT_VARIANT_FALLBACK,
1066
+ hidden: !enableInlineMarkdown,
1067
+ props: { options: [
1068
+ {
1069
+ label: i18n("schemas.text.synthetic"),
1070
+ value: FONT_VARIANT_FALLBACK_SYNTHETIC
1071
+ },
1072
+ {
1073
+ label: i18n("schemas.text.plain"),
1074
+ value: FONT_VARIANT_FALLBACK_PLAIN
1075
+ },
1076
+ {
1077
+ label: i18n("schemas.text.error"),
1078
+ value: FONT_VARIANT_FALLBACK_ERROR
1079
+ }
1080
+ ] },
1081
+ span: 12
1082
+ },
1083
+ fontVariants: {
1084
+ title: i18n("schemas.text.markdownFonts"),
1085
+ type: "object",
1086
+ widget: "card",
1087
+ column: 2,
1088
+ hidden: !enableInlineMarkdown,
1089
+ properties: {
1090
+ bold: {
1091
+ title: i18n("schemas.text.boldFont"),
1092
+ type: "string",
1093
+ widget: "select",
1094
+ props: { options: optionalFontNames }
1095
+ },
1096
+ italic: {
1097
+ title: i18n("schemas.text.italicFont"),
1098
+ type: "string",
1099
+ widget: "select",
1100
+ props: { options: optionalFontNames }
1101
+ },
1102
+ boldItalic: {
1103
+ title: i18n("schemas.text.boldItalicFont"),
1104
+ type: "string",
1105
+ widget: "select",
1106
+ props: { options: optionalFontNames }
1107
+ },
1108
+ code: {
1109
+ title: i18n("schemas.text.codeFont"),
1110
+ type: "string",
1111
+ widget: "select",
1112
+ props: { options: optionalFontNames }
1113
+ }
1114
+ }
1115
+ }
1116
+ };
1117
+ },
1118
+ widgets: {
1119
+ UseDynamicFontSize,
1120
+ UseInlineMarkdown
1121
+ },
1122
+ defaultSchema: {
1123
+ name: "",
1124
+ type: "text",
1125
+ content: "Type Something...",
1126
+ position: {
1127
+ x: 0,
1128
+ y: 0
1129
+ },
1130
+ width: 45,
1131
+ height: 10,
1132
+ rotate: 0,
1133
+ alignment: DEFAULT_ALIGNMENT,
1134
+ verticalAlignment: "top",
1135
+ fontSize: 13,
1136
+ textFormat: DEFAULT_TEXT_FORMAT,
1137
+ fontVariantFallback: DEFAULT_FONT_VARIANT_FALLBACK,
1138
+ lineHeight: 1,
1139
+ characterSpacing: 0,
1140
+ dynamicFontSize: void 0,
1141
+ fontColor: DEFAULT_FONT_COLOR,
1142
+ fontName: void 0,
1143
+ backgroundColor: "",
1144
+ opacity: 1,
1145
+ strikethrough: false,
1146
+ underline: false
1147
+ }
1148
+ };
1149
+ //#endregion
1150
+ //#region src/text/uiRender.ts
1151
+ var replaceUnsupportedChars = (text, fontKitFont) => {
1152
+ const charSupportCache = {};
1153
+ const isCharSupported = (char) => {
1154
+ if (char in charSupportCache) return charSupportCache[char];
1155
+ const isSupported = fontKitFont.hasGlyphForCodePoint(char.codePointAt(0) || 0);
1156
+ charSupportCache[char] = isSupported;
1157
+ return isSupported;
1158
+ };
1159
+ return text.split(/(\r\n|\n|\r)/).map((segment) => {
1160
+ if (/\r\n|\n|\r/.test(segment)) return segment;
1161
+ return Array.from(segment).map((char) => {
1162
+ if (/\s/.test(char) || char.charCodeAt(0) < 32) return char;
1163
+ return isCharSupported(char) ? char : "〿";
1164
+ }).join("");
1165
+ }).join("");
1166
+ };
1167
+ var uiRender = async (arg) => {
1168
+ const { value, schema, mode, onChange, stopEditing, tabIndex, placeholder, options, _cache } = arg;
1169
+ const usePlaceholder = isEditable(mode, schema) && placeholder && !value;
1170
+ const getText = (element) => {
1171
+ let text = element.innerText;
1172
+ if (text.endsWith("\n")) text = text.slice(0, -1);
1173
+ return text;
1174
+ };
1175
+ const font = options?.font || getDefaultFont();
1176
+ const fontKitFont = await getFontKitFont(schema.fontName, font, _cache);
1177
+ const enableInlineMarkdown = isInlineMarkdownTextSchema(schema);
1178
+ const displayValue = enableInlineMarkdown ? stripInlineMarkdown(value) : value;
1179
+ const dynamicRichTextFontSize = enableInlineMarkdown && schema.dynamicFontSize ? await calculateDynamicRichTextFontSize({
1180
+ value: usePlaceholder ? placeholder : value,
1181
+ schema,
1182
+ font,
1183
+ _cache
1184
+ }) : void 0;
1185
+ const textBlock = buildStyledTextContainer(arg, fontKitFont, usePlaceholder ? placeholder : displayValue, dynamicRichTextFontSize);
1186
+ const processedText = replaceUnsupportedChars(value, fontKitFont);
1187
+ if (!isEditable(mode, schema)) {
1188
+ if (enableInlineMarkdown) {
1189
+ await renderInlineMarkdownReadOnly({
1190
+ textBlock,
1191
+ value,
1192
+ schema,
1193
+ font,
1194
+ _cache
1195
+ });
1196
+ return;
1197
+ }
1198
+ textBlock.innerHTML = processedText.split("").map((l, i) => {
1199
+ const escaped = l.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
1200
+ return `<span style="letter-spacing:${String(value).length === i + 1 ? 0 : "inherit"};">${escaped}</span>`;
1201
+ }).join("");
1202
+ return;
1203
+ }
1204
+ makeElementPlainTextContentEditable(textBlock);
1205
+ textBlock.tabIndex = tabIndex || 0;
1206
+ textBlock.innerText = mode === "designer" ? value : processedText;
1207
+ textBlock.addEventListener("blur", (e) => {
1208
+ if (onChange) onChange({
1209
+ key: "content",
1210
+ value: getText(e.target)
1211
+ });
1212
+ if (stopEditing) stopEditing();
1213
+ });
1214
+ if (schema.dynamicFontSize) {
1215
+ let dynamicFontSize = void 0;
1216
+ textBlock.addEventListener("keyup", () => {
1217
+ setTimeout(() => {
1218
+ (() => {
1219
+ if (!textBlock.textContent) return;
1220
+ dynamicFontSize = calculateDynamicFontSize({
1221
+ textSchema: schema,
1222
+ fontKitFont,
1223
+ value: isInlineMarkdownTextSchema(schema) ? stripInlineMarkdown(getText(textBlock)) : getText(textBlock),
1224
+ startingFontSize: dynamicFontSize
1225
+ });
1226
+ textBlock.style.fontSize = `${dynamicFontSize}pt`;
1227
+ const { topAdj: newTopAdj, bottomAdj: newBottomAdj } = getBrowserVerticalFontAdjustments(fontKitFont, dynamicFontSize ?? schema.fontSize ?? 13, schema.lineHeight ?? 1, schema.verticalAlignment ?? "top");
1228
+ textBlock.style.paddingTop = `${newTopAdj}px`;
1229
+ textBlock.style.marginBottom = `${newBottomAdj}px`;
1230
+ })();
1231
+ }, 0);
1232
+ });
1233
+ }
1234
+ if (usePlaceholder) {
1235
+ textBlock.style.color = PLACEHOLDER_FONT_COLOR;
1236
+ textBlock.addEventListener("focus", () => {
1237
+ if (textBlock.innerText === placeholder) {
1238
+ textBlock.innerText = "";
1239
+ textBlock.style.color = schema.fontColor ?? "#000000";
1240
+ }
1241
+ });
1242
+ }
1243
+ if (mode === "designer") setTimeout(() => {
1244
+ textBlock.focus();
1245
+ const selection = window.getSelection();
1246
+ const range = document.createRange();
1247
+ if (selection && range) {
1248
+ range.selectNodeContents(textBlock);
1249
+ range.collapse(false);
1250
+ selection?.removeAllRanges();
1251
+ selection?.addRange(range);
1252
+ }
1253
+ });
1254
+ };
1255
+ var renderInlineMarkdownReadOnly = async (arg) => {
1256
+ const { textBlock, value, schema, font, _cache } = arg;
1257
+ const runs = await resolveRichTextRuns({
1258
+ runs: parseInlineMarkdown(value),
1259
+ schema,
1260
+ font,
1261
+ _cache
1262
+ });
1263
+ textBlock.innerHTML = "";
1264
+ runs.forEach((run) => {
1265
+ const span = document.createElement("span");
1266
+ span.textContent = replaceUnsupportedChars(run.text, run.fontKitFont);
1267
+ if (run.fontName) span.style.fontFamily = `'${run.fontName}'`;
1268
+ if (run.syntheticBold) {
1269
+ span.style.fontWeight = "800";
1270
+ span.style.textShadow = SYNTHETIC_BOLD_CSS_TEXT_SHADOW;
1271
+ }
1272
+ if (run.syntheticItalic) span.style.fontStyle = "italic";
1273
+ if (run.strikethrough) span.style.textDecoration = "line-through";
1274
+ if (run.code) {
1275
+ span.style.backgroundColor = CODE_BACKGROUND_COLOR;
1276
+ span.style.borderRadius = "2px";
1277
+ span.style.padding = "0 0.15em";
1278
+ if (!schema.fontVariants?.code || !font[schema.fontVariants.code]) span.style.fontFamily = run.fontName ? `'${run.fontName}', monospace` : "monospace";
1279
+ }
1280
+ textBlock.appendChild(span);
1281
+ });
1282
+ };
1283
+ var buildStyledTextContainer = (arg, fontKitFont, value, resolvedDynamicFontSize) => {
1284
+ const { schema, rootElement, mode } = arg;
1285
+ let dynamicFontSize = resolvedDynamicFontSize;
1286
+ if (dynamicFontSize === void 0 && schema.dynamicFontSize && value) dynamicFontSize = calculateDynamicFontSize({
1287
+ textSchema: schema,
1288
+ fontKitFont,
1289
+ value,
1290
+ startingFontSize: dynamicFontSize
1291
+ });
1292
+ const { topAdj, bottomAdj } = getBrowserVerticalFontAdjustments(fontKitFont, dynamicFontSize ?? schema.fontSize ?? 13, schema.lineHeight ?? 1, schema.verticalAlignment ?? "top");
1293
+ const topAdjustment = topAdj.toString();
1294
+ const bottomAdjustment = bottomAdj.toString();
1295
+ const container = document.createElement("div");
1296
+ const containerStyle = {
1297
+ padding: 0,
1298
+ resize: "none",
1299
+ backgroundColor: getBackgroundColor(value, schema),
1300
+ border: "none",
1301
+ display: "flex",
1302
+ flexDirection: "column",
1303
+ justifyContent: mapVerticalAlignToFlex(schema.verticalAlignment),
1304
+ width: "100%",
1305
+ height: "100%",
1306
+ cursor: isEditable(mode, schema) ? "text" : "default"
1307
+ };
1308
+ Object.assign(container.style, containerStyle);
1309
+ rootElement.innerHTML = "";
1310
+ rootElement.appendChild(container);
1311
+ const textDecorations = [];
1312
+ if (schema.strikethrough) textDecorations.push("line-through");
1313
+ if (schema.underline) textDecorations.push("underline");
1314
+ const textBlockStyle = {
1315
+ fontFamily: schema.fontName ? `'${schema.fontName}'` : "inherit",
1316
+ color: schema.fontColor ? schema.fontColor : DEFAULT_FONT_COLOR,
1317
+ fontSize: `${dynamicFontSize ?? schema.fontSize ?? 13}pt`,
1318
+ letterSpacing: `${schema.characterSpacing ?? 0}pt`,
1319
+ lineHeight: `${schema.lineHeight ?? 1}em`,
1320
+ textAlign: schema.alignment ?? "left",
1321
+ whiteSpace: "pre-wrap",
1322
+ wordBreak: "break-word",
1323
+ resize: "none",
1324
+ border: "none",
1325
+ outline: "none",
1326
+ marginBottom: `${bottomAdjustment}px`,
1327
+ paddingTop: `${topAdjustment}px`,
1328
+ backgroundColor: "transparent",
1329
+ textDecoration: textDecorations.join(" ")
1330
+ };
1331
+ const textBlock = document.createElement("div");
1332
+ textBlock.id = "text-" + String(schema.id);
1333
+ Object.assign(textBlock.style, textBlockStyle);
1334
+ container.appendChild(textBlock);
1335
+ return textBlock;
1336
+ };
1337
+ /**
1338
+ * Firefox doesn't support 'plaintext-only' contentEditable mode, which we want to avoid mark-up.
1339
+ * This function adds a workaround for Firefox to make the contentEditable element behave like 'plaintext-only'.
1340
+ */
1341
+ var makeElementPlainTextContentEditable = (element) => {
1342
+ if (!isFirefox()) {
1343
+ element.contentEditable = "plaintext-only";
1344
+ return;
1345
+ }
1346
+ element.contentEditable = "true";
1347
+ element.addEventListener("keydown", (e) => {
1348
+ if (e.key === "Enter" && !e.shiftKey) {
1349
+ e.preventDefault();
1350
+ document.execCommand("insertLineBreak", false, void 0);
1351
+ }
1352
+ });
1353
+ element.addEventListener("paste", (e) => {
1354
+ e.preventDefault();
1355
+ const paste = e.clipboardData?.getData("text");
1356
+ const selection = window.getSelection();
1357
+ if (!selection?.rangeCount) return;
1358
+ selection.deleteFromDocument();
1359
+ selection.getRangeAt(0).insertNode(document.createTextNode(paste || ""));
1360
+ selection.collapseToEnd();
1361
+ });
1362
+ };
1363
+ var mapVerticalAlignToFlex = (verticalAlignmentValue) => {
1364
+ switch (verticalAlignmentValue) {
1365
+ case "top": return "flex-start";
1366
+ case VERTICAL_ALIGN_MIDDLE: return "center";
1367
+ case VERTICAL_ALIGN_BOTTOM: return "flex-end";
1368
+ }
1369
+ return "flex-start";
1370
+ };
1371
+ var getBackgroundColor = (value, schema) => {
1372
+ if (!value || !schema.backgroundColor) return "transparent";
1373
+ return schema.backgroundColor;
1374
+ };
1375
+ //#endregion
1376
+ //#region src/text/index.ts
1377
+ var textSchema = {
1378
+ pdf: pdfRender,
1379
+ ui: uiRender,
1380
+ propPanel,
1381
+ icon: createSvgStr(TextCursorInput)
1382
+ };
1383
+ //#endregion
1384
+ //#region src/builtins.ts
1385
+ var builtInPlugins = { Text: textSchema };
1386
+ //#endregion
1387
+ export { mapVerticalAlignToFlex as a, Formatter as c, isInlineMarkdownTextSchema as d, resolveFontVariant as f, makeElementPlainTextContentEditable as i, getExtraFormatterSchema as l, parseInlineMarkdown as m, textSchema as n, uiRender as o, escapeInlineMarkdown as p, buildStyledTextContainer as r, propPanel as s, builtInPlugins as t, pdfRender as u };
1388
+
1389
+ //# sourceMappingURL=builtins-CWHhKSVA.js.map