@next2d/text 1.16.0 → 1.17.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.
@@ -0,0 +1,300 @@
1
+ import { parseDocument } from "htmlparser2";
2
+ import { TextData } from "./TextData";
3
+ import { $toColorInt } from "@next2d/share";
4
+ /**
5
+ * @type {OffscreenCanvasRenderingContext2D}
6
+ * @private
7
+ */
8
+ const $context = new OffscreenCanvas(1, 1).getContext("2d");
9
+ /**
10
+ * @type {number}
11
+ * @private
12
+ */
13
+ let $currentWidth = 0;
14
+ /**
15
+ * @param {string} texts
16
+ * @param {TextFormat} text_format
17
+ * @param {TextData} text_data
18
+ * @param {object} options
19
+ * @return {void}
20
+ * @method
21
+ * @private
22
+ */
23
+ const _$parseText = (texts, text_format, text_data, options) => {
24
+ let line = text_data.lineTable.length - 1;
25
+ const maxWidth = options.width - text_format._$widthMargin() - 4;
26
+ for (let idx = 0; idx < texts.length; ++idx) {
27
+ const text = texts[idx];
28
+ const object = {
29
+ "mode": "text",
30
+ "text": text,
31
+ "x": 0,
32
+ "y": 0,
33
+ "w": 0,
34
+ "h": 0,
35
+ "line": line,
36
+ "textFormat": text_format._$clone()
37
+ };
38
+ const breakCode = options.multiline
39
+ && text === "\n"
40
+ || text === "\r"
41
+ || text === "\n\r";
42
+ $context.font = text_format._$generateFontStyle();
43
+ const mesure = $context.measureText(text || "");
44
+ let width = mesure.width;
45
+ if (text_format.letterSpacing) {
46
+ width += text_format.letterSpacing;
47
+ }
48
+ let height = mesure.fontBoundingBoxAscent + mesure.fontBoundingBoxDescent;
49
+ if (text_format.leading) {
50
+ height += text_format.leading;
51
+ }
52
+ // setup
53
+ object.x = mesure.actualBoundingBoxLeft;
54
+ object.y = mesure.actualBoundingBoxAscent;
55
+ object.w = width;
56
+ object.h = height;
57
+ $currentWidth += width;
58
+ if (breakCode || options.wordWrap && $currentWidth > maxWidth) {
59
+ $currentWidth = width;
60
+ // update
61
+ line++;
62
+ object.line = line;
63
+ // break object
64
+ const wrapObject = {
65
+ "mode": breakCode ? "break" : "wrap",
66
+ "text": "",
67
+ "x": 0,
68
+ "y": 0,
69
+ "w": 0,
70
+ "h": 0,
71
+ "line": line,
72
+ "textFormat": text_format._$clone()
73
+ };
74
+ // new line
75
+ text_data.widthTable[line] = 0;
76
+ text_data.heightTable[line] = 0;
77
+ text_data.ascentTable[line] = 0;
78
+ text_data.textTable.push(wrapObject);
79
+ text_data.lineTable.push(wrapObject);
80
+ }
81
+ if (!breakCode) {
82
+ text_data.widthTable[line] = $currentWidth;
83
+ text_data.heightTable[line] = Math.max(text_data.heightTable[line], height);
84
+ text_data.ascentTable[line] = Math.max(text_data.ascentTable[line], object.y);
85
+ text_data.textTable.push(object);
86
+ }
87
+ }
88
+ };
89
+ /**
90
+ * @param {array} attributes
91
+ * @param {TextFormat} text_format
92
+ * @param {object} options
93
+ * @return {void}
94
+ * @method
95
+ * @private
96
+ */
97
+ const _$setAttributes = (attributes, text_format, options) => {
98
+ for (let idx = 0; idx < attributes.length; ++idx) {
99
+ const object = attributes[idx];
100
+ switch (object.name) {
101
+ case "align":
102
+ text_format.align = object.value;
103
+ break;
104
+ case "face":
105
+ text_format.font = object.value;
106
+ break;
107
+ case "size":
108
+ text_format.size = +object.value;
109
+ if (options.subFontSize) {
110
+ text_format.size -= options.subFontSize;
111
+ if (1 > text_format.size) {
112
+ text_format.size = 1;
113
+ }
114
+ }
115
+ break;
116
+ case "color":
117
+ text_format.color = $toColorInt(object.value);
118
+ break;
119
+ case "letterSpacing":
120
+ text_format.letterSpacing = +object.value;
121
+ break;
122
+ case "leading":
123
+ text_format.leading = +object.value;
124
+ break;
125
+ case "leftMargin":
126
+ text_format.leftMargin = +object.value;
127
+ break;
128
+ case "rightMargin":
129
+ text_format.rightMargin = +object.value;
130
+ break;
131
+ default:
132
+ break;
133
+ }
134
+ }
135
+ };
136
+ /**
137
+ * @param {TextData} text_data
138
+ * @param {TextFormat} text_format
139
+ * @return {void}
140
+ * @method
141
+ * @private
142
+ */
143
+ const _$createNewLine = (text_data, text_format) => {
144
+ $currentWidth = 0;
145
+ const line = text_data.lineTable.length;
146
+ $context.font = text_format._$generateFontStyle();
147
+ const mesure = $context.measureText("");
148
+ const object = {
149
+ "mode": "break",
150
+ "text": "",
151
+ "x": 0,
152
+ "y": 0,
153
+ "w": 0,
154
+ "h": mesure.fontBoundingBoxAscent + mesure.fontBoundingBoxDescent,
155
+ "line": line,
156
+ "textFormat": text_format._$clone()
157
+ };
158
+ text_data.heightTable[line] = 0;
159
+ text_data.ascentTable[line] = 0;
160
+ text_data.widthTable[line] = 0;
161
+ // register
162
+ text_data.lineTable.push(object);
163
+ text_data.textTable.push(object);
164
+ };
165
+ /**
166
+ * @param {object} document
167
+ * @param {TextFormat} text_format
168
+ * @param {TextData} text_data
169
+ * @param {object} options
170
+ * @return {void}
171
+ * @method
172
+ * @private
173
+ */
174
+ const _$parseTag = (document, text_format, text_data, options) => {
175
+ for (let idx = 0; idx < document.children.length; ++idx) {
176
+ const node = document.children[idx];
177
+ if (node.nodeType === 3) {
178
+ _$parseText(node.nodeValue || "", text_format, text_data, options);
179
+ continue;
180
+ }
181
+ const tf = text_format._$clone();
182
+ switch (node.name.toUpperCase()) {
183
+ case "DIV": // div tag
184
+ case "P": // p tag
185
+ _$setAttributes(node.attributes, tf, options);
186
+ if (options.multiline) {
187
+ _$createNewLine(text_data, tf);
188
+ }
189
+ _$parseTag(node, tf, text_data, options);
190
+ if (options.multiline) {
191
+ _$createNewLine(text_data, tf);
192
+ }
193
+ continue;
194
+ case "U": // underline
195
+ tf.underline = true;
196
+ break;
197
+ case "B": // bold
198
+ tf.bold = true;
199
+ break;
200
+ case "I": // italic
201
+ tf.italic = true;
202
+ break;
203
+ case "FONT": // FONT tag
204
+ case "SPAN": // SPAN tag
205
+ _$setAttributes(node.attributes, tf, options);
206
+ break;
207
+ case "BR":
208
+ if (!options.multiline) {
209
+ continue;
210
+ }
211
+ _$createNewLine(text_data, tf);
212
+ break;
213
+ default:
214
+ break;
215
+ }
216
+ _$parseTag(node, tf, text_data, options);
217
+ }
218
+ };
219
+ /**
220
+ * @param {TextData} text_data
221
+ * @return {void}
222
+ * @method
223
+ * @private
224
+ */
225
+ const _$adjustmentHeight = (text_data) => {
226
+ const length = text_data.heightTable.length - 1;
227
+ for (let idx = 1; idx < length; ++idx) {
228
+ const height = text_data.heightTable[idx];
229
+ if (height > 0) {
230
+ continue;
231
+ }
232
+ // 改行があって、高さの設定がなければ前の行の高さを設定する
233
+ const object = text_data.lineTable[idx];
234
+ text_data.heightTable[idx] = object.h = text_data.heightTable[idx - 1];
235
+ }
236
+ };
237
+ /**
238
+ * @description 文字列のテキストを分解・解析して配列戻す
239
+ *
240
+ * @param {string} text
241
+ * @param {object} [options = null]
242
+ * @return {TextData}
243
+ * @method
244
+ * @public
245
+ */
246
+ export const parsePlainText = (text, text_format, options) => {
247
+ const lineText = options.multiline
248
+ ? text.split("\n")
249
+ : [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
+ for (let idx = 0; idx < lineText.length; ++idx) {
261
+ if (options.wordWrap || options.multiline) {
262
+ _$createNewLine(textData, textFormat);
263
+ }
264
+ const texts = lineText[idx];
265
+ if (texts) {
266
+ $currentWidth = 0;
267
+ _$parseText(texts, textFormat, textData, options);
268
+ }
269
+ }
270
+ _$adjustmentHeight(textData);
271
+ return textData;
272
+ };
273
+ /**
274
+ * @description HTMLを分解・解析して配列戻す
275
+ *
276
+ * @param {string} html_text
277
+ * @param {object} [options = null]
278
+ * @return {array}
279
+ * @method
280
+ * @public
281
+ */
282
+ export const parseHtmlText = (html_text, text_format, options) => {
283
+ const htmlText = html_text
284
+ .trim()
285
+ .replace(/\r?\n/g, "")
286
+ .replace(/\t/g, "");
287
+ const textData = new TextData();
288
+ const textFormat = text_format._$clone();
289
+ if (options.subFontSize && options.subFontSize > 0 && textFormat.size) {
290
+ textFormat.size -= options.subFontSize;
291
+ if (1 > textFormat.size) {
292
+ textFormat.size = 1;
293
+ }
294
+ }
295
+ const document = parseDocument(htmlText);
296
+ _$createNewLine(textData, textFormat);
297
+ _$parseTag(document, textFormat, textData, options);
298
+ _$adjustmentHeight(textData);
299
+ return textData;
300
+ };
package/dist/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
- export * from "./TextField";
2
1
  export * from "./TextFormat";
2
+ export * from "./TextParser";
3
+ export * from "./TextData";
package/dist/index.js CHANGED
@@ -1,2 +1,3 @@
1
- export * from "./TextField";
2
1
  export * from "./TextFormat";
2
+ export * from "./TextParser";
3
+ export * from "./TextData";
@@ -0,0 +1,6 @@
1
+ export interface OptionsImpl {
2
+ width: number;
3
+ multiline: boolean;
4
+ wordWrap: boolean;
5
+ subFontSize?: number;
6
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,12 @@
1
+ import { TextFormat } from "../TextFormat";
2
+ import { TextObjectModeImpl } from "./TextObjectModeImpl";
3
+ export interface TextObjectImpl {
4
+ mode: TextObjectModeImpl;
5
+ text: string;
6
+ textFormat: TextFormat;
7
+ x: number;
8
+ y: number;
9
+ w: number;
10
+ h: number;
11
+ line: number;
12
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export type TextObjectModeImpl = "break" | "wrap" | "image" | "text";
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@next2d/text",
3
- "version": "1.16.0",
3
+ "version": "1.17.0",
4
4
  "description": "Next2D Text Packages",
5
5
  "author": "Toshiyuki Ienaga<ienaga@tvon.jp> (https://github.com/ienaga/)",
6
6
  "license": "MIT",
@@ -32,12 +32,6 @@
32
32
  "url": "git+https://github.com/Next2D/Player.git"
33
33
  },
34
34
  "peerDependencies": {
35
- "@next2d/display": "1.16.0",
36
- "@next2d/events": "1.16.0",
37
- "@next2d/ui": "1.16.0",
38
- "@next2d/geom": "1.16.0",
39
- "@next2d/interface": "1.16.0",
40
- "@next2d/core": "1.16.0",
41
- "@next2d/webgl": "1.16.0"
35
+ "@next2d/share": "1.17.0"
42
36
  }
43
37
  }