2
0

text_layer.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705
  1. /**
  2. * @licstart The following is the entire license notice for the
  3. * Javascript code in this page
  4. *
  5. * Copyright 2019 Mozilla Foundation
  6. *
  7. * Licensed under the Apache License, Version 2.0 (the "License");
  8. * you may not use this file except in compliance with the License.
  9. * You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing, software
  14. * distributed under the License is distributed on an "AS IS" BASIS,
  15. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. * See the License for the specific language governing permissions and
  17. * limitations under the License.
  18. *
  19. * @licend The above is the entire license notice for the
  20. * Javascript code in this page
  21. */
  22. "use strict";
  23. Object.defineProperty(exports, "__esModule", {
  24. value: true
  25. });
  26. exports.renderTextLayer = void 0;
  27. var _util = require("../shared/util");
  28. var _global_scope = _interopRequireDefault(require("../shared/global_scope"));
  29. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
  30. var renderTextLayer = function renderTextLayerClosure() {
  31. var MAX_TEXT_DIVS_TO_RENDER = 100000;
  32. var NonWhitespaceRegexp = /\S/;
  33. function isAllWhitespace(str) {
  34. return !NonWhitespaceRegexp.test(str);
  35. }
  36. function appendText(task, geom, styles) {
  37. var textDiv = document.createElement('span');
  38. var textDivProperties = {
  39. angle: 0,
  40. canvasWidth: 0,
  41. isWhitespace: false,
  42. originalTransform: null,
  43. paddingBottom: 0,
  44. paddingLeft: 0,
  45. paddingRight: 0,
  46. paddingTop: 0,
  47. scale: 1
  48. };
  49. task._textDivs.push(textDiv);
  50. if (isAllWhitespace(geom.str)) {
  51. textDivProperties.isWhitespace = true;
  52. task._textDivProperties.set(textDiv, textDivProperties);
  53. return;
  54. }
  55. var tx = _util.Util.transform(task._viewport.transform, geom.transform);
  56. var angle = Math.atan2(tx[1], tx[0]);
  57. var style = styles[geom.fontName];
  58. if (style.vertical) {
  59. angle += Math.PI / 2;
  60. }
  61. var fontHeight = Math.sqrt(tx[2] * tx[2] + tx[3] * tx[3]);
  62. var fontAscent = fontHeight;
  63. if (style.ascent) {
  64. fontAscent = style.ascent * fontAscent;
  65. } else if (style.descent) {
  66. fontAscent = (1 + style.descent) * fontAscent;
  67. }
  68. var left, top;
  69. if (angle === 0) {
  70. left = tx[4];
  71. top = tx[5] - fontAscent;
  72. } else {
  73. left = tx[4] + fontAscent * Math.sin(angle);
  74. top = tx[5] - fontAscent * Math.cos(angle);
  75. }
  76. textDiv.style.left = "".concat(left, "px");
  77. textDiv.style.top = "".concat(top, "px");
  78. textDiv.style.fontSize = "".concat(fontHeight, "px");
  79. textDiv.style.fontFamily = style.fontFamily;
  80. textDiv.textContent = geom.str;
  81. if (task._fontInspectorEnabled) {
  82. textDiv.dataset.fontName = geom.fontName;
  83. }
  84. if (angle !== 0) {
  85. textDivProperties.angle = angle * (180 / Math.PI);
  86. }
  87. if (geom.str.length > 1) {
  88. if (style.vertical) {
  89. textDivProperties.canvasWidth = geom.height * task._viewport.scale;
  90. } else {
  91. textDivProperties.canvasWidth = geom.width * task._viewport.scale;
  92. }
  93. }
  94. task._textDivProperties.set(textDiv, textDivProperties);
  95. if (task._textContentStream) {
  96. task._layoutText(textDiv);
  97. }
  98. if (task._enhanceTextSelection) {
  99. var angleCos = 1,
  100. angleSin = 0;
  101. if (angle !== 0) {
  102. angleCos = Math.cos(angle);
  103. angleSin = Math.sin(angle);
  104. }
  105. var divWidth = (style.vertical ? geom.height : geom.width) * task._viewport.scale;
  106. var divHeight = fontHeight;
  107. var m, b;
  108. if (angle !== 0) {
  109. m = [angleCos, angleSin, -angleSin, angleCos, left, top];
  110. b = _util.Util.getAxialAlignedBoundingBox([0, 0, divWidth, divHeight], m);
  111. } else {
  112. b = [left, top, left + divWidth, top + divHeight];
  113. }
  114. task._bounds.push({
  115. left: b[0],
  116. top: b[1],
  117. right: b[2],
  118. bottom: b[3],
  119. div: textDiv,
  120. size: [divWidth, divHeight],
  121. m: m
  122. });
  123. }
  124. }
  125. function render(task) {
  126. if (task._canceled) {
  127. return;
  128. }
  129. var textDivs = task._textDivs;
  130. var capability = task._capability;
  131. var textDivsLength = textDivs.length;
  132. if (textDivsLength > MAX_TEXT_DIVS_TO_RENDER) {
  133. task._renderingDone = true;
  134. capability.resolve();
  135. return;
  136. }
  137. if (!task._textContentStream) {
  138. for (var i = 0; i < textDivsLength; i++) {
  139. task._layoutText(textDivs[i]);
  140. }
  141. }
  142. task._renderingDone = true;
  143. capability.resolve();
  144. }
  145. function expand(task) {
  146. var bounds = task._bounds;
  147. var viewport = task._viewport;
  148. var expanded = expandBounds(viewport.width, viewport.height, bounds);
  149. for (var i = 0; i < expanded.length; i++) {
  150. var div = bounds[i].div;
  151. var divProperties = task._textDivProperties.get(div);
  152. if (divProperties.angle === 0) {
  153. divProperties.paddingLeft = bounds[i].left - expanded[i].left;
  154. divProperties.paddingTop = bounds[i].top - expanded[i].top;
  155. divProperties.paddingRight = expanded[i].right - bounds[i].right;
  156. divProperties.paddingBottom = expanded[i].bottom - bounds[i].bottom;
  157. task._textDivProperties.set(div, divProperties);
  158. continue;
  159. }
  160. var e = expanded[i],
  161. b = bounds[i];
  162. var m = b.m,
  163. c = m[0],
  164. s = m[1];
  165. var points = [[0, 0], [0, b.size[1]], [b.size[0], 0], b.size];
  166. var ts = new Float64Array(64);
  167. points.forEach(function (p, i) {
  168. var t = _util.Util.applyTransform(p, m);
  169. ts[i + 0] = c && (e.left - t[0]) / c;
  170. ts[i + 4] = s && (e.top - t[1]) / s;
  171. ts[i + 8] = c && (e.right - t[0]) / c;
  172. ts[i + 12] = s && (e.bottom - t[1]) / s;
  173. ts[i + 16] = s && (e.left - t[0]) / -s;
  174. ts[i + 20] = c && (e.top - t[1]) / c;
  175. ts[i + 24] = s && (e.right - t[0]) / -s;
  176. ts[i + 28] = c && (e.bottom - t[1]) / c;
  177. ts[i + 32] = c && (e.left - t[0]) / -c;
  178. ts[i + 36] = s && (e.top - t[1]) / -s;
  179. ts[i + 40] = c && (e.right - t[0]) / -c;
  180. ts[i + 44] = s && (e.bottom - t[1]) / -s;
  181. ts[i + 48] = s && (e.left - t[0]) / s;
  182. ts[i + 52] = c && (e.top - t[1]) / -c;
  183. ts[i + 56] = s && (e.right - t[0]) / s;
  184. ts[i + 60] = c && (e.bottom - t[1]) / -c;
  185. });
  186. var findPositiveMin = function findPositiveMin(ts, offset, count) {
  187. var result = 0;
  188. for (var i = 0; i < count; i++) {
  189. var t = ts[offset++];
  190. if (t > 0) {
  191. result = result ? Math.min(t, result) : t;
  192. }
  193. }
  194. return result;
  195. };
  196. var boxScale = 1 + Math.min(Math.abs(c), Math.abs(s));
  197. divProperties.paddingLeft = findPositiveMin(ts, 32, 16) / boxScale;
  198. divProperties.paddingTop = findPositiveMin(ts, 48, 16) / boxScale;
  199. divProperties.paddingRight = findPositiveMin(ts, 0, 16) / boxScale;
  200. divProperties.paddingBottom = findPositiveMin(ts, 16, 16) / boxScale;
  201. task._textDivProperties.set(div, divProperties);
  202. }
  203. }
  204. function expandBounds(width, height, boxes) {
  205. var bounds = boxes.map(function (box, i) {
  206. return {
  207. x1: box.left,
  208. y1: box.top,
  209. x2: box.right,
  210. y2: box.bottom,
  211. index: i,
  212. x1New: undefined,
  213. x2New: undefined
  214. };
  215. });
  216. expandBoundsLTR(width, bounds);
  217. var expanded = new Array(boxes.length);
  218. bounds.forEach(function (b) {
  219. var i = b.index;
  220. expanded[i] = {
  221. left: b.x1New,
  222. top: 0,
  223. right: b.x2New,
  224. bottom: 0
  225. };
  226. });
  227. boxes.map(function (box, i) {
  228. var e = expanded[i],
  229. b = bounds[i];
  230. b.x1 = box.top;
  231. b.y1 = width - e.right;
  232. b.x2 = box.bottom;
  233. b.y2 = width - e.left;
  234. b.index = i;
  235. b.x1New = undefined;
  236. b.x2New = undefined;
  237. });
  238. expandBoundsLTR(height, bounds);
  239. bounds.forEach(function (b) {
  240. var i = b.index;
  241. expanded[i].top = b.x1New;
  242. expanded[i].bottom = b.x2New;
  243. });
  244. return expanded;
  245. }
  246. function expandBoundsLTR(width, bounds) {
  247. bounds.sort(function (a, b) {
  248. return a.x1 - b.x1 || a.index - b.index;
  249. });
  250. var fakeBoundary = {
  251. x1: -Infinity,
  252. y1: -Infinity,
  253. x2: 0,
  254. y2: Infinity,
  255. index: -1,
  256. x1New: 0,
  257. x2New: 0
  258. };
  259. var horizon = [{
  260. start: -Infinity,
  261. end: Infinity,
  262. boundary: fakeBoundary
  263. }];
  264. bounds.forEach(function (boundary) {
  265. var i = 0;
  266. while (i < horizon.length && horizon[i].end <= boundary.y1) {
  267. i++;
  268. }
  269. var j = horizon.length - 1;
  270. while (j >= 0 && horizon[j].start >= boundary.y2) {
  271. j--;
  272. }
  273. var horizonPart, affectedBoundary;
  274. var q,
  275. k,
  276. maxXNew = -Infinity;
  277. for (q = i; q <= j; q++) {
  278. horizonPart = horizon[q];
  279. affectedBoundary = horizonPart.boundary;
  280. var xNew;
  281. if (affectedBoundary.x2 > boundary.x1) {
  282. xNew = affectedBoundary.index > boundary.index ? affectedBoundary.x1New : boundary.x1;
  283. } else if (affectedBoundary.x2New === undefined) {
  284. xNew = (affectedBoundary.x2 + boundary.x1) / 2;
  285. } else {
  286. xNew = affectedBoundary.x2New;
  287. }
  288. if (xNew > maxXNew) {
  289. maxXNew = xNew;
  290. }
  291. }
  292. boundary.x1New = maxXNew;
  293. for (q = i; q <= j; q++) {
  294. horizonPart = horizon[q];
  295. affectedBoundary = horizonPart.boundary;
  296. if (affectedBoundary.x2New === undefined) {
  297. if (affectedBoundary.x2 > boundary.x1) {
  298. if (affectedBoundary.index > boundary.index) {
  299. affectedBoundary.x2New = affectedBoundary.x2;
  300. }
  301. } else {
  302. affectedBoundary.x2New = maxXNew;
  303. }
  304. } else if (affectedBoundary.x2New > maxXNew) {
  305. affectedBoundary.x2New = Math.max(maxXNew, affectedBoundary.x2);
  306. }
  307. }
  308. var changedHorizon = [],
  309. lastBoundary = null;
  310. for (q = i; q <= j; q++) {
  311. horizonPart = horizon[q];
  312. affectedBoundary = horizonPart.boundary;
  313. var useBoundary = affectedBoundary.x2 > boundary.x2 ? affectedBoundary : boundary;
  314. if (lastBoundary === useBoundary) {
  315. changedHorizon[changedHorizon.length - 1].end = horizonPart.end;
  316. } else {
  317. changedHorizon.push({
  318. start: horizonPart.start,
  319. end: horizonPart.end,
  320. boundary: useBoundary
  321. });
  322. lastBoundary = useBoundary;
  323. }
  324. }
  325. if (horizon[i].start < boundary.y1) {
  326. changedHorizon[0].start = boundary.y1;
  327. changedHorizon.unshift({
  328. start: horizon[i].start,
  329. end: boundary.y1,
  330. boundary: horizon[i].boundary
  331. });
  332. }
  333. if (boundary.y2 < horizon[j].end) {
  334. changedHorizon[changedHorizon.length - 1].end = boundary.y2;
  335. changedHorizon.push({
  336. start: boundary.y2,
  337. end: horizon[j].end,
  338. boundary: horizon[j].boundary
  339. });
  340. }
  341. for (q = i; q <= j; q++) {
  342. horizonPart = horizon[q];
  343. affectedBoundary = horizonPart.boundary;
  344. if (affectedBoundary.x2New !== undefined) {
  345. continue;
  346. }
  347. var used = false;
  348. for (k = i - 1; !used && k >= 0 && horizon[k].start >= affectedBoundary.y1; k--) {
  349. used = horizon[k].boundary === affectedBoundary;
  350. }
  351. for (k = j + 1; !used && k < horizon.length && horizon[k].end <= affectedBoundary.y2; k++) {
  352. used = horizon[k].boundary === affectedBoundary;
  353. }
  354. for (k = 0; !used && k < changedHorizon.length; k++) {
  355. used = changedHorizon[k].boundary === affectedBoundary;
  356. }
  357. if (!used) {
  358. affectedBoundary.x2New = maxXNew;
  359. }
  360. }
  361. Array.prototype.splice.apply(horizon, [i, j - i + 1].concat(changedHorizon));
  362. });
  363. horizon.forEach(function (horizonPart) {
  364. var affectedBoundary = horizonPart.boundary;
  365. if (affectedBoundary.x2New === undefined) {
  366. affectedBoundary.x2New = Math.max(width, affectedBoundary.x2);
  367. }
  368. });
  369. }
  370. function TextLayerRenderTask(_ref) {
  371. var _this = this;
  372. var textContent = _ref.textContent,
  373. textContentStream = _ref.textContentStream,
  374. container = _ref.container,
  375. viewport = _ref.viewport,
  376. textDivs = _ref.textDivs,
  377. textContentItemsStr = _ref.textContentItemsStr,
  378. enhanceTextSelection = _ref.enhanceTextSelection;
  379. this._textContent = textContent;
  380. this._textContentStream = textContentStream;
  381. this._container = container;
  382. this._viewport = viewport;
  383. this._textDivs = textDivs || [];
  384. this._textContentItemsStr = textContentItemsStr || [];
  385. this._enhanceTextSelection = !!enhanceTextSelection;
  386. this._fontInspectorEnabled = !!(_global_scope["default"].FontInspector && _global_scope["default"].FontInspector.enabled);
  387. this._reader = null;
  388. this._layoutTextLastFontSize = null;
  389. this._layoutTextLastFontFamily = null;
  390. this._layoutTextCtx = null;
  391. this._textDivProperties = new WeakMap();
  392. this._renderingDone = false;
  393. this._canceled = false;
  394. this._capability = (0, _util.createPromiseCapability)();
  395. this._renderTimer = null;
  396. this._bounds = [];
  397. this._capability.promise["finally"](function () {
  398. if (_this._layoutTextCtx) {
  399. _this._layoutTextCtx.canvas.width = 0;
  400. _this._layoutTextCtx.canvas.height = 0;
  401. _this._layoutTextCtx = null;
  402. }
  403. })["catch"](function () {});
  404. }
  405. TextLayerRenderTask.prototype = {
  406. get promise() {
  407. return this._capability.promise;
  408. },
  409. cancel: function TextLayer_cancel() {
  410. this._canceled = true;
  411. if (this._reader) {
  412. this._reader.cancel(new _util.AbortException('TextLayer task cancelled.'));
  413. this._reader = null;
  414. }
  415. if (this._renderTimer !== null) {
  416. clearTimeout(this._renderTimer);
  417. this._renderTimer = null;
  418. }
  419. this._capability.reject(new Error('TextLayer task cancelled.'));
  420. },
  421. _processItems: function _processItems(items, styleCache) {
  422. for (var i = 0, len = items.length; i < len; i++) {
  423. this._textContentItemsStr.push(items[i].str);
  424. appendText(this, items[i], styleCache);
  425. }
  426. },
  427. _layoutText: function _layoutText(textDiv) {
  428. var textDivProperties = this._textDivProperties.get(textDiv);
  429. if (textDivProperties.isWhitespace) {
  430. return;
  431. }
  432. var transform = '';
  433. if (textDivProperties.canvasWidth !== 0) {
  434. var _textDiv$style = textDiv.style,
  435. fontSize = _textDiv$style.fontSize,
  436. fontFamily = _textDiv$style.fontFamily;
  437. if (fontSize !== this._layoutTextLastFontSize || fontFamily !== this._layoutTextLastFontFamily) {
  438. this._layoutTextCtx.font = "".concat(fontSize, " ").concat(fontFamily);
  439. this._layoutTextLastFontSize = fontSize;
  440. this._layoutTextLastFontFamily = fontFamily;
  441. }
  442. var _this$_layoutTextCtx$ = this._layoutTextCtx.measureText(textDiv.textContent),
  443. width = _this$_layoutTextCtx$.width;
  444. if (width > 0) {
  445. textDivProperties.scale = textDivProperties.canvasWidth / width;
  446. transform = "scaleX(".concat(textDivProperties.scale, ")");
  447. }
  448. }
  449. if (textDivProperties.angle !== 0) {
  450. transform = "rotate(".concat(textDivProperties.angle, "deg) ").concat(transform);
  451. }
  452. if (transform.length > 0) {
  453. if (this._enhanceTextSelection) {
  454. textDivProperties.originalTransform = transform;
  455. }
  456. textDiv.style.transform = transform;
  457. }
  458. this._textDivProperties.set(textDiv, textDivProperties);
  459. this._container.appendChild(textDiv);
  460. },
  461. _render: function TextLayer_render(timeout) {
  462. var _this2 = this;
  463. var capability = (0, _util.createPromiseCapability)();
  464. var styleCache = Object.create(null);
  465. var canvas = document.createElement('canvas');
  466. canvas.mozOpaque = true;
  467. this._layoutTextCtx = canvas.getContext('2d', {
  468. alpha: false
  469. });
  470. if (this._textContent) {
  471. var textItems = this._textContent.items;
  472. var textStyles = this._textContent.styles;
  473. this._processItems(textItems, textStyles);
  474. capability.resolve();
  475. } else if (this._textContentStream) {
  476. var pump = function pump() {
  477. _this2._reader.read().then(function (_ref2) {
  478. var value = _ref2.value,
  479. done = _ref2.done;
  480. if (done) {
  481. capability.resolve();
  482. return;
  483. }
  484. Object.assign(styleCache, value.styles);
  485. _this2._processItems(value.items, styleCache);
  486. pump();
  487. }, capability.reject);
  488. };
  489. this._reader = this._textContentStream.getReader();
  490. pump();
  491. } else {
  492. throw new Error('Neither "textContent" nor "textContentStream"' + ' parameters specified.');
  493. }
  494. capability.promise.then(function () {
  495. styleCache = null;
  496. if (!timeout) {
  497. render(_this2);
  498. } else {
  499. _this2._renderTimer = setTimeout(function () {
  500. render(_this2);
  501. _this2._renderTimer = null;
  502. }, timeout);
  503. }
  504. }, this._capability.reject);
  505. },
  506. expandTextDivs: function TextLayer_expandTextDivs(expandDivs) {
  507. if (!this._enhanceTextSelection || !this._renderingDone) {
  508. return;
  509. }
  510. if (this._bounds !== null) {
  511. expand(this);
  512. this._bounds = null;
  513. }
  514. var NO_PADDING = '0 0 0 0';
  515. var transformBuf = [],
  516. paddingBuf = [];
  517. for (var i = 0, ii = this._textDivs.length; i < ii; i++) {
  518. var div = this._textDivs[i];
  519. var divProps = this._textDivProperties.get(div);
  520. if (divProps.isWhitespace) {
  521. continue;
  522. }
  523. if (expandDivs) {
  524. transformBuf.length = 0;
  525. paddingBuf.length = 0;
  526. if (divProps.originalTransform) {
  527. transformBuf.push(divProps.originalTransform);
  528. }
  529. if (divProps.paddingTop > 0) {
  530. paddingBuf.push("".concat(divProps.paddingTop, "px"));
  531. transformBuf.push("translateY(".concat(-divProps.paddingTop, "px)"));
  532. } else {
  533. paddingBuf.push(0);
  534. }
  535. if (divProps.paddingRight > 0) {
  536. paddingBuf.push("".concat(divProps.paddingRight / divProps.scale, "px"));
  537. } else {
  538. paddingBuf.push(0);
  539. }
  540. if (divProps.paddingBottom > 0) {
  541. paddingBuf.push("".concat(divProps.paddingBottom, "px"));
  542. } else {
  543. paddingBuf.push(0);
  544. }
  545. if (divProps.paddingLeft > 0) {
  546. paddingBuf.push("".concat(divProps.paddingLeft / divProps.scale, "px"));
  547. transformBuf.push("translateX(".concat(-divProps.paddingLeft / divProps.scale, "px)"));
  548. } else {
  549. paddingBuf.push(0);
  550. }
  551. var padding = paddingBuf.join(' ');
  552. if (padding !== NO_PADDING) {
  553. div.style.padding = padding;
  554. }
  555. if (transformBuf.length) {
  556. div.style.transform = transformBuf.join(' ');
  557. }
  558. } else {
  559. div.style.padding = null;
  560. div.style.transform = divProps.originalTransform;
  561. }
  562. }
  563. }
  564. };
  565. function renderTextLayer(renderParameters) {
  566. var task = new TextLayerRenderTask({
  567. textContent: renderParameters.textContent,
  568. textContentStream: renderParameters.textContentStream,
  569. container: renderParameters.container,
  570. viewport: renderParameters.viewport,
  571. textDivs: renderParameters.textDivs,
  572. textContentItemsStr: renderParameters.textContentItemsStr,
  573. enhanceTextSelection: renderParameters.enhanceTextSelection
  574. });
  575. task._render(renderParameters.timeout);
  576. return task;
  577. }
  578. return renderTextLayer;
  579. }();
  580. exports.renderTextLayer = renderTextLayer;