@pixldocs/canvas-renderer 0.5.458 → 0.5.460

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.
@@ -295,6 +295,172 @@ function findCommonAncestorGroup(selectedIds, pageChildren) {
295
295
  }
296
296
  return null;
297
297
  }
298
+ let activeThemeColors = {};
299
+ function setMarkdownThemeColors(c) {
300
+ activeThemeColors = { ...c };
301
+ }
302
+ function resolveColorToken(token, theme) {
303
+ const raw = token.trim();
304
+ const t = raw.toLowerCase();
305
+ if (t === "primary") return theme.primary;
306
+ if (t === "secondary") return theme.secondary;
307
+ if (/^#([0-9a-f]{3,8})$/i.test(raw)) return raw;
308
+ if (/^(rgb|rgba|hsl|hsla)\(/i.test(raw)) return raw;
309
+ if (/^[a-z]+$/i.test(raw)) return raw;
310
+ return void 0;
311
+ }
312
+ function mergeStyle(a, b) {
313
+ return { ...a, ...b };
314
+ }
315
+ function tokenize(input, theme) {
316
+ const runs = [];
317
+ const stack = [];
318
+ let buf = "";
319
+ const activeStyle = () => {
320
+ let s = {};
321
+ for (const e of stack) s = mergeStyle(s, e.style);
322
+ return s;
323
+ };
324
+ const flush = () => {
325
+ if (buf.length === 0) return;
326
+ runs.push({ text: buf, style: activeStyle() });
327
+ buf = "";
328
+ };
329
+ let i = 0;
330
+ const n = input.length;
331
+ const peek = (s, at = i) => input.startsWith(s, at);
332
+ const findUnescaped = (needle, from) => {
333
+ let p = from;
334
+ while (p < n) {
335
+ if (input[p] === "\\" && p + 1 < n) {
336
+ p += 2;
337
+ continue;
338
+ }
339
+ if (input.startsWith(needle, p)) return p;
340
+ p++;
341
+ }
342
+ return -1;
343
+ };
344
+ const tryOpenBracket = () => {
345
+ if (input[i] !== "[") return -1;
346
+ const m = /^\[(c|bg)=([^\]]+)\]/.exec(input.slice(i));
347
+ if (!m) return -1;
348
+ const kind = m[1];
349
+ const tokenRaw = m[2];
350
+ const closer = kind === "c" ? "[/c]" : "[/bg]";
351
+ if (findUnescaped(closer, i + m[0].length) === -1) return -1;
352
+ const color = resolveColorToken(tokenRaw, theme);
353
+ const style = {};
354
+ if (color) {
355
+ if (kind === "c") style.fill = color;
356
+ else style.textBackgroundColor = color;
357
+ }
358
+ flush();
359
+ stack.push({ kind, style, closer });
360
+ return i + m[0].length;
361
+ };
362
+ const tryCloseBracket = () => {
363
+ for (let s = stack.length - 1; s >= 0; s--) {
364
+ const top = stack[s];
365
+ if (top.kind !== "c" && top.kind !== "bg") continue;
366
+ if (peek(top.closer)) {
367
+ if (s !== stack.length - 1) stack.length = s + 1;
368
+ flush();
369
+ stack.pop();
370
+ return i + top.closer.length;
371
+ }
372
+ break;
373
+ }
374
+ return -1;
375
+ };
376
+ const toggle = (delim, kind, style) => {
377
+ if (!peek(delim)) return null;
378
+ const topIdx = stack.findIndex((e) => e.kind === kind);
379
+ if (topIdx >= 0) {
380
+ if (topIdx !== stack.length - 1) return null;
381
+ flush();
382
+ stack.length = topIdx;
383
+ return i + delim.length;
384
+ }
385
+ if (findUnescaped(delim, i + delim.length) === -1) return null;
386
+ flush();
387
+ stack.push({ kind, style, closer: delim });
388
+ return i + delim.length;
389
+ };
390
+ while (i < n) {
391
+ const ch = input[i];
392
+ if (ch === "\\" && i + 1 < n) {
393
+ buf += input[i + 1];
394
+ i += 2;
395
+ continue;
396
+ }
397
+ if (ch === "[") {
398
+ const closed = tryCloseBracket();
399
+ if (closed > 0) {
400
+ i = closed;
401
+ continue;
402
+ }
403
+ const opened = tryOpenBracket();
404
+ if (opened > 0) {
405
+ i = opened;
406
+ continue;
407
+ }
408
+ }
409
+ let next;
410
+ if ((next = toggle("**", "bold", { fontWeight: 700 })) !== null) {
411
+ i = next;
412
+ continue;
413
+ }
414
+ if ((next = toggle("__", "under", { underline: true })) !== null) {
415
+ i = next;
416
+ continue;
417
+ }
418
+ if ((next = toggle("~~", "strike", { linethrough: true })) !== null) {
419
+ i = next;
420
+ continue;
421
+ }
422
+ if ((next = toggle("==", "highlight", { textBackgroundColor: theme.secondary || "#ffe066" })) !== null) {
423
+ i = next;
424
+ continue;
425
+ }
426
+ if ((next = toggle("*", "italic", { fontStyle: "italic" })) !== null) {
427
+ i = next;
428
+ continue;
429
+ }
430
+ buf += ch;
431
+ i++;
432
+ }
433
+ flush();
434
+ return runs;
435
+ }
436
+ function parseTextMarkdown(input, themeColors) {
437
+ const theme = activeThemeColors;
438
+ const runs = tokenize(input ?? "", theme);
439
+ let plain = "";
440
+ const styles = {};
441
+ let lineIdx = 0;
442
+ let charIdx = 0;
443
+ let hasFormatting = false;
444
+ for (const run of runs) {
445
+ const styleHasContent = Object.keys(run.style).length > 0;
446
+ if (styleHasContent) hasFormatting = true;
447
+ for (const ch of run.text) {
448
+ if (ch === "\n") {
449
+ plain += "\n";
450
+ lineIdx++;
451
+ charIdx = 0;
452
+ continue;
453
+ }
454
+ plain += ch;
455
+ if (styleHasContent) {
456
+ if (!styles[lineIdx]) styles[lineIdx] = {};
457
+ styles[lineIdx][charIdx] = { ...run.style };
458
+ }
459
+ charIdx++;
460
+ }
461
+ }
462
+ return { plainText: plain, styles, hasFormatting };
463
+ }
298
464
  const heightCache = /* @__PURE__ */ new Map();
299
465
  const CACHE_TTL = 5e3;
300
466
  const WIDTH_BUCKET_PX = 8;
@@ -351,10 +517,21 @@ function getTextboxWidthFitMetrics(textbox, targetWidth) {
351
517
  fitsWidth: maxLineWidth <= targetWidth + 1
352
518
  };
353
519
  }
