@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.
@@ -277,6 +277,172 @@ function findCommonAncestorGroup(selectedIds, pageChildren) {
277
277
  }
278
278
  return null;
279
279
  }
280
+ let activeThemeColors = {};
281
+ function setMarkdownThemeColors(c) {
282
+ activeThemeColors = { ...c };
283
+ }
284
+ function resolveColorToken(token, theme) {
285
+ const raw = token.trim();
286
+ const t = raw.toLowerCase();
287
+ if (t === "primary") return theme.primary;
288
+ if (t === "secondary") return theme.secondary;
289
+ if (/^#([0-9a-f]{3,8})$/i.test(raw)) return raw;
290
+ if (/^(rgb|rgba|hsl|hsla)\(/i.test(raw)) return raw;
291
+ if (/^[a-z]+$/i.test(raw)) return raw;
292
+ return void 0;
293
+ }
294
+ function mergeStyle(a, b) {
295
+ return { ...a, ...b };
296
+ }
297
+ function tokenize(input, theme) {
298
+ const runs = [];
299
+ const stack = [];
300
+ let buf = "";
301
+ const activeStyle = () => {
302
+ let s = {};
303
+ for (const e of stack) s = mergeStyle(s, e.style);
304
+ return s;
305
+ };
306
+ const flush = () => {
307
+ if (buf.length === 0) return;
308
+ runs.push({ text: buf, style: activeStyle() });
309
+ buf = "";
310
+ };
311
+ let i = 0;
312
+ const n = input.length;
313
+ const peek = (s, at = i) => input.startsWith(s, at);
314
+ const findUnescaped = (needle, from) => {
315
+ let p = from;
316
+ while (p < n) {
317
+ if (input[p] === "\\" && p + 1 < n) {
318
+ p += 2;
319
+ continue;
320
+ }
321
+ if (input.startsWith(needle, p)) return p;
322
+ p++;
323
+ }
324
+ return -1;
325
+ };
326
+ const tryOpenBracket = () => {
327
+ if (input[i] !== "[") return -1;
328
+ const m = /^\[(c|bg)=([^\]]+)\]/.exec(input.slice(i));
329
+ if (!m) return -1;
330
+ const kind = m[1];
331
+ const tokenRaw = m[2];
332
+ const closer = kind === "c" ? "[/c]" : "[/bg]";
333
+ if (findUnescaped(closer, i + m[0].length) === -1) return -1;
334
+ const color = resolveColorToken(tokenRaw, theme);
335
+ const style = {};
336
+ if (color) {
337
+ if (kind === "c") style.fill = color;
338
+ else style.textBackgroundColor = color;
339
+ }
340
+ flush();
341
+ stack.push({ kind, style, closer });
342
+ return i + m[0].length;
343
+ };
344
+ const tryCloseBracket = () => {
345
+ for (let s = stack.length - 1; s >= 0; s--) {
346
+ const top = stack[s];
347
+ if (top.kind !== "c" && top.kind !== "bg") continue;
348
+ if (peek(top.closer)) {
349
+ if (s !== stack.length - 1) stack.length = s + 1;
350
+ flush();
351
+ stack.pop();
352
+ return i + top.closer.length;
353
+ }
354
+ break;
355
+ }
356
+ return -1;
357
+ };
358
+ const toggle = (delim, kind, style) => {
359
+ if (!peek(delim)) return null;
360
+ const topIdx = stack.findIndex((e) => e.kind === kind);
361
+ if (topIdx >= 0) {
362
+ if (topIdx !== stack.length - 1) return null;
363
+ flush();
364
+ stack.length = topIdx;
365
+ return i + delim.length;
366
+ }
367
+ if (findUnescaped(delim, i + delim.length) === -1) return null;
368
+ flush();
369
+ stack.push({ kind, style, closer: delim });
370
+ return i + delim.length;
371
+ };
372
+ while (i < n) {
373
+ const ch = input[i];
374
+ if (ch === "\\" && i + 1 < n) {
375
+ buf += input[i + 1];
376
+ i += 2;
377
+ continue;
378
+ }
379
+ if (ch === "[") {
380
+ const closed = tryCloseBracket();
381
+ if (closed > 0) {
382
+ i = closed;
383
+ continue;
384
+ }
385
+ const opened = tryOpenBracket();
386
+ if (opened > 0) {
387
+ i = opened;
388
+ continue;
389
+ }
390
+ }
391
+ let next;
392
+ if ((next = toggle("**", "bold", { fontWeight: 700 })) !== null) {
393
+ i = next;
394
+ continue;
395
+ }
396
+ if ((next = toggle("__", "under", { underline: true })) !== null) {
397
+ i = next;
398
+ continue;
399
+ }
400
+ if ((next = toggle("~~", "strike", { linethrough: true })) !== null) {
401
+ i = next;
402
+ continue;
403
+ }
404
+ if ((next = toggle("==", "highlight", { textBackgroundColor: theme.secondary || "#ffe066" })) !== null) {
405
+ i = next;
406
+ continue;
407
+ }
408
+ if ((next = toggle("*", "italic", { fontStyle: "italic" })) !== null) {
409
+ i = next;
410
+ continue;
411
+ }
412
+ buf += ch;
413
+ i++;
414
+ }
415
+ flush();
416
+ return runs;
417
+ }
418
+ function parseTextMarkdown(input, themeColors) {
419
+ const theme = activeThemeColors;
420
+ const runs = tokenize(input ?? "", theme);
421
+ let plain = "";
422
+ const styles = {};
423
+ let lineIdx = 0;
424
+ let charIdx = 0;
425
+ let hasFormatting = false;
426
+ for (const run of runs) {
427
+ const styleHasContent = Object.keys(run.style).length > 0;
428
+ if (styleHasContent) hasFormatting = true;
429
+ for (const ch of run.text) {
430
+ if (ch === "\n") {
431
+ plain += "\n";
432
+ lineIdx++;
433
+ charIdx = 0;
434
+ continue;
435
+ }
436
+ plain += ch;
437
+ if (styleHasContent) {
438
+ if (!styles[lineIdx]) styles[lineIdx] = {};
439
+ styles[lineIdx][charIdx] = { ...run.style };
440
+ }
441
+ charIdx++;
442
+ }
443
+ }
444
+ return { plainText: plain, styles, hasFormatting };
445
+ }
280
446
  const heightCache = /* @__PURE__ */ new Map();
281
447
  const CACHE_TTL = 5e3;
282
448
  const WIDTH_BUCKET_PX = 8;
@@ -333,10 +499,21 @@ function getTextboxWidthFitMetrics(textbox, targetWidth) {
333
499
  fitsWidth: maxLineWidth <= targetWidth + 1
334
500
  };
335
501
  }
