text_layer.js 21 KB

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