pa_font 0.3.5 → 0.3.7

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 CHANGED
@@ -39,6 +39,26 @@ const regions = shape.toRegions();
39
39
  const points = shape.toPoints({ step: 8 });
40
40
  ```
41
41
 
42
+ 문자열 길이에 따라 비슷한 최종 폭으로 맞추고 싶다면 `size`에 객체를 넣을 수 있습니다.
43
+
44
+ ```js
45
+ const short = font.text("펑", {
46
+ x: canvasWidth * 0.5,
47
+ y: canvasHeight * 0.5,
48
+ anchor: 0.5,
49
+ size: { width: canvasWidth * 0.7, basis: "bbox" },
50
+ });
51
+
52
+ const long = font.text("왕밤빵", {
53
+ x: canvasWidth * 0.5,
54
+ y: canvasHeight * 0.5,
55
+ anchor: 0.5,
56
+ size: { width: canvasWidth * 0.7, basis: "bbox", min: 48, max: 320 },
57
+ });
58
+
59
+ console.log(short.metrics.size, long.metrics.size);
60
+ ```
61
+
42
62
  ### 2. 문단을 canvas에 그리고, 필요하면 geometry로 바꾸기
43
63
 
44
64
  ```js
@@ -84,11 +104,18 @@ const regions = paragraph.toRegions({ step: 6, openWidth: 1 });
84
104
 
85
105
  - `x`, `y`: 기본값은 baseline 시작 위치
86
106
  - `anchor`: 주면 `x`, `y`를 실제 글자 bbox 기준점으로 사용
87
- - `size`: 글자 크기
107
+ - `size`: 글자 크기. `number` 또는 `{ width, basis?, min?, max? }`
88
108
  - `flatten`: 곡선 평탄화 허용 오차
89
109
  - `kerning`, `letterSpacing`, `tracking`
90
110
  - `script`, `language`, `features`
91
111
 
112
+ `size` 객체를 주면 내부에서 최종 숫자 size를 계산한 뒤 기존 텍스트 생성 로직으로 이어집니다.
113
+
114
+ - `width`: 목표 폭
115
+ - `basis`: `"bbox"` 또는 `"advance"`, 기본값은 `"bbox"`
116
+ - `min`: 최소 size, 기본값은 `0`
117
+ - `max`: 최대 size, 기본값은 `Infinity`
118
+
92
119
  ### `font.glyph(value, options?)`
93
120
 
94
121
  한 글자만 `PAShape`로 만듭니다.
@@ -100,7 +127,15 @@ const regions = paragraph.toRegions({ step: 6, openWidth: 1 });
100
127
 
101
128
  ```js
102
129
  const metrics = font.metrics("안녕", { size: 120 });
103
- console.log(metrics.width, metrics.bbox);
130
+ console.log(metrics.size, metrics.width, metrics.bbox);
131
+ ```
132
+
133
+ ```js
134
+ const metrics = font.metrics("왕밤빵", {
135
+ size: { width: 600, basis: "advance", min: 80, max: 240 },
136
+ });
137
+
138
+ console.log(metrics.size, metrics.width, metrics.bbox);
104
139
  ```
105
140
 
106
141
  ### `font.paragraph(value, options?)`
@@ -115,6 +150,8 @@ console.log(metrics.width, metrics.bbox);
115
150
  - `height`: `overflow: "hidden"`과 같이 쓰면 clip/clamp
116
151
  - `x`, `y`, `size`, `lineHeight`, `gap`, `anchor`, `align`
117
152
 
153
+ `font.paragraph()`의 `size`는 현재도 숫자만 받습니다.
154
+
118
155
  세부 제어 옵션:
119
156
 
120
157
  - `whiteSpace`: `normal | pre-wrap | nowrap`
package/dist/paFont.cjs CHANGED
@@ -276,6 +276,8 @@ function mod(value, divisor) {
276
276
  }
277
277
  //#endregion
278
278
  //#region src/paFont/core.js
