PdfListView.js 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894
  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. })('PDFListView', this, function (name, context) {
  6. //#expand __BUNDLE__
  7. function Logger() {
  8. this.logLevel = Logger.INFO;
  9. var self = this;
  10. if (typeof(console) == "object" && typeof(console.log) == "function") {
  11. this.debug = function() {
  12. if (self.logLevel <= Logger.DEBUG) {
  13. console.log.apply(console, arguments);
  14. }
  15. };
  16. this.info = function() {
  17. if (self.logLevel <= Logger.INFO) {
  18. console.log.apply(console, arguments);
  19. }
  20. };
  21. this.error = function() {
  22. if (self.logLevel <= Logger.ERROR) {
  23. console.log.apply(console, arguments);
  24. }
  25. };
  26. } else {
  27. this.debug = this.info = this.error = function nop() {};
  28. }
  29. }
  30. Logger.DEBUG = 0;
  31. Logger.INFO = 1;
  32. Logger.ERROR = 2;
  33. var logger = new Logger();
  34. function _flat(arr) {
  35. var res = arr.reduce(function(a, b) {
  36. return a.concat(b);
  37. });
  38. return res;
  39. }
  40. function failDumper(err) {
  41. logger.error(err);
  42. }
  43. if (typeof(PDFJS) === "undefined") {
  44. logger.error("PDF.js is not yet loaded.");
  45. }
  46. // -----------------------------------------------------------------------------
  47. /**
  48. * Wrapper around the raw PDF.JS document.
  49. */
  50. function Document(url, password, onProgress) {
  51. this.pdfDocument = null;
  52. this.pages = null;
  53. var parameters = {password: password};
  54. if (typeof url === 'string') { // URL
  55. parameters.url = url;
  56. } else if (url && 'byteLength' in url) { // ArrayBuffer
  57. parameters.data = url;
  58. }
  59. this.initialized = new PDFJS.LegacyPromise();
  60. PDFJS.getDocument(parameters, null, null, onProgress).then(this.loadPages.bind(this), failDumper);
  61. }
  62. Document.prototype.loadPages = function(pdfDocument) {
  63. this.pdfDocument = pdfDocument;
  64. var pagesCount = this.pagesCount = pdfDocument.numPages;
  65. var pagePromises = [];
  66. for (var i = 1; i <= pagesCount; i++) {
  67. pagePromises.push(pdfDocument.getPage(i));
  68. }
  69. this.pageRefMap = pageRefMap = {};
  70. var pagesPromise = Promise.all(pagePromises);
  71. var doc = this
  72. pagesPromise.then(function(promisedPages) {
  73. doc.pages = promisedPages.map(function(pdfPage, i) {
  74. var pageRef = pdfPage.ref
  75. pageRefMap[pageRef.num + ' ' + pageRef.gen] = i;
  76. return new Page(pdfPage);
  77. });
  78. });
  79. var destinationsPromise = pdfDocument.getDestinations();
  80. destinationsPromise.then(function(destinations) {
  81. doc.destinations = destinations;
  82. });
  83. Promise.all([pagesPromise, destinationsPromise]).then(function() {
  84. doc.initialized.resolve();
  85. }, failDumper);
  86. };
  87. /**
  88. * Handles the rendering. Multiple ListViews can be bound to a RenderController.
  89. * In that case, the RenderController figures out what's page has the highest
  90. * priority to render
  91. */
  92. function RenderController() {
  93. this.listViews = [];
  94. this.renderList = [];
  95. }
  96. RenderController.prototype = {
  97. addListView: function(listView) {
  98. // TODO: assert listView not already in list of this.listView
  99. this.listViews.push(listView);
  100. },
  101. updateRenderList: function() {
  102. this.renderList = _flat(this.listViews.map(function(listView) {
  103. return listView.getPagesToRender();
  104. }));
  105. // TODO: Some "highest-priority" sorting algorithm on the renderList.
  106. this.doRender();
  107. },
  108. pageToRender: function() {
  109. if (this.renderList.length === 0) return null;
  110. return this.renderList[0];
  111. },
  112. doRender: function() {
  113. var pageToRender = this.pageToRender();
  114. if (!pageToRender) return;
  115. pageToRender.render(this);
  116. },
  117. finishedRendering: function(pageView) {
  118. var idx = this.renderList.indexOf(pageView);
  119. // If the finished pageView is in the list of pages to render,
  120. // then remove it from the list and render start rendering the
  121. // next page.
  122. if (idx !== -1) {
  123. this.renderList.splice(idx, 1);
  124. this.doRender();
  125. }
  126. },
  127. onResize: function() {
  128. var renderAgain = false
  129. this.listViews.map(function(listView) {
  130. if (listView.calculateScale()) {
  131. listView.layout();
  132. renderAgain = true;
  133. }
  134. });
  135. if (renderAgain) {
  136. this.updateRenderList();
  137. }
  138. }
  139. };
  140. var LAYOUT_SINGLE = 'layout_single';
  141. var SCALE_MODE_AUTO = 'scale_mode_auto';
  142. var SCALE_MODE_VALUE = 'scale_mode_value';
  143. var SCALE_MODE_FIT_WIDTH = 'scale_mode_fit_width';
  144. var SCALE_MODE_FIT_HEIGHT = 'scale_mode_fit_height';
  145. /**
  146. * Main view that holds the single pageContainer/pageViews of the pdfDoc.
  147. */
  148. function ListView(dom, options) {
  149. this.dom = dom;
  150. this.options = options;
  151. this.pageLayout = LAYOUT_SINGLE;
  152. this.scaleMode = SCALE_MODE_VALUE;
  153. this.scale = 1.0;
  154. this.pageWidthOffset = 0;
  155. this.pageHeightOffset = 0;
  156. this.pageViews = [];
  157. this.containerViews = [];
  158. }
  159. ListView.prototype = {
  160. setDocument: function(pdfDoc) {
  161. this.clearPages();
  162. this.pdfDoc = pdfDoc;
  163. this.assignPagesToContainer();
  164. this.layout();
  165. },
  166. clearPages: function() {
  167. var self = this;
  168. this.containerViews.map(function(container) {
  169. self.dom.removeChild(container.dom);
  170. });
  171. this.pageViews = [];
  172. this.containerViews = [];
  173. },
  174. assignPagesToContainer: function() {
  175. // TODO: Handle multiple layout types here. For now, assume to have one page
  176. // per pageContainer.
  177. this.pdfDoc.pages.map(function(page) {
  178. var pageView = new PageView(page, this);
  179. // TODO: Switch over to a proper event handler
  180. var that = this;
  181. var index = that.pageViews.length;
  182. pageView.ondblclick = function(e) {
  183. e.page = index;
  184. if (that.ondblclick) {
  185. that.ondblclick.call(that, e);
  186. }
  187. }
  188. this.pageViews.push(pageView);
  189. var container = new PageContainerView(this);
  190. container.setPageView(pageView, 0);
  191. this.containerViews.push(container);
  192. this.dom.appendChild(container.dom);
  193. }, this);
  194. },
  195. layout: function() {
  196. this.savePdfPosition();
  197. this.containerViews.forEach(function(containerView) {
  198. containerView.layout();
  199. });
  200. this.restorePdfPosition();
  201. },
  202. getScale: function() {
  203. return this.scale;
  204. },
  205. getScaleMode: function() {
  206. return this.scaleMode;
  207. },
  208. setScaleMode: function(scaleMode, scale) {
  209. this.scaleMode = scaleMode;
  210. if (scaleMode == SCALE_MODE_VALUE) {
  211. this.scale = scale;
  212. }
  213. this.calculateScale();
  214. this.layout();
  215. },
  216. setScale: function(scale) {
  217. this.setScaleMode(SCALE_MODE_VALUE, scale);
  218. },
  219. setToAutoScale: function() {
  220. this.setScaleMode(SCALE_MODE_AUTO);
  221. },
  222. setToFitWidth: function() {
  223. this.setScaleMode(SCALE_MODE_FIT_WIDTH);
  224. },
  225. setToFitHeight: function() {
  226. this.setScaleMode(SCALE_MODE_FIT_HEIGHT);
  227. },
  228. // Calculates the new scale. Returns `true` if the scale changed.
  229. calculateScale: function() {
  230. var newScale = this.scale;
  231. var oldScale = newScale;
  232. var scaleMode = this.scaleMode;
  233. if (scaleMode === SCALE_MODE_FIT_WIDTH || scaleMode === SCALE_MODE_AUTO) {
  234. var clientWidth = this.dom.clientWidth;
  235. if (clientWidth == 0) {
  236. logger.debug("LIST VIEW NOT VISIBLE")
  237. return false;
  238. }
  239. var maxNormalWidth = 0;
  240. this.containerViews.forEach(function(containerView) {
  241. maxNormalWidth = Math.max(maxNormalWidth, containerView.normalWidth);
  242. });
  243. var scale = (clientWidth - this.pageWidthOffset)/maxNormalWidth;
  244. if (scaleMode === SCALE_MODE_AUTO) {
  245. scale = Math.min(1.0, scale);
  246. }
  247. newScale = scale;
  248. } else if (scaleMode === SCALE_MODE_FIT_HEIGHT) {
  249. var clientHeight = this.dom.clientHeight;
  250. if (clientHeight == 0) {
  251. logger.debug("LIST VIEW NOT VISIBLE")
  252. return false;
  253. }
  254. var maxNormalHeight = 0;
  255. this.containerViews.forEach(function(containerView) {
  256. maxNormalHeight = Math.max(maxNormalHeight, containerView.normalHeight);
  257. });
  258. newScale = (clientHeight - this.pageHeightOffset)/maxNormalHeight;
  259. }
  260. this.scale = newScale;
  261. return newScale !== oldScale;
  262. },
  263. getPagesToRender: function() {
  264. // Cache these results to avoid dom access.
  265. this.scrollTop = this.dom.scrollTop;
  266. this.scrollBottom = this.scrollTop + this.dom.clientHeight;
  267. // TODO: For now, this only returns the visible pages and not
  268. // +1/-1 one to render in advance.
  269. return this.pageViews.filter(function(pageView) {
  270. var isVisible = pageView.isVisible();
  271. if (isVisible && !pageView.isRendered) {
  272. return true;
  273. }
  274. });
  275. },
  276. navigateTo: function(destRef) {
  277. var destination = this.pdfDoc.destinations[destRef]
  278. if (typeof destination !== "object") {
  279. return;
  280. }
  281. logger.debug("NAVIGATING TO", destination);
  282. var pageRef = destination[0];
  283. var pageNumber = this.pdfDoc.pageRefMap[pageRef.num + ' ' + pageRef.gen];
  284. if (typeof pageNumber !== "number") {
  285. return;
  286. }
  287. logger.debug("PAGE NUMBER", pageNumber);
  288. var pageView = this.pageViews[pageNumber];
  289. var destinationType = destination[1].name;
  290. switch(destinationType) {
  291. case "XYZ":
  292. var x = destination[2];
  293. var y = destination[3];
  294. break;
  295. default:
  296. // TODO
  297. return;
  298. }
  299. var position = pageView.getPdfPositionInViewer(x,y);
  300. this.dom.scrollTop = position.top;
  301. this.dom.scrollLeft = position.left;
  302. },
  303. savePdfPosition: function() {
  304. this.pdfPosition = this.getPdfPosition();
  305. logger.debug("SAVED PDF POSITION", this.pdfPosition);
  306. },
  307. restorePdfPosition: function() {
  308. logger.debug("RESTORING PDF POSITION", this.pdfPosition);
  309. this.setPdfPosition(this.pdfPosition);
  310. },
  311. getPdfPosition: function() {
  312. var pdfPosition = null;
  313. for (var i = 0; i < this.pageViews.length; i++) {
  314. var pageView = this.pageViews[i];
  315. var pdfOffset = pageView.getUppermostVisiblePdfOffset();
  316. if (pdfOffset !== null) {
  317. pdfPosition = {
  318. page: i,
  319. offset: {
  320. top: pdfOffset,
  321. left: 0 // TODO
  322. }
  323. }
  324. break;
  325. }
  326. }
  327. return pdfPosition;
  328. },
  329. setPdfPosition: function(pdfPosition) {
  330. if (typeof pdfPosition !== "undefined" && pdfPosition != null) {
  331. var offset = pdfPosition.offset;
  332. var page_index = pdfPosition.page;
  333. var pageView = this.pageViews[page_index];
  334. var position = pageView.getPdfPositionInViewer(offset.left, offset.top);
  335. this.dom.scrollTop = position.top;
  336. }
  337. }
  338. };
  339. /*
  340. * A PageContainerView holds multiple PageViews. E.g. in a two-page layout,
  341. * every pageContainerView holds two PageViews and is responsible to layout
  342. * them.
  343. */
  344. function PageContainerView(listView) {
  345. this.listView = listView;
  346. var dom = this.dom = document.createElement('div');
  347. dom.className = 'plv-page-container page-container';
  348. this.pages = [];
  349. }
  350. PageContainerView.prototype = {
  351. setPageView: function(pageView, idx) {
  352. // TODO: handle case if there is already a page here
  353. this.pages[idx] = pageView;
  354. // TODO: handle page idx properly
  355. this.dom.appendChild(pageView.dom);
  356. },
  357. removePageView: function(idx) {
  358. // TODO: check if idx is set on page[]
  359. this.dom.removeChild(this.pages[idx].dom);
  360. },
  361. layout: function() {
  362. var scale = this.listView.scale;
  363. var normalWidth = 0;
  364. var normalHeight = 0;
  365. this.pages.forEach(function(pageView) {
  366. pageView.layout();
  367. normalWidth += pageView.normalWidth;
  368. normalHeight = Math.max(pageView.normalHeight, normalHeight);
  369. });
  370. this.normalWidth = normalWidth;
  371. this.normalHeight = normalHeight;
  372. this.dom.style.width = (normalWidth * scale) + 'px';
  373. this.dom.style.height = (normalHeight * scale) + 'px';
  374. }
  375. };
  376. var RenderingStates = {
  377. INITIAL: 0,
  378. RUNNING: 1,
  379. PAUSED: 2,
  380. FINISHED: 3
  381. };
  382. var idCounter = 0;
  383. /**
  384. * The view for a single page.
  385. */
  386. function PageView(page, listView) {
  387. this.page = page;
  388. this.listView = listView;
  389. this.id = idCounter++;
  390. this.number = this.page.number;
  391. this.rotation = 0;
  392. this.isRendered = false;
  393. this.renderState = RenderingStates.INITIAL;
  394. var dom = this.dom = document.createElement('div');
  395. dom.className = "plv-page-view page-view";
  396. var that = this;
  397. dom.ondblclick = function(e) {
  398. var layerX = e.layerX;
  399. var layerY = e.layerY;
  400. var element = e.target;
  401. while (element.offsetParent && element.offsetParent !== dom) {
  402. layerX = layerX + element.offsetLeft;
  403. layerY = layerY + element.offsetTop;
  404. element = element.offsetParent;
  405. }
  406. var pdfPoint = that.viewport.convertToPdfPoint(layerX, layerY);
  407. var event = {
  408. x: pdfPoint[0],
  409. y: that.normalHeight - pdfPoint[1]
  410. };
  411. if (that.ondblclick) {
  412. that.ondblclick.call(that.listView, event)
  413. }
  414. }
  415. this.createNewCanvas();
  416. }
  417. PageView.prototype = {
  418. layout: function() {
  419. var scale = this.listView.scale;
  420. var viewport = this.viewport =
  421. this.page.pdfPage.getViewport(scale, this.rotation);
  422. this.normalWidth = viewport.width / scale;
  423. this.normalHeight = viewport.height / scale;
  424. // Only change the width/height property of the canvas if it really
  425. // changed. Every assignment to the width/height property clears the
  426. // content of the canvas.
  427. var outputScale = this.getOutputScale();
  428. var scaledWidth = (Math.floor(viewport.width) * outputScale.sx) | 0;
  429. var scaledHeight = (Math.floor(viewport.height) * outputScale.sy) | 0;
  430. var newWidth = Math.floor(viewport.width);
  431. var newHeight = Math.floor(viewport.height);
  432. if (this.canvas.width !== newWidth) {
  433. this.canvas.width = scaledWidth;
  434. this.canvas.style.width = newWidth + 'px';
  435. this.resetRenderState();
  436. }
  437. if (this.canvas.height !== newHeight) {
  438. this.canvas.height = scaledHeight;
  439. this.canvas.style.height = newHeight + 'px';
  440. this.resetRenderState();
  441. }
  442. if(outputScale.scaled){
  443. var ctx = this.getCanvasContext()
  444. ctx.scale(outputScale.sx, outputScale.sy)
  445. }
  446. this.width = viewport.width;
  447. this.height = viewport.height;
  448. },
  449. isVisible: function() {
  450. var listView = this.listView;
  451. var dom = this.dom;
  452. var offsetTop = dom.offsetTop;
  453. var offsetBottom = offsetTop + this.height;
  454. return offsetBottom >= listView.scrollTop &&
  455. offsetTop <= listView.scrollBottom;
  456. },
  457. resetRenderState: function() {
  458. this.renderState = RenderingStates.INITIAL;
  459. this.isRendered = false;
  460. if (this.textLayerDiv) {
  461. this.dom.removeChild(this.textLayerDiv);
  462. delete this.textLayerDiv;
  463. }
  464. if (this.annotationsLayerDiv) {
  465. this.dom.removeChild(this.annotationsLayerDiv);
  466. delete this.annotationsLayerDiv;
  467. }
  468. },
  469. render: function(renderController) {
  470. return this.page.render(this, renderController);
  471. },
  472. getCanvasContext: function() {
  473. return this.canvas.getContext('2d');
  474. },
  475. /**
  476. * Returns scale factor for the canvas. It makes sense for the HiDPI displays.
  477. * @return {Object} The object with horizontal (sx) and vertical (sy)
  478. scales. The scaled property is set to false if scaling is
  479. not required, true otherwise.
  480. */
  481. getOutputScale: function(){
  482. var ctx = this.getCanvasContext()
  483. var devicePixelRatio = window.devicePixelRatio || 1;
  484. var backingStoreRatio = ctx.webkitBackingStorePixelRatio ||
  485. ctx.mozBackingStorePixelRatio ||
  486. ctx.msBackingStorePixelRatio ||
  487. ctx.oBackingStorePixelRatio ||
  488. ctx.backingStorePixelRatio || 1;
  489. var pixelRatio = devicePixelRatio / backingStoreRatio;
  490. return {
  491. sx: pixelRatio,
  492. sy: pixelRatio,
  493. scaled: pixelRatio != 1
  494. };
  495. },
  496. createNewCanvas: function() {
  497. if (this.canvas) {
  498. this.dom.removeChild(this.canvas);
  499. }
  500. var canvas = this.canvas = document.createElement('canvas');
  501. this.dom.appendChild(canvas);
  502. this.layout();
  503. },
  504. pdfPositionToPixels: function(x, y) {
  505. return this.viewport.convertToViewportPoint(x, y);
  506. },
  507. getCanvasPositionInViewer: function() {
  508. return {
  509. left: this.canvas.offsetLeft + this.dom.offsetLeft,
  510. top: this.canvas.offsetTop + this.dom.offsetTop
  511. }
  512. },
  513. getPdfPositionInViewer: function(x, y) {
  514. pageOffset = this.pdfPositionToPixels(x, y);
  515. canvasOffset = this.getCanvasPositionInViewer();
  516. return {
  517. left: canvasOffset.left + pageOffset[0],
  518. top: canvasOffset.top + pageOffset[1]
  519. }
  520. },
  521. getUppermostVisibleCanvasOffset: function() {
  522. var pagePosition = this.getCanvasPositionInViewer();
  523. var pageHeight = this.canvas.height;
  524. var viewportTop = this.listView.dom.scrollTop;
  525. var viewportHeight = this.listView.dom.clientHeight;
  526. // Check if the top of the page is showing, i.e:
  527. // _______________
  528. // | |
  529. // | ........ |
  530. // | . . |
  531. // ----.------.---
  532. // . .
  533. // ........
  534. var topVisible = (pagePosition.top > viewportTop && pagePosition.top < viewportTop + viewportHeight);
  535. // Check if at least some of the page is showing, i.e:
  536. // ........ ........
  537. // ____.______.___ ---.------.---
  538. // | . . | or | . . |
  539. // | . . | | . . |
  540. // | ........ | | . . |
  541. // --------------- ---.------.---
  542. // ........
  543. var someContentVisible = (pagePosition.top < viewportTop && pagePosition.top + pageHeight > viewportTop);
  544. if (topVisible) {
  545. return 0;
  546. } else if (someContentVisible) {
  547. return viewportTop - pagePosition.top;
  548. } else {
  549. return null;
  550. }
  551. },
  552. getUppermostVisiblePdfOffset: function() {
  553. var canvasOffset = this.getUppermostVisibleCanvasOffset();
  554. if (canvasOffset === null) {
  555. return null;
  556. }
  557. var pdfOffset = this.viewport.convertToPdfPoint(0, canvasOffset);
  558. return pdfOffset[1];
  559. }
  560. };
  561. /**
  562. * An abstraction around the raw page object of PDF.JS, that also handles the
  563. * rendering logic of (maybe multiple) pageView(s) that are based on this page.
  564. */
  565. function Page(pdfPage, number) {
  566. this.number = number;
  567. this.pdfPage = pdfPage;
  568. this.renderContextList = {};
  569. }
  570. Page.prototype = {
  571. render: function(pageView, renderController) {
  572. var renderContext;
  573. // FEATURE: If the page was rendered already once, then use the old
  574. // version as a placeholder until the new version is rendered at the
  575. // expected quality.
  576. // FEATURE: If the page can be rendered at low quality (thumbnail) and
  577. // there is already a higher resolution rendering, then use this one
  578. // instead of rerendering from scratch again.
  579. // PageView is not layouted.
  580. if (!pageView.viewport) return;
  581. // Nothing todo.
  582. if (pageView.isRendered) return;
  583. // Not most important page to render ATM.
  584. if (renderController.pageToRender() !== pageView) return;
  585. var self = this;
  586. var viewport;
  587. if (renderContext = this.renderContextList[pageView.id]) {
  588. viewport = renderContext.viewport;
  589. // TODO: handle rotation
  590. if (viewport.height !== pageView.viewport.height ||
  591. viewport.height !== pageView.viewport.height)
  592. {
  593. // The viewport changed -> need to rerender.
  594. renderContext.abandon = true;
  595. delete self.renderContextList[pageView.id];
  596. pageView.createNewCanvas();
  597. self.render(pageView, renderController);
  598. } else if (renderContext.state === RenderingStates.PAUSED) {
  599. // There is already a not finished renderState ->
  600. logger.debug('RESUME', pageView.id);
  601. renderContext.resume();
  602. }
  603. }
  604. if (!renderContext) {
  605. viewport = pageView.viewport;
  606. // No rendering data yet -> create a new renderContext and start
  607. // the rendering process.
  608. var textLayer;
  609. var textLayerBuilder = pageView.listView.options.textLayerBuilder;
  610. if (textLayerBuilder) {
  611. var textLayerDiv = pageView.textLayerDiv = document.createElement("div");
  612. textLayerDiv.className = 'plv-text-layer text-layer';
  613. pageView.dom.appendChild(textLayerDiv);
  614. textLayer = new textLayerBuilder(textLayerDiv);
  615. this.pdfPage.getTextContent().then(
  616. function(textContent) {
  617. textLayer.setTextContent(textContent);
  618. }
  619. );
  620. }
  621. var annotationsLayerBuilder = pageView.listView.options.annotationsLayerBuilder;
  622. if (annotationsLayerBuilder) {
  623. var annotationsLayerDiv = pageView.annotationsLayerDiv = document.createElement("div");
  624. annotationsLayerDiv.className = 'plv-annotations-layer annotations-layer';
  625. pageView.dom.appendChild(annotationsLayerDiv);
  626. var annotationsLayer = new annotationsLayerBuilder(pageView, annotationsLayerDiv);
  627. this.pdfPage.getAnnotations().then(
  628. function(annotations) {
  629. annotationsLayer.setAnnotations(annotations)
  630. }
  631. );
  632. }
  633. renderContext = {
  634. canvasContext: pageView.getCanvasContext(),
  635. viewport: viewport,
  636. textLayer: textLayer,
  637. continueCallback: function pdfViewContinueCallback(cont) {
  638. if (renderContext.abandon) {
  639. logger.debug("ABANDON", pageView.id);
  640. return;
  641. }
  642. if (renderController.pageToRender() !== pageView) {
  643. logger.debug('PAUSE', pageView.id);
  644. renderContext.state = RenderingStates.PAUSED;
  645. renderContext.resume = function resumeCallback() {
  646. renderContext.state = RenderingStates.RUNNING;
  647. cont();
  648. };
  649. return;
  650. }
  651. logger.debug('CONT', pageView.id);
  652. cont();
  653. }
  654. };
  655. this.renderContextList[pageView.id] = renderContext;
  656. logger.debug("BEGIN", pageView.id);
  657. renderContext.renderPromise = this.pdfPage.render(renderContext).promise;
  658. renderContext.renderPromise.then(
  659. function pdfPageRenderCallback() {
  660. logger.debug('DONE', pageView.id);
  661. pageView.isRendered = true;
  662. renderController.finishedRendering(pageView);
  663. },
  664. failDumper
  665. );
  666. }
  667. return renderContext.renderPromise;
  668. }
  669. };
  670. function PDFListView(mainDiv, options) {
  671. if (typeof(options) != "object") {
  672. options = {};
  673. }
  674. if (typeof(options.logLevel) != "number") {
  675. options.logLevel = Logger.INFO;
  676. }
  677. logger.logLevel = options.logLevel;
  678. var self = this;
  679. this.listView = new ListView(mainDiv, options);
  680. this.listView.ondblclick = function(e) {
  681. if (options.ondblclick) {
  682. options.ondblclick.call(self, e);
  683. }
  684. }
  685. this.renderController = new RenderController();
  686. this.renderController.addListView(this.listView);
  687. this.renderController.updateRenderList();
  688. mainDiv.addEventListener('scroll', function() {
  689. // This will update the list AND start rendering if needed.
  690. self.renderController.updateRenderList();
  691. });
  692. window.addEventListener('resize', function() {
  693. // Check if the scale changed due to the resizing.
  694. if (self.listView.calculateScale()) {
  695. // Update the layout and start rendering. Changing the layout
  696. // of the PageView makes it rendering stop.
  697. self.listView.layout();
  698. self.renderController.updateRenderList();
  699. }
  700. });
  701. }
  702. PDFListView.prototype = {
  703. loadPdf: function(url, onProgress) {
  704. this.doc = new Document(url, null, onProgress);
  705. var self = this;
  706. var promise = this.doc.initialized;
  707. promise.then(function() {
  708. logger.debug('LOADED');
  709. self.listView.setDocument(self.doc);
  710. self.renderController.updateRenderList();
  711. }, failDumper);
  712. return promise;
  713. },
  714. getScale: function() {
  715. return this.listView.getScale();
  716. },
  717. getScaleMode: function() {
  718. return this.listView.getScaleMode()
  719. },
  720. setScaleMode: function(scaleMode, scale) {
  721. this.listView.setScaleMode(scaleMode, scale);
  722. this.renderController.updateRenderList();
  723. },
  724. setScale: function(scale) {
  725. this.listView.setScale(scale);
  726. this.renderController.updateRenderList();
  727. },
  728. setToAutoScale: function() {
  729. this.listView.setToAutoScale();
  730. this.renderController.updateRenderList();
  731. },
  732. setToFitWidth: function() {
  733. this.listView.setToFitWidth();
  734. this.renderController.updateRenderList();
  735. },
  736. setToFitHeight: function() {
  737. this.listView.setToFitHeight();
  738. this.renderController.updateRenderList();
  739. },
  740. onResize: function() {
  741. this.renderController.onResize();
  742. },
  743. getPdfPosition: function() {
  744. return this.listView.getPdfPosition();
  745. },
  746. setPdfPosition: function(pdfPosition) {
  747. this.listView.setPdfPosition(pdfPosition)
  748. }
  749. };
  750. PDFListView.Logger = Logger;
  751. return PDFListView;
  752. });