@pixldocs/canvas-renderer 0.5.57 → 0.5.59
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.cjs +119 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +24 -1
- package/dist/index.js +119 -26
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -264,6 +264,57 @@ const WIDTH_BUCKET_PX = 8;
|
|
|
264
264
|
function bucketWidth(w) {
|
|
265
265
|
return Math.round(w / WIDTH_BUCKET_PX) * WIDTH_BUCKET_PX;
|
|
266
266
|
}
|
|
267
|
+
let measureCanvas = null;
|
|
268
|
+
function getMeasureContext() {
|
|
269
|
+
if (typeof document === "undefined") return null;
|
|
270
|
+
measureCanvas ?? (measureCanvas = document.createElement("canvas"));
|
|
271
|
+
return measureCanvas.getContext("2d");
|
|
272
|
+
}
|
|
273
|
+
function lineToString(line) {
|
|
274
|
+
return Array.isArray(line) ? line.join("") : String(line ?? "");
|
|
275
|
+
}
|
|
276
|
+
function measureTextLineWithCanvas(textbox, lineText, lineIndex) {
|
|
277
|
+
var _a, _b, _c, _d, _e;
|
|
278
|
+
if (!lineText) return 0;
|
|
279
|
+
const ctx = getMeasureContext();
|
|
280
|
+
if (!ctx) return null;
|
|
281
|
+
const tb = textbox;
|
|
282
|
+
const fontSize = Number(((_a = tb.getValueOfPropertyAt) == null ? void 0 : _a.call(tb, lineIndex, 0, "fontSize")) ?? textbox.fontSize ?? 16);
|
|
283
|
+
const fontStyle = String(((_b = tb.getValueOfPropertyAt) == null ? void 0 : _b.call(tb, lineIndex, 0, "fontStyle")) ?? textbox.fontStyle ?? "normal");
|
|
284
|
+
const fontWeight = String(((_c = tb.getValueOfPropertyAt) == null ? void 0 : _c.call(tb, lineIndex, 0, "fontWeight")) ?? textbox.fontWeight ?? "400");
|
|
285
|
+
const fontFamily = String(((_d = tb.getValueOfPropertyAt) == null ? void 0 : _d.call(tb, lineIndex, 0, "fontFamily")) ?? textbox.fontFamily ?? "Open Sans");
|
|
286
|
+
const charSpacing = Number(((_e = tb.getValueOfPropertyAt) == null ? void 0 : _e.call(tb, lineIndex, 0, "charSpacing")) ?? textbox.charSpacing ?? 0);
|
|
287
|
+
ctx.save();
|
|
288
|
+
ctx.font = `${fontStyle} normal ${fontWeight} ${fontSize}px ${fontFamily}`;
|
|
289
|
+
const measured = ctx.measureText(lineText).width;
|
|
290
|
+
ctx.restore();
|
|
291
|
+
const graphemeCount = Array.from(lineText).length;
|
|
292
|
+
const spacingWidth = graphemeCount > 1 ? charSpacing / 1e3 * fontSize * (graphemeCount - 1) : 0;
|
|
293
|
+
return Math.max(0, measured + spacingWidth);
|
|
294
|
+
}
|
|
295
|
+
function getCanvasMeasuredTextboxLineWidths(textbox) {
|
|
296
|
+
const rawLines = textbox._textLines ?? textbox.textLines ?? [];
|
|
297
|
+
const fallback = textbox.__lineWidths ?? [];
|
|
298
|
+
if (!rawLines.length) return fallback;
|
|
299
|
+
const measured = rawLines.map((line, index) => measureTextLineWithCanvas(textbox, lineToString(line), index));
|
|
300
|
+
if (measured.some((w) => w == null || Number.isNaN(w))) return fallback;
|
|
301
|
+
return measured;
|
|
302
|
+
}
|
|
303
|
+
function getTextboxWidthFitMetrics(textbox, targetWidth) {
|
|
304
|
+
const actualTextboxWidth = Number(textbox.width ?? targetWidth);
|
|
305
|
+
const dynamicMinWidth = Number(textbox.dynamicMinWidth ?? 0);
|
|
306
|
+
const lineWidths = getCanvasMeasuredTextboxLineWidths(textbox);
|
|
307
|
+
const maxLineWidth = lineWidths.length > 0 ? Math.max(...lineWidths) : 0;
|
|
308
|
+
const grownWidth = Math.max(actualTextboxWidth, dynamicMinWidth);
|
|
309
|
+
const widthDidGrow = grownWidth > targetWidth + 0.5;
|
|
310
|
+
return {
|
|
311
|
+
actualTextboxWidth,
|
|
312
|
+
dynamicMinWidth,
|
|
313
|
+
maxLineWidth,
|
|
314
|
+
widthDidGrow,
|
|
315
|
+
fitsWidth: !widthDidGrow && maxLineWidth <= targetWidth + 1
|
|
316
|
+
};
|
|
317
|
+
}
|
|
267
318
|
function getCacheKey(element) {
|
|
268
319
|
const widthPx = typeof element.width === "number" ? element.width : 200;
|
|
269
320
|
return JSON.stringify({
|
|
@@ -320,11 +371,7 @@ function measureTextHeight(element) {
|
|
|
320
371
|
const renderedLineCount = ((_a = testTb.textLines) == null ? void 0 : _a.length) || 1;
|
|
321
372
|
const hasNoImplicitWrap = renderedLineCount <= explicitLineCount;
|
|
322
373
|
const fitsHeight = !baseHeight || textHeight <= baseHeight;
|
|
323
|
-
const
|
|
324
|
-
const widthDidGrow = actualTextboxWidth > measureWidth + 0.5;
|
|
325
|
-
const lineWidths = testTb.__lineWidths;
|
|
326
|
-
const maxLineWidth = lineWidths && lineWidths.length > 0 ? Math.max(...lineWidths) : 0;
|
|
327
|
-
const fitsWidth = !widthDidGrow && maxLineWidth <= measureWidth + 1;
|
|
374
|
+
const { fitsWidth } = getTextboxWidthFitMetrics(testTb, measureWidth);
|
|
328
375
|
if (hasNoImplicitWrap && fitsHeight && fitsWidth) break;
|
|
329
376
|
fontSize--;
|
|
330
377
|
}
|
|
@@ -5309,20 +5356,16 @@ function createText(element) {
|
|
|
5309
5356
|
const renderedLineCount = ((_a = testTextbox.textLines) == null ? void 0 : _a.length) || 1;
|
|
5310
5357
|
const hasNoImplicitWrap = renderedLineCount <= explicitLineCount;
|
|
5311
5358
|
const fitsHeight = !baseHeight || textHeight <= baseHeight;
|
|
5312
|
-
const
|
|
5313
|
-
const
|
|
5314
|
-
const lineWidths = testTextbox.__lineWidths;
|
|
5315
|
-
const maxLineWidth = lineWidths && lineWidths.length > 0 ? Math.max(...lineWidths) : 0;
|
|
5316
|
-
const fitsWidth = !widthDidGrow && maxLineWidth <= fixedWidth + 1;
|
|
5359
|
+
const widthMetrics = getTextboxWidthFitMetrics(testTextbox, fixedWidth);
|
|
5360
|
+
const { fitsWidth } = widthMetrics;
|
|
5317
5361
|
if (debugAutoShrink) {
|
|
5318
5362
|
lastIter = {
|
|
5319
5363
|
fontSize,
|
|
5320
5364
|
renderedLineCount,
|
|
5321
5365
|
explicitLineCount,
|
|
5322
5366
|
textHeight,
|
|
5323
|
-
|
|
5367
|
+
...widthMetrics,
|
|
5324
5368
|
fixedWidth,
|
|
5325
|
-
widthDidGrow,
|
|
5326
5369
|
hasNoImplicitWrap,
|
|
5327
5370
|
fitsHeight,
|
|
5328
5371
|
fitsWidth
|
|
@@ -8873,11 +8916,7 @@ const PageCanvas = react.forwardRef(
|
|
|
8873
8916
|
const renderedLineCount = ((_b = testTextbox.textLines) == null ? void 0 : _b.length) || 1;
|
|
8874
8917
|
const hasNoImplicitWrap = renderedLineCount <= explicitLineCount;
|
|
8875
8918
|
const fitsHeight = rH <= 0 || textHeight <= rH;
|
|
8876
|
-
const
|
|
8877
|
-
const widthDidGrow = actualTextboxWidth > fixedWidth + 0.5;
|
|
8878
|
-
const lineWidths = testTextbox.__lineWidths;
|
|
8879
|
-
const maxLineWidth = lineWidths && lineWidths.length > 0 ? Math.max(...lineWidths) : 0;
|
|
8880
|
-
const fitsWidth = !widthDidGrow && maxLineWidth <= fixedWidth + 1;
|
|
8919
|
+
const { fitsWidth } = getTextboxWidthFitMetrics(testTextbox, fixedWidth);
|
|
8881
8920
|
if (hasNoImplicitWrap && fitsHeight && fitsWidth) {
|
|
8882
8921
|
break;
|
|
8883
8922
|
}
|
|
@@ -12263,6 +12302,46 @@ async function ensureFontsForResolvedConfig(config) {
|
|
|
12263
12302
|
});
|
|
12264
12303
|
}
|
|
12265
12304
|
}
|
|
12305
|
+
function configHasAutoShrinkText$1(config) {
|
|
12306
|
+
var _a;
|
|
12307
|
+
if (!((_a = config == null ? void 0 : config.pages) == null ? void 0 : _a.length)) return false;
|
|
12308
|
+
const walk = (nodes) => {
|
|
12309
|
+
for (const node of nodes || []) {
|
|
12310
|
+
if (!node) continue;
|
|
12311
|
+
if (node.type === "text" && node.overflowPolicy === "auto-shrink") return true;
|
|
12312
|
+
if (Array.isArray(node.children) && node.children.length && walk(node.children)) return true;
|
|
12313
|
+
}
|
|
12314
|
+
return false;
|
|
12315
|
+
};
|
|
12316
|
+
for (const page of config.pages) {
|
|
12317
|
+
if (walk(page.children || [])) return true;
|
|
12318
|
+
}
|
|
12319
|
+
return false;
|
|
12320
|
+
}
|
|
12321
|
+
async function awaitFontsForConfig(config, maxWaitMs) {
|
|
12322
|
+
if (typeof document === "undefined" || !document.fonts) return;
|
|
12323
|
+
void ensureFontsForResolvedConfig(config);
|
|
12324
|
+
const descriptors = collectFontDescriptorsFromConfig(config);
|
|
12325
|
+
if (descriptors.length === 0) return;
|
|
12326
|
+
const loads = Promise.all(
|
|
12327
|
+
descriptors.map((d) => {
|
|
12328
|
+
const stylePrefix = d.style === "italic" ? "italic " : "";
|
|
12329
|
+
const spec = `${stylePrefix}${d.weight} 16px "${d.family}"`;
|
|
12330
|
+
return document.fonts.load(spec).catch(() => []);
|
|
12331
|
+
})
|
|
12332
|
+
).then(() => void 0);
|
|
12333
|
+
await Promise.race([
|
|
12334
|
+
loads,
|
|
12335
|
+
new Promise((resolve) => setTimeout(resolve, maxWaitMs))
|
|
12336
|
+
]);
|
|
12337
|
+
await Promise.race([
|
|
12338
|
+
document.fonts.ready.catch(() => void 0).then(() => void 0),
|
|
12339
|
+
new Promise((r) => setTimeout(r, Math.min(500, maxWaitMs)))
|
|
12340
|
+
]);
|
|
12341
|
+
await new Promise(
|
|
12342
|
+
(resolve) => requestAnimationFrame(() => requestAnimationFrame(() => resolve()))
|
|
12343
|
+
);
|
|
12344
|
+
}
|
|
12266
12345
|
const PREVIEW_DEBUG_PREFIX = "[canvas-renderer][preview-debug]";
|
|
12267
12346
|
function countUnderlinedNodes(config) {
|
|
12268
12347
|
var _a;
|
|
@@ -12348,15 +12427,17 @@ function PixldocsPreview(props) {
|
|
|
12348
12427
|
underlinedNodes: countUnderlinedNodes(resolved.config)
|
|
12349
12428
|
});
|
|
12350
12429
|
setResolvedConfig(resolved.config);
|
|
12351
|
-
|
|
12430
|
+
const hasAutoShrink = configHasAutoShrinkText$1(resolved.config);
|
|
12431
|
+
const waitMs = hasAutoShrink ? 4e3 : 1800;
|
|
12432
|
+
awaitFontsForConfig(resolved.config, waitMs).then(() => {
|
|
12352
12433
|
if (!cancelled) {
|
|
12353
|
-
console.log(PREVIEW_DEBUG_PREFIX, "resolve-mode fonts
|
|
12434
|
+
console.log(PREVIEW_DEBUG_PREFIX, "resolve-mode fonts settled", { hasAutoShrink, waitMs });
|
|
12354
12435
|
setFontsReady(true);
|
|
12355
12436
|
setIsLoading(false);
|
|
12356
12437
|
}
|
|
12357
12438
|
}).catch((err) => {
|
|
12358
12439
|
if (!cancelled) {
|
|
12359
|
-
console.warn(PREVIEW_DEBUG_PREFIX, "resolve-mode
|
|
12440
|
+
console.warn(PREVIEW_DEBUG_PREFIX, "resolve-mode font wait failed", err);
|
|
12360
12441
|
setFontsReady(true);
|
|
12361
12442
|
setIsLoading(false);
|
|
12362
12443
|
}
|
|
@@ -12425,16 +12506,26 @@ function PixldocsPreview(props) {
|
|
|
12425
12506
|
setFontsReady(false);
|
|
12426
12507
|
setCanvasSettled(false);
|
|
12427
12508
|
setStabilizationPass(0);
|
|
12428
|
-
|
|
12429
|
-
|
|
12509
|
+
let cancelled = false;
|
|
12510
|
+
const hasAutoShrink = configHasAutoShrinkText$1(config);
|
|
12511
|
+
const waitMs = hasAutoShrink ? 4e3 : 1800;
|
|
12512
|
+
awaitFontsForConfig(config, waitMs).then(() => {
|
|
12513
|
+
if (cancelled) return;
|
|
12514
|
+
console.log(PREVIEW_DEBUG_PREFIX, "config-mode fonts settled", {
|
|
12430
12515
|
pageIndex,
|
|
12516
|
+
hasAutoShrink,
|
|
12517
|
+
waitMs,
|
|
12431
12518
|
underlinedNodes: countUnderlinedNodes(config)
|
|
12432
12519
|
});
|
|
12433
12520
|
setFontsReady(true);
|
|
12434
12521
|
}).catch((err) => {
|
|
12435
|
-
|
|
12522
|
+
if (cancelled) return;
|
|
12523
|
+
console.warn(PREVIEW_DEBUG_PREFIX, "config-mode font wait failed", err);
|
|
12436
12524
|
setFontsReady(true);
|
|
12437
12525
|
});
|
|
12526
|
+
return () => {
|
|
12527
|
+
cancelled = true;
|
|
12528
|
+
};
|
|
12438
12529
|
}, [isResolveMode, config]);
|
|
12439
12530
|
const handleCanvasReady = react.useCallback(() => {
|
|
12440
12531
|
if (stabilizationPass === 0) {
|
|
@@ -12471,7 +12562,7 @@ function PixldocsPreview(props) {
|
|
|
12471
12562
|
!canvasSettled && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "absolute", inset: 0, display: "flex", alignItems: "center", justifyContent: "center", minHeight: 200 }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "#888", fontSize: 14 }, children: "Loading preview..." }) })
|
|
12472
12563
|
] });
|
|
12473
12564
|
}
|
|
12474
|
-
const PACKAGE_VERSION = "0.5.
|
|
12565
|
+
const PACKAGE_VERSION = "0.5.59";
|
|
12475
12566
|
let __underlineFixInstalled = false;
|
|
12476
12567
|
function installUnderlineFix(fab) {
|
|
12477
12568
|
var _a;
|
|
@@ -15150,8 +15241,8 @@ async function convertTextDecorationsToLines(svg) {
|
|
|
15150
15241
|
let ctx = null;
|
|
15151
15242
|
try {
|
|
15152
15243
|
const realDoc = typeof document !== "undefined" ? document : doc;
|
|
15153
|
-
const
|
|
15154
|
-
ctx =
|
|
15244
|
+
const measureCanvas2 = realDoc.createElement("canvas");
|
|
15245
|
+
ctx = measureCanvas2.getContext("2d");
|
|
15155
15246
|
} catch {
|
|
15156
15247
|
}
|
|
15157
15248
|
const textEls = Array.from(svg.querySelectorAll("text"));
|
|
@@ -15642,9 +15733,11 @@ exports.PixldocsPreview = PixldocsPreview;
|
|
|
15642
15733
|
exports.PixldocsRenderer = PixldocsRenderer;
|
|
15643
15734
|
exports.applyThemeToConfig = applyThemeToConfig;
|
|
15644
15735
|
exports.assemblePdfFromSvgs = assemblePdfFromSvgs;
|
|
15736
|
+
exports.awaitFontsForConfig = awaitFontsForConfig;
|
|
15645
15737
|
exports.collectFontDescriptorsFromConfig = collectFontDescriptorsFromConfig;
|
|
15646
15738
|
exports.collectFontsFromConfig = collectFontsFromConfig;
|
|
15647
15739
|
exports.collectImageUrls = collectImageUrls;
|
|
15740
|
+
exports.configHasAutoShrinkText = configHasAutoShrinkText$1;
|
|
15648
15741
|
exports.embedFont = embedFont;
|
|
15649
15742
|
exports.embedFontsForConfig = embedFontsForConfig;
|
|
15650
15743
|
exports.embedFontsInPdf = embedFontsInPdf;
|