279
+ var DEFAULT_TEXT_SIZE = 72;
280
+ var DEFAULT_TEXT_SIZE_BASIS = "bbox";
279
281
  function layoutGlyphs(font, value, opts) {
280
282
  const glyphs = [];
281
283
  const renderOptions = toRenderOptions(opts);
@@ -325,12 +327,14 @@ function measureText(font, value, opts) {
325
327
  const renderOptions = toRenderOptions(opts);
326
328
  const bbox = resolveMeasuredBBox(font.getPath(value, opts.x, opts.y, opts.size, renderOptions).getBoundingBox(), opts.x, opts.y);
327
329
  if (opts.anchor == null) return {
330
+ size: opts.size,
328
331
  width: measureAdvanceWidth(font, value, opts),
329
332
  bbox
330
333
  };
331
334
  const tx = opts.x - (bbox.x + bbox.w * opts.anchor.x);
332
335
  const ty = opts.y - (bbox.y + bbox.h * opts.anchor.y);
333
336
  return {
337
+ size: opts.size,
334
338
  width: measureAdvanceWidth(font, value, opts),
335
339
  bbox: translateRect(bbox, tx, ty)
336
340
  };
@@ -548,8 +552,30 @@ function translateGlyphGeometry(geometry, tx, ty, glyphPosition) {
548
552
  bbox: translateRect(geometry.bbox, tx, ty)
549
553
  };
550
554
  }
555
+ function resolveTextOptions(font, value, options = {}, config = {}) {
556
+ const sourceOptions = options ?? {};
557
+ return normalizeTextOptions({
558
+ ...sourceOptions,
559
+ size: resolveTextSize(font, value, sourceOptions, config)
560
+ });
561
+ }
562
+ function resolveTextSize(font, value, options = {}, config = {}) {
563
+ const sourceOptions = options ?? {};
564
+ const sizeOption = sourceOptions.size;
565
+ if (!isTextSizeConstraint(sizeOption)) return normalizePositive(sizeOption, DEFAULT_TEXT_SIZE);
566
+ const fit = normalizeTextSizeConstraint(sizeOption, config.methodName);
567
+ const referenceSize = DEFAULT_TEXT_SIZE;
568
+ const measurement = measureText(font, String(value ?? ""), normalizeTextOptions({
569
+ ...sourceOptions,
570
+ anchor: null,
571
+ size: referenceSize
572
+ }));
573
+ const measuredWidth = fit.basis === "advance" ? measurement.width : measurement.bbox.w;
574
+ if (!Number.isFinite(measuredWidth) || measuredWidth <= 0) return clampTextSize(referenceSize, fit.min, fit.max);
575
+ return clampTextSize(referenceSize * fit.width / measuredWidth, fit.min, fit.max);
576
+ }
551
577
  function normalizeTextOptions(options = {}) {
552
- const size = normalizePositive(options.size, 72);
578
+ const size = normalizePositive(options.size, DEFAULT_TEXT_SIZE);
553
579
  const anchor = normalizeAnchor(options.anchor);
554
580
  return {
555
581
  x: normalizeNumber(options.x, 0),
@@ -617,6 +643,28 @@ function resolveMeasuredBBox(box, fallbackX, fallbackY) {
617
643
  function defaultEdgeEpsilon(size) {
618
644
  return Math.min(1, Math.max(.01, size * .0025));
619
645
  }
646
+ function isTextSizeConstraint(value) {
647
+ return value != null && typeof value === "object" && !Array.isArray(value);
648
+ }
649
+ function normalizeTextSizeConstraint(value, methodName) {
650
+ const width = normalizeNonNegativeNumber(value.width, NaN);
651
+ if (!Number.isFinite(width)) throw new TypeError(`${methodName ?? "Text"} option "size.width" must be a non-negative number.`);
652
+ const min = normalizeNonNegativeNumber(value.min, 0);
653
+ const maxValue = value.max === Infinity ? Infinity : normalizeNonNegativeNumber(value.max, Number.POSITIVE_INFINITY);
654
+ const max = Number.isFinite(maxValue) ? Math.max(min, maxValue) : Infinity;
655
+ return {
656
+ width,
657
+ basis: value.basis === "advance" ? "advance" : DEFAULT_TEXT_SIZE_BASIS,
658
+ min,
659
+ max
660
+ };
661
+ }
662
+ function normalizeNonNegativeNumber(value, fallback) {
663
+ return Number.isFinite(value) && value >= 0 ? Number(value) : fallback;
664
+ }
665
+ function clampTextSize(value, min, max) {
666
+ return Math.min(max, Math.max(min, normalizeNonNegativeNumber(value, min)));
667
+ }
620
668
  function toRenderOptions(opts) {
621
669
  const renderOptions = { kerning: opts.kerning };
622
670
  if (opts.letterSpacing != null) renderOptions.letterSpacing = opts.letterSpacing;
@@ -4597,14 +4645,17 @@ var paFont = class paFont {
4597
4645
  return new paFont(await (0, opentype_js.load)(target, void 0, loadOptions));
4598
4646
  }
4599
4647
  text(value, options = {}) {
4600
- const opts = normalizeTextOptions(options);
4601
- assertValidAnchorOption("font.text()", options, opts.anchor);
4602
- return createTextShape(this._layoutText(String(value ?? ""), opts), opts, this);
4648
+ const text = String(value ?? "");
4649
+ const sourceOptions = options ?? {};
4650
+ const opts = resolveTextOptions(this.font, text, sourceOptions, { methodName: "font.text()" });
4651
+ assertValidAnchorOption("font.text()", sourceOptions, opts.anchor);
4652
+ return createTextShape(this._layoutText(text, opts), opts, this);
4603
4653
  }
4604
4654
  glyph(value, options = {}) {
4605
- const opts = normalizeTextOptions(options);
4606
- assertValidAnchorOption("font.glyph()", options, opts.anchor);
4607
4655
  const text = Array.from(String(value ?? ""))[0] ?? "";
4656
+ const sourceOptions = options ?? {};
4657
+ const opts = resolveTextOptions(this.font, text, sourceOptions, { methodName: "font.glyph()" });
4658
+ assertValidAnchorOption("font.glyph()", sourceOptions, opts.anchor);
4608
4659
  const glyph = this.font.charToGlyph(text);
4609
4660
  const layout = {
4610
4661
  text,
@@ -4625,9 +4676,11 @@ var paFont = class paFont {
4625
4676
  return createTextShape(anchorSingleGlyphLayout(this.font, text, layout, opts), opts, this);
4626
4677
  }
4627
4678
  metrics(value, options = {}) {
4628
- const opts = normalizeTextOptions(options);
4629
- assertValidAnchorOption("font.metrics()", options, opts.anchor);
4630
- return measureText(this.font, String(value ?? ""), opts);
4679
+ const text = String(value ?? "");
4680
+ const sourceOptions = options ?? {};
4681
+ const opts = resolveTextOptions(this.font, text, sourceOptions, { methodName: "font.metrics()" });
4682
+ assertValidAnchorOption("font.metrics()", sourceOptions, opts.anchor);
4683
+ return measureText(this.font, text, opts);
4631
4684
  }
4632
4685
  paragraph(value, options = {}) {
4633
4686
  return createParagraph(this, String(value ?? ""), options);