pa_font 0.3.1 → 0.3.3
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/USAGE.md +21 -25
- package/dist/paFont.cjs +92 -44
- package/dist/paFont.cjs.map +1 -1
- package/dist/paFont.js +92 -44
- package/dist/paFont.js.map +1 -1
- package/paFont.d.ts +8 -4
- package/package.json +1 -1
package/dist/paFont.js
CHANGED
|
@@ -3317,30 +3317,28 @@ function layoutParagraph(fontInstance, text, options = {}, state = {}) {
|
|
|
3317
3317
|
const textBBox = combineRects(lines.map((line) => line.bbox)) ?? emptyRect();
|
|
3318
3318
|
const textWidth = lines.reduce((max, line) => Math.max(max, line.width), 0);
|
|
3319
3319
|
const textHeight = resolvePositionedTextHeight(lines, layoutBox.contentY);
|
|
3320
|
-
const
|
|
3320
|
+
const anchoredLayout = applyParagraphAnchor(lines, textBBox, finalizeLayoutBox(layoutBox, normalized, textHeight), normalized.anchor);
|
|
3321
3321
|
const cachedPrepared = pretextState?.prepared ?? retainedPreparedState.prepared ?? null;
|
|
3322
3322
|
const cachedPreparedWhiteSpace = pretextState?.preparedWhiteSpace ?? retainedPreparedState.preparedWhiteSpace ?? null;
|
|
3323
3323
|
return {
|
|
3324
3324
|
options: normalized,
|
|
3325
|
-
lines,
|
|
3325
|
+
lines: anchoredLayout.lines,
|
|
3326
3326
|
metrics: {
|
|
3327
|
-
x:
|
|
3328
|
-
y:
|
|
3327
|
+
x: anchoredLayout.layoutBox.contentBox.x,
|
|
3328
|
+
y: anchoredLayout.layoutBox.contentBox.y,
|
|
3329
3329
|
width: textWidth,
|
|
3330
3330
|
height: textHeight,
|
|
3331
|
-
viewportHeight: finalLayoutBox.contentBox.h,
|
|
3332
|
-
maxScrollTop: Math.max(0, textHeight - finalLayoutBox.contentBox.h),
|
|
3333
3331
|
lineCount: lines.length,
|
|
3334
|
-
bbox: textBBox,
|
|
3335
|
-
contentBox: { ...
|
|
3336
|
-
paddingBox: { ...
|
|
3337
|
-
marginBox: { ...
|
|
3338
|
-
clipBox: { ...
|
|
3332
|
+
bbox: anchoredLayout.textBBox,
|
|
3333
|
+
contentBox: { ...anchoredLayout.layoutBox.contentBox },
|
|
3334
|
+
paddingBox: { ...anchoredLayout.layoutBox.paddingBox },
|
|
3335
|
+
marginBox: { ...anchoredLayout.layoutBox.marginBox },
|
|
3336
|
+
clipBox: { ...anchoredLayout.layoutBox.clipBox }
|
|
3339
3337
|
},
|
|
3340
3338
|
prepared: cachedPrepared,
|
|
3341
3339
|
preparedWhiteSpace: cachedPreparedWhiteSpace,
|
|
3342
3340
|
layoutEngine: layoutState.layoutEngine,
|
|
3343
|
-
layoutBox:
|
|
3341
|
+
layoutBox: anchoredLayout.layoutBox,
|
|
3344
3342
|
containerWidth: layoutBox.containerWidth,
|
|
3345
3343
|
containerHeight: layoutBox.containerHeight
|
|
3346
3344
|
};
|
|
@@ -3356,12 +3354,11 @@ function normalizeParagraphOptions(fontInstance, options = {}) {
|
|
|
3356
3354
|
const width = normalizeDimension(options.width);
|
|
3357
3355
|
const height = normalizeDimension(options.height);
|
|
3358
3356
|
const gap = normalizeGap(options.gap);
|
|
3359
|
-
const
|
|
3360
|
-
const overflowY = resolveOverflowY(options.overflowY, overflow);
|
|
3357
|
+
const anchor = normalizeAnchor(options.anchor);
|
|
3361
3358
|
if (options.width != null && width == null) throw new TypeError("font.paragraph() option \"width\" must be a positive number.");
|
|
3362
3359
|
if (options.height != null && height == null) throw new TypeError("font.paragraph() option \"height\" must be a positive number.");
|
|
3363
3360
|
if (options.gap != null && gap == null) throw new TypeError("font.paragraph() option \"gap\" must be a non-negative number.");
|
|
3364
|
-
if (
|
|
3361
|
+
if (options.anchor != null && anchor == null) throw new TypeError("font.paragraph() option \"anchor\" must be a number, [x, y], or { x, y }.");
|
|
3365
3362
|
const font = resolveCanvasFont(fontInstance, textOptions.size, options);
|
|
3366
3363
|
return {
|
|
3367
3364
|
...textOptions,
|
|
@@ -3377,6 +3374,10 @@ function normalizeParagraphOptions(fontInstance, options = {}) {
|
|
|
3377
3374
|
width,
|
|
3378
3375
|
height,
|
|
3379
3376
|
gap: gap ?? DEFAULT_PARAGRAPH_GAP,
|
|
3377
|
+
anchor: anchor ?? {
|
|
3378
|
+
x: 0,
|
|
3379
|
+
y: 0
|
|
3380
|
+
},
|
|
3380
3381
|
lineHeight: resolveLineHeight(options.lineHeight, textOptions.size),
|
|
3381
3382
|
align: normalizeEnum(options.align, [
|
|
3382
3383
|
"left",
|
|
@@ -3399,8 +3400,7 @@ function normalizeParagraphOptions(fontInstance, options = {}) {
|
|
|
3399
3400
|
"break-all",
|
|
3400
3401
|
"keep-all"
|
|
3401
3402
|
], wrapDefaults.wordBreak),
|
|
3402
|
-
overflow,
|
|
3403
|
-
overflowY,
|
|
3403
|
+
overflow: normalizeEnum(options.overflow, ["visible", "hidden"], "visible"),
|
|
3404
3404
|
textOverflow: normalizeNullableEnum(options.textOverflow, ["clip", "ellipsis"], null),
|
|
3405
3405
|
maxLines: normalizeMaxLines(options.maxLines),
|
|
3406
3406
|
ellipsis: normalizeEllipsis(options.ellipsis),
|
|
@@ -3589,7 +3589,7 @@ function applyOverflowClamping(lines, options, layoutBox, measureWidth) {
|
|
|
3589
3589
|
const contentWidth = layoutBox.contentWidth;
|
|
3590
3590
|
const result = lines.map((line) => ({ ...line }));
|
|
3591
3591
|
let lineLimit = options.maxLines;
|
|
3592
|
-
if (
|
|
3592
|
+
if (options.overflow === "hidden" && layoutBox.clipContentHeight != null) {
|
|
3593
3593
|
const visibleLineCount = countVisibleLinesForHeight(result, options, layoutBox.clipContentHeight);
|
|
3594
3594
|
lineLimit = lineLimit == null ? visibleLineCount : Math.min(lineLimit, visibleLineCount);
|
|
3595
3595
|
}
|
|
@@ -3814,10 +3814,48 @@ function finalizeLayoutBox(layoutBox, options, textHeight) {
|
|
|
3814
3814
|
x: layoutBox.contentX,
|
|
3815
3815
|
y: layoutBox.contentY,
|
|
3816
3816
|
w: layoutBox.contentWidth,
|
|
3817
|
-
h:
|
|
3817
|
+
h: options.overflow === "hidden" ? contentHeight : textHeight
|
|
3818
3818
|
}
|
|
3819
3819
|
};
|
|
3820
3820
|
}
|
|
3821
|
+
function applyParagraphAnchor(lines, textBBox, layoutBox, anchor) {
|
|
3822
|
+
if (anchor == null || Math.abs(anchor.x) <= JUSTIFY_EPSILON && Math.abs(anchor.y) <= JUSTIFY_EPSILON) return {
|
|
3823
|
+
lines,
|
|
3824
|
+
textBBox,
|
|
3825
|
+
layoutBox
|
|
3826
|
+
};
|
|
3827
|
+
const tx = -layoutBox.contentBox.w * anchor.x;
|
|
3828
|
+
const ty = -layoutBox.contentBox.h * anchor.y;
|
|
3829
|
+
return {
|
|
3830
|
+
lines: translatePositionedLines(lines, tx, ty),
|
|
3831
|
+
textBBox: translateRect(textBBox, tx, ty),
|
|
3832
|
+
layoutBox: translateLayoutBox(layoutBox, tx, ty)
|
|
3833
|
+
};
|
|
3834
|
+
}
|
|
3835
|
+
function translatePositionedLines(lines, tx, ty) {
|
|
3836
|
+
return lines.map((line) => ({
|
|
3837
|
+
...line,
|
|
3838
|
+
x: line.x + tx,
|
|
3839
|
+
y: line.y + ty,
|
|
3840
|
+
baseline: line.baseline + ty,
|
|
3841
|
+
bbox: translateRect(line.bbox, tx, ty),
|
|
3842
|
+
fragments: line.fragments.map((fragment) => ({
|
|
3843
|
+
...fragment,
|
|
3844
|
+
x: fragment.x + tx
|
|
3845
|
+
}))
|
|
3846
|
+
}));
|
|
3847
|
+
}
|
|
3848
|
+
function translateLayoutBox(layoutBox, tx, ty) {
|
|
3849
|
+
return {
|
|
3850
|
+
...layoutBox,
|
|
3851
|
+
contentX: layoutBox.contentX + tx,
|
|
3852
|
+
contentY: layoutBox.contentY + ty,
|
|
3853
|
+
contentBox: translateRect(layoutBox.contentBox, tx, ty),
|
|
3854
|
+
paddingBox: translateRect(layoutBox.paddingBox, tx, ty),
|
|
3855
|
+
marginBox: translateRect(layoutBox.marginBox, tx, ty),
|
|
3856
|
+
clipBox: translateRect(layoutBox.clipBox, tx, ty)
|
|
3857
|
+
};
|
|
3858
|
+
}
|
|
3821
3859
|
function resolveContainerDimension(explicit, fallback) {
|
|
3822
3860
|
if (Number.isFinite(explicit) && explicit > 0) return explicit;
|
|
3823
3861
|
if (Number.isFinite(fallback) && fallback > 0) return fallback;
|
|
@@ -3830,19 +3868,6 @@ function normalizeNullableEnum(value, supported, fallback) {
|
|
|
3830
3868
|
if (value == null) return fallback;
|
|
3831
3869
|
return typeof value === "string" && supported.includes(value) ? value : fallback;
|
|
3832
3870
|
}
|
|
3833
|
-
function resolveOverflowY(value, overflow) {
|
|
3834
|
-
return normalizeEnum(value, [
|
|
3835
|
-
"visible",
|
|
3836
|
-
"hidden",
|
|
3837
|
-
"scroll"
|
|
3838
|
-
], overflow === "hidden" ? "hidden" : "visible");
|
|
3839
|
-
}
|
|
3840
|
-
function shouldClampLinesToHeight(options) {
|
|
3841
|
-
return options.overflowY === "hidden";
|
|
3842
|
-
}
|
|
3843
|
-
function shouldClipToContentHeight(options) {
|
|
3844
|
-
return options.overflow === "hidden" || options.overflowY === "hidden" || options.overflowY === "scroll";
|
|
3845
|
-
}
|
|
3846
3871
|
function normalizeEllipsis(value) {
|
|
3847
3872
|
if (value === false) return false;
|
|
3848
3873
|
if (typeof value === "string") return value;
|
|
@@ -3859,6 +3884,40 @@ function normalizeDimension(value) {
|
|
|
3859
3884
|
function normalizeGap(value) {
|
|
3860
3885
|
return Number.isFinite(value) && value >= 0 ? Number(value) : null;
|
|
3861
3886
|
}
|
|
3887
|
+
function normalizeAnchor(value) {
|
|
3888
|
+
if (value == null) return null;
|
|
3889
|
+
if (Number.isFinite(value)) {
|
|
3890
|
+
const next = Number(value);
|
|
3891
|
+
return {
|
|
3892
|
+
x: next,
|
|
3893
|
+
y: next
|
|
3894
|
+
};
|
|
3895
|
+
}
|
|
3896
|
+
if (Array.isArray(value)) {
|
|
3897
|
+
if (value.length === 1 && Number.isFinite(value[0])) {
|
|
3898
|
+
const next = Number(value[0]);
|
|
3899
|
+
return {
|
|
3900
|
+
x: next,
|
|
3901
|
+
y: next
|
|
3902
|
+
};
|
|
3903
|
+
}
|
|
3904
|
+
if (value.length >= 2 && Number.isFinite(value[0]) && Number.isFinite(value[1])) return {
|
|
3905
|
+
x: Number(value[0]),
|
|
3906
|
+
y: Number(value[1])
|
|
3907
|
+
};
|
|
3908
|
+
return null;
|
|
3909
|
+
}
|
|
3910
|
+
if (typeof value === "object") {
|
|
3911
|
+
const hasX = Number.isFinite(value.x);
|
|
3912
|
+
const hasY = Number.isFinite(value.y);
|
|
3913
|
+
if (!hasX && !hasY) return null;
|
|
3914
|
+
return {
|
|
3915
|
+
x: hasX ? Number(value.x) : 0,
|
|
3916
|
+
y: hasY ? Number(value.y) : 0
|
|
3917
|
+
};
|
|
3918
|
+
}
|
|
3919
|
+
return null;
|
|
3920
|
+
}
|
|
3862
3921
|
function normalizeSpacing(value) {
|
|
3863
3922
|
if (value == null) return zeroSpacing();
|
|
3864
3923
|
if (Number.isFinite(value)) {
|
|
@@ -4279,7 +4338,6 @@ var paParagraph = class paParagraph {
|
|
|
4279
4338
|
if (!ctx || typeof ctx.fillText !== "function") throw new TypeError("drawText() expects a CanvasRenderingContext2D.");
|
|
4280
4339
|
const fill = options.fill !== false;
|
|
4281
4340
|
const stroke = options.stroke === true;
|
|
4282
|
-
const scrollTop = clampScrollTop(options.scrollTop, this.metrics.maxScrollTop);
|
|
4283
4341
|
if (!fill && !stroke) return;
|
|
4284
4342
|
this._syncLayoutWithContext(ctx);
|
|
4285
4343
|
ctx.save();
|
|
@@ -4288,13 +4346,12 @@ var paParagraph = class paParagraph {
|
|
|
4288
4346
|
ctx.textBaseline = "alphabetic";
|
|
4289
4347
|
if (options.fillStyle != null) ctx.fillStyle = options.fillStyle;
|
|
4290
4348
|
if (options.strokeStyle != null) ctx.strokeStyle = options.strokeStyle;
|
|
4291
|
-
if (
|
|
4349
|
+
if (this.options.overflow === "hidden" && this._state.layoutBox?.clipBox) {
|
|
4292
4350
|
const clipBox = this._state.layoutBox.clipBox;
|
|
4293
4351
|
ctx.beginPath();
|
|
4294
4352
|
ctx.rect(clipBox.x, clipBox.y, clipBox.w, clipBox.h);
|
|
4295
4353
|
ctx.clip();
|
|
4296
4354
|
}
|
|
4297
|
-
if (scrollTop > 0) ctx.translate(0, -scrollTop);
|
|
4298
4355
|
this._state.lines.forEach((line) => {
|
|
4299
4356
|
line.fragments.forEach((fragment) => {
|
|
4300
4357
|
if (fill) ctx.fillText(fragment.text, fragment.x, line.baseline);
|
|
@@ -4345,8 +4402,6 @@ var paParagraph = class paParagraph {
|
|
|
4345
4402
|
this.metrics = {
|
|
4346
4403
|
...state.metrics,
|
|
4347
4404
|
bbox: { ...state.metrics.bbox },
|
|
4348
|
-
viewportHeight: state.metrics.viewportHeight,
|
|
4349
|
-
maxScrollTop: state.metrics.maxScrollTop,
|
|
4350
4405
|
contentBox: state.metrics.contentBox ? { ...state.metrics.contentBox } : void 0,
|
|
4351
4406
|
paddingBox: state.metrics.paddingBox ? { ...state.metrics.paddingBox } : void 0,
|
|
4352
4407
|
marginBox: state.metrics.marginBox ? { ...state.metrics.marginBox } : void 0,
|
|
@@ -4441,13 +4496,6 @@ function normalizeParagraphPointOptions(options = {}) {
|
|
|
4441
4496
|
layout: options.layout ?? "current"
|
|
4442
4497
|
};
|
|
4443
4498
|
}
|
|
4444
|
-
function shouldClipParagraph(options) {
|
|
4445
|
-
return options.overflow === "hidden" || options.overflowY === "hidden" || options.overflowY === "scroll";
|
|
4446
|
-
}
|
|
4447
|
-
function clampScrollTop(value, maxScrollTop) {
|
|
4448
|
-
if (!Number.isFinite(value) || value <= 0) return 0;
|
|
4449
|
-
return Math.min(Number(value), maxScrollTop ?? 0);
|
|
4450
|
-
}
|
|
4451
4499
|
//#endregion
|
|
4452
4500
|
//#region src/paFont/paFont.js
|
|
4453
4501
|
var browserFontRegistrationId = 0;
|