502
+ function getRenderedTextForMeasurement(element) {
503
+ const raw = element.text ?? "";
504
+ if (!raw) return "";
505
+ if (element.formattingEnabled !== true) return raw;
506
+ try {
507
+ const { plainText } = parseTextMarkdown(raw);
508
+ return plainText || "";
509
+ } catch {
510
+ return raw;
511
+ }
512
+ }
336
513
  function getCacheKey(element) {
337
514
  const widthPx = typeof element.width === "number" ? element.width : 200;
338
515
  return JSON.stringify({
339
- text: element.text,
516
+ text: getRenderedTextForMeasurement(element),
340
517
  widthBucket: bucketWidth(widthPx),
341
518
  fontSize: element.fontSize,
342
519
  fontFamily: element.fontFamily,
@@ -360,7 +537,8 @@ function measureTextHeight(element) {
360
537
  if (element.type !== "text") {
361
538
  return element.height || 20;
362
539
  }
363
- const textToMeasure = element.text || " ";
540
+ const rendered = getRenderedTextForMeasurement(element);
541
+ const textToMeasure = rendered || " ";
364
542
  const cacheKey = getCacheKey(element);
365
543
  const cached = heightCache.get(cacheKey);
366
544
  if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
@@ -9348,172 +9526,6 @@ function buildRoundedTrianglePath(w, h, rTop, rBR, rBL) {
9348
9526
  ];
9349
9527
  return parts.join(" ");
9350
9528
  }
9351
- let activeThemeColors = {};
9352
- function setMarkdownThemeColors(c) {
9353
- activeThemeColors = { ...c };
9354
- }
9355
- function resolveColorToken(token, theme) {
9356
- const raw = token.trim();
9357
- const t = raw.toLowerCase();
9358
- if (t === "primary") return theme.primary;
9359
- if (t === "secondary") return theme.secondary;
9360
- if (/^#([0-9a-f]{3,8})$/i.test(raw)) return raw;
9361
- if (/^(rgb|rgba|hsl|hsla)\(/i.test(raw)) return raw;
9362
- if (/^[a-z]+$/i.test(raw)) return raw;
9363
- return void 0;
9364
- }
9365
- function mergeStyle(a, b) {
9366
- return { ...a, ...b };
9367
- }
9368
- function tokenize(input, theme) {
9369
- const runs = [];
9370
- const stack = [];
9371
- let buf = "";
9372
- const activeStyle = () => {
9373
- let s = {};
9374
- for (const e of stack) s = mergeStyle(s, e.style);
9375
- return s;
9376
- };
9377
- const flush = () => {
9378
- if (buf.length === 0) return;
9379
- runs.push({ text: buf, style: activeStyle() });
9380
- buf = "";
9381
- };
9382
- let i = 0;
9383
- const n = input.length;
9384
- const peek = (s, at = i) => input.startsWith(s, at);
9385
- const findUnescaped = (needle, from) => {
9386
- let p = from;
9387
- while (p < n) {
9388
- if (input[p] === "\\" && p + 1 < n) {
9389
- p += 2;
9390
- continue;
9391
- }
9392
- if (input.startsWith(needle, p)) return p;
9393
- p++;
9394
- }
9395
- return -1;
9396
- };
9397
- const tryOpenBracket = () => {
9398
- if (input[i] !== "[") return -1;
9399
- const m = /^\[(c|bg)=([^\]]+)\]/.exec(input.slice(i));
9400
- if (!m) return -1;
9401
- const kind = m[1];
9402
- const tokenRaw = m[2];
9403
- const closer = kind === "c" ? "[/c]" : "[/bg]";
9404
- if (findUnescaped(closer, i + m[0].length) === -1) return -1;
9405
- const color = resolveColorToken(tokenRaw, theme);
9406
- const style = {};
9407
- if (color) {
9408
- if (kind === "c") style.fill = color;
9409
- else style.textBackgroundColor = color;
9410
- }
9411
- flush();
9412
- stack.push({ kind, style, closer });
9413
- return i + m[0].length;
9414
- };
9415
- const tryCloseBracket = () => {
9416
- for (let s = stack.length - 1; s >= 0; s--) {
9417
- const top = stack[s];
9418
- if (top.kind !== "c" && top.kind !== "bg") continue;
9419
- if (peek(top.closer)) {
9420
- if (s !== stack.length - 1) stack.length = s + 1;
9421
- flush();
9422
- stack.pop();
9423
- return i + top.closer.length;
9424
- }
9425
- break;
9426
- }
9427
- return -1;
9428
- };
9429
- const toggle = (delim, kind, style) => {
9430
- if (!peek(delim)) return null;
9431
- const topIdx = stack.findIndex((e) => e.kind === kind);
9432
- if (topIdx >= 0) {
9433
- if (topIdx !== stack.length - 1) return null;
9434
- flush();
9435
- stack.length = topIdx;
9436
- return i + delim.length;
9437
- }
9438
- if (findUnescaped(delim, i + delim.length) === -1) return null;
9439
- flush();
9440
- stack.push({ kind, style, closer: delim });
9441
- return i + delim.length;
9442
- };
9443
- while (i < n) {
9444
- const ch = input[i];
9445
- if (ch === "\\" && i + 1 < n) {
9446
- buf += input[i + 1];
9447
- i += 2;
9448
- continue;
9449
- }
9450
- if (ch === "[") {
9451
- const closed = tryCloseBracket();
9452
- if (closed > 0) {
9453
- i = closed;
9454
- continue;
9455
- }
9456
- const opened = tryOpenBracket();
9457
- if (opened > 0) {
9458
- i = opened;
9459
- continue;
9460
- }
9461
- }
9462
- let next;
9463
- if ((next = toggle("**", "bold", { fontWeight: 700 })) !== null) {
9464
- i = next;
9465
- continue;
9466
- }
9467
- if ((next = toggle("__", "under", { underline: true })) !== null) {
9468
- i = next;
9469
- continue;
9470
- }
9471
- if ((next = toggle("~~", "strike", { linethrough: true })) !== null) {
9472
- i = next;
9473
- continue;
9474
- }
9475
- if ((next = toggle("==", "highlight", { textBackgroundColor: theme.secondary || "#ffe066" })) !== null) {
9476
- i = next;
9477
- continue;
9478
- }
9479
- if ((next = toggle("*", "italic", { fontStyle: "italic" })) !== null) {
9480
- i = next;
9481
- continue;
9482
- }
9483
- buf += ch;
9484
- i++;
9485
- }
9486
- flush();
9487
- return runs;
9488
- }
9489
- function parseTextMarkdown(input, themeColors) {
9490
- const theme = activeThemeColors;
9491
- const runs = tokenize(input ?? "", theme);
9492
- let plain = "";
9493
- const styles = {};
9494
- let lineIdx = 0;
9495
- let charIdx = 0;
9496
- let hasFormatting = false;
9497
- for (const run of runs) {
9498
- const styleHasContent = Object.keys(run.style).length > 0;
9499
- if (styleHasContent) hasFormatting = true;
9500
- for (const ch of run.text) {
9501
- if (ch === "\n") {
9502
- plain += "\n";
9503
- lineIdx++;
9504
- charIdx = 0;
9505
- continue;
9506
- }
9507
- plain += ch;
9508
- if (styleHasContent) {
9509
- if (!styles[lineIdx]) styles[lineIdx] = {};
9510
- styles[lineIdx][charIdx] = { ...run.style };
9511
- }
9512
- charIdx++;
9513
- }
9514
- }
9515
- return { plainText: plain, styles, hasFormatting };
9516
- }
9517
9529
  function angleToCoords(angleDeg) {
9518
9530
  const rad = angleDeg * Math.PI / 180;
9519
9531
  const x1 = 0.5 - Math.sin(rad) * 0.5;
@@ -21062,10 +21074,19 @@ function applyTextCase(text, textCase) {
21062
21074
  function hasInlineFormattingMarkers(value) {
21063
21075
  return /\*\*[\s\S]+?\*\*|__[\s\S]+?__|~~[\s\S]+?~~|==[\s\S]+?==|\[(?:c|bg)=[^\]]+\][\s\S]+?\[\/(?:c|bg)\]|(^|[^*])\*[^*\n]+?\*/i.test(value);
21064
21076
  }
21065
- function setInTree(nodes, elementId, targetProperty, value) {
21077
+ function setInTree(nodes, elementId, targetProperty, value, svgColorKey) {
21066
21078
  for (const node of nodes) {
21067
21079
  if (node.id === elementId) {
21068
- if (targetProperty === "link") {
21080
+ if (svgColorKey) {
21081
+ const next = { ...node.svgColorMap || {} };
21082
+ const v = typeof value === "string" ? value.trim() : "";
21083
+ if (!v || v.toLowerCase() === String(svgColorKey).toLowerCase()) {
21084
+ delete next[svgColorKey];
21085
+ } else {
21086
+ next[svgColorKey] = v;
21087
+ }
21088
+ node.svgColorMap = Object.keys(next).length > 0 ? next : void 0;
21089
+ } else if (targetProperty === "link") {
21069
21090
  node.linkConfig = { ...node.linkConfig || {}, url: String(value ?? "") };
21070
21091
  } else if (targetProperty.startsWith("smartProp:") && node.smartElementType) {
21071
21092
  const propKey = targetProperty.slice("smartProp:".length);
@@ -21115,14 +21136,19 @@ function setInTree(nodes, elementId, targetProperty, value) {
21115
21136
  }
21116
21137
  if (targetProperty === "text" && node.type === "text") {
21117
21138
  const overflowPolicy = String(node.overflowPolicy ?? "grow-and-push");
21118
- if (overflowPolicy !== "auto-shrink") {
21119
- delete node.height;
21139
+ if (overflowPolicy === "auto-shrink") {
21140
+ const explicitH = typeof node.height === "number" ? node.height : 0;
21141
+ const existingMin = Math.max(0, Number(node.minBoxHeight) || 0);
21142
+ if (explicitH > 0 && existingMin <= 0) {
21143
+ node.minBoxHeight = explicitH;
21144
+ }
21120
21145
  }
21146
+ delete node.height;
21121
21147
  }
21122
21148
  return true;
21123
21149
  }
21124
21150
  if (node.children && Array.isArray(node.children)) {
21125
- if (setInTree(node.children, elementId, targetProperty, value)) return true;
21151
+ if (setInTree(node.children, elementId, targetProperty, value, svgColorKey)) return true;
21126
21152
  }
21127
21153
  }
21128
21154
  return false;
@@ -21758,7 +21784,7 @@ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsF
21758
21784
  }
21759
21785
  return void 0;
21760
21786
  }
21761
- const applyValue = (elementId, targetProperty, value, fieldKey) => {
21787
+ const applyValue = (elementId, targetProperty, value, fieldKey, svgColorKey) => {
21762
21788
  var _a3;
21763
21789
  const isTextLike = targetProperty === "text" || targetProperty === "content";
21764
21790
  const isImageLike = targetProperty === "src" || targetProperty === "imageUrl";
@@ -21800,7 +21826,7 @@ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsF
21800
21826
  }
21801
21827
  }
21802
21828
  for (const page of pages) {
21803
- if (page.children && setInTree(page.children, elementId, targetProperty, effectiveValue)) return true;
21829
+ if (page.children && setInTree(page.children, elementId, targetProperty, effectiveValue, svgColorKey)) return true;
21804
21830
  }
21805
21831
  return false;
21806
21832
  };
@@ -21869,7 +21895,8 @@ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsF
21869
21895
  }
21870
21896
  if (!elementIds || elementIds.length === 0) continue;
21871
21897
  const targetProperty = mapping.targetProperty || "text";
21872
- for (const elementId of elementIds) applyValue(elementId, targetProperty, value, key);
21898
+ const mSvgColorKey = mapping.svgColorKey;
21899
+ for (const elementId of elementIds) applyValue(elementId, targetProperty, value, key, mSvgColorKey);
21873
21900
  }
21874
21901
  }
21875
21902
  }
@@ -21914,7 +21941,8 @@ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsF
21914
21941
  }
21915
21942
  if (elementIds.size === 0) continue;
21916
21943
  const targetProperty = mapping.targetProperty || "text";
21917
- for (const elementId of elementIds) applyValue(elementId, targetProperty, value, key);
21944
+ const mSvgColorKey = mapping.svgColorKey;
21945
+ for (const elementId of elementIds) applyValue(elementId, targetProperty, value, key, mSvgColorKey);
21918
21946
  }
21919
21947
  }