520
+ function getRenderedTextForMeasurement(element) {
521
+ const raw = element.text ?? "";
522
+ if (!raw) return "";
523
+ if (element.formattingEnabled !== true) return raw;
524
+ try {
525
+ const { plainText } = parseTextMarkdown(raw);
526
+ return plainText || "";
527
+ } catch {
528
+ return raw;
529
+ }
530
+ }
354
531
  function getCacheKey(element) {
355
532
  const widthPx = typeof element.width === "number" ? element.width : 200;
356
533
  return JSON.stringify({
357
- text: element.text,
534
+ text: getRenderedTextForMeasurement(element),
358
535
  widthBucket: bucketWidth(widthPx),
359
536
  fontSize: element.fontSize,
360
537
  fontFamily: element.fontFamily,
@@ -378,7 +555,8 @@ function measureTextHeight(element) {
378
555
  if (element.type !== "text") {
379
556
  return element.height || 20;
380
557
  }
381
- const textToMeasure = element.text || " ";
558
+ const rendered = getRenderedTextForMeasurement(element);
559
+ const textToMeasure = rendered || " ";
382
560
  const cacheKey = getCacheKey(element);
383
561
  const cached = heightCache.get(cacheKey);
384
562
  if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
@@ -9366,172 +9544,6 @@ function buildRoundedTrianglePath(w, h, rTop, rBR, rBL) {
9366
9544
  ];
9367
9545
  return parts.join(" ");
9368
9546
  }
9369
- let activeThemeColors = {};
9370
- function setMarkdownThemeColors(c) {
9371
- activeThemeColors = { ...c };
9372
- }
9373
- function resolveColorToken(token, theme) {
9374
- const raw = token.trim();
9375
- const t = raw.toLowerCase();
9376
- if (t === "primary") return theme.primary;
9377
- if (t === "secondary") return theme.secondary;
9378
- if (/^#([0-9a-f]{3,8})$/i.test(raw)) return raw;
9379
- if (/^(rgb|rgba|hsl|hsla)\(/i.test(raw)) return raw;
9380
- if (/^[a-z]+$/i.test(raw)) return raw;
9381
- return void 0;
9382
- }
9383
- function mergeStyle(a, b) {
9384
- return { ...a, ...b };
9385
- }
9386
- function tokenize(input, theme) {
9387
- const runs = [];
9388
- const stack = [];
9389
- let buf = "";
9390
- const activeStyle = () => {
9391
- let s = {};
9392
- for (const e of stack) s = mergeStyle(s, e.style);
9393
- return s;
9394
- };
9395
- const flush = () => {
9396
- if (buf.length === 0) return;
9397
- runs.push({ text: buf, style: activeStyle() });
9398
- buf = "";
9399
- };
9400
- let i = 0;
9401
- const n = input.length;
9402
- const peek = (s, at = i) => input.startsWith(s, at);
9403
- const findUnescaped = (needle, from) => {
9404
- let p = from;
9405
- while (p < n) {
9406
- if (input[p] === "\\" && p + 1 < n) {
9407
- p += 2;
9408
- continue;
9409
- }
9410
- if (input.startsWith(needle, p)) return p;
9411
- p++;
9412
- }
9413
- return -1;
9414
- };
9415
- const tryOpenBracket = () => {
9416
- if (input[i] !== "[") return -1;
9417
- const m = /^\[(c|bg)=([^\]]+)\]/.exec(input.slice(i));
9418
- if (!m) return -1;
9419
- const kind = m[1];
9420
- const tokenRaw = m[2];
9421
- const closer = kind === "c" ? "[/c]" : "[/bg]";
9422
- if (findUnescaped(closer, i + m[0].length) === -1) return -1;
9423
- const color = resolveColorToken(tokenRaw, theme);
9424
- const style = {};
9425
- if (color) {
9426
- if (kind === "c") style.fill = color;
9427
- else style.textBackgroundColor = color;
9428
- }
9429
- flush();
9430
- stack.push({ kind, style, closer });
9431
- return i + m[0].length;
9432
- };
9433
- const tryCloseBracket = () => {
9434
- for (let s = stack.length - 1; s >= 0; s--) {
9435
- const top = stack[s];
9436
- if (top.kind !== "c" && top.kind !== "bg") continue;
9437
- if (peek(top.closer)) {
9438
- if (s !== stack.length - 1) stack.length = s + 1;
9439
- flush();
9440
- stack.pop();
9441
- return i + top.closer.length;
9442
- }
9443
- break;
9444
- }
9445
- return -1;
9446
- };
9447
- const toggle = (delim, kind, style) => {
9448
- if (!peek(delim)) return null;
9449
- const topIdx = stack.findIndex((e) => e.kind === kind);
9450
- if (topIdx >= 0) {
9451
- if (topIdx !== stack.length - 1) return null;
9452
- flush();
9453
- stack.length = topIdx;
9454
- return i + delim.length;
9455
- }
9456
- if (findUnescaped(delim, i + delim.length) === -1) return null;
9457
- flush();
9458
- stack.push({ kind, style, closer: delim });
9459
- return i + delim.length;
9460
- };
9461
- while (i < n) {
9462
- const ch = input[i];
9463
- if (ch === "\\" && i + 1 < n) {
9464
- buf += input[i + 1];
9465
- i += 2;
9466
- continue;
9467
- }
9468
- if (ch === "[") {
9469
- const closed = tryCloseBracket();
9470
- if (closed > 0) {
9471
- i = closed;
9472
- continue;
9473
- }
9474
- const opened = tryOpenBracket();
9475
- if (opened > 0) {
9476
- i = opened;
9477
- continue;
9478
- }
9479
- }
9480
- let next;
9481
- if ((next = toggle("**", "bold", { fontWeight: 700 })) !== null) {
9482
- i = next;
9483
- continue;
9484
- }
9485
- if ((next = toggle("__", "under", { underline: true })) !== null) {
9486
- i = next;
9487
- continue;
9488
- }
9489
- if ((next = toggle("~~", "strike", { linethrough: true })) !== null) {
9490
- i = next;
9491
- continue;
9492
- }
9493
- if ((next = toggle("==", "highlight", { textBackgroundColor: theme.secondary || "#ffe066" })) !== null) {
9494
- i = next;
9495
- continue;
9496
- }
9497
- if ((next = toggle("*", "italic", { fontStyle: "italic" })) !== null) {
9498
- i = next;
9499
- continue;
9500
- }
9501
- buf += ch;
9502
- i++;
9503
- }
9504
- flush();
9505
- return runs;
9506
- }
9507
- function parseTextMarkdown(input, themeColors) {
9508
- const theme = activeThemeColors;
9509
- const runs = tokenize(input ?? "", theme);
9510
- let plain = "";
9511
- const styles = {};
9512
- let lineIdx = 0;
9513
- let charIdx = 0;
9514
- let hasFormatting = false;
9515
- for (const run of runs) {
9516
- const styleHasContent = Object.keys(run.style).length > 0;
9517
- if (styleHasContent) hasFormatting = true;
9518
- for (const ch of run.text) {
9519
- if (ch === "\n") {
9520
- plain += "\n";
9521
- lineIdx++;
9522
- charIdx = 0;
9523
- continue;
9524
- }
9525
- plain += ch;
9526
- if (styleHasContent) {
9527
- if (!styles[lineIdx]) styles[lineIdx] = {};
9528
- styles[lineIdx][charIdx] = { ...run.style };
9529
- }
9530
- charIdx++;
9531
- }
9532
- }
9533
- return { plainText: plain, styles, hasFormatting };
9534
- }
9535
9547
  function angleToCoords(angleDeg) {
9536
9548
  const rad = angleDeg * Math.PI / 180;
9537
9549
  const x1 = 0.5 - Math.sin(rad) * 0.5;
@@ -21080,10 +21092,19 @@ function applyTextCase(text, textCase) {
21080
21092
  function hasInlineFormattingMarkers(value) {
21081
21093
  return /\*\*[\s\S]+?\*\*|__[\s\S]+?__|~~[\s\S]+?~~|==[\s\S]+?==|\[(?:c|bg)=[^\]]+\][\s\S]+?\[\/(?:c|bg)\]|(^|[^*])\*[^*\n]+?\*/i.test(value);
21082
21094
  }
21083
- function setInTree(nodes, elementId, targetProperty, value) {
21095
+ function setInTree(nodes, elementId, targetProperty, value, svgColorKey) {
21084
21096
  for (const node of nodes) {
21085
21097
  if (node.id === elementId) {
21086
- if (targetProperty === "link") {
21098
+ if (svgColorKey) {
21099
+ const next = { ...node.svgColorMap || {} };
21100
+ const v = typeof value === "string" ? value.trim() : "";
21101
+ if (!v || v.toLowerCase() === String(svgColorKey).toLowerCase()) {
21102
+ delete next[svgColorKey];
21103
+ } else {
21104
+ next[svgColorKey] = v;
21105
+ }
21106
+ node.svgColorMap = Object.keys(next).length > 0 ? next : void 0;
21107
+ } else if (targetProperty === "link") {
21087
21108
  node.linkConfig = { ...node.linkConfig || {}, url: String(value ?? "") };
21088
21109
  } else if (targetProperty.startsWith("smartProp:") && node.smartElementType) {
21089
21110
  const propKey = targetProperty.slice("smartProp:".length);
@@ -21133,14 +21154,19 @@ function setInTree(nodes, elementId, targetProperty, value) {
21133
21154
  }
21134
21155
  if (targetProperty === "text" && node.type === "text") {
21135
21156
  const overflowPolicy = String(node.overflowPolicy ?? "grow-and-push");
21136
- if (overflowPolicy !== "auto-shrink") {
21137
- delete node.height;
21157
+ if (overflowPolicy === "auto-shrink") {
21158
+ const explicitH = typeof node.height === "number" ? node.height : 0;
21159
+ const existingMin = Math.max(0, Number(node.minBoxHeight) || 0);
21160
+ if (explicitH > 0 && existingMin <= 0) {
21161
+ node.minBoxHeight = explicitH;
21162
+ }
21138
21163
  }
21164
+ delete node.height;
21139
21165
  }
21140
21166
  return true;
21141
21167
  }
21142
21168
  if (node.children && Array.isArray(node.children)) {
21143
- if (setInTree(node.children, elementId, targetProperty, value)) return true;
21169
+ if (setInTree(node.children, elementId, targetProperty, value, svgColorKey)) return true;
21144
21170
  }
21145
21171
  }
21146
21172
  return false;
@@ -21776,7 +21802,7 @@ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsF
21776
21802
  }
21777
21803
  return void 0;
21778
21804
  }
21779
- const applyValue = (elementId, targetProperty, value, fieldKey) => {
21805
+ const applyValue = (elementId, targetProperty, value, fieldKey, svgColorKey) => {
21780
21806
  var _a3;
21781
21807
  const isTextLike = targetProperty === "text" || targetProperty === "content";
21782
21808
  const isImageLike = targetProperty === "src" || targetProperty === "imageUrl";
@@ -21818,7 +21844,7 @@ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsF
21818
21844
  }
21819
21845
  }
21820
21846
  for (const page of pages) {
21821
- if (page.children && setInTree(page.children, elementId, targetProperty, effectiveValue)) return true;
21847
+ if (page.children && setInTree(page.children, elementId, targetProperty, effectiveValue, svgColorKey)) return true;
21822
21848
  }
21823
21849
  return false;
21824
21850
  };
@@ -21887,7 +21913,8 @@ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsF
21887
21913
  }
21888
21914
  if (!elementIds || elementIds.length === 0) continue;
21889
21915
  const targetProperty = mapping.targetProperty || "text";
21890
- for (const elementId of elementIds) applyValue(elementId, targetProperty, value, key);
21916
+ const mSvgColorKey = mapping.svgColorKey;
21917
+ for (const elementId of elementIds) applyValue(elementId, targetProperty, value, key, mSvgColorKey);
21891
21918
  }
21892
21919
  }
21893
21920
  }
@@ -21932,7 +21959,8 @@ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsF
21932
21959
  }
21933
21960
  if (elementIds.size === 0) continue;
21934
21961
  const targetProperty = mapping.targetProperty || "text";
21935
- for (const elementId of elementIds) applyValue(elementId, targetProperty, value, key);
21962
+ const mSvgColorKey = mapping.svgColorKey;
21963
+ for (const elementId of elementIds) applyValue(elementId, targetProperty, value, key, mSvgColorKey);
21936
21964
  }
21937
21965
  }
