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 +39 -2
- package/dist/paFont.cjs +62 -9
- package/dist/paFont.cjs.map +1 -1
- package/dist/paFont.js +62 -9
- package/dist/paFont.js.map +1 -1
- package/paFont.d.ts +13 -2
- package/package.json +1 -1
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,
|
|
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
|
|
4601
|
-
|
|
4602
|
-
|
|
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
|
|
4629
|
-
|
|
4630
|
-
|
|
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);
|