@next2d/text 1.17.5 → 1.18.0
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/TextData.js +0 -3
- package/dist/TextFormat.d.ts +12 -0
- package/dist/TextFormat.js +88 -1
- package/dist/TextParser.js +192 -33
- package/dist/interface/OptionsImpl.d.ts +2 -0
- package/package.json +2 -2
package/dist/TextData.js
CHANGED
package/dist/TextFormat.d.ts
CHANGED
|
@@ -191,6 +191,18 @@ export declare class TextFormat {
|
|
|
191
191
|
*/
|
|
192
192
|
get underline(): boolean | null;
|
|
193
193
|
set underline(underline: boolean | null);
|
|
194
|
+
/**
|
|
195
|
+
* @return {string}
|
|
196
|
+
* @method
|
|
197
|
+
* @private
|
|
198
|
+
*/
|
|
199
|
+
_$toStyleString(): string;
|
|
200
|
+
/**
|
|
201
|
+
* @return {boolean}
|
|
202
|
+
* @method
|
|
203
|
+
* @private
|
|
204
|
+
*/
|
|
205
|
+
_$isSame(text_format: TextFormat): boolean;
|
|
194
206
|
/**
|
|
195
207
|
* @return {next2d.text.TextFormat}
|
|
196
208
|
* @method
|
package/dist/TextFormat.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $clamp, $toColorInt } from "@next2d/share";
|
|
1
|
+
import { $clamp, $intToRGBA, $toColorInt } from "@next2d/share";
|
|
2
2
|
/**
|
|
3
3
|
* TextFormat クラスは、文字フォーマット情報を表します。
|
|
4
4
|
* TextFormat クラスを使用して、テキストフィールドに特定のテキストフォーマットを作成します。
|
|
@@ -304,6 +304,93 @@ export class TextFormat {
|
|
|
304
304
|
set underline(underline) {
|
|
305
305
|
this._$underline = underline !== null ? !!underline : null;
|
|
306
306
|
}
|
|
307
|
+
/**
|
|
308
|
+
* @return {string}
|
|
309
|
+
* @method
|
|
310
|
+
* @private
|
|
311
|
+
*/
|
|
312
|
+
_$toStyleString() {
|
|
313
|
+
let style = "";
|
|
314
|
+
if (this._$font) {
|
|
315
|
+
style += `font-family: ${this._$font};`;
|
|
316
|
+
}
|
|
317
|
+
if (this._$size) {
|
|
318
|
+
style += `font-size: ${this._$size}px;`;
|
|
319
|
+
}
|
|
320
|
+
if (this._$color) {
|
|
321
|
+
const color = $intToRGBA($toColorInt(this._$color));
|
|
322
|
+
const R = color.R.toString(16).padStart(2, "0");
|
|
323
|
+
const G = color.G.toString(16).padStart(2, "0");
|
|
324
|
+
const B = color.B.toString(16).padStart(2, "0");
|
|
325
|
+
style += `color: #${R}${G}${B};`;
|
|
326
|
+
}
|
|
327
|
+
if (this._$bold) {
|
|
328
|
+
style += "font-weight: bold;";
|
|
329
|
+
}
|
|
330
|
+
if (this._$italic) {
|
|
331
|
+
style += "font-style: italic;";
|
|
332
|
+
}
|
|
333
|
+
if (this._$underline) {
|
|
334
|
+
style += "text-decoration: underline;";
|
|
335
|
+
}
|
|
336
|
+
if (this._$align) {
|
|
337
|
+
style += `text-align: ${this._$align};`;
|
|
338
|
+
}
|
|
339
|
+
if (this._$leftMargin) {
|
|
340
|
+
style += `margin-left: ${this._$leftMargin}px;`;
|
|
341
|
+
}
|
|
342
|
+
if (this._$rightMargin) {
|
|
343
|
+
style += `margin-right: ${this._$rightMargin}px;`;
|
|
344
|
+
}
|
|
345
|
+
if (this._$leading) {
|
|
346
|
+
style += `margin-bottom: ${this._$leading}px;`;
|
|
347
|
+
}
|
|
348
|
+
if (this._$letterSpacing) {
|
|
349
|
+
style += `letter-spacing: ${this._$letterSpacing}px;`;
|
|
350
|
+
}
|
|
351
|
+
return style;
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* @return {boolean}
|
|
355
|
+
* @method
|
|
356
|
+
* @private
|
|
357
|
+
*/
|
|
358
|
+
_$isSame(text_format) {
|
|
359
|
+
if (this._$font !== text_format.font) {
|
|
360
|
+
return false;
|
|
361
|
+
}
|
|
362
|
+
if (this._$size !== text_format.size) {
|
|
363
|
+
return false;
|
|
364
|
+
}
|
|
365
|
+
if (this._$color !== text_format.color) {
|
|
366
|
+
return false;
|
|
367
|
+
}
|
|
368
|
+
if (this._$bold !== text_format.bold) {
|
|
369
|
+
return false;
|
|
370
|
+
}
|
|
371
|
+
if (this._$italic !== text_format.italic) {
|
|
372
|
+
return false;
|
|
373
|
+
}
|
|
374
|
+
if (this._$underline !== text_format.underline) {
|
|
375
|
+
return false;
|
|
376
|
+
}
|
|
377
|
+
if (this._$align !== text_format.align) {
|
|
378
|
+
return false;
|
|
379
|
+
}
|
|
380
|
+
if (this._$leftMargin !== text_format.leftMargin) {
|
|
381
|
+
return false;
|
|
382
|
+
}
|
|
383
|
+
if (this._$rightMargin !== text_format.rightMargin) {
|
|
384
|
+
return false;
|
|
385
|
+
}
|
|
386
|
+
if (this._$leading !== text_format.leading) {
|
|
387
|
+
return false;
|
|
388
|
+
}
|
|
389
|
+
if (this._$letterSpacing !== text_format.letterSpacing) {
|
|
390
|
+
return false;
|
|
391
|
+
}
|
|
392
|
+
return true;
|
|
393
|
+
}
|
|
307
394
|
/**
|
|
308
395
|
* @return {next2d.text.TextFormat}
|
|
309
396
|
* @method
|
package/dist/TextParser.js
CHANGED
|
@@ -24,6 +24,9 @@ const _$parseText = (texts, text_format, text_data, options) => {
|
|
|
24
24
|
let line = text_data.lineTable.length - 1;
|
|
25
25
|
const maxWidth = options.width - text_format._$widthMargin() - 4;
|
|
26
26
|
for (let idx = 0; idx < texts.length; ++idx) {
|
|
27
|
+
const textFormat = options.textFormats === null
|
|
28
|
+
? text_format
|
|
29
|
+
: options.textFormats.shift();
|
|
27
30
|
const text = texts[idx];
|
|
28
31
|
const object = {
|
|
29
32
|
"mode": "text",
|
|
@@ -33,58 +36,192 @@ const _$parseText = (texts, text_format, text_data, options) => {
|
|
|
33
36
|
"w": 0,
|
|
34
37
|
"h": 0,
|
|
35
38
|
"line": line,
|
|
36
|
-
"textFormat":
|
|
39
|
+
"textFormat": textFormat._$clone()
|
|
37
40
|
};
|
|
38
|
-
|
|
39
|
-
&& text === "\n"
|
|
40
|
-
|| text === "\r"
|
|
41
|
-
|| text === "\n\r";
|
|
42
|
-
$context.font = text_format._$generateFontStyle();
|
|
41
|
+
$context.font = textFormat._$generateFontStyle();
|
|
43
42
|
const mesure = $context.measureText(text || "");
|
|
44
43
|
let width = mesure.width;
|
|
45
|
-
if (
|
|
46
|
-
width +=
|
|
44
|
+
if (textFormat.letterSpacing) {
|
|
45
|
+
width += textFormat.letterSpacing;
|
|
47
46
|
}
|
|
48
47
|
let height = mesure.fontBoundingBoxAscent + mesure.fontBoundingBoxDescent;
|
|
49
|
-
if (
|
|
50
|
-
height +=
|
|
48
|
+
if (textFormat.leading) {
|
|
49
|
+
height += textFormat.leading;
|
|
51
50
|
}
|
|
52
51
|
// setup
|
|
53
|
-
object.x =
|
|
54
|
-
object.y = mesure.
|
|
52
|
+
object.x = 0;
|
|
53
|
+
object.y = mesure.fontBoundingBoxAscent;
|
|
55
54
|
object.w = width;
|
|
56
55
|
object.h = height;
|
|
57
56
|
$currentWidth += width;
|
|
58
|
-
if (
|
|
57
|
+
if (options.wordWrap && $currentWidth > maxWidth) {
|
|
59
58
|
$currentWidth = width;
|
|
60
59
|
// update
|
|
61
60
|
line++;
|
|
62
61
|
object.line = line;
|
|
63
62
|
// break object
|
|
64
63
|
const wrapObject = {
|
|
65
|
-
"mode":
|
|
64
|
+
"mode": "wrap",
|
|
66
65
|
"text": "",
|
|
67
66
|
"x": 0,
|
|
68
67
|
"y": 0,
|
|
69
68
|
"w": 0,
|
|
70
69
|
"h": 0,
|
|
71
70
|
"line": line,
|
|
72
|
-
"textFormat":
|
|
71
|
+
"textFormat": textFormat._$clone()
|
|
73
72
|
};
|
|
73
|
+
let chunkLength = 1;
|
|
74
|
+
let isSeparated = true;
|
|
75
|
+
const pattern = /[0-9a-zA-Z?!;:.,?!。、;:〜]/g;
|
|
76
|
+
for (;;) {
|
|
77
|
+
const index = text_data.textTable.length - chunkLength;
|
|
78
|
+
if (0 >= index) {
|
|
79
|
+
isSeparated = false;
|
|
80
|
+
chunkLength = 0;
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
const prevObj = text_data.textTable[index];
|
|
84
|
+
if (!prevObj) {
|
|
85
|
+
isSeparated = false;
|
|
86
|
+
chunkLength = 0;
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
if (prevObj.mode !== "text") {
|
|
90
|
+
isSeparated = false;
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
if (prevObj.text === " ") {
|
|
94
|
+
chunkLength--;
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
if (!prevObj.text.match(pattern)) {
|
|
98
|
+
chunkLength--;
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
chunkLength++;
|
|
102
|
+
}
|
|
74
103
|
// new line
|
|
75
104
|
text_data.widthTable[line] = 0;
|
|
76
105
|
text_data.heightTable[line] = 0;
|
|
77
106
|
text_data.ascentTable[line] = 0;
|
|
78
|
-
|
|
79
|
-
|
|
107
|
+
if (chunkLength > 0 && isSeparated) {
|
|
108
|
+
const insertIdx = text_data.textTable.length - chunkLength;
|
|
109
|
+
text_data.textTable.splice(insertIdx, 0, wrapObject);
|
|
110
|
+
text_data.lineTable.push(wrapObject);
|
|
111
|
+
const prevLine = line - 1;
|
|
112
|
+
// reset
|
|
113
|
+
text_data.widthTable[prevLine] = 0;
|
|
114
|
+
text_data.heightTable[prevLine] = 0;
|
|
115
|
+
text_data.ascentTable[prevLine] = 0;
|
|
116
|
+
for (let idx = 0; idx < insertIdx; ++idx) {
|
|
117
|
+
const textObject = text_data.textTable[idx];
|
|
118
|
+
if (textObject.line !== prevLine) {
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
if (textObject.mode !== "text") {
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
text_data.widthTable[prevLine] += textObject.w;
|
|
125
|
+
text_data.heightTable[prevLine] = Math.max(text_data.heightTable[prevLine], textObject.h);
|
|
126
|
+
text_data.ascentTable[prevLine] = Math.max(text_data.ascentTable[prevLine], textObject.y);
|
|
127
|
+
}
|
|
128
|
+
// reset
|
|
129
|
+
$currentWidth = 0;
|
|
130
|
+
for (let idx = insertIdx + 1; idx < text_data.textTable.length; ++idx) {
|
|
131
|
+
const textObject = text_data.textTable[idx];
|
|
132
|
+
textObject.line = line;
|
|
133
|
+
$currentWidth += textObject.w;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
text_data.textTable.push(wrapObject);
|
|
138
|
+
text_data.lineTable.push(wrapObject);
|
|
139
|
+
}
|
|
80
140
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
141
|
+
text_data.widthTable[line] = $currentWidth;
|
|
142
|
+
text_data.heightTable[line] = Math.max(text_data.heightTable[line], height);
|
|
143
|
+
text_data.ascentTable[line] = Math.max(text_data.ascentTable[line], object.y);
|
|
144
|
+
text_data.textTable.push(object);
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* @param {string} value
|
|
149
|
+
* @param {TextFormat} text_format
|
|
150
|
+
* @return {void}
|
|
151
|
+
* @method
|
|
152
|
+
* @private
|
|
153
|
+
*/
|
|
154
|
+
const _$parseStyle = (value, text_format, options) => {
|
|
155
|
+
const values = value
|
|
156
|
+
.trim()
|
|
157
|
+
.split(";");
|
|
158
|
+
const attributes = [];
|
|
159
|
+
for (let idx = 0; idx < values.length; ++idx) {
|
|
160
|
+
const styleValue = values[idx];
|
|
161
|
+
if (!styleValue) {
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
const styles = styleValue.split(":");
|
|
165
|
+
const name = styles[0].trim();
|
|
166
|
+
const value = styles[1].trim();
|
|
167
|
+
switch (name) {
|
|
168
|
+
case "font-size":
|
|
169
|
+
attributes.push({
|
|
170
|
+
"name": "size",
|
|
171
|
+
"value": parseFloat(value)
|
|
172
|
+
});
|
|
173
|
+
break;
|
|
174
|
+
case "font-family":
|
|
175
|
+
attributes.push({
|
|
176
|
+
"name": "face",
|
|
177
|
+
"value": value.replace(/'|"/g, "")
|
|
178
|
+
});
|
|
179
|
+
break;
|
|
180
|
+
case "letter-spacing":
|
|
181
|
+
attributes.push({
|
|
182
|
+
"name": "letterSpacing",
|
|
183
|
+
"value": value
|
|
184
|
+
});
|
|
185
|
+
break;
|
|
186
|
+
case "margin-bottom":
|
|
187
|
+
attributes.push({
|
|
188
|
+
"name": "leading",
|
|
189
|
+
"value": parseFloat(value)
|
|
190
|
+
});
|
|
191
|
+
break;
|
|
192
|
+
case "margin-left":
|
|
193
|
+
attributes.push({
|
|
194
|
+
"name": "leftMargin",
|
|
195
|
+
"value": parseFloat(value)
|
|
196
|
+
});
|
|
197
|
+
break;
|
|
198
|
+
case "margin-right":
|
|
199
|
+
attributes.push({
|
|
200
|
+
"name": "rightMargin",
|
|
201
|
+
"value": parseFloat(value)
|
|
202
|
+
});
|
|
203
|
+
break;
|
|
204
|
+
case "color":
|
|
205
|
+
case "align":
|
|
206
|
+
attributes.push({
|
|
207
|
+
"name": name,
|
|
208
|
+
"value": value
|
|
209
|
+
});
|
|
210
|
+
break;
|
|
211
|
+
case "text-decoration":
|
|
212
|
+
case "font-weight":
|
|
213
|
+
case "font-style":
|
|
214
|
+
attributes.push({
|
|
215
|
+
"name": value,
|
|
216
|
+
"value": true
|
|
217
|
+
});
|
|
218
|
+
break;
|
|
219
|
+
default:
|
|
220
|
+
break;
|
|
86
221
|
}
|
|
87
222
|
}
|
|
223
|
+
// eslint-disable-next-line no-use-before-define
|
|
224
|
+
_$setAttributes(attributes, text_format, options);
|
|
88
225
|
};
|
|
89
226
|
/**
|
|
90
227
|
* @param {array} attributes
|
|
@@ -98,6 +235,9 @@ const _$setAttributes = (attributes, text_format, options) => {
|
|
|
98
235
|
for (let idx = 0; idx < attributes.length; ++idx) {
|
|
99
236
|
const object = attributes[idx];
|
|
100
237
|
switch (object.name) {
|
|
238
|
+
case "style":
|
|
239
|
+
_$parseStyle(object.value, text_format, options);
|
|
240
|
+
break;
|
|
101
241
|
case "align":
|
|
102
242
|
text_format.align = object.value;
|
|
103
243
|
break;
|
|
@@ -128,6 +268,15 @@ const _$setAttributes = (attributes, text_format, options) => {
|
|
|
128
268
|
case "rightMargin":
|
|
129
269
|
text_format.rightMargin = +object.value;
|
|
130
270
|
break;
|
|
271
|
+
case "underline":
|
|
272
|
+
text_format.underline = true;
|
|
273
|
+
break;
|
|
274
|
+
case "bold":
|
|
275
|
+
text_format.bold = true;
|
|
276
|
+
break;
|
|
277
|
+
case "italic":
|
|
278
|
+
text_format.italic = true;
|
|
279
|
+
break;
|
|
131
280
|
default:
|
|
132
281
|
break;
|
|
133
282
|
}
|
|
@@ -244,20 +393,27 @@ const _$adjustmentHeight = (text_data) => {
|
|
|
244
393
|
* @public
|
|
245
394
|
*/
|
|
246
395
|
export const parsePlainText = (text, text_format, options) => {
|
|
396
|
+
const textData = new TextData();
|
|
397
|
+
if (!text) {
|
|
398
|
+
return textData;
|
|
399
|
+
}
|
|
247
400
|
const lineText = options.multiline
|
|
248
401
|
? text.split("\n")
|
|
249
402
|
: [text.replace("\n", "")];
|
|
250
|
-
const textData = new TextData();
|
|
251
|
-
// clone
|
|
252
|
-
const textFormat = text_format._$clone();
|
|
253
|
-
if (options.subFontSize
|
|
254
|
-
&& options.subFontSize > 0 && textFormat.size) {
|
|
255
|
-
textFormat.size -= options.subFontSize;
|
|
256
|
-
if (1 > textFormat.size) {
|
|
257
|
-
textFormat.size = 1;
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
403
|
for (let idx = 0; idx < lineText.length; ++idx) {
|
|
404
|
+
let textFormat = text_format._$clone();
|
|
405
|
+
if (options.textFormats) {
|
|
406
|
+
textFormat = idx === 0
|
|
407
|
+
? options.textFormats[0]
|
|
408
|
+
: options.textFormats.shift();
|
|
409
|
+
}
|
|
410
|
+
if (options.subFontSize
|
|
411
|
+
&& options.subFontSize > 0 && textFormat.size) {
|
|
412
|
+
textFormat.size -= options.subFontSize;
|
|
413
|
+
if (1 > textFormat.size) {
|
|
414
|
+
textFormat.size = 1;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
261
417
|
if (idx === 0 || options.wordWrap || options.multiline) {
|
|
262
418
|
_$createNewLine(textData, textFormat);
|
|
263
419
|
}
|
|
@@ -280,11 +436,14 @@ export const parsePlainText = (text, text_format, options) => {
|
|
|
280
436
|
* @public
|
|
281
437
|
*/
|
|
282
438
|
export const parseHtmlText = (html_text, text_format, options) => {
|
|
439
|
+
const textData = new TextData();
|
|
440
|
+
if (!html_text) {
|
|
441
|
+
return textData;
|
|
442
|
+
}
|
|
283
443
|
const htmlText = html_text
|
|
284
444
|
.trim()
|
|
285
445
|
.replace(/\r?\n/g, "")
|
|
286
446
|
.replace(/\t/g, "");
|
|
287
|
-
const textData = new TextData();
|
|
288
447
|
const textFormat = text_format._$clone();
|
|
289
448
|
if (options.subFontSize && options.subFontSize > 0 && textFormat.size) {
|
|
290
449
|
textFormat.size -= options.subFontSize;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@next2d/text",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.18.0",
|
|
4
4
|
"description": "Next2D Text Packages",
|
|
5
5
|
"author": "Toshiyuki Ienaga<ienaga@tvon.jp> (https://github.com/ienaga/)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -32,6 +32,6 @@
|
|
|
32
32
|
"url": "git+https://github.com/Next2D/Player.git"
|
|
33
33
|
},
|
|
34
34
|
"peerDependencies": {
|
|
35
|
-
"@next2d/share": "1.
|
|
35
|
+
"@next2d/share": "1.18.0"
|
|
36
36
|
}
|
|
37
37
|
}
|