@idraw/renderer 0.4.0-beta.4 → 0.4.0-beta.40

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.
@@ -3,6 +3,8 @@ import { EventEmitter } from '@idraw/util';
3
3
  export declare class Loader extends EventEmitter<LoaderEventMap> implements RendererLoader {
4
4
  #private;
5
5
  constructor();
6
+ isDestroyed(): boolean;
7
+ destroy(): void;
6
8
  load(element: Element<LoadElementType>, assets: ElementAssets): void;
7
9
  getContent(element: Element<LoadElementType>): LoadContent | null;
8
10
  getLoadItemMap(): LoadItemMap;
@@ -18,7 +18,7 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
18
18
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
19
19
  return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
20
20
  };
21
- var _Loader_instances, _Loader_loadFuncMap, _Loader_currentLoadItemMap, _Loader_storageLoadItemMap, _Loader_registerLoadFunc, _Loader_getLoadElementSource, _Loader_createLoadItem, _Loader_emitLoad, _Loader_emitError, _Loader_loadResource, _Loader_isExistingErrorStorage;
21
+ var _Loader_instances, _Loader_loadFuncMap, _Loader_currentLoadItemMap, _Loader_storageLoadItemMap, _Loader_hasDestroyed, _Loader_registerLoadFunc, _Loader_getLoadElementSource, _Loader_createLoadItem, _Loader_emitLoad, _Loader_emitError, _Loader_loadResource, _Loader_isExistingErrorStorage;
22
22
  import { loadImage, loadHTML, loadSVG, EventEmitter, createAssetId, isAssetId, createUUID } from '@idraw/util';
23
23
  const supportElementTypes = ['image', 'svg', 'html'];
