pa_font 0.3.2 → 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/dist/paFont.js CHANGED
@@ -3317,28 +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 finalLayoutBox = finalizeLayoutBox(layoutBox, normalized, textHeight);
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: finalLayoutBox.contentBox.x,
3328
- y: finalLayoutBox.contentBox.y,
3327
+ x: anchoredLayout.layoutBox.contentBox.x,
3328
+ y: anchoredLayout.layoutBox.contentBox.y,
3329
3329
  width: textWidth,
3330
3330
  height: textHeight,
3331
3331
  lineCount: lines.length,
3332
- bbox: textBBox,
3333
- contentBox: { ...finalLayoutBox.contentBox },
3334
- paddingBox: { ...finalLayoutBox.paddingBox },
3335
- marginBox: { ...finalLayoutBox.marginBox },
3336
- clipBox: { ...finalLayoutBox.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 }
3337
3337
  },
3338
3338
  prepared: cachedPrepared,
3339
3339
  preparedWhiteSpace: cachedPreparedWhiteSpace,
3340
3340
  layoutEngine: layoutState.layoutEngine,
3341
- layoutBox: finalLayoutBox,
3341
+ layoutBox: anchoredLayout.layoutBox,
3342
3342
  containerWidth: layoutBox.containerWidth,
3343
3343
  containerHeight: layoutBox.containerHeight
3344
3344
  };
@@ -3354,9 +3354,11 @@ function normalizeParagraphOptions(fontInstance, options = {}) {
3354
3354
  const width = normalizeDimension(options.width);
3355
3355
  const height = normalizeDimension(options.height);
3356
3356
  const gap = normalizeGap(options.gap);
3357
+ const anchor = normalizeAnchor(options.anchor);
3357
3358
  if (options.width != null && width == null) throw new TypeError("font.paragraph() option \"width\" must be a positive number.");
3358
3359
  if (options.height != null && height == null) throw new TypeError("font.paragraph() option \"height\" must be a positive number.");
3359
3360
  if (options.gap != null && gap == null) throw new TypeError("font.paragraph() option \"gap\" must be a non-negative number.");
3361
+ if (options.anchor != null && anchor == null) throw new TypeError("font.paragraph() option \"anchor\" must be a number, [x, y], or { x, y }.");
3360
3362
  const font = resolveCanvasFont(fontInstance, textOptions.size, options);
3361
3363
  return {
3362
3364
  ...textOptions,
@@ -3372,6 +3374,10 @@ function normalizeParagraphOptions(fontInstance, options = {}) {
3372
3374
  width,
3373
3375
  height,
3374
3376
  gap: gap ?? DEFAULT_PARAGRAPH_GAP,
3377
+ anchor: anchor ?? {
3378
+ x: 0,
3379
+ y: 0
3380
+ },
3375
3381
  lineHeight: resolveLineHeight(options.lineHeight, textOptions.size),
3376
3382
  align: normalizeEnum(options.align, [
3377
3383
  "left",
@@ -3812,6 +3818,44 @@ function finalizeLayoutBox(layoutBox, options, textHeight) {
3812
3818
  }
3813
3819
  };
3814
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
+ }
3815
3859
  function resolveContainerDimension(explicit, fallback) {
3816
3860
  if (Number.isFinite(explicit) && explicit > 0) return explicit;
3817
3861
  if (Number.isFinite(fallback) && fallback > 0) return fallback;
@@ -3840,6 +3884,40 @@ function normalizeDimension(value) {
3840
3884
  function normalizeGap(value) {
3841
3885
  return Number.isFinite(value) && value >= 0 ? Number(value) : null;
3842
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
+ }
3843
3921
  function normalizeSpacing(value) {
3844
3922
  if (value == null) return zeroSpacing();
3845
3923
  if (Number.isFinite(value)) {