TextLayerBuilder.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  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. // The content of the div is set in the `setTextContent` function.
  70. this.textDivs.push(textDiv);
  71. },
  72. setTextContent: function(textContent) {
  73. this.textContent = textContent;
  74. this.insertDivContent();
  75. },
  76. insertDivContent: function() {
  77. // Only set the content of the divs once layout has finished, the content
  78. // for the divs is available and content is not yet set on the divs.
  79. if (!this.layoutDone || this.divContentDone || !this.textContent)
  80. return;
  81. this.divContentDone = true;
  82. var textDivs = this.textDivs;
  83. var bidiTexts = this.textContent.bidiTexts;
  84. for (var i = 0; i < bidiTexts.length; i++) {
  85. var bidiText = bidiTexts[i];
  86. var textDiv = textDivs[i];
  87. if (!/\S/.test(bidiText.str)) {
  88. textDiv.dataset.isWhitespace = true;
  89. continue;
  90. }
  91. textDiv.textContent = bidiText.str;
  92. // bidiText.dir may be 'ttb' for vertical texts.
  93. textDiv.dir = bidiText.dir === 'rtl' ? 'rtl' : 'ltr';
  94. }
  95. this.renderLayer();
  96. },
  97. renderLayer: function() {
  98. var self = this;
  99. var textDivs = this.textDivs;
  100. var bidiTexts = this.textContent.bidiTexts;
  101. var textLayerDiv = this.textLayerDiv;
  102. var canvas = document.createElement('canvas');
  103. var ctx = canvas.getContext('2d');
  104. var textLayerFrag = document.createDocumentFragment();
  105. // No point in rendering so many divs as it'd make the browser unusable
  106. // even after the divs are rendered
  107. var MAX_TEXT_DIVS_TO_RENDER = 100000;
  108. if (textDivs.length > MAX_TEXT_DIVS_TO_RENDER)
  109. return;
  110. for (var i = 0, ii = textDivs.length; i < ii; i++) {
  111. var textDiv = textDivs[i];
  112. if ('isWhitespace' in textDiv.dataset) {
  113. continue;
  114. }
  115. textLayerFrag.appendChild(textDiv);
  116. ctx.font = textDiv.style.fontSize + ' ' + textDiv.style.fontFamily;
  117. var width = ctx.measureText(textDiv.textContent).width;
  118. if (width > 0) {
  119. var textScale = textDiv.dataset.canvasWidth / width;
  120. var transform = 'scale(' + textScale + ', 1)';
  121. if (bidiTexts[i].dir === 'ttb') {
  122. transform = 'rotate(90deg) ' + transform;
  123. }
  124. CustomStyle.setProp('transform' , textDiv, transform);
  125. CustomStyle.setProp('transformOrigin' , textDiv, '0% 0%');
  126. textLayerDiv.appendChild(textDiv);
  127. }
  128. }
  129. this.renderingDone = true;
  130. //this.updateMatches();
  131. textLayerDiv.appendChild(textLayerFrag);
  132. },
  133. /*setupRenderLayoutTimer: function() {
  134. // Schedule renderLayout() if user has been scrolling, otherwise
  135. // run it right away
  136. var RENDER_DELAY = 200; // in ms
  137. var self = this;
  138. if (Date.now() - PDFView.lastScroll > RENDER_DELAY) {
  139. // Render right away
  140. this.renderLayer();
  141. } else {
  142. // Schedule
  143. if (this.renderTimer)
  144. clearTimeout(this.renderTimer);
  145. this.renderTimer = setTimeout(function() {
  146. self.setupRenderLayoutTimer();
  147. }, RENDER_DELAY);
  148. }
  149. }*/
  150. };
  151. return TextLayerBuilder;
  152. });