@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.
@@ -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;
@@ -21115,9 +21127,14 @@ function setInTree(nodes, elementId, targetProperty, value) {
21115
21127
  }
21116
21128
  if (targetProperty === "text" && node.type === "text") {
21117
21129
  const overflowPolicy = String(node.overflowPolicy ?? "grow-and-push");
21118
- if (overflowPolicy !== "auto-shrink") {
21119
- delete node.height;
21130
+ if (overflowPolicy === "auto-shrink") {
21131
+ const explicitH = typeof node.height === "number" ? node.height : 0;
21132
+ const existingMin = Math.max(0, Number(node.minBoxHeight) || 0);
21133
+ if (explicitH > 0 && existingMin <= 0) {
21134
+ node.minBoxHeight = explicitH;
21135
+ }
21120
21136
  }
21137
+ delete node.height;
21121
21138
  }
21122
21139
  return true;
21123
21140
  }
@@ -26192,9 +26209,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
26192
26209
  }
26193
26210
  return svgString;
26194
26211
  }
26195
- const resolvedPackageVersion = "0.5.458";
26212
+ const resolvedPackageVersion = "0.5.459";
26196
26213
  const PACKAGE_VERSION = resolvedPackageVersion;
26197
- const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.458";
26214
+ const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.459";
26198
26215
  const roundParityValue = (value) => {
26199
26216
  if (typeof value !== "number") return value;
26200
26217
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -27008,7 +27025,7 @@ class PixldocsRenderer {
27008
27025
  await this.waitForCanvasScene(container, cloned, i);
27009
27026
  }
27010
27027
  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");
27028
+ const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-De64EUqT.js");
27012
27029
  const prepared = preparePagesForExport(
27013
27030
  cloned.pages,
27014
27031
  canvasWidth,
@@ -29328,7 +29345,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
29328
29345
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
29329
29346
  sanitizeSvgTreeForPdf(svgToDraw);
29330
29347
  try {
29331
- const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-DTC5MXqp.js");
29348
+ const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-De64EUqT.js");
29332
29349
  try {
29333
29350
  await logTextMeasurementDiagnostic(svgToDraw);
29334
29351
  } catch {
@@ -29645,4 +29662,4 @@ export {
29645
29662
  buildTeaserBlurFlatKeys as y,
29646
29663
  collectFontDescriptorsFromConfig as z
29647
29664
  };
29648
- //# sourceMappingURL=index-DgUgxzD1.js.map
29665
+ //# sourceMappingURL=index-BzvyPXLB.js.map