modern-text 0.2.2 → 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 +495 -468
- 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 +478 -471
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -3,17 +3,6 @@
|
|
|
3
3
|
const modernPath2d = require('modern-path2d');
|
|
4
4
|
const modernFont = require('modern-font');
|
|
5
5
|
|
|
6
|
-
class Feature {
|
|
7
|
-
constructor(_text) {
|
|
8
|
-
this._text = _text;
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
class Deformer extends Feature {
|
|
13
|
-
deform() {
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
6
|
function parseColor(ctx, source, box) {
|
|
18
7
|
if (typeof source === "string" && source.startsWith("linear-gradient")) {
|
|
19
8
|
const { x0, y0, x1, y1, stops } = parseCssLinearGradient(source, box.left, box.top, box.width, box.height);
|
|
@@ -65,387 +54,34 @@ function parseCssLinearGradient(css, x, y, width, height) {
|
|
|
65
54
|
|
|
66
55
|
function drawPaths(options) {
|
|
67
56
|
const { ctx, paths, fontSize } = options;
|
|
68
|
-
paths.forEach((path) => {
|
|
69
|
-
ctx.save();
|
|
70
|
-
ctx.beginPath();
|
|
71
|
-
const style = path.userData?.style ?? {};
|
|
72
|
-
const fillStyle = options.color ?? style.fill ?? "none";
|
|
73
|
-
const strokeStyle = options.textStrokeColor ?? style.stroke ?? "none";
|
|
74
|
-
ctx.fillStyle = fillStyle !== "none" ? fillStyle : "#000";
|
|
75
|
-
ctx.strokeStyle = strokeStyle !== "none" ? strokeStyle : "#000";
|
|
76
|
-
ctx.lineWidth = options.textStrokeWidth ? options.textStrokeWidth * fontSize : style.strokeWidth ? style.strokeWidth * fontSize * 0.03 : 0;
|
|
77
|
-
ctx.lineCap = style.strokeLineCap ?? "round";
|
|
78
|
-
ctx.lineJoin = style.strokeLineJoin ?? "miter";
|
|
79
|
-
ctx.miterLimit = style.strokeMiterLimit ?? 0;
|
|
80
|
-
ctx.shadowOffsetX = (options.shadowOffsetX ?? 0) * fontSize;
|
|
81
|
-
ctx.shadowOffsetY = (options.shadowOffsetY ?? 0) * fontSize;
|
|
82
|
-
ctx.shadowBlur = (options.shadowBlur ?? 0) * fontSize;
|
|
83
|
-
ctx.shadowColor = options.shadowColor ?? "rgba(0, 0, 0, 0)";
|
|
84
|
-
const offsetX = (options.offsetX ?? 0) * fontSize;
|
|
85
|
-
const offsetY = (options.offsetY ?? 0) * fontSize;
|
|
86
|
-
ctx.translate(offsetX, offsetY);
|
|
87
|
-
path.drawTo(ctx);
|
|
88
|
-
if (fillStyle !== "none") {
|
|
89
|
-
ctx.fill();
|
|
90
|
-
}
|
|
91
|
-
if (strokeStyle !== "none") {
|
|
92
|
-
ctx.stroke();
|
|
93
|
-
}
|
|
94
|
-
ctx.restore();
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
class Effector extends Feature {
|
|
99
|
-
getBoundingBox() {
|
|
100
|
-
const { characters, effects } = this._text;
|
|
101
|
-
const boxes = [];
|
|
102
|
-
characters.forEach((character) => {
|
|
103
|
-
const fontSize = character.computedStyle.fontSize;
|
|
104
|
-
effects?.forEach((effect) => {
|
|
105
|
-
const offsetX = (effect.offsetX ?? 0) * fontSize;
|
|
106
|
-
const offsetY = (effect.offsetY ?? 0) * fontSize;
|
|
107
|
-
const shadowOffsetX = (effect.shadowOffsetX ?? 0) * fontSize;
|
|
108
|
-
const shadowOffsetY = (effect.shadowOffsetY ?? 0) * fontSize;
|
|
109
|
-
const textStrokeWidth = Math.max(0.1, effect.textStrokeWidth ?? 0) * fontSize;
|
|
110
|
-
const aabb = character.boundingBox.clone();
|
|
111
|
-
aabb.left += offsetX + shadowOffsetX - textStrokeWidth;
|
|
112
|
-
aabb.top += offsetY + shadowOffsetY - textStrokeWidth;
|
|
113
|
-
aabb.width += textStrokeWidth * 2;
|
|
114
|
-
aabb.height += textStrokeWidth * 2;
|
|
115
|
-
boxes.push(aabb);
|
|
116
|
-
});
|
|
117
|
-
});
|
|
118
|
-
return modernPath2d.BoundingBox.from(...boxes);
|
|
119
|
-
}
|
|
120
|
-
draw(options) {
|
|
121
|
-
const { ctx } = options;
|
|
122
|
-
const { effects, characters, boundingBox } = this._text;
|
|
123
|
-
if (effects) {
|
|
124
|
-
effects.forEach((effect) => {
|
|
125
|
-
uploadColor(effect, boundingBox, ctx);
|
|
126
|
-
});
|
|
127
|
-
characters.forEach((character) => {
|
|
128
|
-
effects.forEach((effect) => {
|
|
129
|
-
character.drawTo(ctx, effect);
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
return this;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
var __defProp$4 = Object.defineProperty;
|
|
138
|
-
var __defNormalProp$4 = (obj, key, value) => key in obj ? __defProp$4(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
139
|
-
var __publicField$4 = (obj, key, value) => {
|
|
140
|
-
__defNormalProp$4(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
141
|
-
return value;
|
|
142
|
-
};
|
|
143
|
-
class Highlighter extends Feature {
|
|
144
|
-
constructor() {
|
|
145
|
-
super(...arguments);
|
|
146
|
-
__publicField$4(this, "paths", []);
|
|
147
|
-
}
|
|
148
|
-
getBoundingBox() {
|
|
149
|
-
if (!this.paths.length) {
|
|
150
|
-
return new modernPath2d.BoundingBox();
|
|
151
|
-
}
|
|
152
|
-
const min = modernPath2d.Point2D.MAX;
|
|
153
|
-
const max = modernPath2d.Point2D.MIN;
|
|
154
|
-
this.paths.forEach((path) => path.getMinMax(min, max));
|
|
155
|
-
return new modernPath2d.BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
|
|
156
|
-
}
|
|
157
|
-
highlight() {
|
|
158
|
-
const { characters, computedStyle: style } = this._text;
|
|
159
|
-
const fontSize = style.fontSize;
|
|
160
|
-
let group;
|
|
161
|
-
const groups = [];
|
|
162
|
-
let prevHighlight;
|
|
163
|
-
characters.forEach((character) => {
|
|
164
|
-
const highlight = character.parent.highlight;
|
|
165
|
-
if (highlight?.url) {
|
|
166
|
-
if (prevHighlight?.url === highlight.url) {
|
|
167
|
-
group.push(character);
|
|
168
|
-
} else {
|
|
169
|
-
group = [];
|
|
170
|
-
group.push(character);
|
|
171
|
-
groups.push(group);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
prevHighlight = highlight;
|
|
175
|
-
});
|
|
176
|
-
this.paths = groups.filter((characters2) => characters2.length).map((characters2) => {
|
|
177
|
-
return {
|
|
178
|
-
url: characters2[0].parent.highlight.url,
|
|
179
|
-
box: modernPath2d.BoundingBox.from(...characters2.map((c) => c.glyphBox)),
|
|
180
|
-
baseline: Math.max(...characters2.map((c) => c.baseline))
|
|
181
|
-
};
|
|
182
|
-
}).map((group2) => this._parseGroup(group2, fontSize)).flat();
|
|
183
|
-
}
|
|
184
|
-
_parseSvg(url) {
|
|
185
|
-
const svg = modernPath2d.parseSvgToDom(url);
|
|
186
|
-
const paths = modernPath2d.parseSvg(svg);
|
|
187
|
-
const min = modernPath2d.Point2D.MAX;
|
|
188
|
-
const max = modernPath2d.Point2D.MIN;
|
|
189
|
-
paths.forEach((path) => path.getMinMax(min, max));
|
|
190
|
-
const { x, y, width, height } = new modernPath2d.BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
|
|
191
|
-
const viewBox = svg.getAttribute("viewBox").split(" ").map(Number);
|
|
192
|
-
return {
|
|
193
|
-
paths,
|
|
194
|
-
box: new modernPath2d.BoundingBox(x, y, width, height),
|
|
195
|
-
viewBox: new modernPath2d.BoundingBox(...viewBox)
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
_parseGroup(group, fontSize) {
|
|
199
|
-
const { url, box: groupBox, baseline } = group;
|
|
200
|
-
const { box, viewBox, paths } = this._parseSvg(url);
|
|
201
|
-
const result = [];
|
|
202
|
-
const type = box.height / viewBox.height > 0.3 ? 0 : 1;
|
|
203
|
-
if (type === 0) {
|
|
204
|
-
const offset = {
|
|
205
|
-
x: groupBox.left - fontSize * 0.2,
|
|
206
|
-
y: groupBox.top
|
|
207
|
-
};
|
|
208
|
-
const scaleX = (groupBox.width + fontSize * 0.2 * 2) / box.width;
|
|
209
|
-
const scaleY = groupBox.height / box.height;
|
|
210
|
-
const m = new modernPath2d.Matrix3().translate(-box.x, -box.y).scale(scaleX, scaleY).translate(offset.x, offset.y);
|
|
211
|
-
paths.forEach((original) => {
|
|
212
|
-
result.push(original.clone().transform(m));
|
|
213
|
-
});
|
|
214
|
-
} else if (type === 1) {
|
|
215
|
-
const scale = fontSize / box.width * 2;
|
|
216
|
-
const width = box.width * scale;
|
|
217
|
-
const length = Math.ceil(groupBox.width / width);
|
|
218
|
-
const totalWidth = width * length;
|
|
219
|
-
const offset = {
|
|
220
|
-
x: groupBox.left + (groupBox.width - totalWidth) / 2 + fontSize * 0.1,
|
|
221
|
-
y: groupBox.top + baseline + fontSize * 0.1
|
|
222
|
-
};
|
|
223
|
-
const m = new modernPath2d.Matrix3().translate(-box.x, -box.y).scale(scale, scale).translate(offset.x, offset.y);
|
|
224
|
-
for (let i = 0; i < length; i++) {
|
|
225
|
-
const _m = m.clone().translate(i * width, 0);
|
|
226
|
-
paths.forEach((original) => {
|
|
227
|
-
result.push(original.clone().transform(_m));
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
return result;
|
|
232
|
-
}
|
|
233
|
-
draw({ ctx }) {
|
|
234
|
-
drawPaths({
|
|
235
|
-
ctx,
|
|
236
|
-
paths: this.paths,
|
|
237
|
-
fontSize: this._text.computedStyle.fontSize,
|
|
238
|
-
fill: false
|
|
239
|
-
});
|
|
240
|
-
return this;
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
class Measurer extends Feature {
|
|
245
|
-
_styleToDomStyle(style) {
|
|
246
|
-
const _style = { ...style };
|
|
247
|
-
for (const key in style) {
|
|
248
|
-
if ([
|
|
249
|
-
"width",
|
|
250
|
-
"height",
|
|
251
|
-
"fontSize",
|
|
252
|
-
"letterSpacing",
|
|
253
|
-
"textStrokeWidth",
|
|
254
|
-
"shadowOffsetX",
|
|
255
|
-
"shadowOffsetY",
|
|
256
|
-
"shadowBlur"
|
|
257
|
-
].includes(key)) {
|
|
258
|
-
_style[key] = `${style[key]}px`;
|
|
259
|
-
} else {
|
|
260
|
-
_style[key] = style[key];
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
return _style;
|
|
264
|
-
}
|
|
265
|
-
/**
|
|
266
|
-
* <div style="...">
|
|
267
|
-
* <ul>
|
|
268
|
-
* <li style="...">
|
|
269
|
-
* <div>
|
|
270
|
-
* <span style="...">...</span>
|
|
271
|
-
* <span>...</span>
|
|
272
|
-
* </div>
|
|
273
|
-
* </li>
|
|
274
|
-
* </ul>
|
|
275
|
-
* </div>
|
|
276
|
-
*/
|
|
277
|
-
createDom() {
|
|
278
|
-
const { paragraphs, computedStyle: style } = this._text;
|
|
279
|
-
const documentFragment = document.createDocumentFragment();
|
|
280
|
-
const dom = document.createElement("div");
|
|
281
|
-
Object.assign(dom.style, this._styleToDomStyle(style));
|
|
282
|
-
dom.style.position = "absolute";
|
|
283
|
-
dom.style.visibility = "hidden";
|
|
284
|
-
const ul = document.createElement("ul");
|
|
285
|
-
ul.style.listStyle = "none";
|
|
286
|
-
ul.style.padding = "0";
|
|
287
|
-
ul.style.margin = "0";
|
|
288
|
-
paragraphs.forEach((paragraph) => {
|
|
289
|
-
const li = document.createElement("li");
|
|
290
|
-
const div = document.createElement("div");
|
|
291
|
-
Object.assign(li.style, this._styleToDomStyle(paragraph.style));
|
|
292
|
-
paragraph.fragments.forEach((fragment) => {
|
|
293
|
-
const span = document.createElement("span");
|
|
294
|
-
Object.assign(span.style, this._styleToDomStyle(fragment.style));
|
|
295
|
-
span.appendChild(document.createTextNode(fragment.content));
|
|
296
|
-
div.appendChild(span);
|
|
297
|
-
});
|
|
298
|
-
ul.appendChild(li);
|
|
299
|
-
li.appendChild(div);
|
|
300
|
-
});
|
|
301
|
-
dom.appendChild(ul);
|
|
302
|
-
documentFragment.appendChild(dom);
|
|
303
|
-
document.body.appendChild(documentFragment);
|
|
304
|
-
return {
|
|
305
|
-
dom,
|
|
306
|
-
destory: () => dom.parentNode?.removeChild(dom)
|
|
307
|
-
};
|
|
308
|
-
}
|
|
309
|
-
_measureDom(dom) {
|
|
310
|
-
const paragraphs = [];
|
|
311
|
-
const fragments = [];
|
|
312
|
-
const characters = [];
|
|
313
|
-
dom.querySelectorAll("li").forEach((li, paragraphIndex) => {
|
|
314
|
-
const pBox = li.getBoundingClientRect();
|
|
315
|
-
paragraphs.push({
|
|
316
|
-
paragraphIndex,
|
|
317
|
-
left: pBox.left,
|
|
318
|
-
top: pBox.top,
|
|
319
|
-
width: pBox.width,
|
|
320
|
-
height: pBox.height
|
|
321
|
-
});
|
|
322
|
-
li.querySelectorAll("span").forEach((span, fragmentIndex) => {
|
|
323
|
-
const fBox = li.getBoundingClientRect();
|
|
324
|
-
fragments.push({
|
|
325
|
-
paragraphIndex,
|
|
326
|
-
fragmentIndex,
|
|
327
|
-
left: fBox.left,
|
|
328
|
-
top: fBox.top,
|
|
329
|
-
width: fBox.width,
|
|
330
|
-
height: fBox.height
|
|
331
|
-
});
|
|
332
|
-
const text = span.firstChild;
|
|
333
|
-
if (text instanceof window.Text) {
|
|
334
|
-
const range = document.createRange();
|
|
335
|
-
range.selectNodeContents(text);
|
|
336
|
-
const len = text.data ? text.data.length : 0;
|
|
337
|
-
let i = 0;
|
|
338
|
-
for (; i <= len; ) {
|
|
339
|
-
range.setStart(text, Math.max(i - 1, 0));
|
|
340
|
-
range.setEnd(text, i);
|
|
341
|
-
const rects = range.getClientRects?.() ?? [range.getBoundingClientRect()];
|
|
342
|
-
let rect = rects[rects.length - 1];
|
|
343
|
-
if (rects.length > 1 && rect.width < 2) {
|
|
344
|
-
rect = rects[rects.length - 2];
|
|
345
|
-
}
|
|
346
|
-
const content = range.toString();
|
|
347
|
-
if (content !== "" && rect && rect.width + rect.height !== 0) {
|
|
348
|
-
characters.push({
|
|
349
|
-
content,
|
|
350
|
-
newParagraphIndex: -1,
|
|
351
|
-
paragraphIndex,
|
|
352
|
-
fragmentIndex,
|
|
353
|
-
characterIndex: i - 1,
|
|
354
|
-
top: rect.top,
|
|
355
|
-
left: rect.left,
|
|
356
|
-
height: rect.height,
|
|
357
|
-
width: rect.width,
|
|
358
|
-
textWidth: -1,
|
|
359
|
-
textHeight: -1
|
|
360
|
-
});
|
|
361
|
-
}
|
|
362
|
-
i++;
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
});
|
|
366
|
-
});
|
|
367
|
-
return {
|
|
368
|
-
paragraphs,
|
|
369
|
-
fragments,
|
|
370
|
-
characters
|
|
371
|
-
};
|
|
372
|
-
}
|
|
373
|
-
measureDom(dom) {
|
|
374
|
-
const { paragraphs } = this._text;
|
|
375
|
-
const rect = dom.getBoundingClientRect();
|
|
376
|
-
const innerEl = dom.querySelector("ul");
|
|
377
|
-
const isVertical = window.getComputedStyle(dom).writingMode.includes("vertical");
|
|
378
|
-
const oldLineHeight = innerEl.style.lineHeight;
|
|
379
|
-
innerEl.style.lineHeight = "4000px";
|
|
380
|
-
const _paragraphs = [[]];
|
|
381
|
-
let fragments = _paragraphs[0];
|
|
382
|
-
const { characters: oldCharacters } = this._measureDom(dom);
|
|
383
|
-
if (oldCharacters.length > 0) {
|
|
384
|
-
fragments.push(oldCharacters[0]);
|
|
385
|
-
oldCharacters.reduce((prev, current) => {
|
|
386
|
-
const attr = isVertical ? "left" : "top";
|
|
387
|
-
if (Math.abs(current[attr] - prev[attr]) > 4e3 / 2) {
|
|
388
|
-
fragments = [];
|
|
389
|
-
_paragraphs.push(fragments);
|
|
390
|
-
}
|
|
391
|
-
fragments.push(current);
|
|
392
|
-
return current;
|
|
393
|
-
});
|
|
394
|
-
}
|
|
395
|
-
innerEl.style.lineHeight = oldLineHeight;
|
|
396
|
-
const measured = this._measureDom(dom);
|
|
397
|
-
measured.paragraphs.forEach((p) => {
|
|
398
|
-
const _p = paragraphs[p.paragraphIndex];
|
|
399
|
-
_p.boundingBox.left = p.left;
|
|
400
|
-
_p.boundingBox.top = p.top;
|
|
401
|
-
_p.boundingBox.width = p.width;
|
|
402
|
-
_p.boundingBox.height = p.height;
|
|
403
|
-
});
|
|
404
|
-
measured.fragments.forEach((f) => {
|
|
405
|
-
const _f = paragraphs[f.paragraphIndex].fragments[f.fragmentIndex];
|
|
406
|
-
_f.boundingBox.left = f.left;
|
|
407
|
-
_f.boundingBox.top = f.top;
|
|
408
|
-
_f.boundingBox.width = f.width;
|
|
409
|
-
_f.boundingBox.height = f.height;
|
|
410
|
-
});
|
|
411
|
-
const results = [];
|
|
412
|
-
let i = 0;
|
|
413
|
-
_paragraphs.forEach((oldCharacters2) => {
|
|
414
|
-
oldCharacters2.forEach((oldCharacter) => {
|
|
415
|
-
const character = measured.characters[i];
|
|
416
|
-
const { paragraphIndex, fragmentIndex, characterIndex } = character;
|
|
417
|
-
results.push({
|
|
418
|
-
...character,
|
|
419
|
-
newParagraphIndex: paragraphIndex,
|
|
420
|
-
textWidth: oldCharacter.width,
|
|
421
|
-
textHeight: oldCharacter.height,
|
|
422
|
-
left: character.left - rect.left,
|
|
423
|
-
top: character.top - rect.top
|
|
424
|
-
});
|
|
425
|
-
const item = paragraphs[paragraphIndex].fragments[fragmentIndex].characters[characterIndex];
|
|
426
|
-
item.boundingBox.left = results[i].left;
|
|
427
|
-
item.boundingBox.top = results[i].top;
|
|
428
|
-
item.boundingBox.width = results[i].width;
|
|
429
|
-
item.boundingBox.height = results[i].height;
|
|
430
|
-
item.textWidth = results[i].textWidth;
|
|
431
|
-
item.textHeight = results[i].textHeight;
|
|
432
|
-
i++;
|
|
433
|
-
});
|
|
434
|
-
});
|
|
435
|
-
return {
|
|
436
|
-
paragraphs,
|
|
437
|
-
boundingBox: new modernPath2d.BoundingBox(0, 0, rect.width, rect.height)
|
|
438
|
-
};
|
|
439
|
-
}
|
|
440
|
-
measure(dom) {
|
|
441
|
-
let destory;
|
|
442
|
-
if (!dom) {
|
|
443
|
-
({ dom, destory } = this.createDom());
|
|
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();
|
|
444
79
|
}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
80
|
+
if (strokeStyle !== "none") {
|
|
81
|
+
ctx.stroke();
|
|
82
|
+
}
|
|
83
|
+
ctx.restore();
|
|
84
|
+
});
|
|
449
85
|
}
|
|
450
86
|
|
|
451
87
|
function filterEmpty(val) {
|
|
@@ -510,10 +146,10 @@ function getPointPosition(point, startPoint, rotation = 0, skewX = 0, skewY = 0,
|
|
|
510
146
|
return points[0];
|
|
511
147
|
}
|
|
512
148
|
|
|
513
|
-
var __defProp$
|
|
514
|
-
var __defNormalProp$
|
|
515
|
-
var __publicField$
|
|
516
|
-
__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);
|
|
517
153
|
return value;
|
|
518
154
|
};
|
|
519
155
|
const set1 = /* @__PURE__ */ new Set(["\xA9", "\xAE", "\xF7"]);
|
|
@@ -534,10 +170,10 @@ class Character {
|
|
|
534
170
|
this.content = content;
|
|
535
171
|
this.index = index;
|
|
536
172
|
this.parent = parent;
|
|
537
|
-
__publicField$
|
|
538
|
-
__publicField$
|
|
539
|
-
__publicField$
|
|
540
|
-
__publicField$
|
|
173
|
+
__publicField$4(this, "boundingBox", new modernPath2d.BoundingBox());
|
|
174
|
+
__publicField$4(this, "path", new modernPath2d.Path2D());
|
|
175
|
+
__publicField$4(this, "textWidth", 0);
|
|
176
|
+
__publicField$4(this, "textHeight", 0);
|
|
541
177
|
}
|
|
542
178
|
get computedStyle() {
|
|
543
179
|
return this.parent.computedStyle;
|
|
@@ -735,79 +371,443 @@ class Character {
|
|
|
735
371
|
this.updatePath();
|
|
736
372
|
return this;
|
|
737
373
|
}
|
|
738
|
-
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 modernPath2d.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 modernPath2d.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 modernPath2d.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 modernPath2d.BoundingBox();
|
|
514
|
+
}
|
|
515
|
+
const min = modernPath2d.Point2D.MAX;
|
|
516
|
+
const max = modernPath2d.Point2D.MIN;
|
|
517
|
+
this.paths.forEach((path) => path.getMinMax(min, max));
|
|
518
|
+
return new modernPath2d.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: modernPath2d.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 = modernPath2d.parseSvgToDom(url);
|
|
549
|
+
const paths = modernPath2d.parseSvg(svg);
|
|
550
|
+
const min = modernPath2d.Point2D.MAX;
|
|
551
|
+
const max = modernPath2d.Point2D.MIN;
|
|
552
|
+
paths.forEach((path) => path.getMinMax(min, max));
|
|
553
|
+
const { x, y, width, height } = new modernPath2d.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 modernPath2d.BoundingBox(x, y, width, height),
|
|
558
|
+
viewBox: new modernPath2d.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 modernPath2d.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 modernPath2d.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 }) {
|
|
739
597
|
drawPaths({
|
|
740
598
|
ctx,
|
|
741
|
-
paths:
|
|
742
|
-
fontSize: this.computedStyle.fontSize,
|
|
743
|
-
|
|
744
|
-
...config
|
|
599
|
+
paths: this.paths,
|
|
600
|
+
fontSize: this._text.computedStyle.fontSize,
|
|
601
|
+
fill: false
|
|
745
602
|
});
|
|
603
|
+
return this;
|
|
746
604
|
}
|
|
747
605
|
}
|
|
748
606
|
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
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;
|
|
767
627
|
}
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
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)
|
|
772
670
|
};
|
|
773
|
-
return this;
|
|
774
671
|
}
|
|
775
|
-
|
|
672
|
+
_measureDom(dom) {
|
|
673
|
+
const paragraphs = [];
|
|
674
|
+
const fragments = [];
|
|
776
675
|
const characters = [];
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
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
|
+
};
|
|
799
735
|
}
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
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 modernPath2d.BoundingBox(0, 0, rect.width, rect.height)
|
|
804
801
|
};
|
|
805
|
-
return this;
|
|
806
802
|
}
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
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;
|
|
811
811
|
}
|
|
812
812
|
}
|
|
813
813
|
|
|
@@ -866,6 +866,10 @@ class Parser extends Feature {
|
|
|
866
866
|
}
|
|
867
867
|
}
|
|
868
868
|
|
|
869
|
+
class Reflector extends Feature {
|
|
870
|
+
// TODO
|
|
871
|
+
}
|
|
872
|
+
|
|
869
873
|
class Renderer2D extends Feature {
|
|
870
874
|
setupView(options) {
|
|
871
875
|
const { ctx, pixelRatio } = options;
|
|
@@ -966,6 +970,7 @@ class Text {
|
|
|
966
970
|
__publicField(this, "style");
|
|
967
971
|
__publicField(this, "effects");
|
|
968
972
|
__publicField(this, "deformation");
|
|
973
|
+
__publicField(this, "measureDom");
|
|
969
974
|
__publicField(this, "needsUpdate", true);
|
|
970
975
|
__publicField(this, "computedStyle", { ...defaultTextStyles });
|
|
971
976
|
__publicField(this, "paragraphs", []);
|
|
@@ -977,16 +982,17 @@ class Text {
|
|
|
977
982
|
__publicField(this, "_effector", new Effector(this));
|
|
978
983
|
__publicField(this, "_highlighter", new Highlighter(this));
|
|
979
984
|
__publicField(this, "_renderer2D", new Renderer2D(this));
|
|
980
|
-
const { content = "", style = {}, effects, deformation } = options;
|
|
985
|
+
const { content = "", style = {}, effects, deformation, measureDom } = options;
|
|
981
986
|
this.content = content;
|
|
982
987
|
this.style = style;
|
|
983
988
|
this.effects = effects;
|
|
984
989
|
this.deformation = deformation;
|
|
990
|
+
this.measureDom = measureDom;
|
|
985
991
|
}
|
|
986
992
|
get characters() {
|
|
987
993
|
return this.paragraphs.flatMap((p) => p.fragments.flatMap((f) => f.characters));
|
|
988
994
|
}
|
|
989
|
-
measure(dom) {
|
|
995
|
+
measure(dom = this.measureDom) {
|
|
990
996
|
this.computedStyle = { ...defaultTextStyles, ...this.style };
|
|
991
997
|
this.paragraphs = this._parser.parse();
|
|
992
998
|
return this._measurer.measure(dom);
|
|
@@ -1026,27 +1032,48 @@ class Text {
|
|
|
1026
1032
|
this._effector.getBoundingBox(),
|
|
1027
1033
|
this._highlighter.getBoundingBox()
|
|
1028
1034
|
);
|
|
1029
|
-
this._renderer2D.setupView({ pixelRatio, ctx });
|
|
1030
|
-
this._renderer2D.uploadColors({ ctx });
|
|
1031
|
-
this._highlighter.draw({ ctx });
|
|
1032
|
-
this._effector.draw({ ctx });
|
|
1033
1035
|
} else {
|
|
1034
1036
|
this.renderBoundingBox = modernPath2d.BoundingBox.from(
|
|
1035
1037
|
this.boundingBox,
|
|
1036
1038
|
this.renderBoundingBox,
|
|
1037
1039
|
this._highlighter.getBoundingBox()
|
|
1038
1040
|
);
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
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 {
|
|
1042
1048
|
this._renderer2D.draw({ ctx });
|
|
1043
1049
|
}
|
|
1044
1050
|
return this;
|
|
1045
1051
|
}
|
|
1046
1052
|
}
|
|
1047
1053
|
|
|
1054
|
+
exports.Character = Character;
|
|
1055
|
+
exports.Deformer = Deformer;
|
|
1056
|
+
exports.Effector = Effector;
|
|
1057
|
+
exports.Fragment = Fragment;
|
|
1058
|
+
exports.Highlighter = Highlighter;
|
|
1059
|
+
exports.Measurer = Measurer;
|
|
1060
|
+
exports.Paragraph = Paragraph;
|
|
1061
|
+
exports.Parser = Parser;
|
|
1062
|
+
exports.Reflector = Reflector;
|
|
1063
|
+
exports.Renderer2D = Renderer2D;
|
|
1048
1064
|
exports.Text = Text;
|
|
1049
1065
|
exports.defaultTextStyles = defaultTextStyles;
|
|
1066
|
+
exports.drawPaths = drawPaths;
|
|
1067
|
+
exports.filterEmpty = filterEmpty;
|
|
1068
|
+
exports.getPointPosition = getPointPosition;
|
|
1069
|
+
exports.getRotationPoint = getRotationPoint;
|
|
1070
|
+
exports.getScalePoint = getScalePoint;
|
|
1071
|
+
exports.getSkewPoint = getSkewPoint;
|
|
1072
|
+
exports.parseColor = parseColor;
|
|
1073
|
+
exports.uploadColor = uploadColor;
|
|
1074
|
+
Object.keys(modernPath2d).forEach(function (k) {
|
|
1075
|
+
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) exports[k] = modernPath2d[k];
|
|
1076
|
+
});
|
|
1050
1077
|
Object.keys(modernFont).forEach(function (k) {
|
|
1051
1078
|
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) exports[k] = modernFont[k];
|
|
1052
1079
|
});
|