@pixldocs/canvas-renderer 0.5.458 → 0.5.459

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;
@@ -21133,9 +21145,14 @@ function setInTree(nodes, elementId, targetProperty, value) {
21133
21145
  }
21134
21146
  if (targetProperty === "text" && node.type === "text") {
21135
21147
  const overflowPolicy = String(node.overflowPolicy ?? "grow-and-push");
21136
- if (overflowPolicy !== "auto-shrink") {
21137
- delete node.height;
21148
+ if (overflowPolicy === "auto-shrink") {
21149
+ const explicitH = typeof node.height === "number" ? node.height : 0;
21150
+ const existingMin = Math.max(0, Number(node.minBoxHeight) || 0);
21151
+ if (explicitH > 0 && existingMin <= 0) {
21152
+ node.minBoxHeight = explicitH;
21153
+ }
21138
21154
  }
21155
+ delete node.height;
21139
21156
  }
21140
21157
  return true;
21141
21158
  }
@@ -26210,9 +26227,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
26210
26227
  }
26211
26228
  return svgString;
26212
26229
  }
26213
- const resolvedPackageVersion = "0.5.458";
26230
+ const resolvedPackageVersion = "0.5.459";
26214
26231
  const PACKAGE_VERSION = resolvedPackageVersion;
26215
- const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.458";
26232
+ const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.459";
26216
26233
  const roundParityValue = (value) => {
26217
26234
  if (typeof value !== "number") return value;
26218
26235
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -27026,7 +27043,7 @@ class PixldocsRenderer {
27026
27043
  await this.waitForCanvasScene(container, cloned, i);
27027
27044
  }
27028
27045
  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"));
27046
+ const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-CpKSMd8E.cjs"));
27030
27047
  const prepared = preparePagesForExport(
27031
27048
  cloned.pages,
27032
27049
  canvasWidth,
@@ -29346,7 +29363,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
29346
29363
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
29347
29364
  sanitizeSvgTreeForPdf(svgToDraw);
29348
29365
  try {
29349
- const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-DySc4jWC.cjs"));
29366
+ const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-CpKSMd8E.cjs"));
29350
29367
  try {
29351
29368
  await logTextMeasurementDiagnostic(svgToDraw);
29352
29369
  } catch {
@@ -29660,4 +29677,4 @@ exports.setAutoShrinkDebug = setAutoShrinkDebug;
29660
29677
  exports.setBundledAssetPrefixes = setBundledAssetPrefixes;
29661
29678
  exports.warmResolvedTemplateForPreview = warmResolvedTemplateForPreview;
29662
29679
  exports.warmTemplateFromForm = warmTemplateFromForm;
29663
- //# sourceMappingURL=index-CScn1p9A.cjs.map
29680
+ //# sourceMappingURL=index-HqxKxsb3.cjs.map