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