24
24
  const getAssetIdFromElement = (element) => {
@@ -48,6 +48,7 @@ export class Loader extends EventEmitter {
48
48
  _Loader_loadFuncMap.set(this, {});
49
49
  _Loader_currentLoadItemMap.set(this, {});
50
50
  _Loader_storageLoadItemMap.set(this, {});
51
+ _Loader_hasDestroyed.set(this, false);
51
52
  __classPrivateFieldGet(this, _Loader_instances, "m", _Loader_registerLoadFunc).call(this, 'image', (elem, assets) => __awaiter(this, void 0, void 0, function* () {
52
53
  var _a;
53
54
  const src = ((_a = assets[elem.detail.src]) === null || _a === void 0 ? void 0 : _a.value) || elem.detail.src;
@@ -59,11 +60,11 @@ export class Loader extends EventEmitter {
59
60
  };
60
61
  }));
61
62
  __classPrivateFieldGet(this, _Loader_instances, "m", _Loader_registerLoadFunc).call(this, 'html', (elem, assets) => __awaiter(this, void 0, void 0, function* () {
62
- var _b;
63
- const html = ((_b = assets[elem.detail.html]) === null || _b === void 0 ? void 0 : _b.value) || elem.detail.html;
63
+ var _a;
64
+ const html = ((_a = assets[elem.detail.html]) === null || _a === void 0 ? void 0 : _a.value) || elem.detail.html;
64
65
  const content = yield loadHTML(html, {
65
- width: elem.detail.width || elem.w,
66
- height: elem.detail.height || elem.h
66
+ width: elem.detail.originW || elem.w,
67
+ height: elem.detail.originH || elem.h
67
68
  });
68
69
  return {
69
70
  uuid: elem.uuid,
@@ -72,8 +73,8 @@ export class Loader extends EventEmitter {
72
73
  };
73
74
  }));
74
75
  __classPrivateFieldGet(this, _Loader_instances, "m", _Loader_registerLoadFunc).call(this, 'svg', (elem, assets) => __awaiter(this, void 0, void 0, function* () {
75
- var _c;
76
- const svg = ((_c = assets[elem.detail.svg]) === null || _c === void 0 ? void 0 : _c.value) || elem.detail.svg;
76
+ var _a;
77
+ const svg = ((_a = assets[elem.detail.svg]) === null || _a === void 0 ? void 0 : _a.value) || elem.detail.svg;
77
78
  const content = yield loadSVG(svg);
78
79
  return {
79
80
  uuid: elem.uuid,
@@ -82,7 +83,20 @@ export class Loader extends EventEmitter {
82
83
  };
83
84
  }));
84
85
  }
86
+ isDestroyed() {
87
+ return __classPrivateFieldGet(this, _Loader_hasDestroyed, "f");
88
+ }
89
+ destroy() {
90
+ __classPrivateFieldSet(this, _Loader_hasDestroyed, true, "f");
91
+ this.clear();
92
+ __classPrivateFieldSet(this, _Loader_loadFuncMap, null, "f");
93
+ __classPrivateFieldSet(this, _Loader_currentLoadItemMap, null, "f");
94
+ __classPrivateFieldSet(this, _Loader_storageLoadItemMap, null, "f");
95
+ }
85
96
  load(element, assets) {
97
+ if (__classPrivateFieldGet(this, _Loader_hasDestroyed, "f") === true) {
98
+ return;
99
+ }
86
100
  if (__classPrivateFieldGet(this, _Loader_instances, "m", _Loader_isExistingErrorStorage).call(this, element)) {
87
101
  return;
88
102
  }
@@ -102,7 +116,7 @@ export class Loader extends EventEmitter {
102
116
  __classPrivateFieldSet(this, _Loader_storageLoadItemMap, itemMap, "f");
103
117
  }
104
118
  }
105
- _Loader_loadFuncMap = new WeakMap(), _Loader_currentLoadItemMap = new WeakMap(), _Loader_storageLoadItemMap = new WeakMap(), _Loader_instances = new WeakSet(), _Loader_registerLoadFunc = function _Loader_registerLoadFunc(type, func) {
119
+ _Loader_loadFuncMap = new WeakMap(), _Loader_currentLoadItemMap = new WeakMap(), _Loader_storageLoadItemMap = new WeakMap(), _Loader_hasDestroyed = new WeakMap(), _Loader_instances = new WeakSet(), _Loader_registerLoadFunc = function _Loader_registerLoadFunc(type, func) {
106
120
  __classPrivateFieldGet(this, _Loader_loadFuncMap, "f")[type] = func;
107
121
  }, _Loader_getLoadElementSource = function _Loader_getLoadElementSource(element) {
108
122
  var _a, _b, _c;
@@ -130,42 +144,52 @@ _Loader_loadFuncMap = new WeakMap(), _Loader_currentLoadItemMap = new WeakMap(),
130
144
  }, _Loader_emitLoad = function _Loader_emitLoad(item) {
131
145
  const assetId = getAssetIdFromElement(item.element);
132
146
  const storageItem = __classPrivateFieldGet(this, _Loader_storageLoadItemMap, "f")[assetId];
133
- if (storageItem) {
134
- if (storageItem.startTime < item.startTime) {
147
+ if (!__classPrivateFieldGet(this, _Loader_hasDestroyed, "f")) {
148
+ if (storageItem) {
149
+ if (storageItem.startTime < item.startTime) {
150
+ __classPrivateFieldGet(this, _Loader_storageLoadItemMap, "f")[assetId] = item;
151
+ this.trigger('load', Object.assign(Object.assign({}, item), { countTime: item.endTime - item.startTime }));
152
+ }
153
+ }
154
+ else {
135
155
  __classPrivateFieldGet(this, _Loader_storageLoadItemMap, "f")[assetId] = item;
136
156
  this.trigger('load', Object.assign(Object.assign({}, item), { countTime: item.endTime - item.startTime }));
137
157
  }
138
158
  }
139
- else {
140
- __classPrivateFieldGet(this, _Loader_storageLoadItemMap, "f")[assetId] = item;
141
- this.trigger('load', Object.assign(Object.assign({}, item), { countTime: item.endTime - item.startTime }));
142
- }
143
159
  }, _Loader_emitError = function _Loader_emitError(item) {
160
+ var _a;
144
161
  const assetId = getAssetIdFromElement(item.element);
145
- const storageItem = __classPrivateFieldGet(this, _Loader_storageLoadItemMap, "f")[assetId];
146
- if (storageItem) {
147
- if (storageItem.startTime < item.startTime) {
162
+ const storageItem = (_a = __classPrivateFieldGet(this, _Loader_storageLoadItemMap, "f")) === null || _a === void 0 ? void 0 : _a[assetId];
163
+ if (!__classPrivateFieldGet(this, _Loader_hasDestroyed, "f")) {
164
+ if (storageItem) {
165
+ if (storageItem.startTime < item.startTime) {
166
+ __classPrivateFieldGet(this, _Loader_storageLoadItemMap, "f")[assetId] = item;
167
+ this.trigger('error', Object.assign(Object.assign({}, item), { countTime: item.endTime - item.startTime }));
168
+ }
169
+ }
170
+ else {
148
171
  __classPrivateFieldGet(this, _Loader_storageLoadItemMap, "f")[assetId] = item;
149
172
  this.trigger('error', Object.assign(Object.assign({}, item), { countTime: item.endTime - item.startTime }));
150
173
  }
151
174
  }
152
- else {
153
- __classPrivateFieldGet(this, _Loader_storageLoadItemMap, "f")[assetId] = item;
154
- this.trigger('error', Object.assign(Object.assign({}, item), { countTime: item.endTime - item.startTime }));
155
- }
156
175
  }, _Loader_loadResource = function _Loader_loadResource(element, assets) {
157
176
  const item = __classPrivateFieldGet(this, _Loader_instances, "m", _Loader_createLoadItem).call(this, element);
158
177
  const assetId = getAssetIdFromElement(element);
178
+ if (__classPrivateFieldGet(this, _Loader_currentLoadItemMap, "f")[assetId]) {
179
+ return;
180
+ }
159
181
  __classPrivateFieldGet(this, _Loader_currentLoadItemMap, "f")[assetId] = item;
160
182
  const loadFunc = __classPrivateFieldGet(this, _Loader_loadFuncMap, "f")[element.type];
161
- if (typeof loadFunc === 'function') {
183
+ if (typeof loadFunc === 'function' && !__classPrivateFieldGet(this, _Loader_hasDestroyed, "f")) {
162
184
  item.startTime = Date.now();
163
185
  loadFunc(element, assets)
164
186
  .then((result) => {
165
- item.content = result.content;
166
- item.endTime = Date.now();
167
- item.status = 'load';
168
- __classPrivateFieldGet(this, _Loader_instances, "m", _Loader_emitLoad).call(this, item);
187
+ if (!__classPrivateFieldGet(this, _Loader_hasDestroyed, "f")) {
188
+ item.content = result.content;
189
+ item.endTime = Date.now();
190
+ item.status = 'load';
191
+ __classPrivateFieldGet(this, _Loader_instances, "m", _Loader_emitLoad).call(this, item);
192
+ }
169
193
  })
170
194
  .catch((err) => {
171
195
  console.warn(`Load element source "${item.source}" fail`, err, element);
@@ -0,0 +1,22 @@
1
+ import { Elements, ViewScaleInfo, ViewSizeInfo, ViewRectInfo, VirtualFlatItemMap, ViewContext2D } from '@idraw/types';
2
+ export declare function sortElementsViewVisiableInfoMap(elements: Elements, opts: {
3
+ viewScaleInfo: ViewScaleInfo;
4
+ viewSizeInfo: ViewSizeInfo;
5
+ tempContext: ViewContext2D;
6
+ }): {
7
+ virtualFlatItemMap: VirtualFlatItemMap;
8
+ visibleCount: number;
9
+ invisibleCount: number;
10
+ };
11
+ export declare function updateVirtualFlatItemMapStatus(virtualFlatItemMap: VirtualFlatItemMap, opts: {
12
+ viewScaleInfo: ViewScaleInfo;
13
+ viewSizeInfo: ViewSizeInfo;
14
+ }): {
15
+ virtualFlatItemMap: VirtualFlatItemMap;
16
+ visibleCount: number;
17
+ invisibleCount: number;
18
+ };
19
+ export declare function calcVisibleOriginCanvasRectInfo(opts: {
20
+ viewScaleInfo: ViewScaleInfo;
21
+ viewSizeInfo: ViewSizeInfo;
22
+ }): ViewRectInfo;
@@ -0,0 +1,63 @@
1
+ import { calcElementCenter } from '@idraw/util';
2
+ import { elementsToVirtualFlatMap } from '../virtual-flat';
3
+ export function sortElementsViewVisiableInfoMap(elements, opts) {
4
+ const { viewScaleInfo, viewSizeInfo, tempContext } = opts;
5
+ const visibleInfoMap = elementsToVirtualFlatMap(elements, { tempContext });
6
+ return updateVirtualFlatItemMapStatus(visibleInfoMap, { viewScaleInfo, viewSizeInfo });
7
+ }
8
+ function isRangeRectInfoCollide(info1, info2) {
9
+ const rect1MinX = Math.min(info1.topLeft.x, info1.topRight.x, info1.bottomLeft.x, info1.bottomRight.x);
10
+ const rect1MaxX = Math.max(info1.topLeft.x, info1.topRight.x, info1.bottomLeft.x, info1.bottomRight.x);
11
+ const rect1MinY = Math.min(info1.topLeft.y, info1.topRight.y, info1.bottomLeft.y, info1.bottomRight.y);
12
+ const rect1MaxY = Math.max(info1.topLeft.y, info1.topRight.y, info1.bottomLeft.y, info1.bottomRight.y);
13
+ const rect2MinX = Math.min(info2.topLeft.x, info2.topRight.x, info2.bottomLeft.x, info2.bottomRight.x);
14
+ const rect2MaxX = Math.max(info2.topLeft.x, info2.topRight.x, info2.bottomLeft.x, info2.bottomRight.x);
15
+ const rect2MinY = Math.min(info2.topLeft.y, info2.topRight.y, info2.bottomLeft.y, info2.bottomRight.y);
16
+ const rect2MaxY = Math.max(info2.topLeft.y, info2.topRight.y, info2.bottomLeft.y, info2.bottomRight.y);
17
+ if ((rect1MinX <= rect2MaxX && rect1MaxX >= rect2MinX && rect1MinY <= rect2MaxY && rect1MaxY >= rect2MinY) ||
18
+ (rect2MaxX <= rect1MaxY && rect2MaxX >= rect1MaxY && rect2MaxX <= rect1MaxY && rect2MaxX >= rect1MaxY)) {
19
+ return true;
20
+ }
21
+ return false;
22
+ }
23
+ export function updateVirtualFlatItemMapStatus(virtualFlatItemMap, opts) {
24
+ const canvasRectInfo = calcVisibleOriginCanvasRectInfo(opts);
25
+ let visibleCount = 0;
26
+ let invisibleCount = 0;
27
+ Object.keys(virtualFlatItemMap).forEach((uuid) => {
28
+ const info = virtualFlatItemMap[uuid];
29
+ info.isVisibleInView = isRangeRectInfoCollide(info.rangeRectInfo, canvasRectInfo);
30
+ info.isVisibleInView ? visibleCount++ : invisibleCount++;
31
+ });
32
+ return { virtualFlatItemMap, visibleCount, invisibleCount };
33
+ }
34
+ export function calcVisibleOriginCanvasRectInfo(opts) {
35
+ const { viewScaleInfo, viewSizeInfo } = opts;
36
+ const { scale, offsetTop, offsetLeft } = viewScaleInfo;
37
+ const { width, height } = viewSizeInfo;
38
+ const x = 0 - offsetLeft / scale;
39
+ const y = 0 - offsetTop / scale;
40
+ const w = width / scale;
41
+ const h = height / scale;
42
+ const center = calcElementCenter({ x, y, w, h });
43
+ const topLeft = { x, y };
44
+ const topRight = { x: x + w, y };
45
+ const bottomLeft = { x, y: y + h };
46
+ const bottomRight = { x: x + w, y: y + h };
47
+ const left = { x, y: center.y };
48
+ const top = { x: center.x, y };
49
+ const right = { x: x + w, y: center.y };
50
+ const bottom = { x: center.x, y: y + h };
51
+ const rectInfo = {
52
+ center,
53
+ topLeft,
54
+ topRight,
55
+ bottomLeft,
56
+ bottomRight,
57
+ left,
58
+ top,
59
+ right,
60
+ bottom
61
+ };
62
+ return rectInfo;
63
+ }
@@ -0,0 +1,7 @@
1
+ import { Element, Elements, VirtualFlatItemMap, VirtualFlatDetail, ViewContext2D } from '@idraw/types';
2
+ export declare function calcVirtualFlatDetail(elem: Element, opts: {
3
+ tempContext: ViewContext2D;
4
+ }): VirtualFlatDetail;
5
+ export declare function elementsToVirtualFlatMap(elements: Elements, opts: {
6
+ tempContext: ViewContext2D;
7
+ }): VirtualFlatItemMap;
@@ -0,0 +1,45 @@
1
+ import { is, getGroupQueueByElementPosition, calcElementOriginRectInfo, originRectInfoToRangeRectInfo } from '@idraw/util';
2
+ import { calcVirtualTextDetail } from './text';
3
+ export function calcVirtualFlatDetail(elem, opts) {
4
+ let virtualDetail = {};
5
+ if (elem.type === 'text') {
6
+ virtualDetail = calcVirtualTextDetail(elem, opts);
7
+ }
8
+ return virtualDetail;
9
+ }
10
+ export function elementsToVirtualFlatMap(elements, opts) {
11
+ const virtualFlatMap = {};
12
+ const currentPosition = [];
13
+ const _walk = (elem) => {
14
+ const baseInfo = {
15
+ type: elem.type,
16
+ isVisibleInView: true,
17
+ position: [...currentPosition]
18
+ };
19
+ let originRectInfo = null;
20
+ const groupQueue = getGroupQueueByElementPosition(elements, currentPosition);
21
+ originRectInfo = calcElementOriginRectInfo(elem, {
22
+ groupQueue: groupQueue || []
23
+ });
24
+ const virtualItem = Object.assign(Object.assign(Object.assign({}, baseInfo), {
25
+ originRectInfo: originRectInfo,
26
+ rangeRectInfo: is.angle(elem.angle)
27
+ ? originRectInfoToRangeRectInfo(originRectInfo)
28
+ : originRectInfo
29
+ }), calcVirtualFlatDetail(elem, opts));
30
+ virtualFlatMap[elem.uuid] = virtualItem;
31
+ if (elem.type === 'group') {
32
+ elem.detail.children.forEach((ele, i) => {
33
+ currentPosition.push(i);
34
+ _walk(ele);
35
+ currentPosition.pop();
36
+ });
37
+ }
38
+ };
39
+ elements.forEach((elem, index) => {
40
+ currentPosition.push(index);
41
+ _walk(elem);
42
+ currentPosition.pop();
43
+ });
44
+ return virtualFlatMap;
45
+ }
@@ -0,0 +1,2 @@
1
+ import type { Element, CalcVirtualDetailOptions, VirtualFlatTextDetail } from '@idraw/types';
2
+ export declare function calcVirtualTextDetail(elem: Element<'text'>, opts: CalcVirtualDetailOptions): VirtualFlatTextDetail;
@@ -0,0 +1,151 @@
1
+ import { enhanceFontFamliy, getDefaultElementDetailConfig } from '@idraw/util';
2
+ const detailConfig = getDefaultElementDetailConfig();
3
+ function isTextWidthWithinErrorRange(w0, w1, scale) {
4
+ if (scale < 0.5) {
5
+ if (w0 < w1 && (w0 - w1) / w0 > -0.15) {
6
+ return true;
7
+ }
8
+ }
9
+ return w0 >= w1;
10
+ }
11
+ export function calcVirtualTextDetail(elem, opts) {
12
+ const { w, h } = elem;
13
+ const x = 0;
14
+ const y = 0;
15
+ const ctx = opts.tempContext;
16
+ const lines = [];
17
+ const detail = Object.assign(Object.assign({}, detailConfig), elem.detail);
18
+ const originFontSize = detail.fontSize || detailConfig.fontSize;
19
+ const fontSize = originFontSize;
20
+ if (fontSize < 2) {
21
+ return {};
22
+ }
23
+ const originLineHeight = detail.lineHeight || originFontSize;
24
+ const lineHeight = originLineHeight;
25
+ ctx.textBaseline = 'top';
26
+ ctx.$setFont({
27
+ fontWeight: detail.fontWeight,
28
+ fontSize: fontSize,
29
+ fontFamily: enhanceFontFamliy(detail.fontFamily)
30
+ });
31
+ let detailText = detail.text.replace(/\r\n/gi, '\n');
32
+ if (detail.textTransform === 'lowercase') {
33
+ detailText = detailText.toLowerCase();
34
+ }
35
+ else if (detail.textTransform === 'uppercase') {
36
+ detailText = detailText.toUpperCase();
37
+ }
38
+ const fontHeight = lineHeight;
39
+ const detailTextList = detailText.split('\n');
40
+ let lineNum = 0;
41
+ detailTextList.forEach((itemText, idx) => {
42
+ if (detail.minInlineSize === 'maxContent') {
43
+ lines.push({
44
+ x,
45
+ y: 0,
46
+ text: itemText,
47
+ width: ctx.$undoPixelRatio(ctx.measureText(itemText).width)
48
+ });
49
+ }
50
+ else {
51
+ let lineText = '';
52
+ let splitStr = '';
53
+ let tempStrList = itemText.split(splitStr);
54
+ if (detail.wordBreak === 'normal') {
55
+ splitStr = ' ';
56
+ const wordList = itemText.split(splitStr);
57
+ tempStrList = [];
58
+ wordList.forEach((word, idx) => {
59
+ tempStrList.push(word);
60
+ if (idx < wordList.length - 1) {
61
+ tempStrList.push(splitStr);
62
+ }
63
+ });
64
+ }
65
+ if (tempStrList.length === 1 && detail.overflow === 'visible') {
66
+ lines.push({
67
+ x,
68
+ y: 0,
69
+ text: tempStrList[0],
70
+ width: ctx.$undoPixelRatio(ctx.measureText(tempStrList[0]).width)
71
+ });
72
+ }
73
+ else if (tempStrList.length > 0) {
74
+ for (let i = 0; i < tempStrList.length; i++) {
75
+ if (isTextWidthWithinErrorRange(ctx.$doPixelRatio(w), ctx.measureText(lineText + tempStrList[i]).width, 1)) {
76
+ lineText += tempStrList[i] || '';
77
+ }
78
+ else {
79
+ lines.push({
80
+ x,
81
+ y: 0,
82
+ text: lineText,
83
+ width: ctx.$undoPixelRatio(ctx.measureText(lineText).width)
84
+ });
85
+ lineText = tempStrList[i] || '';
86
+ lineNum++;
87
+ }
88
+ if ((lineNum + 1) * fontHeight > h && detail.overflow === 'hidden') {
89
+ break;
90
+ }
91
+ if (tempStrList.length - 1 === i) {
92
+ if ((lineNum + 1) * fontHeight <= h) {
93
+ lines.push({
94
+ x,
95
+ y: 0,
96
+ text: lineText,
97
+ width: ctx.$undoPixelRatio(ctx.measureText(lineText).width)
98
+ });
99
+ if (idx < detailTextList.length - 1) {
100
+ lineNum++;
101
+ }
102
+ break;
103
+ }
104
+ }
105
+ }
106
+ }
107
+ else {
108
+ lines.push({
109
+ x,
110
+ y: 0,
111
+ text: '',
112
+ width: 0
113
+ });
114
+ }
115
+ }
116
+ });
117
+ let startY = 0;
118
+ let eachLineStartY = 0;
119
+ if (fontHeight > fontSize) {
120
+ eachLineStartY = (fontHeight - fontSize) / 2;
121
+ }
122
+ if (lines.length * fontHeight < h) {
123
+ if (elem.detail.verticalAlign === 'top') {
124
+ startY = 0;
125
+ }
126
+ else if (elem.detail.verticalAlign === 'bottom') {
127
+ startY += h - lines.length * fontHeight;
128
+ }
129
+ else {
130
+ startY += (h - lines.length * fontHeight) / 2;
131
+ }
132
+ }
133
+ {
134
+ const _y = y + startY;
135
+ lines.forEach((line, i) => {
136
+ let _x = x;
137
+ if (detail.textAlign === 'center') {
138
+ _x = x + (w - line.width) / 2;
139
+ }
140
+ else if (detail.textAlign === 'right') {
141
+ _x = x + (w - line.width);
142
+ }
143
+ lines[i].x = _x;
144
+ lines[i].y = _y + fontHeight * i + eachLineStartY;
145
+ });
146
+ }
147
+ const virtualTextDetail = {
148
+ textLines: lines
149
+ };
150
+ return virtualTextDetail;
151
+ }