@pixldocs/canvas-renderer 0.5.76 → 0.5.77

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.
package/dist/index.d.ts CHANGED
@@ -256,7 +256,7 @@ export declare function normalizeFontFamily(fontStack: string): string;
256
256
  * Package version banner. Bump alongside package.json so we can confirm
257
257
  * (via browser:log) that the deployed bundle matches the expected build.
258
258
  */
259
- export declare const PACKAGE_VERSION = "0.5.76";
259
+ export declare const PACKAGE_VERSION = "0.5.77";
260
260
 
261
261
  export declare interface PageSettings {
262
262
  backgroundColor?: string;
@@ -447,6 +447,7 @@ export declare class PixldocsRenderer {
447
447
  * using the global __fabricCanvasRegistry (set by PageCanvas).
448
448
  */
449
449
  private getFabricCanvasFromContainer;
450
+ private logFabricTextParitySnapshot;
450
451
  private waitForStableTextMetrics;
451
452
  }
452
453
 
package/dist/index.js CHANGED
@@ -5194,6 +5194,17 @@ function buildRoundedTrianglePath(w, h, rTop, rBR, rBL) {
5194
5194
  ];
5195
5195
  return parts.join(" ");
5196
5196
  }
5197
+ const roundDiag = (value) => {
5198
+ if (typeof value !== "number") return value;
5199
+ return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
5200
+ };
5201
+ const stringifyDiag = (payload) => {
5202
+ try {
5203
+ return JSON.stringify(payload, (_key, value) => roundDiag(value));
5204
+ } catch {
5205
+ return String(payload);
5206
+ }
5207
+ };
5197
5208
  function buildRoundedRectPath(w, h, tl, tr, br, bl) {
5198
5209
  return buildRoundedRectPath$1(w, h, getRoundedRectRadii(w, h, { rxTL: tl, rxTR: tr, rxBR: br, rxBL: bl }));
5199
5210
  }
@@ -5304,11 +5315,11 @@ function createShape(element) {
5304
5315
  }
5305
5316
  }