21938
21966
  }
@@ -21942,7 +21970,7 @@ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsF
21942
21970
  for (const m of mappings) {
21943
21971
  if (repeatableKeySet.has(m.field_key)) continue;
21944
21972
  const value = formValues[m.field_key];
21945
- applyValue(m.element_id, m.target_property, value, m.field_key);
21973
+ applyValue(m.element_id, m.target_property, value, m.field_key, m.svg_color_key);
21946
21974
  }
21947
21975
  if ((repeatablePagesFromSchema == null ? void 0 : repeatablePagesFromSchema.length) && (dynamicFields == null ? void 0 : dynamicFields.length)) {
21948
21976
  const findInPage = (page, originalId) => {
@@ -21977,10 +22005,11 @@ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsF
21977
22005
  for (const m of field.mappings ?? []) {
21978
22006
  const originalId = m.elementId;
21979
22007
  const targetProperty = m.targetProperty || "text";
22008
+ const mSvgColorKey = m.svgColorKey;
21980
22009
  if (!originalId) continue;
21981
22010
  const resolvedId = findInPage(page, originalId);
21982
22011
  if (!resolvedId) continue;
21983
- if (page.children) setInTree(page.children, resolvedId, targetProperty, value);
22012
+ if (page.children) setInTree(page.children, resolvedId, targetProperty, value, mSvgColorKey);
21984
22013
  }
21985
22014
  }
21986
22015
  }