21920
21948
  }
@@ -21924,7 +21952,7 @@ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsF
21924
21952
  for (const m of mappings) {
21925
21953
  if (repeatableKeySet.has(m.field_key)) continue;
21926
21954
  const value = formValues[m.field_key];
21927
- applyValue(m.element_id, m.target_property, value, m.field_key);
21955
+ applyValue(m.element_id, m.target_property, value, m.field_key, m.svg_color_key);
21928
21956
  }
21929
21957
  if ((repeatablePagesFromSchema == null ? void 0 : repeatablePagesFromSchema.length) && (dynamicFields == null ? void 0 : dynamicFields.length)) {
21930
21958
  const findInPage = (page, originalId) => {
@@ -21959,10 +21987,11 @@ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsF
21959
21987
  for (const m of field.mappings ?? []) {
21960
21988
  const originalId = m.elementId;
21961
21989
  const targetProperty = m.targetProperty || "text";
21990
+ const mSvgColorKey = m.svgColorKey;
21962
21991
  if (!originalId) continue;
21963
21992
  const resolvedId = findInPage(page, originalId);
21964
21993
  if (!resolvedId) continue;
21965
- if (page.children) setInTree(page.children, resolvedId, targetProperty, value);
21994
+ if (page.children) setInTree(page.children, resolvedId, targetProperty, value, mSvgColorKey);
21966
21995
  }
21967
21996
  }
21968
21997
  }
