modern-text 0.2.1 → 0.2.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/index.cjs +496 -469
- package/dist/index.d.cts +114 -65
- package/dist/index.d.mts +114 -65
- package/dist/index.d.ts +114 -65
- package/dist/index.js +2 -2
- package/dist/index.mjs +479 -472
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,18 +1,8 @@
|
|
|
1
|
-
import { BoundingBox, Point2D, parseSvgToDom, parseSvg, Matrix3
|
|
1
|
+
import { BoundingBox, Path2D, Point2D, parseSvgToDom, parseSvg, Matrix3 } from 'modern-path2d';
|
|
2
|
+
export * from 'modern-path2d';
|
|
2
3
|
import { fonts, Woff, Ttf } from 'modern-font';
|
|
3
4
|
export * from 'modern-font';
|
|
4
5
|
|
|
5
|
-
class Feature {
|
|
6
|
-
constructor(_text) {
|
|
7
|
-
this._text = _text;
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
class Deformer extends Feature {
|
|
12
|
-
deform() {
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
6
|
function parseColor(ctx, source, box) {
|
|
17
7
|
if (typeof source === "string" && source.startsWith("linear-gradient")) {
|
|
18
8
|
const { x0, y0, x1, y1, stops } = parseCssLinearGradient(source, box.left, box.top, box.width, box.height);
|
|
@@ -63,388 +53,35 @@ function parseCssLinearGradient(css, x, y, width, height) {
|
|
|
63
53
|
}
|
|
64
54
|
|
|
65
55
|
function drawPaths(options) {
|
|
66
|
-
const { ctx, paths, fontSize } = options;
|
|
67
|
-
paths.forEach((path) => {
|
|
68
|
-
ctx.save();
|
|
69
|
-
ctx.beginPath();
|
|
70
|
-
const style = path.userData?.style ?? {};
|
|
71
|
-
const fillStyle = options.color ?? style.fill ?? "none";
|
|
72
|
-
const strokeStyle = options.textStrokeColor ?? style.stroke ?? "none";
|
|
73
|
-
ctx.fillStyle = fillStyle !== "none" ? fillStyle : "#000";
|
|
74
|
-
ctx.strokeStyle = strokeStyle !== "none" ? strokeStyle : "#000";
|
|
75
|
-
ctx.lineWidth = options.textStrokeWidth ? options.textStrokeWidth * fontSize : style.strokeWidth ? style.strokeWidth * fontSize * 0.03 : 0;
|
|
76
|
-
ctx.lineCap = style.strokeLineCap ?? "round";
|
|
77
|
-
ctx.lineJoin = style.strokeLineJoin ?? "miter";
|
|
78
|
-
ctx.miterLimit = style.strokeMiterLimit ?? 0;
|
|
79
|
-
ctx.shadowOffsetX = (options.shadowOffsetX ?? 0) * fontSize;
|
|
80
|
-
ctx.shadowOffsetY = (options.shadowOffsetY ?? 0) * fontSize;
|
|
81
|
-
ctx.shadowBlur = (options.shadowBlur ?? 0) * fontSize;
|
|
82
|
-
ctx.shadowColor = options.shadowColor ?? "rgba(0, 0, 0, 0)";
|
|
83
|
-
const offsetX = (options.offsetX ?? 0) * fontSize;
|
|
84
|
-
const offsetY = (options.offsetY ?? 0) * fontSize;
|
|
85
|
-
ctx.translate(offsetX, offsetY);
|
|
86
|
-
path.drawTo(ctx);
|
|
87
|
-
if (fillStyle !== "none") {
|
|
88
|
-
ctx.fill();
|
|
89
|
-
}
|
|
90
|
-
if (strokeStyle !== "none") {
|
|
91
|
-
ctx.stroke();
|
|
92
|
-
}
|
|
93
|
-
ctx.restore();
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
class Effector extends Feature {
|
|
98
|
-
getBoundingBox() {
|
|
99
|
-
const { characters, effects } = this._text;
|
|
100
|
-
const boxes = [];
|
|
101
|
-
characters.forEach((character) => {
|
|
102
|
-
const fontSize = character.computedStyle.fontSize;
|
|
103
|
-
effects?.forEach((effect) => {
|
|
104
|
-
const offsetX = (effect.offsetX ?? 0) * fontSize;
|
|
105
|
-
const offsetY = (effect.offsetY ?? 0) * fontSize;
|
|
106
|
-
const shadowOffsetX = (effect.shadowOffsetX ?? 0) * fontSize;
|
|
107
|
-
const shadowOffsetY = (effect.shadowOffsetY ?? 0) * fontSize;
|
|
108
|
-
const textStrokeWidth = Math.max(0.1, effect.textStrokeWidth ?? 0) * fontSize;
|
|
109
|
-
const aabb = character.boundingBox.clone();
|
|
110
|
-
aabb.left += offsetX + shadowOffsetX - textStrokeWidth;
|
|
111
|
-
aabb.top += offsetY + shadowOffsetY - textStrokeWidth;
|
|
112
|
-
aabb.width += textStrokeWidth * 2;
|
|
113
|
-
aabb.height += textStrokeWidth * 2;
|
|
114
|
-
boxes.push(aabb);
|
|
115
|
-
});
|
|
116
|
-
});
|
|
117
|
-
return BoundingBox.from(...boxes);
|
|
118
|
-
}
|
|
119
|
-
draw(options) {
|
|
120
|
-
const { ctx } = options;
|
|
121
|
-
const { effects, characters, boundingBox } = this._text;
|
|
122
|
-
if (effects) {
|
|
123
|
-
effects.forEach((effect) => {
|
|
124
|
-
uploadColor(effect, boundingBox, ctx);
|
|
125
|
-
});
|
|
126
|
-
characters.forEach((character) => {
|
|
127
|
-
effects.forEach((effect) => {
|
|
128
|
-
character.drawTo(ctx, effect);
|
|
129
|
-
});
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
return this;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
var __defProp$4 = Object.defineProperty;
|
|
137
|
-
var __defNormalProp$4 = (obj, key, value) => key in obj ? __defProp$4(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
138
|
-
var __publicField$4 = (obj, key, value) => {
|
|
139
|
-
__defNormalProp$4(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
140
|
-
return value;
|
|
141
|
-
};
|
|
142
|
-
class Highlighter extends Feature {
|
|
143
|
-
constructor() {
|
|
144
|
-
super(...arguments);
|
|
145
|
-
__publicField$4(this, "paths", []);
|
|
146
|
-
}
|
|
147
|
-
getBoundingBox() {
|
|
148
|
-
if (!this.paths.length) {
|
|
149
|
-
return new BoundingBox();
|
|
150
|
-
}
|
|
151
|
-
const min = Point2D.MAX;
|
|
152
|
-
const max = Point2D.MIN;
|
|
153
|
-
this.paths.forEach((path) => path.getMinMax(min, max));
|
|
154
|
-
return new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
|
|
155
|
-
}
|
|
156
|
-
highlight() {
|
|
157
|
-
const { characters, computedStyle: style } = this._text;
|
|
158
|
-
const fontSize = style.fontSize;
|
|
159
|
-
let group;
|
|
160
|
-
const groups = [];
|
|
161
|
-
let prevHighlight;
|
|
162
|
-
characters.forEach((character) => {
|
|
163
|
-
const highlight = character.parent.highlight;
|
|
164
|
-
if (highlight?.url) {
|
|
165
|
-
if (prevHighlight?.url === highlight.url) {
|
|
166
|
-
group.push(character);
|
|
167
|
-
} else {
|
|
168
|
-
group = [];
|
|
169
|
-
group.push(character);
|
|
170
|
-
groups.push(group);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
prevHighlight = highlight;
|
|
174
|
-
});
|
|
175
|
-
this.paths = groups.filter((characters2) => characters2.length).map((characters2) => {
|
|
176
|
-
return {
|
|
177
|
-
url: characters2[0].parent.highlight.url,
|
|
178
|
-
box: BoundingBox.from(...characters2.map((c) => c.glyphBox)),
|
|
179
|
-
baseline: Math.max(...characters2.map((c) => c.baseline))
|
|
180
|
-
};
|
|
181
|
-
}).map((group2) => this._parseGroup(group2, fontSize)).flat();
|
|
182
|
-
}
|
|
183
|
-
_parseSvg(url) {
|
|
184
|
-
const svg = parseSvgToDom(url);
|
|
185
|
-
const paths = parseSvg(svg);
|
|
186
|
-
const min = Point2D.MAX;
|
|
187
|
-
const max = Point2D.MIN;
|
|
188
|
-
paths.forEach((path) => path.getMinMax(min, max));
|
|
189
|
-
const { x, y, width, height } = new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
|
|
190
|
-
const viewBox = svg.getAttribute("viewBox").split(" ").map(Number);
|
|
191
|
-
return {
|
|
192
|
-
paths,
|
|
193
|
-
box: new BoundingBox(x, y, width, height),
|
|
194
|
-
viewBox: new BoundingBox(...viewBox)
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
_parseGroup(group, fontSize) {
|
|
198
|
-
const { url, box: groupBox, baseline } = group;
|
|
199
|
-
const { box, viewBox, paths } = this._parseSvg(url);
|
|
200
|
-
const result = [];
|
|
201
|
-
const type = box.height / viewBox.height > 0.3 ? 0 : 1;
|
|
202
|
-
if (type === 0) {
|
|
203
|
-
const offset = {
|
|
204
|
-
x: groupBox.left - fontSize * 0.2,
|
|
205
|
-
y: groupBox.top
|
|
206
|
-
};
|
|
207
|
-
const scaleX = (groupBox.width + fontSize * 0.2 * 2) / box.width;
|
|
208
|
-
const scaleY = groupBox.height / box.height;
|
|
209
|
-
const m = new Matrix3().translate(-box.x, -box.y).scale(scaleX, scaleY).translate(offset.x, offset.y);
|
|
210
|
-
paths.forEach((original) => {
|
|
211
|
-
result.push(original.clone().transform(m));
|
|
212
|
-
});
|
|
213
|
-
} else if (type === 1) {
|
|
214
|
-
const scale = fontSize / box.width * 2;
|
|
215
|
-
const width = box.width * scale;
|
|
216
|
-
const length = Math.ceil(groupBox.width / width);
|
|
217
|
-
const totalWidth = width * length;
|
|
218
|
-
const offset = {
|
|
219
|
-
x: groupBox.left + (groupBox.width - totalWidth) / 2 + fontSize * 0.1,
|
|
220
|
-
y: groupBox.top + baseline + fontSize * 0.1
|
|
221
|
-
};
|
|
222
|
-
const m = new Matrix3().translate(-box.x, -box.y).scale(scale, scale).translate(offset.x, offset.y);
|
|
223
|
-
for (let i = 0; i < length; i++) {
|
|
224
|
-
const _m = m.clone().translate(i * width, 0);
|
|
225
|
-
paths.forEach((original) => {
|
|
226
|
-
result.push(original.clone().transform(_m));
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
return result;
|
|
231
|
-
}
|
|
232
|
-
draw({ ctx }) {
|
|
233
|
-
drawPaths({
|
|
234
|
-
ctx,
|
|
235
|
-
paths: this.paths,
|
|
236
|
-
fontSize: this._text.computedStyle.fontSize,
|
|
237
|
-
fill: false
|
|
238
|
-
});
|
|
239
|
-
return this;
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
class Measurer extends Feature {
|
|
244
|
-
_styleToDomStyle(style) {
|
|
245
|
-
const _style = { ...style };
|
|
246
|
-
for (const key in style) {
|
|
247
|
-
if ([
|
|
248
|
-
"width",
|
|
249
|
-
"height",
|
|
250
|
-
"fontSize",
|
|
251
|
-
"letterSpacing",
|
|
252
|
-
"textStrokeWidth",
|
|
253
|
-
"shadowOffsetX",
|
|
254
|
-
"shadowOffsetY",
|
|
255
|
-
"shadowBlur"
|
|
256
|
-
].includes(key)) {
|
|
257
|
-
_style[key] = `${style[key]}px`;
|
|
258
|
-
} else {
|
|
259
|
-
_style[key] = style[key];
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
return _style;
|
|
263
|
-
}
|
|
264
|
-
/**
|
|
265
|
-
* <div style="...">
|
|
266
|
-
* <ul>
|
|
267
|
-
* <li style="...">
|
|
268
|
-
* <div>
|
|
269
|
-
* <span style="...">...</span>
|
|
270
|
-
* <span>...</span>
|
|
271
|
-
* </div>
|
|
272
|
-
* </li>
|
|
273
|
-
* </ul>
|
|
274
|
-
* </div>
|
|
275
|
-
*/
|
|
276
|
-
createDom() {
|
|
277
|
-
const { paragraphs, computedStyle: style } = this._text;
|
|
278
|
-
const documentFragment = document.createDocumentFragment();
|
|
279
|
-
const dom = document.createElement("div");
|
|
280
|
-
Object.assign(dom.style, this._styleToDomStyle(style));
|
|
281
|
-
dom.style.position = "absolute";
|
|
282
|
-
dom.style.visibility = "hidden";
|
|
283
|
-
const ul = document.createElement("ul");
|
|
284
|
-
ul.style.listStyle = "none";
|
|
285
|
-
ul.style.padding = "0";
|
|
286
|
-
ul.style.margin = "0";
|
|
287
|
-
paragraphs.forEach((paragraph) => {
|
|
288
|
-
const li = document.createElement("li");
|
|
289
|
-
const div = document.createElement("div");
|
|
290
|
-
Object.assign(li.style, this._styleToDomStyle(paragraph.style));
|
|
291
|
-
paragraph.fragments.forEach((fragment) => {
|
|
292
|
-
const span = document.createElement("span");
|
|
293
|
-
Object.assign(span.style, this._styleToDomStyle(fragment.style));
|
|
294
|
-
span.appendChild(document.createTextNode(fragment.content));
|
|
295
|
-
div.appendChild(span);
|
|
296
|
-
});
|
|
297
|
-
ul.appendChild(li);
|
|
298
|
-
li.appendChild(div);
|
|
299
|
-
});
|
|
300
|
-
dom.appendChild(ul);
|
|
301
|
-
documentFragment.appendChild(dom);
|
|
302
|
-
document.body.appendChild(documentFragment);
|
|
303
|
-
return {
|
|
304
|
-
dom,
|
|
305
|
-
destory: () => dom.parentNode?.removeChild(dom)
|
|
306
|
-
};
|
|
307
|
-
}
|
|
308
|
-
_measureDom(dom) {
|
|
309
|
-
const paragraphs = [];
|
|
310
|
-
const fragments = [];
|
|
311
|
-
const characters = [];
|
|
312
|
-
dom.querySelectorAll("li").forEach((li, paragraphIndex) => {
|
|
313
|
-
const pBox = li.getBoundingClientRect();
|
|
314
|
-
paragraphs.push({
|
|
315
|
-
paragraphIndex,
|
|
316
|
-
left: pBox.left,
|
|
317
|
-
top: pBox.top,
|
|
318
|
-
width: pBox.width,
|
|
319
|
-
height: pBox.height
|
|
320
|
-
});
|
|
321
|
-
li.querySelectorAll("span").forEach((span, fragmentIndex) => {
|
|
322
|
-
const fBox = li.getBoundingClientRect();
|
|
323
|
-
fragments.push({
|
|
324
|
-
paragraphIndex,
|
|
325
|
-
fragmentIndex,
|
|
326
|
-
left: fBox.left,
|
|
327
|
-
top: fBox.top,
|
|
328
|
-
width: fBox.width,
|
|
329
|
-
height: fBox.height
|
|
330
|
-
});
|
|
331
|
-
const text = span.firstChild;
|
|
332
|
-
if (text instanceof window.Text) {
|
|
333
|
-
const range = document.createRange();
|
|
334
|
-
range.selectNodeContents(text);
|
|
335
|
-
const len = text.data ? text.data.length : 0;
|
|
336
|
-
let i = 0;
|
|
337
|
-
for (; i <= len; ) {
|
|
338
|
-
range.setStart(text, Math.max(i - 1, 0));
|
|
339
|
-
range.setEnd(text, i);
|
|
340
|
-
const rects = range.getClientRects?.() ?? [range.getBoundingClientRect()];
|
|
341
|
-
let rect = rects[rects.length - 1];
|
|
342
|
-
if (rects.length > 1 && rect.width < 2) {
|
|
343
|
-
rect = rects[rects.length - 2];
|
|
344
|
-
}
|
|
345
|
-
const content = range.toString();
|
|
346
|
-
if (content !== "" && rect && rect.width + rect.height !== 0) {
|
|
347
|
-
characters.push({
|
|
348
|
-
content,
|
|
349
|
-
newParagraphIndex: -1,
|
|
350
|
-
paragraphIndex,
|
|
351
|
-
fragmentIndex,
|
|
352
|
-
characterIndex: i - 1,
|
|
353
|
-
top: rect.top,
|
|
354
|
-
left: rect.left,
|
|
355
|
-
height: rect.height,
|
|
356
|
-
width: rect.width,
|
|
357
|
-
textWidth: -1,
|
|
358
|
-
textHeight: -1
|
|
359
|
-
});
|
|
360
|
-
}
|
|
361
|
-
i++;
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
});
|
|
365
|
-
});
|
|
366
|
-
return {
|
|
367
|
-
paragraphs,
|
|
368
|
-
fragments,
|
|
369
|
-
characters
|
|
370
|
-
};
|
|
371
|
-
}
|
|
372
|
-
measureDom(dom) {
|
|
373
|
-
const { paragraphs } = this._text;
|
|
374
|
-
const rect = dom.getBoundingClientRect();
|
|
375
|
-
const innerEl = dom.querySelector("ul");
|
|
376
|
-
const isVertical = window.getComputedStyle(dom).writingMode.includes("vertical");
|
|
377
|
-
const oldLineHeight = innerEl.style.lineHeight;
|
|
378
|
-
innerEl.style.lineHeight = "4000px";
|
|
379
|
-
const _paragraphs = [[]];
|
|
380
|
-
let fragments = _paragraphs[0];
|
|
381
|
-
const { characters: oldCharacters } = this._measureDom(dom);
|
|
382
|
-
if (oldCharacters.length > 0) {
|
|
383
|
-
fragments.push(oldCharacters[0]);
|
|
384
|
-
oldCharacters.reduce((prev, current) => {
|
|
385
|
-
const attr = isVertical ? "left" : "top";
|
|
386
|
-
if (Math.abs(current[attr] - prev[attr]) > 4e3 / 2) {
|
|
387
|
-
fragments = [];
|
|
388
|
-
_paragraphs.push(fragments);
|
|
389
|
-
}
|
|
390
|
-
fragments.push(current);
|
|
391
|
-
return current;
|
|
392
|
-
});
|
|
393
|
-
}
|
|
394
|
-
innerEl.style.lineHeight = oldLineHeight;
|
|
395
|
-
const measured = this._measureDom(dom);
|
|
396
|
-
measured.paragraphs.forEach((p) => {
|
|
397
|
-
const _p = paragraphs[p.paragraphIndex];
|
|
398
|
-
_p.boundingBox.left = p.left;
|
|
399
|
-
_p.boundingBox.top = p.top;
|
|
400
|
-
_p.boundingBox.width = p.width;
|
|
401
|
-
_p.boundingBox.height = p.height;
|
|
402
|
-
});
|
|
403
|
-
measured.fragments.forEach((f) => {
|
|
404
|
-
const _f = paragraphs[f.paragraphIndex].fragments[f.fragmentIndex];
|
|
405
|
-
_f.boundingBox.left = f.left;
|
|
406
|
-
_f.boundingBox.top = f.top;
|
|
407
|
-
_f.boundingBox.width = f.width;
|
|
408
|
-
_f.boundingBox.height = f.height;
|
|
409
|
-
});
|
|
410
|
-
const results = [];
|
|
411
|
-
let i = 0;
|
|
412
|
-
_paragraphs.forEach((oldCharacters2) => {
|
|
413
|
-
oldCharacters2.forEach((oldCharacter) => {
|
|
414
|
-
const character = measured.characters[i];
|
|
415
|
-
const { paragraphIndex, fragmentIndex, characterIndex } = character;
|
|
416
|
-
results.push({
|
|
417
|
-
...character,
|
|
418
|
-
newParagraphIndex: paragraphIndex,
|
|
419
|
-
textWidth: oldCharacter.width,
|
|
420
|
-
textHeight: oldCharacter.height,
|
|
421
|
-
left: character.left - rect.left,
|
|
422
|
-
top: character.top - rect.top
|
|
423
|
-
});
|
|
424
|
-
const item = paragraphs[paragraphIndex].fragments[fragmentIndex].characters[characterIndex];
|
|
425
|
-
item.boundingBox.left = results[i].left;
|
|
426
|
-
item.boundingBox.top = results[i].top;
|
|
427
|
-
item.boundingBox.width = results[i].width;
|
|
428
|
-
item.boundingBox.height = results[i].height;
|
|
429
|
-
item.textWidth = results[i].textWidth;
|
|
430
|
-
item.textHeight = results[i].textHeight;
|
|
431
|
-
i++;
|
|
432
|
-
});
|
|
433
|
-
});
|
|
434
|
-
return {
|
|
435
|
-
paragraphs,
|
|
436
|
-
boundingBox: new BoundingBox(0, 0, rect.width, rect.height)
|
|
437
|
-
};
|
|
438
|
-
}
|
|
439
|
-
measure(dom) {
|
|
440
|
-
let destory;
|
|
441
|
-
if (!dom) {
|
|
442
|
-
({ dom, destory } = this.createDom());
|
|
56
|
+
const { ctx, paths, fontSize } = options;
|
|
57
|
+
paths.forEach((path) => {
|
|
58
|
+
ctx.save();
|
|
59
|
+
ctx.beginPath();
|
|
60
|
+
const style = path.userData?.style ?? {};
|
|
61
|
+
const fillStyle = options.color ?? style.fill ?? "none";
|
|
62
|
+
const strokeStyle = options.textStrokeColor ?? style.stroke ?? "none";
|
|
63
|
+
ctx.fillStyle = fillStyle !== "none" ? fillStyle : "#000";
|
|
64
|
+
ctx.strokeStyle = strokeStyle !== "none" ? strokeStyle : "#000";
|
|
65
|
+
ctx.lineWidth = options.textStrokeWidth ? options.textStrokeWidth * fontSize : style.strokeWidth ? style.strokeWidth * fontSize * 0.03 : 0;
|
|
66
|
+
ctx.lineCap = style.strokeLineCap ?? "round";
|
|
67
|
+
ctx.lineJoin = style.strokeLineJoin ?? "miter";
|
|
68
|
+
ctx.miterLimit = style.strokeMiterLimit ?? 0;
|
|
69
|
+
ctx.shadowOffsetX = (options.shadowOffsetX ?? 0) * fontSize;
|
|
70
|
+
ctx.shadowOffsetY = (options.shadowOffsetY ?? 0) * fontSize;
|
|
71
|
+
ctx.shadowBlur = (options.shadowBlur ?? 0) * fontSize;
|
|
72
|
+
ctx.shadowColor = options.shadowColor ?? "rgba(0, 0, 0, 0)";
|
|
73
|
+
const offsetX = (options.offsetX ?? 0) * fontSize;
|
|
74
|
+
const offsetY = (options.offsetY ?? 0) * fontSize;
|
|
75
|
+
ctx.translate(offsetX, offsetY);
|
|
76
|
+
path.drawTo(ctx);
|
|
77
|
+
if (fillStyle !== "none") {
|
|
78
|
+
ctx.fill();
|
|
443
79
|
}
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
80
|
+
if (strokeStyle !== "none") {
|
|
81
|
+
ctx.stroke();
|
|
82
|
+
}
|
|
83
|
+
ctx.restore();
|
|
84
|
+
});
|
|
448
85
|
}
|
|
449
86
|
|
|
450
87
|
function filterEmpty(val) {
|
|
@@ -509,10 +146,10 @@ function getPointPosition(point, startPoint, rotation = 0, skewX = 0, skewY = 0,
|
|
|
509
146
|
return points[0];
|
|
510
147
|
}
|
|
511
148
|
|
|
512
|
-
var __defProp$
|
|
513
|
-
var __defNormalProp$
|
|
514
|
-
var __publicField$
|
|
515
|
-
__defNormalProp$
|
|
149
|
+
var __defProp$4 = Object.defineProperty;
|
|
150
|
+
var __defNormalProp$4 = (obj, key, value) => key in obj ? __defProp$4(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
151
|
+
var __publicField$4 = (obj, key, value) => {
|
|
152
|
+
__defNormalProp$4(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
516
153
|
return value;
|
|
517
154
|
};
|
|
518
155
|
const set1 = /* @__PURE__ */ new Set(["\xA9", "\xAE", "\xF7"]);
|
|
@@ -533,10 +170,10 @@ class Character {
|
|
|
533
170
|
this.content = content;
|
|
534
171
|
this.index = index;
|
|
535
172
|
this.parent = parent;
|
|
536
|
-
__publicField$
|
|
537
|
-
__publicField$
|
|
538
|
-
__publicField$
|
|
539
|
-
__publicField$
|
|
173
|
+
__publicField$4(this, "boundingBox", new BoundingBox());
|
|
174
|
+
__publicField$4(this, "path", new Path2D());
|
|
175
|
+
__publicField$4(this, "textWidth", 0);
|
|
176
|
+
__publicField$4(this, "textHeight", 0);
|
|
540
177
|
}
|
|
541
178
|
get computedStyle() {
|
|
542
179
|
return this.parent.computedStyle;
|
|
@@ -734,79 +371,443 @@ class Character {
|
|
|
734
371
|
this.updatePath();
|
|
735
372
|
return this;
|
|
736
373
|
}
|
|
737
|
-
drawTo(ctx, config = {}) {
|
|
374
|
+
drawTo(ctx, config = {}) {
|
|
375
|
+
drawPaths({
|
|
376
|
+
ctx,
|
|
377
|
+
paths: [this.path],
|
|
378
|
+
fontSize: this.computedStyle.fontSize,
|
|
379
|
+
color: this.computedStyle.color,
|
|
380
|
+
...config
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
var __defProp$3 = Object.defineProperty;
|
|
386
|
+
var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
387
|
+
var __publicField$3 = (obj, key, value) => {
|
|
388
|
+
__defNormalProp$3(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
389
|
+
return value;
|
|
390
|
+
};
|
|
391
|
+
class Fragment {
|
|
392
|
+
constructor(content, style = {}, parent) {
|
|
393
|
+
this.content = content;
|
|
394
|
+
this.style = style;
|
|
395
|
+
this.parent = parent;
|
|
396
|
+
__publicField$3(this, "boundingBox", new BoundingBox());
|
|
397
|
+
__publicField$3(this, "highlight");
|
|
398
|
+
this.updateComputedStyle().initCharacters();
|
|
399
|
+
}
|
|
400
|
+
get computedContent() {
|
|
401
|
+
const style = this.computedStyle;
|
|
402
|
+
return style.textTransform === "uppercase" ? this.content.toUpperCase() : style.textTransform === "lowercase" ? this.content.toLowerCase() : this.content;
|
|
403
|
+
}
|
|
404
|
+
updateComputedStyle() {
|
|
405
|
+
this.computedStyle = {
|
|
406
|
+
...this.parent.computedStyle,
|
|
407
|
+
...filterEmpty(this.style)
|
|
408
|
+
};
|
|
409
|
+
return this;
|
|
410
|
+
}
|
|
411
|
+
initCharacters() {
|
|
412
|
+
const characters = [];
|
|
413
|
+
let index = 0;
|
|
414
|
+
for (const c of this.computedContent) {
|
|
415
|
+
characters.push(new Character(c, index++, this));
|
|
416
|
+
}
|
|
417
|
+
this.characters = characters;
|
|
418
|
+
return this;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
var __defProp$2 = Object.defineProperty;
|
|
423
|
+
var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
424
|
+
var __publicField$2 = (obj, key, value) => {
|
|
425
|
+
__defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
426
|
+
return value;
|
|
427
|
+
};
|
|
428
|
+
class Paragraph {
|
|
429
|
+
constructor(style, parentStyle) {
|
|
430
|
+
this.style = style;
|
|
431
|
+
this.parentStyle = parentStyle;
|
|
432
|
+
__publicField$2(this, "boundingBox", new BoundingBox());
|
|
433
|
+
__publicField$2(this, "fragments", []);
|
|
434
|
+
this.updateComputedStyle();
|
|
435
|
+
}
|
|
436
|
+
updateComputedStyle() {
|
|
437
|
+
this.computedStyle = {
|
|
438
|
+
...filterEmpty(this.parentStyle),
|
|
439
|
+
...filterEmpty(this.style)
|
|
440
|
+
};
|
|
441
|
+
return this;
|
|
442
|
+
}
|
|
443
|
+
addFragment(content, style) {
|
|
444
|
+
const fragment = new Fragment(content, style, this);
|
|
445
|
+
this.fragments.push(fragment);
|
|
446
|
+
return fragment;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
class Feature {
|
|
451
|
+
constructor(_text) {
|
|
452
|
+
this._text = _text;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
class Deformer extends Feature {
|
|
457
|
+
deform() {
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
class Effector extends Feature {
|
|
462
|
+
getBoundingBox() {
|
|
463
|
+
const { characters, effects } = this._text;
|
|
464
|
+
const boxes = [];
|
|
465
|
+
characters.forEach((character) => {
|
|
466
|
+
const fontSize = character.computedStyle.fontSize;
|
|
467
|
+
effects?.forEach((effect) => {
|
|
468
|
+
const offsetX = (effect.offsetX ?? 0) * fontSize;
|
|
469
|
+
const offsetY = (effect.offsetY ?? 0) * fontSize;
|
|
470
|
+
const shadowOffsetX = (effect.shadowOffsetX ?? 0) * fontSize;
|
|
471
|
+
const shadowOffsetY = (effect.shadowOffsetY ?? 0) * fontSize;
|
|
472
|
+
const textStrokeWidth = Math.max(0.1, effect.textStrokeWidth ?? 0) * fontSize;
|
|
473
|
+
const aabb = character.boundingBox.clone();
|
|
474
|
+
aabb.left += offsetX + shadowOffsetX - textStrokeWidth;
|
|
475
|
+
aabb.top += offsetY + shadowOffsetY - textStrokeWidth;
|
|
476
|
+
aabb.width += textStrokeWidth * 2;
|
|
477
|
+
aabb.height += textStrokeWidth * 2;
|
|
478
|
+
boxes.push(aabb);
|
|
479
|
+
});
|
|
480
|
+
});
|
|
481
|
+
return BoundingBox.from(...boxes);
|
|
482
|
+
}
|
|
483
|
+
draw(options) {
|
|
484
|
+
const { ctx } = options;
|
|
485
|
+
const { effects, characters, boundingBox } = this._text;
|
|
486
|
+
if (effects) {
|
|
487
|
+
effects.forEach((effect) => {
|
|
488
|
+
uploadColor(effect, boundingBox, ctx);
|
|
489
|
+
});
|
|
490
|
+
characters.forEach((character) => {
|
|
491
|
+
effects.forEach((effect) => {
|
|
492
|
+
character.drawTo(ctx, effect);
|
|
493
|
+
});
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
return this;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
var __defProp$1 = Object.defineProperty;
|
|
501
|
+
var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
502
|
+
var __publicField$1 = (obj, key, value) => {
|
|
503
|
+
__defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
504
|
+
return value;
|
|
505
|
+
};
|
|
506
|
+
class Highlighter extends Feature {
|
|
507
|
+
constructor() {
|
|
508
|
+
super(...arguments);
|
|
509
|
+
__publicField$1(this, "paths", []);
|
|
510
|
+
}
|
|
511
|
+
getBoundingBox() {
|
|
512
|
+
if (!this.paths.length) {
|
|
513
|
+
return new BoundingBox();
|
|
514
|
+
}
|
|
515
|
+
const min = Point2D.MAX;
|
|
516
|
+
const max = Point2D.MIN;
|
|
517
|
+
this.paths.forEach((path) => path.getMinMax(min, max));
|
|
518
|
+
return new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
|
|
519
|
+
}
|
|
520
|
+
highlight() {
|
|
521
|
+
const { characters, computedStyle: style } = this._text;
|
|
522
|
+
const fontSize = style.fontSize;
|
|
523
|
+
let group;
|
|
524
|
+
const groups = [];
|
|
525
|
+
let prevHighlight;
|
|
526
|
+
characters.forEach((character) => {
|
|
527
|
+
const highlight = character.parent.highlight;
|
|
528
|
+
if (highlight?.url) {
|
|
529
|
+
if (prevHighlight?.url === highlight.url) {
|
|
530
|
+
group.push(character);
|
|
531
|
+
} else {
|
|
532
|
+
group = [];
|
|
533
|
+
group.push(character);
|
|
534
|
+
groups.push(group);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
prevHighlight = highlight;
|
|
538
|
+
});
|
|
539
|
+
this.paths = groups.filter((characters2) => characters2.length).map((characters2) => {
|
|
540
|
+
return {
|
|
541
|
+
url: characters2[0].parent.highlight.url,
|
|
542
|
+
box: BoundingBox.from(...characters2.map((c) => c.glyphBox)),
|
|
543
|
+
baseline: Math.max(...characters2.map((c) => c.baseline))
|
|
544
|
+
};
|
|
545
|
+
}).map((group2) => this._parseGroup(group2, fontSize)).flat();
|
|
546
|
+
}
|
|
547
|
+
_parseSvg(url) {
|
|
548
|
+
const svg = parseSvgToDom(url);
|
|
549
|
+
const paths = parseSvg(svg);
|
|
550
|
+
const min = Point2D.MAX;
|
|
551
|
+
const max = Point2D.MIN;
|
|
552
|
+
paths.forEach((path) => path.getMinMax(min, max));
|
|
553
|
+
const { x, y, width, height } = new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
|
|
554
|
+
const viewBox = svg.getAttribute("viewBox").split(" ").map(Number);
|
|
555
|
+
return {
|
|
556
|
+
paths,
|
|
557
|
+
box: new BoundingBox(x, y, width, height),
|
|
558
|
+
viewBox: new BoundingBox(...viewBox)
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
_parseGroup(group, fontSize) {
|
|
562
|
+
const { url, box: groupBox, baseline } = group;
|
|
563
|
+
const { box, viewBox, paths } = this._parseSvg(url);
|
|
564
|
+
const result = [];
|
|
565
|
+
const type = box.height / viewBox.height > 0.3 ? 0 : 1;
|
|
566
|
+
if (type === 0) {
|
|
567
|
+
const offset = {
|
|
568
|
+
x: groupBox.left - fontSize * 0.2,
|
|
569
|
+
y: groupBox.top
|
|
570
|
+
};
|
|
571
|
+
const scaleX = (groupBox.width + fontSize * 0.2 * 2) / box.width;
|
|
572
|
+
const scaleY = groupBox.height / box.height;
|
|
573
|
+
const m = new Matrix3().translate(-box.x, -box.y).scale(scaleX, scaleY).translate(offset.x, offset.y);
|
|
574
|
+
paths.forEach((original) => {
|
|
575
|
+
result.push(original.clone().transform(m));
|
|
576
|
+
});
|
|
577
|
+
} else if (type === 1) {
|
|
578
|
+
const scale = fontSize / box.width * 2;
|
|
579
|
+
const width = box.width * scale;
|
|
580
|
+
const length = Math.ceil(groupBox.width / width);
|
|
581
|
+
const totalWidth = width * length;
|
|
582
|
+
const offset = {
|
|
583
|
+
x: groupBox.left + (groupBox.width - totalWidth) / 2 + fontSize * 0.1,
|
|
584
|
+
y: groupBox.top + baseline + fontSize * 0.1
|
|
585
|
+
};
|
|
586
|
+
const m = new Matrix3().translate(-box.x, -box.y).scale(scale, scale).translate(offset.x, offset.y);
|
|
587
|
+
for (let i = 0; i < length; i++) {
|
|
588
|
+
const _m = m.clone().translate(i * width, 0);
|
|
589
|
+
paths.forEach((original) => {
|
|
590
|
+
result.push(original.clone().transform(_m));
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
return result;
|
|
595
|
+
}
|
|
596
|
+
draw({ ctx }) {
|
|
738
597
|
drawPaths({
|
|
739
598
|
ctx,
|
|
740
|
-
paths:
|
|
741
|
-
fontSize: this.computedStyle.fontSize,
|
|
742
|
-
|
|
743
|
-
...config
|
|
599
|
+
paths: this.paths,
|
|
600
|
+
fontSize: this._text.computedStyle.fontSize,
|
|
601
|
+
fill: false
|
|
744
602
|
});
|
|
603
|
+
return this;
|
|
745
604
|
}
|
|
746
605
|
}
|
|
747
606
|
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
607
|
+
class Measurer extends Feature {
|
|
608
|
+
_styleToDomStyle(style) {
|
|
609
|
+
const _style = { ...style };
|
|
610
|
+
for (const key in style) {
|
|
611
|
+
if ([
|
|
612
|
+
"width",
|
|
613
|
+
"height",
|
|
614
|
+
"fontSize",
|
|
615
|
+
"letterSpacing",
|
|
616
|
+
"textStrokeWidth",
|
|
617
|
+
"shadowOffsetX",
|
|
618
|
+
"shadowOffsetY",
|
|
619
|
+
"shadowBlur"
|
|
620
|
+
].includes(key)) {
|
|
621
|
+
_style[key] = `${style[key]}px`;
|
|
622
|
+
} else {
|
|
623
|
+
_style[key] = style[key];
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
return _style;
|
|
766
627
|
}
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
628
|
+
/**
|
|
629
|
+
* <section style="...">
|
|
630
|
+
* <ul>
|
|
631
|
+
* <li style="...">
|
|
632
|
+
* <span style="...">...</span>
|
|
633
|
+
* <span>...</span>
|
|
634
|
+
* </li>
|
|
635
|
+
* </ul>
|
|
636
|
+
* </section>
|
|
637
|
+
*/
|
|
638
|
+
createDom() {
|
|
639
|
+
const { paragraphs, computedStyle } = this._text;
|
|
640
|
+
const documentFragment = document.createDocumentFragment();
|
|
641
|
+
const dom = document.createElement("section");
|
|
642
|
+
Object.assign(dom.style, {
|
|
643
|
+
...this._styleToDomStyle(computedStyle),
|
|
644
|
+
position: "absolute",
|
|
645
|
+
visibility: "hidden"
|
|
646
|
+
});
|
|
647
|
+
const ul = document.createElement("ul");
|
|
648
|
+
Object.assign(ul.style, {
|
|
649
|
+
listStyle: "none",
|
|
650
|
+
padding: "0",
|
|
651
|
+
margin: "0"
|
|
652
|
+
});
|
|
653
|
+
paragraphs.forEach((paragraph) => {
|
|
654
|
+
const li = document.createElement("li");
|
|
655
|
+
Object.assign(li.style, this._styleToDomStyle(paragraph.style));
|
|
656
|
+
paragraph.fragments.forEach((fragment) => {
|
|
657
|
+
const span = document.createElement("span");
|
|
658
|
+
Object.assign(span.style, this._styleToDomStyle(fragment.style));
|
|
659
|
+
span.appendChild(document.createTextNode(fragment.content));
|
|
660
|
+
li.appendChild(span);
|
|
661
|
+
});
|
|
662
|
+
ul.appendChild(li);
|
|
663
|
+
});
|
|
664
|
+
dom.appendChild(ul);
|
|
665
|
+
documentFragment.appendChild(dom);
|
|
666
|
+
document.body.appendChild(documentFragment);
|
|
667
|
+
return {
|
|
668
|
+
dom,
|
|
669
|
+
destory: () => dom.parentNode?.removeChild(dom)
|
|
771
670
|
};
|
|
772
|
-
return this;
|
|
773
671
|
}
|
|
774
|
-
|
|
672
|
+
_measureDom(dom) {
|
|
673
|
+
const paragraphs = [];
|
|
674
|
+
const fragments = [];
|
|
775
675
|
const characters = [];
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
676
|
+
dom.querySelectorAll("li").forEach((li, paragraphIndex) => {
|
|
677
|
+
const pBox = li.getBoundingClientRect();
|
|
678
|
+
paragraphs.push({
|
|
679
|
+
paragraphIndex,
|
|
680
|
+
left: pBox.left,
|
|
681
|
+
top: pBox.top,
|
|
682
|
+
width: pBox.width,
|
|
683
|
+
height: pBox.height
|
|
684
|
+
});
|
|
685
|
+
li.querySelectorAll("span").forEach((span, fragmentIndex) => {
|
|
686
|
+
const fBox = li.getBoundingClientRect();
|
|
687
|
+
fragments.push({
|
|
688
|
+
paragraphIndex,
|
|
689
|
+
fragmentIndex,
|
|
690
|
+
left: fBox.left,
|
|
691
|
+
top: fBox.top,
|
|
692
|
+
width: fBox.width,
|
|
693
|
+
height: fBox.height
|
|
694
|
+
});
|
|
695
|
+
const text = span.firstChild;
|
|
696
|
+
if (text instanceof window.Text) {
|
|
697
|
+
const range = document.createRange();
|
|
698
|
+
range.selectNodeContents(text);
|
|
699
|
+
const len = text.data ? text.data.length : 0;
|
|
700
|
+
let i = 0;
|
|
701
|
+
for (; i <= len; ) {
|
|
702
|
+
range.setStart(text, Math.max(i - 1, 0));
|
|
703
|
+
range.setEnd(text, i);
|
|
704
|
+
const rects = range.getClientRects?.() ?? [range.getBoundingClientRect()];
|
|
705
|
+
let rect = rects[rects.length - 1];
|
|
706
|
+
if (rects.length > 1 && rect.width < 2) {
|
|
707
|
+
rect = rects[rects.length - 2];
|
|
708
|
+
}
|
|
709
|
+
const content = range.toString();
|
|
710
|
+
if (content !== "" && rect && rect.width + rect.height !== 0) {
|
|
711
|
+
characters.push({
|
|
712
|
+
content,
|
|
713
|
+
newParagraphIndex: -1,
|
|
714
|
+
paragraphIndex,
|
|
715
|
+
fragmentIndex,
|
|
716
|
+
characterIndex: i - 1,
|
|
717
|
+
top: rect.top,
|
|
718
|
+
left: rect.left,
|
|
719
|
+
height: rect.height,
|
|
720
|
+
width: rect.width,
|
|
721
|
+
textWidth: -1,
|
|
722
|
+
textHeight: -1
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
i++;
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
});
|
|
729
|
+
});
|
|
730
|
+
return {
|
|
731
|
+
paragraphs,
|
|
732
|
+
fragments,
|
|
733
|
+
characters
|
|
734
|
+
};
|
|
798
735
|
}
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
736
|
+
measureDom(dom) {
|
|
737
|
+
const { paragraphs } = this._text;
|
|
738
|
+
const rect = dom.getBoundingClientRect();
|
|
739
|
+
const innerEl = dom.querySelector("ul");
|
|
740
|
+
const isVertical = window.getComputedStyle(dom).writingMode.includes("vertical");
|
|
741
|
+
const oldLineHeight = innerEl.style.lineHeight;
|
|
742
|
+
innerEl.style.lineHeight = "4000px";
|
|
743
|
+
const _paragraphs = [[]];
|
|
744
|
+
let fragments = _paragraphs[0];
|
|
745
|
+
const { characters: oldCharacters } = this._measureDom(dom);
|
|
746
|
+
if (oldCharacters.length > 0) {
|
|
747
|
+
fragments.push(oldCharacters[0]);
|
|
748
|
+
oldCharacters.reduce((prev, current) => {
|
|
749
|
+
const attr = isVertical ? "left" : "top";
|
|
750
|
+
if (Math.abs(current[attr] - prev[attr]) > 4e3 / 2) {
|
|
751
|
+
fragments = [];
|
|
752
|
+
_paragraphs.push(fragments);
|
|
753
|
+
}
|
|
754
|
+
fragments.push(current);
|
|
755
|
+
return current;
|
|
756
|
+
});
|
|
757
|
+
}
|
|
758
|
+
innerEl.style.lineHeight = oldLineHeight;
|
|
759
|
+
const measured = this._measureDom(dom);
|
|
760
|
+
measured.paragraphs.forEach((p) => {
|
|
761
|
+
const _p = paragraphs[p.paragraphIndex];
|
|
762
|
+
_p.boundingBox.left = p.left;
|
|
763
|
+
_p.boundingBox.top = p.top;
|
|
764
|
+
_p.boundingBox.width = p.width;
|
|
765
|
+
_p.boundingBox.height = p.height;
|
|
766
|
+
});
|
|
767
|
+
measured.fragments.forEach((f) => {
|
|
768
|
+
const _f = paragraphs[f.paragraphIndex].fragments[f.fragmentIndex];
|
|
769
|
+
_f.boundingBox.left = f.left;
|
|
770
|
+
_f.boundingBox.top = f.top;
|
|
771
|
+
_f.boundingBox.width = f.width;
|
|
772
|
+
_f.boundingBox.height = f.height;
|
|
773
|
+
});
|
|
774
|
+
const results = [];
|
|
775
|
+
let i = 0;
|
|
776
|
+
_paragraphs.forEach((oldCharacters2) => {
|
|
777
|
+
oldCharacters2.forEach((oldCharacter) => {
|
|
778
|
+
const character = measured.characters[i];
|
|
779
|
+
const { paragraphIndex, fragmentIndex, characterIndex } = character;
|
|
780
|
+
results.push({
|
|
781
|
+
...character,
|
|
782
|
+
newParagraphIndex: paragraphIndex,
|
|
783
|
+
textWidth: oldCharacter.width,
|
|
784
|
+
textHeight: oldCharacter.height,
|
|
785
|
+
left: character.left - rect.left,
|
|
786
|
+
top: character.top - rect.top
|
|
787
|
+
});
|
|
788
|
+
const item = paragraphs[paragraphIndex].fragments[fragmentIndex].characters[characterIndex];
|
|
789
|
+
item.boundingBox.left = results[i].left;
|
|
790
|
+
item.boundingBox.top = results[i].top;
|
|
791
|
+
item.boundingBox.width = results[i].width;
|
|
792
|
+
item.boundingBox.height = results[i].height;
|
|
793
|
+
item.textWidth = results[i].textWidth;
|
|
794
|
+
item.textHeight = results[i].textHeight;
|
|
795
|
+
i++;
|
|
796
|
+
});
|
|
797
|
+
});
|
|
798
|
+
return {
|
|
799
|
+
paragraphs,
|
|
800
|
+
boundingBox: new BoundingBox(0, 0, rect.width, rect.height)
|
|
803
801
|
};
|
|
804
|
-
return this;
|
|
805
802
|
}
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
803
|
+
measure(dom) {
|
|
804
|
+
let destory;
|
|
805
|
+
if (!dom) {
|
|
806
|
+
({ dom, destory } = this.createDom());
|
|
807
|
+
}
|
|
808
|
+
const result = this.measureDom(dom);
|
|
809
|
+
destory?.();
|
|
810
|
+
return result;
|
|
810
811
|
}
|
|
811
812
|
}
|
|
812
813
|
|
|
@@ -865,6 +866,10 @@ class Parser extends Feature {
|
|
|
865
866
|
}
|
|
866
867
|
}
|
|
867
868
|
|
|
869
|
+
class Reflector extends Feature {
|
|
870
|
+
// TODO
|
|
871
|
+
}
|
|
872
|
+
|
|
868
873
|
class Renderer2D extends Feature {
|
|
869
874
|
setupView(options) {
|
|
870
875
|
const { ctx, pixelRatio } = options;
|
|
@@ -960,11 +965,12 @@ const defaultTextStyles = {
|
|
|
960
965
|
textOrientation: "mixed"
|
|
961
966
|
};
|
|
962
967
|
class Text {
|
|
963
|
-
constructor(options) {
|
|
968
|
+
constructor(options = {}) {
|
|
964
969
|
__publicField(this, "content");
|
|
965
970
|
__publicField(this, "style");
|
|
966
971
|
__publicField(this, "effects");
|
|
967
972
|
__publicField(this, "deformation");
|
|
973
|
+
__publicField(this, "measureDom");
|
|
968
974
|
__publicField(this, "needsUpdate", true);
|
|
969
975
|
__publicField(this, "computedStyle", { ...defaultTextStyles });
|
|
970
976
|
__publicField(this, "paragraphs", []);
|
|
@@ -976,16 +982,17 @@ class Text {
|
|
|
976
982
|
__publicField(this, "_effector", new Effector(this));
|
|
977
983
|
__publicField(this, "_highlighter", new Highlighter(this));
|
|
978
984
|
__publicField(this, "_renderer2D", new Renderer2D(this));
|
|
979
|
-
const { content, style = {}, effects, deformation } = options;
|
|
985
|
+
const { content = "", style = {}, effects, deformation, measureDom } = options;
|
|
980
986
|
this.content = content;
|
|
981
987
|
this.style = style;
|
|
982
988
|
this.effects = effects;
|
|
983
989
|
this.deformation = deformation;
|
|
990
|
+
this.measureDom = measureDom;
|
|
984
991
|
}
|
|
985
992
|
get characters() {
|
|
986
993
|
return this.paragraphs.flatMap((p) => p.fragments.flatMap((f) => f.characters));
|
|
987
994
|
}
|
|
988
|
-
measure(dom) {
|
|
995
|
+
measure(dom = this.measureDom) {
|
|
989
996
|
this.computedStyle = { ...defaultTextStyles, ...this.style };
|
|
990
997
|
this.paragraphs = this._parser.parse();
|
|
991
998
|
return this._measurer.measure(dom);
|
|
@@ -1025,23 +1032,23 @@ class Text {
|
|
|
1025
1032
|
this._effector.getBoundingBox(),
|
|
1026
1033
|
this._highlighter.getBoundingBox()
|
|
1027
1034
|
);
|
|
1028
|
-
this._renderer2D.setupView({ pixelRatio, ctx });
|
|
1029
|
-
this._renderer2D.uploadColors({ ctx });
|
|
1030
|
-
this._highlighter.draw({ ctx });
|
|
1031
|
-
this._effector.draw({ ctx });
|
|
1032
1035
|
} else {
|
|
1033
1036
|
this.renderBoundingBox = BoundingBox.from(
|
|
1034
1037
|
this.boundingBox,
|
|
1035
1038
|
this.renderBoundingBox,
|
|
1036
1039
|
this._highlighter.getBoundingBox()
|
|
1037
1040
|
);
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
+
}
|
|
1042
|
+
this._renderer2D.setupView({ pixelRatio, ctx });
|
|
1043
|
+
this._renderer2D.uploadColors({ ctx });
|
|
1044
|
+
this._highlighter.draw({ ctx });
|
|
1045
|
+
if (this.effects?.length) {
|
|
1046
|
+
this._effector.draw({ ctx });
|
|
1047
|
+
} else {
|
|
1041
1048
|
this._renderer2D.draw({ ctx });
|
|
1042
1049
|
}
|
|
1043
1050
|
return this;
|
|
1044
1051
|
}
|
|
1045
1052
|
}
|
|
1046
1053
|
|
|
1047
|
-
export { Text, defaultTextStyles };
|
|
1054
|
+
export { Character, Deformer, Effector, Fragment, Highlighter, Measurer, Paragraph, Parser, Reflector, Renderer2D, Text, defaultTextStyles, drawPaths, filterEmpty, getPointPosition, getRotationPoint, getScalePoint, getSkewPoint, parseColor, uploadColor };
|