text_layer.js 19 KB

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