TextLayerBuilder.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. (function (name, context, definition) {
  2. if (typeof module != 'undefined' && module.exports) module.exports = definition()
  3. else if (typeof define == 'function' && define.amd) define(definition)
  4. else context[name] = definition()
  5. })('TextLayerBuilder', this, function (name, context) {
  6. // optimised CSS custom property getter/setter
  7. var CustomStyle = (function CustomStyleClosure() {
  8. // As noted on: http://www.zachstronaut.com/posts/2009/02/17/
  9. // animate-css-transforms-firefox-webkit.html
  10. // in some versions of IE9 it is critical that ms appear in this list
  11. // before Moz
  12. var prefixes = ['ms', 'Moz', 'Webkit', 'O'];
  13. var _cache = { };
  14. function CustomStyle() {
  15. }
  16. CustomStyle.getProp = function get(propName, element) {
  17. // check cache only when no element is given
  18. if (arguments.length == 1 && typeof _cache[propName] == 'string') {
  19. return _cache[propName];
  20. }
  21. element = element || document.documentElement;
  22. var style = element.style, prefixed, uPropName;
  23. // test standard property first
  24. if (typeof style[propName] == 'string') {
  25. return (_cache[propName] = propName);
  26. }
  27. // capitalize
  28. uPropName = propName.charAt(0).toUpperCase() + propName.slice(1);
  29. // test vendor specific properties
  30. for (var i = 0, l = prefixes.length; i < l; i++) {
  31. prefixed = prefixes[i] + uPropName;
  32. if (typeof style[prefixed] == 'string') {
  33. return (_cache[propName] = prefixed);
  34. }
  35. }
  36. //if all fails then set to undefined
  37. return (_cache[propName] = 'undefined');
  38. };
  39. CustomStyle.setProp = function set(propName, element, str) {
  40. var prop = this.getProp(propName);
  41. if (prop != 'undefined')
  42. element.style[prop] = str;
  43. };
  44. return CustomStyle;
  45. })();
  46. function TextLayerBuilder(textLayerDiv) {
  47. this.textLayerDiv = textLayerDiv;
  48. };
  49. TextLayerBuilder.prototype = {
  50. beginLayout: function() {
  51. this.textDivs = [];
  52. this.textLayerQueue = [];
  53. this.renderingDone = false;
  54. },
  55. endLayout: function() {
  56. this.layoutDone = true;
  57. this.insertDivContent();
  58. },
  59. appendText: function(geom) {
  60. var textDiv = document.createElement('div');
  61. // vScale and hScale already contain the scaling to pixel units
  62. var fontHeight = geom.fontSize * Math.abs(geom.vScale);
  63. textDiv.dataset.canvasWidth = geom.canvasWidth * geom.hScale;
  64. textDiv.dataset.fontName = geom.fontName;
  65. textDiv.style.fontSize = fontHeight + 'px';
  66. textDiv.style.fontFamily = geom.fontFamily;
  67. textDiv.style.left = geom.x + 'px';
  68. textDiv.style.top = (geom.y - fontHeight) + 'px';
  69. textDiv.ondblclick = function(e) {
  70. if (window.getSelection)
  71. window.getSelection().removeAllRanges();
  72. else if (document.selection)
  73. document.selection.empty();
  74. }
  75. // The content of the div is set in the `setTextContent` function.
  76. this.textDivs.push(textDiv);
  77. },
  78. setTextContent: function(textContent) {
  79. this.textContent = textContent;
  80. this.insertDivContent();
  81. },
  82. insertDivContent: function() {
  83. // Only set the content of the divs once layout has finished, the content
  84. // for the divs is available and content is not yet set on the divs.
  85. if (!this.layoutDone || this.divContentDone || !this.textContent)
  86. return;
  87. this.divContentDone = true;
  88. var textDivs = this.textDivs;
  89. var bidiTexts = this.textContent.bidiTexts;
  90. for (var i = 0; i < bidiTexts.length; i++) {
  91. var bidiText = bidiTexts[i];
  92. var textDiv = textDivs[i];
  93. if (!/\S/.test(bidiText.str)) {
  94. textDiv.dataset.isWhitespace = true;
  95. continue;
  96. }
  97. textDiv.textContent = bidiText.str;
  98. // bidiText.dir may be 'ttb' for vertical texts.
  99. textDiv.dir = bidiText.dir === 'rtl' ? 'rtl' : 'ltr';
  100. }
  101. this.renderLayer();
  102. },
  103. renderLayer: function() {
  104. var self = this;
  105. var textDivs = this.textDivs;
  106. var bidiTexts = this.textContent.bidiTexts;
  107. var textLayerDiv = this.textLayerDiv;
  108. var canvas = document.createElement('canvas');
  109. var ctx = canvas.getContext('2d');
  110. var textLayerFrag = document.createDocumentFragment();
  111. // No point in rendering so many divs as it'd make the browser unusable
  112. // even after the divs are rendered
  113. var MAX_TEXT_DIVS_TO_RENDER = 100000;
  114. if (textDivs.length > MAX_TEXT_DIVS_TO_RENDER)
  115. return;
  116. for (var i = 0, ii = textDivs.length; i < ii; i++) {
  117. var textDiv = textDivs[i];
  118. if ('isWhitespace' in textDiv.dataset) {
  119. continue;
  120. }
  121. textLayerFrag.appendChild(textDiv);
  122. ctx.font = textDiv.style.fontSize + ' ' + textDiv.style.fontFamily;
  123. var width = ctx.measureText(textDiv.textContent).width;
  124. if (width > 0) {
  125. var textScale = textDiv.dataset.canvasWidth / width;
  126. var transform = 'scale(' + textScale + ', 1)';
  127. if (bidiTexts[i].dir === 'ttb') {
  128. transform = 'rotate(90deg) ' + transform;
  129. }
  130. CustomStyle.setProp('transform' , textDiv, transform);
  131. CustomStyle.setProp('transformOrigin' , textDiv, '0% 0%');
  132. textLayerDiv.appendChild(textDiv);
  133. }
  134. }
  135. this.renderingDone = true;
  136. //this.updateMatches();
  137. textLayerDiv.appendChild(textLayerFrag);
  138. },
  139. /*setupRenderLayoutTimer: function() {
  140. // Schedule renderLayout() if user has been scrolling, otherwise
  141. // run it right away
  142. var RENDER_DELAY = 200; // in ms
  143. var self = this;
  144. if (Date.now() - PDFView.lastScroll > RENDER_DELAY) {
  145. // Render right away
  146. this.renderLayer();
  147. } else {
  148. // Schedule
  149. if (this.renderTimer)
  150. clearTimeout(this.renderTimer);
  151. this.renderTimer = setTimeout(function() {
  152. self.setupRenderLayoutTimer();
  153. }, RENDER_DELAY);
  154. }
  155. }*/
  156. };
  157. return TextLayerBuilder;
  158. });