5306
5317
  function createText(element) {
5307
- var _a;
5318
+ var _a, _b;
5308
5319
  const overflowPolicy = element.overflowPolicy || "grow-and-push";
5309
5320
  let text = element.text || "Text";
5310
5321
  let fontSize = element.fontSize || 16;
5311
- element.minFontSize || 8;
5322
+ const minFontSize = element.minFontSize || 8;
5312
5323
  const maxLines = element.maxLines || 3;
5313
5324
  const baseWidth = element.width && element.width > 0 ? element.width : 200;
5314
5325
  const baseHeight = element.height;
@@ -5320,6 +5331,7 @@ function createText(element) {
5320
5331
  const startFontSize = fontSize;
5321
5332
  let breakReason = "min-font-size-reached";
5322
5333
  let lastIter = null;
5334
+ const iterationSamples = [];
5323
5335
  while (fontSize > 1) {
5324
5336
  const testTextbox = new fabric.Textbox(text, {
5325
5337
  width: fixedWidth,
@@ -5337,7 +5349,7 @@ function createText(element) {
5337
5349
  const hasNoImplicitWrap = renderedLineCount <= explicitLineCount;
5338
5350
  const fitsHeight = !baseHeight || textHeight <= baseHeight;
5339
5351
  const widthMetrics = getTextboxWidthFitMetrics(testTextbox, fixedWidth);
5340
- const { fitsWidth } = widthMetrics;
5352
+ const { maxLineWidth, widthDidGrow, fitsWidth } = widthMetrics;
5341
5353
  if (debugAutoShrink) {
5342
5354
  lastIter = {
5343
5355
  fontSize,
@@ -5348,8 +5360,12 @@ function createText(element) {
5348
5360
  fixedWidth,
5349
5361
  hasNoImplicitWrap,
5350
5362
  fitsHeight,
5351
- fitsWidth
5363
+ fitsWidth,
5364
+ textLines: (testTextbox.textLines || []).map((line) => Array.isArray(line) ? line.join("") : String(line ?? ""))
5352
5365
  };
5366
+ if (iterationSamples.length < 6 || fontSize <= minFontSize + 2) {
5367
+ iterationSamples.push(lastIter);
5368
+ }
5353
5369
  }
5354
5370
  if (hasNoImplicitWrap && fitsHeight && fitsWidth) {
5355
5371
  breakReason = "fits";
@@ -5358,10 +5374,11 @@ function createText(element) {
5358
5374
  fontSize--;
5359
5375
  }
5360
5376
  if (debugAutoShrink) {
5361
- console.log("[auto-shrink][diag]", {
5377
+ console.log("[auto-shrink][diag] " + stringifyDiag({
5362
5378
  id: element.id,
5363
5379
  name: element.name,
5364
- text,
5380
+ text: text.slice(0, 180),
5381
+ textLength: text.length,
5365
5382
  fontFamily: element.fontFamily,
5366
5383
  fontWeight: element.fontWeight,
5367
5384
  elementWidth: element.width,
@@ -5373,10 +5390,11 @@ function createText(element) {
5373
5390
  startFontSize,
5374
5391
  finalFontSize: fontSize,
5375
5392
  breakReason,
5393
+ iterations: iterationSamples,
5376
5394
  lastIter,
5377
5395
  fontCheckRegular: typeof document !== "undefined" && document.fonts ? document.fonts.check(`16px "${element.fontFamily || "Open Sans"}"`) : null,
5378
5396
  fontCheckBold: typeof document !== "undefined" && document.fonts ? document.fonts.check(`bold 16px "${element.fontFamily || "Open Sans"}"`) : null
5379
- });
5397
+ }));
5380
5398
  }
5381
5399
  }
5382
5400
  if (overflowPolicy === "max-lines-ellipsis") {
@@ -5451,6 +5469,22 @@ function createText(element) {
5451
5469
  textbox.setCoords();
5452
5470
  }
5453
5471
  textbox.dirty = true;
5472
+ if (overflowPolicy === "auto-shrink" && typeof window !== "undefined" && window.__pixldocsDebugAutoShrink === true) {
5473
+ console.log("[auto-shrink][final-textbox] " + stringifyDiag({
5474
+ id: element.id,
5475
+ name: element.name,
5476
+ text: text.slice(0, 180),
5477
+ targetWidth,
5478
+ targetScaleX,
5479
+ targetScaleY,
5480
+ finalFontSize: fontSize,
5481
+ textboxWidth: textbox.width,
5482
+ textboxHeight: textbox.height,
5483
+ lineCount: ((_b = textbox.textLines) == null ? void 0 : _b.length) || 0,
5484
+ lines: (textbox.textLines || []).map((line) => Array.isArray(line) ? line.join("") : String(line ?? "")),
5485
+ widthMetrics: getTextboxWidthFitMetrics(textbox, targetWidth)
5486
+ }));
5487
+ }
5454
5488
  applyTextBackground(textbox, extractTextBgConfig(element));
5455
5489
  const shadow = buildTextShadow(element);
5456
5490
  if (shadow) textbox.set("shadow", shadow);
@@ -12534,7 +12568,18 @@ function PixldocsPreview(props) {
12534
12568
  !canvasSettled && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, display: "flex", alignItems: "center", justifyContent: "center", minHeight: 200 }, children: /* @__PURE__ */ jsx("div", { style: { color: "#888", fontSize: 14 }, children: "Loading preview..." }) })
12535
12569
  ] });
12536
12570
  }
12537
- const PACKAGE_VERSION = "0.5.76";
12571
+ const PACKAGE_VERSION = "0.5.77";
12572
+ const roundParityValue = (value) => {
12573
+ if (typeof value !== "number") return value;
12574
+ return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
12575
+ };
12576
+ function logJsonLine(tag, payload) {
12577
+ try {
12578
+ console.log(`${tag} ${JSON.stringify(payload, (_key, value) => roundParityValue(value))}`);
12579
+ } catch {
12580
+ console.log(tag, payload);
12581
+ }
12582
+ }
12538
12583
  let __underlineFixInstalled = false;
12539
12584
  function installUnderlineFix(fab) {
12540
12585
  var _a;
@@ -13410,6 +13455,42 @@ class PixldocsRenderer {
13410
13455
  }
13411
13456
  return null;
13412
13457
  }
13458
+ logFabricTextParitySnapshot(stage, fabricInstance) {
13459
+ var _a;
13460
+ if (typeof window === "undefined" || window.__pixldocsDebugAutoShrink !== true) return;
13461
+ const sample = [];
13462
+ const visit = (obj, groupPath = "") => {
13463
+ var _a2;
13464
+ if (!obj) return;
13465
+ if (obj instanceof fabric.Textbox) {
13466
+ const lineWidths = getCanvasMeasuredTextboxLineWidths(obj);
13467
+ sample.push({
13468
+ id: getObjectId(obj),
13469
+ groupPath,
13470
+ text: String(obj.text ?? "").slice(0, 180),
13471
+ left: obj.left,
13472
+ top: obj.top,
13473
+ width: obj.width,
13474
+ height: obj.height,
13475
+ scaleX: obj.scaleX,
13476
+ scaleY: obj.scaleY,
13477
+ fontSize: obj.fontSize,
13478
+ fontFamily: obj.fontFamily,
13479
+ fontWeight: obj.fontWeight,
13480
+ lineCount: ((_a2 = obj.textLines) == null ? void 0 : _a2.length) || 0,
13481
+ lines: (obj.textLines || []).map((line) => Array.isArray(line) ? line.join("") : String(line ?? "")),
13482
+ lineWidths,
13483
+ maxLineWidth: lineWidths.length ? Math.max(...lineWidths) : 0
13484
+ });
13485
+ }
13486
+ if (obj instanceof fabric.Group && typeof obj.getObjects === "function") {
13487
+ const nextPath = [groupPath, getObjectId(obj) || obj.type || "group"].filter(Boolean).join("/");
13488
+ obj.getObjects().forEach((child) => visit(child, nextPath));
13489
+ }
13490
+ };
13491
+ (_a = fabricInstance == null ? void 0 : fabricInstance.getObjects) == null ? void 0 : _a.call(fabricInstance).forEach((obj) => visit(obj));
13492
+ logJsonLine("[canvas-renderer][fabric-text-parity]", { stage, textboxes: sample.length, sample });
13493
+ }
13413
13494
  async waitForStableTextMetrics(container, config) {
13414
13495
  var _a, _b, _c;
13415
13496
  if (typeof document !== "undefined") {
@@ -13418,6 +13499,7 @@ class PixldocsRenderer {
13418
13499
  }
13419
13500
  const fabricInstance = this.getFabricCanvasFromContainer(container);
13420
13501
  if (!(fabricInstance == null ? void 0 : fabricInstance.getObjects)) return;
13502
+ this.logFabricTextParitySnapshot("before-stable-text-metrics", fabricInstance);
13421
13503
  const waitForPaint = () => new Promise((r) => requestAnimationFrame(() => requestAnimationFrame(() => r())));
13422
13504
  const primeCharBounds = (obj) => {
13423
13505
  if (obj instanceof fabric.Textbox) {
@@ -13444,6 +13526,7 @@ class PixldocsRenderer {
13444
13526
  await waitForPaint();
13445
13527
  (_c = fabricInstance.renderAll) == null ? void 0 : _c.call(fabricInstance);
13446
13528
  await waitForPaint();
13529
+ this.logFabricTextParitySnapshot("after-stable-text-metrics", fabricInstance);
13447
13530
  }
13448
13531
  }
13449
13532
  const FONT_WEIGHT_LABELS = {
@@ -14265,6 +14348,16 @@ const pdfFonts = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProp
14265
14348
  resolveFontWeight,
14266
14349
  rewriteSvgFontsForJsPDF
14267
14350
  }, Symbol.toStringTag, { value: "Module" }));
14351
+ function logParityJson(tag, stage, payload) {
14352
+ try {
14353
+ console.log(`${tag} ${stage} ${JSON.stringify(payload, (_key, value) => {
14354
+ if (typeof value !== "number") return value;
14355
+ return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
14356
+ })}`);
14357
+ } catch {
14358
+ console.log(`${tag} ${stage}`, payload);
14359
+ }
14360
+ }
14268
14361
  function dumpSvgTextDiagnostics(svgStr, pageIndex, tag, stage, maxItems = 30) {
14269
14362
  try {
14270
14363
  if (typeof DOMParser === "undefined") return;
@@ -14287,7 +14380,7 @@ function dumpSvgTextDiagnostics(svgStr, pageIndex, tag, stage, maxItems = 30) {
14287
14380
  svgViewBox,
14288
14381
  textCount: texts.length
14289
14382
  };
14290
- console.log(`${tag} ${stage} page=${pageIndex} summary`, summary);
14383
+ logParityJson(tag, stage, { kind: "summary", ...summary });
14291
14384
  const sample = texts.slice(0, maxItems).map((t, idx) => {
14292
14385
  var _a, _b;
14293
14386
  const tspans = Array.from(t.querySelectorAll("tspan"));
@@ -14319,7 +14412,7 @@ function dumpSvgTextDiagnostics(svgStr, pageIndex, tag, stage, maxItems = 30) {
14319
14412
  tspanSample: tspanInfo
14320
14413
  };
14321
14414
  });
14322
- console.log(`${tag} ${stage} page=${pageIndex} text-sample (first ${sample.length}/${texts.length})`, sample);
14415
+ logParityJson(tag, stage, { kind: "text-sample", page: pageIndex, count: sample.length, total: texts.length, sample });
14323
14416
  } catch (err) {
14324
14417
  console.warn(`${tag} ${stage} page=${pageIndex} dump threw`, err);
14325
14418
  }