@@ -26210,9 +26239,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
26210
26239
  }
26211
26240
  return svgString;
26212
26241
  }
26213
- const resolvedPackageVersion = "0.5.458";
26242
+ const resolvedPackageVersion = "0.5.460";
26214
26243
  const PACKAGE_VERSION = resolvedPackageVersion;
26215
- const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.458";
26244
+ const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.460";
26216
26245
  const roundParityValue = (value) => {
26217
26246
  if (typeof value !== "number") return value;
26218
26247
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -27026,7 +27055,7 @@ class PixldocsRenderer {
27026
27055
  await this.waitForCanvasScene(container, cloned, i);
27027
27056
  }
27028
27057
  console.log(`[canvas-renderer][pdf-unified] mounted ${cloned.pages.length} page(s), handing off to client exportMultiPagePdf`);
27029
- const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-DySc4jWC.cjs"));
27058
+ const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-B5Xxp9RH.cjs"));
27030
27059
  const prepared = preparePagesForExport(
27031
27060
  cloned.pages,
27032
27061
  canvasWidth,
@@ -29346,7 +29375,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
29346
29375
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
29347
29376
  sanitizeSvgTreeForPdf(svgToDraw);
29348
29377
  try {
29349
- const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-DySc4jWC.cjs"));
29378
+ const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-B5Xxp9RH.cjs"));
29350
29379
  try {
29351
29380
  await logTextMeasurementDiagnostic(svgToDraw);
29352
29381
  } catch {
@@ -29660,4 +29689,4 @@ exports.setAutoShrinkDebug = setAutoShrinkDebug;
29660
29689
  exports.setBundledAssetPrefixes = setBundledAssetPrefixes;
29661
29690
  exports.warmResolvedTemplateForPreview = warmResolvedTemplateForPreview;
29662
29691
  exports.warmTemplateFromForm = warmTemplateFromForm;
29663
- //# sourceMappingURL=index-CScn1p9A.cjs.map
29692
+ //# sourceMappingURL=index-CtJvkbzV.cjs.map