@@ -26192,9 +26221,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
26192
26221
  }
26193
26222
  return svgString;
26194
26223
  }
26195
- const resolvedPackageVersion = "0.5.458";
26224
+ const resolvedPackageVersion = "0.5.460";
26196
26225
  const PACKAGE_VERSION = resolvedPackageVersion;
26197
- const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.458";
26226
+ const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.460";
26198
26227
  const roundParityValue = (value) => {
26199
26228
  if (typeof value !== "number") return value;
26200
26229
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -27008,7 +27037,7 @@ class PixldocsRenderer {
27008
27037
  await this.waitForCanvasScene(container, cloned, i);
27009
27038
  }
27010
27039
  console.log(`[canvas-renderer][pdf-unified] mounted ${cloned.pages.length} page(s), handing off to client exportMultiPagePdf`);
27011
- const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-DTC5MXqp.js");
27040
+ const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-CWiNW81v.js");
27012
27041
  const prepared = preparePagesForExport(
27013
27042
  cloned.pages,
27014
27043
  canvasWidth,
@@ -29328,7 +29357,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
29328
29357
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
29329
29358
  sanitizeSvgTreeForPdf(svgToDraw);
29330
29359
  try {
29331
- const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-DTC5MXqp.js");
29360
+ const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-CWiNW81v.js");
29332
29361
  try {
29333
29362
  await logTextMeasurementDiagnostic(svgToDraw);
29334
29363
  } catch {
@@ -29645,4 +29674,4 @@ export {
29645
29674
  buildTeaserBlurFlatKeys as y,
29646
29675
  collectFontDescriptorsFromConfig as z
29647
29676
  };
29648
- //# sourceMappingURL=index-DgUgxzD1.js.map
29677
+ //# sourceMappingURL=index-B306IfvT.js.map