pa_font 0.3.0 → 0.3.1

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/paFont.js CHANGED
@@ -3328,6 +3328,8 @@ function layoutParagraph(fontInstance, text, options = {}, state = {}) {
3328
3328
  y: finalLayoutBox.contentBox.y,
3329
3329
  width: textWidth,
3330
3330
  height: textHeight,
3331
+ viewportHeight: finalLayoutBox.contentBox.h,
3332
+ maxScrollTop: Math.max(0, textHeight - finalLayoutBox.contentBox.h),
3331
3333
  lineCount: lines.length,
3332
3334
  bbox: textBBox,
3333
3335
  contentBox: { ...finalLayoutBox.contentBox },
@@ -3354,9 +3356,12 @@ function normalizeParagraphOptions(fontInstance, options = {}) {
3354
3356
  const width = normalizeDimension(options.width);
3355
3357
  const height = normalizeDimension(options.height);
3356
3358
  const gap = normalizeGap(options.gap);
3359
+ const overflow = normalizeEnum(options.overflow, ["visible", "hidden"], "visible");
3360
+ const overflowY = resolveOverflowY(options.overflowY, overflow);
3357
3361
  if (options.width != null && width == null) throw new TypeError("font.paragraph() option \"width\" must be a positive number.");
3358
3362
  if (options.height != null && height == null) throw new TypeError("font.paragraph() option \"height\" must be a positive number.");
3359
3363
  if (options.gap != null && gap == null) throw new TypeError("font.paragraph() option \"gap\" must be a non-negative number.");
3364
+ if (overflowY === "scroll" && height == null) throw new TypeError("font.paragraph() option \"height\" is required when \"overflowY\" is \"scroll\".");
3360
3365
  const font = resolveCanvasFont(fontInstance, textOptions.size, options);
3361
3366
  return {
3362
3367
  ...textOptions,
@@ -3394,7 +3399,8 @@ function normalizeParagraphOptions(fontInstance, options = {}) {
3394
3399
  "break-all",
3395
3400
  "keep-all"
3396
3401
  ], wrapDefaults.wordBreak),
3397
- overflow: normalizeEnum(options.overflow, ["visible", "hidden"], "visible"),
3402
+ overflow,
3403
+ overflowY,
3398
3404
  textOverflow: normalizeNullableEnum(options.textOverflow, ["clip", "ellipsis"], null),
3399
3405
  maxLines: normalizeMaxLines(options.maxLines),
3400
3406
  ellipsis: normalizeEllipsis(options.ellipsis),
@@ -3583,7 +3589,7 @@ function applyOverflowClamping(lines, options, layoutBox, measureWidth) {
3583
3589
  const contentWidth = layoutBox.contentWidth;
3584
3590
  const result = lines.map((line) => ({ ...line }));
3585
3591
  let lineLimit = options.maxLines;
3586
- if (options.overflow === "hidden" && layoutBox.clipContentHeight != null) {
3592
+ if (shouldClampLinesToHeight(options) && layoutBox.clipContentHeight != null) {
3587
3593
  const visibleLineCount = countVisibleLinesForHeight(result, options, layoutBox.clipContentHeight);
3588
3594
  lineLimit = lineLimit == null ? visibleLineCount : Math.min(lineLimit, visibleLineCount);
3589
3595
  }
@@ -3808,7 +3814,7 @@ function finalizeLayoutBox(layoutBox, options, textHeight) {
3808
3814
  x: layoutBox.contentX,
3809
3815
  y: layoutBox.contentY,
3810
3816
  w: layoutBox.contentWidth,
3811
- h: options.overflow === "hidden" ? contentHeight : textHeight
3817
+ h: shouldClipToContentHeight(options) ? contentHeight : textHeight
3812
3818
  }
3813
3819
  };
3814
3820
  }
@@ -3824,6 +3830,19 @@ function normalizeNullableEnum(value, supported, fallback) {
3824
3830
  if (value == null) return fallback;
3825
3831
  return typeof value === "string" && supported.includes(value) ? value : fallback;
3826
3832
  }
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
+ }
3827
3846
  function normalizeEllipsis(value) {
3828
3847
  if (value === false) return false;
3829
3848
  if (typeof value === "string") return value;
@@ -4260,6 +4279,7 @@ var paParagraph = class paParagraph {
4260
4279
  if (!ctx || typeof ctx.fillText !== "function") throw new TypeError("drawText() expects a CanvasRenderingContext2D.");
4261
4280
  const fill = options.fill !== false;
4262
4281
  const stroke = options.stroke === true;
4282
+ const scrollTop = clampScrollTop(options.scrollTop, this.metrics.maxScrollTop);
4263
4283
  if (!fill && !stroke) return;
4264
4284
  this._syncLayoutWithContext(ctx);
4265
4285
  ctx.save();
@@ -4268,12 +4288,13 @@ var paParagraph = class paParagraph {
4268
4288
  ctx.textBaseline = "alphabetic";
4269
4289
  if (options.fillStyle != null) ctx.fillStyle = options.fillStyle;
4270
4290
  if (options.strokeStyle != null) ctx.strokeStyle = options.strokeStyle;
4271
- if (this.options.overflow === "hidden" && this._state.layoutBox?.clipBox) {
4291
+ if (shouldClipParagraph(this.options) && this._state.layoutBox?.clipBox) {
4272
4292
  const clipBox = this._state.layoutBox.clipBox;
4273
4293
  ctx.beginPath();
4274
4294
  ctx.rect(clipBox.x, clipBox.y, clipBox.w, clipBox.h);
4275
4295
  ctx.clip();
4276
4296
  }
4297
+ if (scrollTop > 0) ctx.translate(0, -scrollTop);
4277
4298
  this._state.lines.forEach((line) => {
4278
4299
  line.fragments.forEach((fragment) => {
4279
4300
  if (fill) ctx.fillText(fragment.text, fragment.x, line.baseline);
@@ -4324,6 +4345,8 @@ var paParagraph = class paParagraph {
4324
4345
  this.metrics = {
4325
4346
  ...state.metrics,
4326
4347
  bbox: { ...state.metrics.bbox },
4348
+ viewportHeight: state.metrics.viewportHeight,
4349
+ maxScrollTop: state.metrics.maxScrollTop,
4327
4350
  contentBox: state.metrics.contentBox ? { ...state.metrics.contentBox } : void 0,
4328
4351
  paddingBox: state.metrics.paddingBox ? { ...state.metrics.paddingBox } : void 0,
4329
4352
  marginBox: state.metrics.marginBox ? { ...state.metrics.marginBox } : void 0,
@@ -4418,6 +4441,13 @@ function normalizeParagraphPointOptions(options = {}) {
4418
4441
  layout: options.layout ?? "current"
4419
4442
  };
4420
4443
  }
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
+ }
4421
4451
  //#endregion
4422
4452
  //#region src/paFont/paFont.js
4423
4453
  var browserFontRegistrationId = 0;