UNPKG

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