parseText.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552
  1. import * as imageHelper from '../helper/image.js';
  2. import { extend, retrieve2, retrieve3, reduce, } from '../../core/util.js';
  3. import { adjustTextX, adjustTextY, ensureFontMeasureInfo, getLineHeight, measureCharWidth, measureWidth, parsePercent, } from '../../contain/text.js';
  4. import BoundingRect from '../../core/BoundingRect.js';
  5. var STYLE_REG = /\{([a-zA-Z0-9_]+)\|([^}]*)\}/g;
  6. export function truncateText(text, containerWidth, font, ellipsis, options) {
  7. var out = {};
  8. truncateText2(out, text, containerWidth, font, ellipsis, options);
  9. return out.text;
  10. }
  11. function truncateText2(out, text, containerWidth, font, ellipsis, options) {
  12. if (!containerWidth) {
  13. out.text = '';
  14. out.isTruncated = false;
  15. return;
  16. }
  17. var textLines = (text + '').split('\n');
  18. options = prepareTruncateOptions(containerWidth, font, ellipsis, options);
  19. var isTruncated = false;
  20. var truncateOut = {};
  21. for (var i = 0, len = textLines.length; i < len; i++) {
  22. truncateSingleLine(truncateOut, textLines[i], options);
  23. textLines[i] = truncateOut.textLine;
  24. isTruncated = isTruncated || truncateOut.isTruncated;
  25. }
  26. out.text = textLines.join('\n');
  27. out.isTruncated = isTruncated;
  28. }
  29. function prepareTruncateOptions(containerWidth, font, ellipsis, options) {
  30. options = options || {};
  31. var preparedOpts = extend({}, options);
  32. ellipsis = retrieve2(ellipsis, '...');
  33. preparedOpts.maxIterations = retrieve2(options.maxIterations, 2);
  34. var minChar = preparedOpts.minChar = retrieve2(options.minChar, 0);
  35. var fontMeasureInfo = preparedOpts.fontMeasureInfo = ensureFontMeasureInfo(font);
  36. var ascCharWidth = fontMeasureInfo.asciiCharWidth;
  37. preparedOpts.placeholder = retrieve2(options.placeholder, '');
  38. var contentWidth = containerWidth = Math.max(0, containerWidth - 1);
  39. for (var i = 0; i < minChar && contentWidth >= ascCharWidth; i++) {
  40. contentWidth -= ascCharWidth;
  41. }
  42. var ellipsisWidth = measureWidth(fontMeasureInfo, ellipsis);
  43. if (ellipsisWidth > contentWidth) {
  44. ellipsis = '';
  45. ellipsisWidth = 0;
  46. }
  47. contentWidth = containerWidth - ellipsisWidth;
  48. preparedOpts.ellipsis = ellipsis;
  49. preparedOpts.ellipsisWidth = ellipsisWidth;
  50. preparedOpts.contentWidth = contentWidth;
  51. preparedOpts.containerWidth = containerWidth;
  52. return preparedOpts;
  53. }
  54. function truncateSingleLine(out, textLine, options) {
  55. var containerWidth = options.containerWidth;
  56. var contentWidth = options.contentWidth;
  57. var fontMeasureInfo = options.fontMeasureInfo;
  58. if (!containerWidth) {
  59. out.textLine = '';
  60. out.isTruncated = false;
  61. return;
  62. }
  63. var lineWidth = measureWidth(fontMeasureInfo, textLine);
  64. if (lineWidth <= containerWidth) {
  65. out.textLine = textLine;
  66. out.isTruncated = false;
  67. return;
  68. }
  69. for (var j = 0;; j++) {
  70. if (lineWidth <= contentWidth || j >= options.maxIterations) {
  71. textLine += options.ellipsis;
  72. break;
  73. }
  74. var subLength = j === 0
  75. ? estimateLength(textLine, contentWidth, fontMeasureInfo)
  76. : lineWidth > 0
  77. ? Math.floor(textLine.length * contentWidth / lineWidth)
  78. : 0;
  79. textLine = textLine.substr(0, subLength);
  80. lineWidth = measureWidth(fontMeasureInfo, textLine);
  81. }
  82. if (textLine === '') {
  83. textLine = options.placeholder;
  84. }
  85. out.textLine = textLine;
  86. out.isTruncated = true;
  87. }
  88. function estimateLength(text, contentWidth, fontMeasureInfo) {
  89. var width = 0;
  90. var i = 0;
  91. for (var len = text.length; i < len && width < contentWidth; i++) {
  92. width += measureCharWidth(fontMeasureInfo, text.charCodeAt(i));
  93. }
  94. return i;
  95. }
  96. export function parsePlainText(rawText, style, defaultOuterWidth, defaultOuterHeight) {
  97. var text = formatText(rawText);
  98. var overflow = style.overflow;
  99. var padding = style.padding;
  100. var paddingH = padding ? padding[1] + padding[3] : 0;
  101. var paddingV = padding ? padding[0] + padding[2] : 0;
  102. var font = style.font;
  103. var truncate = overflow === 'truncate';
  104. var calculatedLineHeight = getLineHeight(font);
  105. var lineHeight = retrieve2(style.lineHeight, calculatedLineHeight);
  106. var truncateLineOverflow = style.lineOverflow === 'truncate';
  107. var isTruncated = false;
  108. var width = style.width;
  109. if (width == null && defaultOuterWidth != null) {
  110. width = defaultOuterWidth - paddingH;
  111. }
  112. var height = style.height;
  113. if (height == null && defaultOuterHeight != null) {
  114. height = defaultOuterHeight - paddingV;
  115. }
  116. var lines;
  117. if (width != null && (overflow === 'break' || overflow === 'breakAll')) {
  118. lines = text ? wrapText(text, style.font, width, overflow === 'breakAll', 0).lines : [];
  119. }
  120. else {
  121. lines = text ? text.split('\n') : [];
  122. }
  123. var contentHeight = lines.length * lineHeight;
  124. if (height == null) {
  125. height = contentHeight;
  126. }
  127. if (contentHeight > height && truncateLineOverflow) {
  128. var lineCount = Math.floor(height / lineHeight);
  129. isTruncated = isTruncated || (lines.length > lineCount);
  130. lines = lines.slice(0, lineCount);
  131. contentHeight = lines.length * lineHeight;
  132. }
  133. if (text && truncate && width != null) {
  134. var options = prepareTruncateOptions(width, font, style.ellipsis, {
  135. minChar: style.truncateMinChar,
  136. placeholder: style.placeholder
  137. });
  138. var singleOut = {};
  139. for (var i = 0; i < lines.length; i++) {
  140. truncateSingleLine(singleOut, lines[i], options);
  141. lines[i] = singleOut.textLine;
  142. isTruncated = isTruncated || singleOut.isTruncated;
  143. }
  144. }
  145. var outerHeight = height;
  146. var contentWidth = 0;
  147. var fontMeasureInfo = ensureFontMeasureInfo(font);
  148. for (var i = 0; i < lines.length; i++) {
  149. contentWidth = Math.max(measureWidth(fontMeasureInfo, lines[i]), contentWidth);
  150. }
  151. if (width == null) {
  152. width = contentWidth;
  153. }
  154. var outerWidth = width;
  155. outerHeight += paddingV;
  156. outerWidth += paddingH;
  157. return {
  158. lines: lines,
  159. height: height,
  160. outerWidth: outerWidth,
  161. outerHeight: outerHeight,
  162. lineHeight: lineHeight,
  163. calculatedLineHeight: calculatedLineHeight,
  164. contentWidth: contentWidth,
  165. contentHeight: contentHeight,
  166. width: width,
  167. isTruncated: isTruncated
  168. };
  169. }
  170. var RichTextToken = (function () {
  171. function RichTextToken() {
  172. }
  173. return RichTextToken;
  174. }());
  175. var RichTextLine = (function () {
  176. function RichTextLine(tokens) {
  177. this.tokens = [];
  178. if (tokens) {
  179. this.tokens = tokens;
  180. }
  181. }
  182. return RichTextLine;
  183. }());
  184. var RichTextContentBlock = (function () {
  185. function RichTextContentBlock() {
  186. this.width = 0;
  187. this.height = 0;
  188. this.contentWidth = 0;
  189. this.contentHeight = 0;
  190. this.outerWidth = 0;
  191. this.outerHeight = 0;
  192. this.lines = [];
  193. this.isTruncated = false;
  194. }
  195. return RichTextContentBlock;
  196. }());
  197. export { RichTextContentBlock };
  198. export function parseRichText(rawText, style, defaultOuterWidth, defaultOuterHeight, topTextAlign) {
  199. var contentBlock = new RichTextContentBlock();
  200. var text = formatText(rawText);
  201. if (!text) {
  202. return contentBlock;
  203. }
  204. var stlPadding = style.padding;
  205. var stlPaddingH = stlPadding ? stlPadding[1] + stlPadding[3] : 0;
  206. var stlPaddingV = stlPadding ? stlPadding[0] + stlPadding[2] : 0;
  207. var topWidth = style.width;
  208. if (topWidth == null && defaultOuterWidth != null) {
  209. topWidth = defaultOuterWidth - stlPaddingH;
  210. }
  211. var topHeight = style.height;
  212. if (topHeight == null && defaultOuterHeight != null) {
  213. topHeight = defaultOuterHeight - stlPaddingV;
  214. }
  215. var overflow = style.overflow;
  216. var wrapInfo = (overflow === 'break' || overflow === 'breakAll') && topWidth != null
  217. ? { width: topWidth, accumWidth: 0, breakAll: overflow === 'breakAll' }
  218. : null;
  219. var lastIndex = STYLE_REG.lastIndex = 0;
  220. var result;
  221. while ((result = STYLE_REG.exec(text)) != null) {
  222. var matchedIndex = result.index;
  223. if (matchedIndex > lastIndex) {
  224. pushTokens(contentBlock, text.substring(lastIndex, matchedIndex), style, wrapInfo);
  225. }
  226. pushTokens(contentBlock, result[2], style, wrapInfo, result[1]);
  227. lastIndex = STYLE_REG.lastIndex;
  228. }
  229. if (lastIndex < text.length) {
  230. pushTokens(contentBlock, text.substring(lastIndex, text.length), style, wrapInfo);
  231. }
  232. var pendingList = [];
  233. var calculatedHeight = 0;
  234. var calculatedWidth = 0;
  235. var truncate = overflow === 'truncate';
  236. var truncateLine = style.lineOverflow === 'truncate';
  237. var tmpTruncateOut = {};
  238. function finishLine(line, lineWidth, lineHeight) {
  239. line.width = lineWidth;
  240. line.lineHeight = lineHeight;
  241. calculatedHeight += lineHeight;
  242. calculatedWidth = Math.max(calculatedWidth, lineWidth);
  243. }
  244. outer: for (var i = 0; i < contentBlock.lines.length; i++) {
  245. var line = contentBlock.lines[i];
  246. var lineHeight = 0;
  247. var lineWidth = 0;
  248. for (var j = 0; j < line.tokens.length; j++) {
  249. var token = line.tokens[j];
  250. var tokenStyle = token.styleName && style.rich[token.styleName] || {};
  251. var textPadding = token.textPadding = tokenStyle.padding;
  252. var paddingH = textPadding ? textPadding[1] + textPadding[3] : 0;
  253. var font = token.font = tokenStyle.font || style.font;
  254. token.contentHeight = getLineHeight(font);
  255. var tokenHeight = retrieve2(tokenStyle.height, token.contentHeight);
  256. token.innerHeight = tokenHeight;
  257. textPadding && (tokenHeight += textPadding[0] + textPadding[2]);
  258. token.height = tokenHeight;
  259. token.lineHeight = retrieve3(tokenStyle.lineHeight, style.lineHeight, tokenHeight);
  260. token.align = tokenStyle && tokenStyle.align || topTextAlign;
  261. token.verticalAlign = tokenStyle && tokenStyle.verticalAlign || 'middle';
  262. if (truncateLine && topHeight != null && calculatedHeight + token.lineHeight > topHeight) {
  263. var originalLength = contentBlock.lines.length;
  264. if (j > 0) {
  265. line.tokens = line.tokens.slice(0, j);
  266. finishLine(line, lineWidth, lineHeight);
  267. contentBlock.lines = contentBlock.lines.slice(0, i + 1);
  268. }
  269. else {
  270. contentBlock.lines = contentBlock.lines.slice(0, i);
  271. }
  272. contentBlock.isTruncated = contentBlock.isTruncated || (contentBlock.lines.length < originalLength);
  273. break outer;
  274. }
  275. var styleTokenWidth = tokenStyle.width;
  276. var tokenWidthNotSpecified = styleTokenWidth == null || styleTokenWidth === 'auto';
  277. if (typeof styleTokenWidth === 'string' && styleTokenWidth.charAt(styleTokenWidth.length - 1) === '%') {
  278. token.percentWidth = styleTokenWidth;
  279. pendingList.push(token);
  280. token.contentWidth = measureWidth(ensureFontMeasureInfo(font), token.text);
  281. }
  282. else {
  283. if (tokenWidthNotSpecified) {
  284. var textBackgroundColor = tokenStyle.backgroundColor;
  285. var bgImg = textBackgroundColor && textBackgroundColor.image;
  286. if (bgImg) {
  287. bgImg = imageHelper.findExistImage(bgImg);
  288. if (imageHelper.isImageReady(bgImg)) {
  289. token.width = Math.max(token.width, bgImg.width * tokenHeight / bgImg.height);
  290. }
  291. }
  292. }
  293. var remainTruncWidth = truncate && topWidth != null
  294. ? topWidth - lineWidth : null;
  295. if (remainTruncWidth != null && remainTruncWidth < token.width) {
  296. if (!tokenWidthNotSpecified || remainTruncWidth < paddingH) {
  297. token.text = '';
  298. token.width = token.contentWidth = 0;
  299. }
  300. else {
  301. truncateText2(tmpTruncateOut, token.text, remainTruncWidth - paddingH, font, style.ellipsis, { minChar: style.truncateMinChar });
  302. token.text = tmpTruncateOut.text;
  303. contentBlock.isTruncated = contentBlock.isTruncated || tmpTruncateOut.isTruncated;
  304. token.width = token.contentWidth = measureWidth(ensureFontMeasureInfo(font), token.text);
  305. }
  306. }
  307. else {
  308. token.contentWidth = measureWidth(ensureFontMeasureInfo(font), token.text);
  309. }
  310. }
  311. token.width += paddingH;
  312. lineWidth += token.width;
  313. tokenStyle && (lineHeight = Math.max(lineHeight, token.lineHeight));
  314. }
  315. finishLine(line, lineWidth, lineHeight);
  316. }
  317. contentBlock.outerWidth = contentBlock.width = retrieve2(topWidth, calculatedWidth);
  318. contentBlock.outerHeight = contentBlock.height = retrieve2(topHeight, calculatedHeight);
  319. contentBlock.contentHeight = calculatedHeight;
  320. contentBlock.contentWidth = calculatedWidth;
  321. contentBlock.outerWidth += stlPaddingH;
  322. contentBlock.outerHeight += stlPaddingV;
  323. for (var i = 0; i < pendingList.length; i++) {
  324. var token = pendingList[i];
  325. var percentWidth = token.percentWidth;
  326. token.width = parseInt(percentWidth, 10) / 100 * contentBlock.width;
  327. }
  328. return contentBlock;
  329. }
  330. function pushTokens(block, str, style, wrapInfo, styleName) {
  331. var isEmptyStr = str === '';
  332. var tokenStyle = styleName && style.rich[styleName] || {};
  333. var lines = block.lines;
  334. var font = tokenStyle.font || style.font;
  335. var newLine = false;
  336. var strLines;
  337. var linesWidths;
  338. if (wrapInfo) {
  339. var tokenPadding = tokenStyle.padding;
  340. var tokenPaddingH = tokenPadding ? tokenPadding[1] + tokenPadding[3] : 0;
  341. if (tokenStyle.width != null && tokenStyle.width !== 'auto') {
  342. var outerWidth_1 = parsePercent(tokenStyle.width, wrapInfo.width) + tokenPaddingH;
  343. if (lines.length > 0) {
  344. if (outerWidth_1 + wrapInfo.accumWidth > wrapInfo.width) {
  345. strLines = str.split('\n');
  346. newLine = true;
  347. }
  348. }
  349. wrapInfo.accumWidth = outerWidth_1;
  350. }
  351. else {
  352. var res = wrapText(str, font, wrapInfo.width, wrapInfo.breakAll, wrapInfo.accumWidth);
  353. wrapInfo.accumWidth = res.accumWidth + tokenPaddingH;
  354. linesWidths = res.linesWidths;
  355. strLines = res.lines;
  356. }
  357. }
  358. if (!strLines) {
  359. strLines = str.split('\n');
  360. }
  361. var fontMeasureInfo = ensureFontMeasureInfo(font);
  362. for (var i = 0; i < strLines.length; i++) {
  363. var text = strLines[i];
  364. var token = new RichTextToken();
  365. token.styleName = styleName;
  366. token.text = text;
  367. token.isLineHolder = !text && !isEmptyStr;
  368. if (typeof tokenStyle.width === 'number') {
  369. token.width = tokenStyle.width;
  370. }
  371. else {
  372. token.width = linesWidths
  373. ? linesWidths[i]
  374. : measureWidth(fontMeasureInfo, text);
  375. }
  376. if (!i && !newLine) {
  377. var tokens = (lines[lines.length - 1] || (lines[0] = new RichTextLine())).tokens;
  378. var tokensLen = tokens.length;
  379. (tokensLen === 1 && tokens[0].isLineHolder)
  380. ? (tokens[0] = token)
  381. : ((text || !tokensLen || isEmptyStr) && tokens.push(token));
  382. }
  383. else {
  384. lines.push(new RichTextLine([token]));
  385. }
  386. }
  387. }
  388. function isAlphabeticLetter(ch) {
  389. var code = ch.charCodeAt(0);
  390. return code >= 0x20 && code <= 0x24F
  391. || code >= 0x370 && code <= 0x10FF
  392. || code >= 0x1200 && code <= 0x13FF
  393. || code >= 0x1E00 && code <= 0x206F;
  394. }
  395. var breakCharMap = reduce(',&?/;] '.split(''), function (obj, ch) {
  396. obj[ch] = true;
  397. return obj;
  398. }, {});
  399. function isWordBreakChar(ch) {
  400. if (isAlphabeticLetter(ch)) {
  401. if (breakCharMap[ch]) {
  402. return true;
  403. }
  404. return false;
  405. }
  406. return true;
  407. }
  408. function wrapText(text, font, lineWidth, isBreakAll, lastAccumWidth) {
  409. var lines = [];
  410. var linesWidths = [];
  411. var line = '';
  412. var currentWord = '';
  413. var currentWordWidth = 0;
  414. var accumWidth = 0;
  415. var fontMeasureInfo = ensureFontMeasureInfo(font);
  416. for (var i = 0; i < text.length; i++) {
  417. var ch = text.charAt(i);
  418. if (ch === '\n') {
  419. if (currentWord) {
  420. line += currentWord;
  421. accumWidth += currentWordWidth;
  422. }
  423. lines.push(line);
  424. linesWidths.push(accumWidth);
  425. line = '';
  426. currentWord = '';
  427. currentWordWidth = 0;
  428. accumWidth = 0;
  429. continue;
  430. }
  431. var chWidth = measureCharWidth(fontMeasureInfo, ch.charCodeAt(0));
  432. var inWord = isBreakAll ? false : !isWordBreakChar(ch);
  433. if (!lines.length
  434. ? lastAccumWidth + accumWidth + chWidth > lineWidth
  435. : accumWidth + chWidth > lineWidth) {
  436. if (!accumWidth) {
  437. if (inWord) {
  438. lines.push(currentWord);
  439. linesWidths.push(currentWordWidth);
  440. currentWord = ch;
  441. currentWordWidth = chWidth;
  442. }
  443. else {
  444. lines.push(ch);
  445. linesWidths.push(chWidth);
  446. }
  447. }
  448. else if (line || currentWord) {
  449. if (inWord) {
  450. if (!line) {
  451. line = currentWord;
  452. currentWord = '';
  453. currentWordWidth = 0;
  454. accumWidth = currentWordWidth;
  455. }
  456. lines.push(line);
  457. linesWidths.push(accumWidth - currentWordWidth);
  458. currentWord += ch;
  459. currentWordWidth += chWidth;
  460. line = '';
  461. accumWidth = currentWordWidth;
  462. }
  463. else {
  464. if (currentWord) {
  465. line += currentWord;
  466. currentWord = '';
  467. currentWordWidth = 0;
  468. }
  469. lines.push(line);
  470. linesWidths.push(accumWidth);
  471. line = ch;
  472. accumWidth = chWidth;
  473. }
  474. }
  475. continue;
  476. }
  477. accumWidth += chWidth;
  478. if (inWord) {
  479. currentWord += ch;
  480. currentWordWidth += chWidth;
  481. }
  482. else {
  483. if (currentWord) {
  484. line += currentWord;
  485. currentWord = '';
  486. currentWordWidth = 0;
  487. }
  488. line += ch;
  489. }
  490. }
  491. if (currentWord) {
  492. line += currentWord;
  493. }
  494. if (line) {
  495. lines.push(line);
  496. linesWidths.push(accumWidth);
  497. }
  498. if (lines.length === 1) {
  499. accumWidth += lastAccumWidth;
  500. }
  501. return {
  502. accumWidth: accumWidth,
  503. lines: lines,
  504. linesWidths: linesWidths
  505. };
  506. }
  507. export function calcInnerTextOverflowArea(out, overflowRect, baseX, baseY, textAlign, textVerticalAlign) {
  508. out.baseX = baseX;
  509. out.baseY = baseY;
  510. out.outerWidth = out.outerHeight = null;
  511. if (!overflowRect) {
  512. return;
  513. }
  514. var textWidth = overflowRect.width * 2;
  515. var textHeight = overflowRect.height * 2;
  516. BoundingRect.set(tmpCITCTextRect, adjustTextX(baseX, textWidth, textAlign), adjustTextY(baseY, textHeight, textVerticalAlign), textWidth, textHeight);
  517. BoundingRect.intersect(overflowRect, tmpCITCTextRect, null, tmpCITCIntersectRectOpt);
  518. var outIntersectRect = tmpCITCIntersectRectOpt.outIntersectRect;
  519. out.outerWidth = outIntersectRect.width;
  520. out.outerHeight = outIntersectRect.height;
  521. out.baseX = adjustTextX(outIntersectRect.x, outIntersectRect.width, textAlign, true);
  522. out.baseY = adjustTextY(outIntersectRect.y, outIntersectRect.height, textVerticalAlign, true);
  523. }
  524. var tmpCITCTextRect = new BoundingRect(0, 0, 0, 0);
  525. var tmpCITCIntersectRectOpt = { outIntersectRect: {}, clamp: true };
  526. function formatText(text) {
  527. return text != null ? (text += '') : (text = '');
  528. }
  529. export function tSpanCreateBoundingRect(style) {
  530. var text = formatText(style.text);
  531. var font = style.font;
  532. var contentWidth = measureWidth(ensureFontMeasureInfo(font), text);
  533. var contentHeight = getLineHeight(font);
  534. return tSpanCreateBoundingRect2(style, contentWidth, contentHeight, null);
  535. }
  536. export function tSpanCreateBoundingRect2(style, contentWidth, contentHeight, forceLineWidth) {
  537. var rect = new BoundingRect(adjustTextX(style.x || 0, contentWidth, style.textAlign), adjustTextY(style.y || 0, contentHeight, style.textBaseline), contentWidth, contentHeight);
  538. var lineWidth = forceLineWidth != null
  539. ? forceLineWidth
  540. : (tSpanHasStroke(style) ? style.lineWidth : 0);
  541. if (lineWidth > 0) {
  542. rect.x -= lineWidth / 2;
  543. rect.y -= lineWidth / 2;
  544. rect.width += lineWidth;
  545. rect.height += lineWidth;
  546. }
  547. return rect;
  548. }
  549. export function tSpanHasStroke(style) {
  550. var stroke = style.stroke;
  551. return stroke != null && stroke !== 'none' && style.lineWidth > 0;
  552. }