PDFViewerPlugin.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. /**
  2. * @license
  3. * Copyright (C) 2013-2014 KO GmbH <copyright@kogmbh.com>
  4. *
  5. * @licstart
  6. * The JavaScript code in this page is free software: you can redistribute it
  7. * and/or modify it under the terms of the GNU Affero General Public License
  8. * (GNU AGPL) as published by the Free Software Foundation, either version 3 of
  9. * the License, or (at your option) any later version. The code is distributed
  10. * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU AGPL for more details.
  12. *
  13. * You should have received a copy of the GNU Affero General Public License
  14. * along with this code. If not, see <http://www.gnu.org/licenses/>.
  15. *
  16. * As additional permission under GNU AGPL version 3 section 7, you
  17. * may distribute non-source (e.g., minimized or compacted) forms of
  18. * that code without the copy of the GNU GPL normally required by
  19. * section 4, provided you include this license notice and a URL
  20. * through which recipients can access the Corresponding Source.
  21. *
  22. * As a special exception to the AGPL, any HTML file which merely makes function
  23. * calls to this code, and for that purpose includes it by reference shall be
  24. * deemed a separate work for copyright law purposes. In addition, the copyright
  25. * holders of this code give you permission to combine this code with free
  26. * software libraries that are released under the GNU LGPL. You may copy and
  27. * distribute such a system following the terms of the GNU AGPL for this code
  28. * and the LGPL for the libraries. If you modify this code, you may extend this
  29. * exception to your version of the code, but you are not obligated to do so.
  30. * If you do not wish to do so, delete this exception statement from your
  31. * version.
  32. *
  33. * This license applies to this entire compilation.
  34. * @licend
  35. * @source: http://viewerjs.org/
  36. * @source: http://github.com/kogmbh/ViewerJS
  37. */
  38. /*global document, PDFJS, console, TextLayerBuilder*/
  39. function PDFViewerPlugin() {
  40. "use strict";
  41. function loadScript(path, callback) {
  42. var script = document.createElement('script');
  43. script.async = false;
  44. script.src = path;
  45. script.type = 'text/javascript';
  46. script.onload = callback || script.onload;
  47. document.getElementsByTagName('head')[0].appendChild(script);
  48. }
  49. function init(callback) {
  50. var pdfLib, textLayerLib, pluginCSS;
  51. loadScript('./compatibility.js', function () {
  52. loadScript('./pdf.js');
  53. loadScript('./pdf_find_bar.js');
  54. loadScript('./pdf_find_controller.js');
  55. loadScript('./ui_utils.js');
  56. loadScript('./text_layer_builder.js');
  57. loadScript('./pdfjsversion.js', callback);
  58. });
  59. pluginCSS = document.createElement('link');
  60. pluginCSS.setAttribute("rel", "stylesheet");
  61. pluginCSS.setAttribute("type", "text/css");
  62. pluginCSS.setAttribute("href", "./PDFViewerPlugin.css");
  63. document.head.appendChild(pluginCSS);
  64. }
  65. var self = this,
  66. pages = [],
  67. domPages = [],
  68. pageText = [],
  69. renderingStates = [],
  70. RENDERING = {
  71. BLANK: 0,
  72. RUNNING: 1,
  73. FINISHED: 2
  74. },
  75. startedTextExtraction = false,
  76. container = null,
  77. initialized = false,
  78. pdfDocument = null,
  79. pageViewScroll = null,
  80. isPresentationMode = false,
  81. scale = 1,
  82. currentPage = 1,
  83. pageWidth,
  84. pageHeight,
  85. createdPageCount = 0;
  86. function scrollIntoView(elem) {
  87. elem.parentNode.scrollTop = elem.offsetTop;
  88. }
  89. function isScrolledIntoView(elem) {
  90. var docViewTop = container.scrollTop,
  91. docViewBottom = docViewTop + container.clientHeight,
  92. elemTop = elem.offsetTop,
  93. elemBottom = elemTop + elem.clientHeight;
  94. // Is in view if either the top or the bottom of the page is between the
  95. // document viewport bounds,
  96. // or if the top is above the viewport and the bottom is below it.
  97. return (elemTop >= docViewTop && elemTop < docViewBottom)
  98. || (elemBottom >= docViewTop && elemBottom < docViewBottom)
  99. || (elemTop < docViewTop && elemBottom >= docViewBottom);
  100. }
  101. function getDomPage(page) {
  102. return domPages[page.pageInfo.pageIndex];
  103. }
  104. function getPageText(page) {
  105. return pageText[page.pageInfo.pageIndex];
  106. }
  107. function getRenderingStatus(page) {
  108. return renderingStates[page.pageInfo.pageIndex];
  109. }
  110. function setRenderingStatus(page, renderStatus) {
  111. renderingStates[page.pageInfo.pageIndex] = renderStatus;
  112. }
  113. function updatePageDimensions(page, width, height) {
  114. var domPage = getDomPage(page),
  115. canvas = domPage.getElementsByTagName('canvas')[0],
  116. textLayer = domPage.getElementsByTagName('div')[0],
  117. cssScale = 'scale(' + scale + ', ' + scale + ')';
  118. domPage.style.width = width + "px";
  119. domPage.style.height = height + "px";
  120. canvas.width = width;
  121. canvas.height = height;
  122. textLayer.style.width = width + "px";
  123. textLayer.style.height = height + "px";
  124. CustomStyle.setProp('transform', textLayer, cssScale);
  125. CustomStyle.setProp('transformOrigin', textLayer, '0% 0%');
  126. // Once the page dimension is updated, the rendering state is blank.
  127. setRenderingStatus(page, RENDERING.BLANK);
  128. }
  129. function renderPage(page) {
  130. var domPage = getDomPage(page),
  131. textLayer = getPageText(page),
  132. canvas = domPage.getElementsByTagName('canvas')[0];
  133. if (getRenderingStatus(page) === RENDERING.BLANK) {
  134. setRenderingStatus(page, RENDERING.RUNNING);
  135. page.render({
  136. canvasContext: canvas.getContext('2d'),
  137. textLayer: textLayer,
  138. viewport: page.getViewport(scale)
  139. }).promise.then(function () {
  140. setRenderingStatus(page, RENDERING.FINISHED);
  141. });
  142. }
  143. }
  144. function createPage(page) {
  145. var pageNumber,
  146. textLayerDiv,
  147. textLayer,
  148. canvas,
  149. domPage,
  150. viewport;
  151. pageNumber = page.pageInfo.pageIndex + 1;
  152. viewport = page.getViewport(scale);
  153. domPage = document.createElement('div');
  154. domPage.id = 'pageContainer' + pageNumber;
  155. domPage.className = 'page';
  156. canvas = document.createElement('canvas');
  157. canvas.id = 'canvas' + pageNumber;
  158. textLayerDiv = document.createElement('div');
  159. textLayerDiv.className = 'textLayer';
  160. textLayerDiv.id = 'textLayer' + pageNumber;
  161. container.appendChild(domPage);
  162. domPage.appendChild(canvas);
  163. domPage.appendChild(textLayerDiv);
  164. pages.push(page);
  165. domPages.push(domPage);
  166. renderingStates.push(RENDERING.BLANK);
  167. updatePageDimensions(page, viewport.width, viewport.height);
  168. pageWidth = viewport.width;
  169. pageHeight = viewport.height;
  170. textLayer = new TextLayerBuilder({
  171. textLayerDiv: textLayerDiv,
  172. pageIndex: pageNumber - 1
  173. });
  174. page.getTextContent().then(function (textContent) {
  175. textLayer.setTextContent(textContent);
  176. });
  177. pageText.push(textLayer);
  178. createdPageCount += 1;
  179. if (createdPageCount === (pdfDocument.numPages)) {
  180. if (self.isSlideshow()) {
  181. domPages.forEach(function (pageElement) {
  182. pageElement.style.display = "none";
  183. });
  184. self.showPage(1);
  185. }
  186. self.onLoad();
  187. }
  188. }
  189. this.initialize = function (viewContainer, location) {
  190. var self = this,
  191. i,
  192. pluginCSS;
  193. init(function () {
  194. PDFJS.workerSrc = "./pdf.worker.js";
  195. PDFJS.getDocument(location).then(function loadPDF(doc) {
  196. pdfDocument = doc;
  197. container = viewContainer;
  198. for (i = 0; i < pdfDocument.numPages; i += 1) {
  199. pdfDocument.getPage(i + 1).then(createPage);
  200. }
  201. initialized = true;
  202. });
  203. });
  204. };
  205. this.isSlideshow = function () {
  206. // A very simple but generally true guess - if the width is greater than the height, treat it as a slideshow
  207. return pageWidth > pageHeight;
  208. };
  209. this.onLoad = function () {};
  210. this.getPages = function () {
  211. return domPages;
  212. };
  213. this.getWidth = function () {
  214. return pageWidth;
  215. };
  216. this.getHeight = function () {
  217. return pageHeight;
  218. };
  219. this.fitToWidth = function (width) {
  220. var zoomLevel;
  221. if (self.getWidth() === width) {
  222. return;
  223. }
  224. zoomLevel = width / pageWidth;
  225. self.setZoomLevel(zoomLevel);
  226. };
  227. this.fitToHeight = function (height) {
  228. var zoomLevel;
  229. if (self.getHeight() === height) {
  230. return;
  231. }
  232. zoomLevel = height / pageHeight;
  233. self.setZoomLevel(zoomLevel);
  234. };
  235. this.fitToPage = function (width, height) {
  236. var zoomLevel = width / pageWidth;
  237. if (height / pageHeight < zoomLevel) {
  238. zoomLevel = height / pageHeight;
  239. }
  240. self.setZoomLevel(zoomLevel);
  241. };
  242. this.fitSmart = function (width, height) {
  243. var zoomLevel = width / pageWidth;
  244. if (height && (height / pageHeight) < zoomLevel) {
  245. zoomLevel = height / pageHeight;
  246. }
  247. zoomLevel = Math.min(1.0, zoomLevel);
  248. self.setZoomLevel(zoomLevel);
  249. };
  250. this.setZoomLevel = function (zoomLevel) {
  251. var i;
  252. if (scale !== zoomLevel) {
  253. scale = zoomLevel;
  254. for (i = 0; i < pages.length; i += 1) {
  255. updatePageDimensions(pages[i], pageWidth * scale, pageHeight * scale);
  256. }
  257. }
  258. };
  259. this.getZoomLevel = function () {
  260. return scale;
  261. };
  262. this.onScroll = function () {
  263. var i;
  264. for (i = 0; i < domPages.length; i += 1) {
  265. if (isScrolledIntoView(domPages[i])) {
  266. if (getRenderingStatus(pages[i]) === RENDERING.BLANK) {
  267. renderPage(pages[i]);
  268. }
  269. }
  270. }
  271. };
  272. this.getPageInView = function () {
  273. var i;
  274. if (self.isSlideshow()) {
  275. return currentPage;
  276. } else {
  277. for (i = 0; i < domPages.length; i += 1) {
  278. if (isScrolledIntoView(domPages[i])) {
  279. return i + 1;
  280. }
  281. }
  282. }
  283. };
  284. this.showPage = function (n) {
  285. if (self.isSlideshow()) {
  286. domPages[currentPage - 1].style.display = "none";
  287. currentPage = n;
  288. domPages[n - 1].style.display = "block";
  289. } else {
  290. scrollIntoView(domPages[n - 1]);
  291. }
  292. };
  293. this.getPluginName = function () {
  294. return "PDF.js"
  295. };
  296. this.getPluginVersion = function () {
  297. var version = (String(typeof pdfjs_version) !== "undefined"
  298. ? pdfjs_version
  299. : "From Source"
  300. );
  301. return version;
  302. };
  303. this.getPluginURL = function () {
  304. return "https://github.com/mozilla/pdf.js/";
  305. };
  306. }