text_layer.js 19 KB

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