jquery.touchPDF.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774
  1. /*
  2. * @fileOverview TouchPDF - jQuery Plugin
  3. * @version 0.4
  4. *
  5. * @author Loic Minghetti http://www.loicminghetti.net
  6. * @see https://github.com/loicminghetti/TouchPDF-Jquery-Plugin
  7. * @see http://plugins.jquery.com/project/touchPDF
  8. *
  9. * Copyright (c) 2014 Loic Minghetti
  10. * Dual licensed under the MIT or GPL Version 2 licenses.
  11. *
  12. */
  13. /**
  14. * See (http://jquery.com/).
  15. * @name $
  16. * @class
  17. * See the jQuery Library (http://jquery.com/) for full details. This just
  18. * documents the function and classes that are added to jQuery by this plug-in.
  19. */
  20. /**
  21. * See (http://jquery.com/)
  22. * @name fn
  23. * @class
  24. * See the jQuery Library (http://jquery.com/) for full details. This just
  25. * documents the function and classes that are added to jQuery by this plug-in.
  26. * @memberOf $
  27. */
  28. (function ($) {
  29. "use strict";
  30. //Constants
  31. var EMPTY = "empty",
  32. INIT = "init",
  33. LOADING = "loading",
  34. LOADED = "loaded",
  35. ZOOMEDIN = "zoomedin",
  36. DRAGGING = "dragging",
  37. RENDERING = "rendering",
  38. PLUGIN_NS = 'TouchPDF',
  39. TOOLBAR_HEIGHT = 30,
  40. BORDER_WIDTH = 1,
  41. TAB_SPACING = -2,
  42. TAB_WIDTH = 41,
  43. TAB_OFFSET_WIDTH = 10;
  44. /**
  45. * The default configuration, and available options to configure touchPDF with.
  46. * You can set the default values by updating any of the properties prior to instantiation.
  47. * @name $.fn.pdf.defaults
  48. * @namespace
  49. * @property {string} [source=""] Path of PDF file to display
  50. * @property {string} [title="TouchPDF"] Title of the PDF to be displayed in the toolbar
  51. * @property {array} [tabs=[]] Array of tabs to display on the side. See doc for syntax.
  52. * @property {string} [tabsColor="beige" Default background color for all tabs. Available colors are "green", "yellow", "orange", "brown", "blue", "white", "black" and you can define your own colors with CSS.
  53. * @property {boolean} [disableZoom=false] Disable zooming of PDF document. By default, PDF can be zoomed using scroll, two fingers pinch, +/- keys, and toolbar buttons
  54. * @property {boolean} [disableSwipe=false] Disable swipe to next/prev page of PDF document. By default, PDF can be swiped using one finger
  55. * @property {boolean} [disableLinks=false] Disable all internal and external links on PDF document
  56. * @property {boolean} [disableKeys=false] Disable the arrow keys for next/previous page and +/- for zooming (if zooming is enabled)
  57. * @property {boolean} [redrawOnWindowResize=true] Force resize of PDF viewer on window resize
  58. * @property {float} [pdfScale=1] Defines the ratio between your PDF page size and the tabs size
  59. * @property {float} [quality=2] Set quality ratio for loaded PDF pages. Set at 2 for sharp display when user zooms up to 200%
  60. * @property {boolean} [showToolbar=true] Show a toolbar on top of the document with title, page number and buttons for next/prev pages and zooming
  61. * @property {function} [loaded=null] A handler triggered when PDF document is loaded (before display of first page)
  62. * @property {function} [changed=null] A handler triggered each time a new page is displayed
  63. * @property {string} [loadingHTML="Loading PDF"] Text or HTML displayed on white page shown before document is loaded
  64. * @property {function} [loadingHeight=841] Height in px of white page shown before document is loaded
  65. * @property {function} [loadingWidth=595] Width in px of white page shown before document is loaded
  66. */
  67. var defaults = {
  68. source: null,
  69. title: "TouchPDF",
  70. tabs: [],
  71. tabsColor: "beige",
  72. disableZoom: false,
  73. disableSwipe: false,
  74. disableLinks: false,
  75. disableKeys: false,
  76. pdfScale: 1,
  77. quality: 2,
  78. redrawOnWindowResize: true,
  79. showToolbar: true,
  80. loaded: null,
  81. changed: null,
  82. loadingHeight: 841,
  83. loadingWidth: 595,
  84. loadingHTML: "Loading PDF"
  85. };
  86. /**
  87. * Load a PDF file in a div
  88. * The TouchPDF plugin can be instantiated via this method, or methods within
  89. * @see TouchPDF
  90. * @class
  91. * @param {Mixed} method If the current DOMNode is a TouchPDF object, and <code>method</code> is a TouchPDF method, then
  92. * the <code>method</code> is executed, and any following arguments are passed to the TouchPDF method.
  93. * If <code>method</code> is an object, then the TouchPDF class is instantiated on the current DOMNode, passing the
  94. * configuration properties defined in the object. See TouchPDF
  95. */
  96. $.fn.pdf = function (method) {
  97. var $this = $(this),
  98. plugin = $this.data(PLUGIN_NS);
  99. //Check if we are already instantiated and trying to execute a method
  100. if (plugin && typeof method === 'string') {
  101. if (plugin[method]) {
  102. return plugin[method].apply(this, Array.prototype.slice.call(arguments, 1));
  103. } else {
  104. $.error('Method ' + method + ' does not exist on jQuery.pdf');
  105. }
  106. }
  107. //Else not instantiated and trying to pass init object (or nothing)
  108. else if (!plugin && (typeof method === 'object' || !method)) {
  109. return init.apply(this, arguments);
  110. }
  111. return $this;
  112. };
  113. //Expose our defaults so a user could override the plugin defaults
  114. $.fn.pdf.defaults = defaults;
  115. /**
  116. * Initialise the plugin for each DOM element matched
  117. * This creates a new instance of the main TouchPDF class for each DOM element, and then
  118. * saves a reference to that instance in the elements data property.
  119. * @internal
  120. */
  121. function init(options) {
  122. //Prep and extend the options
  123. if (!options) {
  124. options = {};
  125. }
  126. options = $.extend({}, $.fn.pdf.defaults, options);
  127. //For each element instantiate the plugin
  128. return this.each(function () {
  129. var $this = $(this);
  130. //Check we havent already initialised the plugin
  131. var plugin = $this.data(PLUGIN_NS);
  132. if (!plugin) {
  133. plugin = new TouchPDF(this, options);
  134. $this.data(PLUGIN_NS, plugin);
  135. }
  136. });
  137. }
  138. /**
  139. * Main TouchPDF Plugin Class.
  140. * Do not use this to construct your TouchPDF object, use the jQuery plugin method $.fn.pdf(); {@link $.fn.pdf}
  141. * @private
  142. * @name TouchPDF
  143. * @param {DOMNode} element The HTML DOM object to apply to plugin to
  144. * @param {Object} options The options to configure the plugin with. @link {$.fn.pdf.defaults}
  145. * @see $.fn.pdf.defaults
  146. * @see $.fn.pdf
  147. * @class
  148. */
  149. function TouchPDF(element, options) {
  150. // Current phase of pdf loading
  151. var state = EMPTY;
  152. // Number of pages
  153. var totalPages = 0;
  154. // Page to be displayed
  155. var pageNum = 0;
  156. // Page currently rendering
  157. var pageNumRendering = 0;
  158. // jQuery wrapped element for this instance
  159. var $element = $(element);
  160. // PDF canvas
  161. var canvas = null;
  162. // jQuery wrapped PDF annotation layer
  163. var $annotations = null;
  164. // PDF.JS object
  165. var pdfDoc = null;
  166. var scale = 1;
  167. var ctx = false;
  168. var pagesRefMap = [];
  169. var plugin = this;
  170. var tabWidth = 0;
  171. var $drag = null, $viewer = null;
  172. var linksDisabled = false;
  173. initDom();
  174. load();
  175. //
  176. //Public methods
  177. //
  178. /**
  179. * Go to specific page of PDF file
  180. * @function
  181. * @name $.fn.pdf#goto
  182. * @return {DOMNode} The Dom element that was registered with TouchPDF
  183. * @example $("#element").pdf("goto", 10);
  184. */
  185. this.goto = function (number) {
  186. goto(number);
  187. return $element;
  188. };
  189. /**
  190. * Go to previous page of PDF file, until first page
  191. * @function
  192. * @name $.fn.pdf#previous
  193. * @return {DOMNode} The Dom element that was registered with TouchPDF
  194. * @example $("#element").pdf("previous");
  195. */
  196. this.previous = function () {
  197. goto(pageNum-1);
  198. return $element;
  199. };
  200. /**
  201. * Go to next page of PDF file, until end of pdf
  202. * @function
  203. * @name $.fn.pdf#next
  204. * @return {DOMNode} The Dom element that was registered with TouchPDF
  205. * @example $("#element").pdf("next");
  206. */
  207. this.next = function () {
  208. goto(pageNum+1);
  209. return $element;
  210. };
  211. /**
  212. * Force redraw of pdf (height, width and zoom)
  213. * @function
  214. * @name $.fn.pdf#redraw
  215. * @return {DOMNode} The Dom element that was registered with TouchPDF
  216. * @example $("#element").pdf("redraw");
  217. */
  218. this.redraw = function () {
  219. redraw();
  220. return $element;
  221. };
  222. /**
  223. * Destroy the pdf container completely.
  224. * @function
  225. * @name $.fn.pdf#destroy
  226. * @example $("#element").pdf("destroy");
  227. */
  228. this.destroy = function () {
  229. $element.empty().removeClass("touchPDF");
  230. };
  231. /**
  232. * Get the current page number (may not be rendered yet)
  233. * @function
  234. * @name $.fn.pdf#getPageNumber
  235. * @return {int} Current page number, 0 if PDF is not loaded
  236. * @example $("#element").pdf("getPageNumber");
  237. */
  238. this.getPageNumber = function () {
  239. return pageNum;
  240. };
  241. /**
  242. * Get the total number of pages of loaded PDF
  243. * @function
  244. * @name $.fn.pdf#getTotalPages
  245. * @return {int} The number of pages, 0 if PDF is not loaded
  246. * @example $("#element").pdf("getTotalPages");
  247. */
  248. this.getTotalPages = function () {
  249. return totalPages;
  250. };
  251. //
  252. // Private methods
  253. //
  254. function goto(number) {
  255. if (state == EMPTY || state == INIT) return;
  256. if (number < 1) number = 1;
  257. if (number > totalPages) number = totalPages;
  258. if (number == 0) return;
  259. pageNum = number;
  260. renderPage();
  261. // update tabs
  262. var z = 1;
  263. $element.find(".pdf-tabs .tab").each(function(i, a) {
  264. var $a = $(a);
  265. var aPageNum = $a.data("page");
  266. if ( aPageNum < number) {
  267. $a.removeClass("right");
  268. $a.css("z-index", 1000+z++);
  269. } else if (aPageNum == number) {
  270. $a.removeClass("right");
  271. $a.css("z-index", 1000+z++);
  272. } else {
  273. $a.addClass("right");
  274. $a.css("z-index", 1000-z++);
  275. }
  276. });
  277. }
  278. function initDom() {
  279. if (state != EMPTY) return;
  280. $element.addClass("touchPDF").html(
  281. '<div class="pdf-outerdiv">'
  282. + '<div class="pdf-tabs"></div>'
  283. + '<div class="pdf-toolbar"></div>'
  284. + '<div class="pdf-viewer">'
  285. + '<div class="pdf-loading">'+options.loadingHTML+'</div>'
  286. + '<div class="pdf-drag">'
  287. + '<div class="pdf-canvas">'
  288. + '<canvas></canvas>'
  289. + '<div class="pdf-annotations"></div>'
  290. + '</div>'
  291. + '</div>'
  292. + '</div>'
  293. + '</div>');
  294. if (options.showToolbar) {
  295. $element.find(".pdf-toolbar").html(
  296. '<div class="pdf-title">'+options.title+'</div>'
  297. + '<div class="pdf-button"><button class="pdf-prev">&lt;</button></div>'
  298. + '<div class="pdf-button"><span class="pdf-page-count"></span></div>'
  299. + '<div class="pdf-button"><button class="pdf-next">&gt;</button></div>'
  300. + (options.disableZoom? '':'<div class="pdf-button"><button class="pdf-zoomin">+</button></div>'
  301. + '<div class="pdf-button"><button class="pdf-zoomout">-</button></div>')
  302. );
  303. $element.find(".pdf-toolbar > .pdf-title").on("click", function() {
  304. goto(1);
  305. });
  306. $element.find(".pdf-toolbar > .pdf-button > .pdf-prev").on("click", function() {
  307. goto(pageNum-1);
  308. });
  309. $element.find(".pdf-toolbar > .pdf-button > .pdf-next").on("click", function() {
  310. goto(pageNum+1);
  311. });
  312. }
  313. $drag = $element.find(".pdf-drag");
  314. $viewer = $element.find(".pdf-viewer");
  315. if (!options.disableKeys) {
  316. $(window).keydown(function(event) {
  317. if (event.keyCode == 37) goto(pageNum-1);
  318. else if (event.keyCode == 39) goto(pageNum+1);
  319. });
  320. }
  321. if (options.redrawOnWindowResize) {
  322. var windowResizeTimeout = false;
  323. $( window ).resize(function() {
  324. clearTimeout(windowResizeTimeout);
  325. windowResizeTimeout = setTimeout(function() {
  326. redraw();
  327. }, 100);
  328. });
  329. }
  330. if (!options.disableZoom) {
  331. $drag.panzoom({
  332. contain: 'invert',
  333. minScale: 1,
  334. disablePan: true,
  335. increment: 0.25,
  336. maxScale: 2,
  337. onChange: function() {
  338. linksDisabled = true;
  339. $drag.panzoom("option", "disablePan", false);
  340. state = ZOOMEDIN;
  341. },
  342. onEnd: function() {
  343. setTimeout(function() {
  344. linksDisabled = false;
  345. if ($drag.panzoom("getMatrix")[0] == 1) zoomReset();
  346. }, 1);
  347. }
  348. });
  349. $drag.panzoom('enable');
  350. $drag.parent().on('mousewheel.focal', function( e ) {
  351. e.preventDefault();
  352. var delta = e.delta || e.originalEvent.wheelDelta;
  353. var direction = delta ? delta < 0 : e.originalEvent.deltaY > 0;
  354. if (direction) zoomOut(e);
  355. else zoomIn(e);
  356. });
  357. if (options.showToolbar) {
  358. $element.find(".pdf-toolbar > .pdf-button > .pdf-zoomin").on("click", function() {
  359. zoomIn();
  360. });
  361. $element.find(".pdf-toolbar > .pdf-button > .pdf-zoomout").on("click", function() {
  362. zoomOut();
  363. });
  364. }
  365. if (!options.disableLinks) {
  366. // enable links while zoomed in
  367. var touchlink = null;
  368. $drag.on('touchstart', "a", function( e ) {
  369. touchlink = this;
  370. setTimeout(function() {
  371. touchlink = null;
  372. }, 100);
  373. });
  374. $drag.on('touchend', "a", function( e ) {
  375. if (this == touchlink) {
  376. e.stopImmediatePropagation();
  377. this.click();
  378. }
  379. });
  380. }
  381. }
  382. if (!options.disableSwipe) {
  383. $viewer.swipe( {
  384. swipe:function(event, direction, distance, duration, fingerCount, fingerData) {
  385. if (state != LOADED) return;
  386. linksDisabled = true;
  387. setTimeout(function() {linksDisabled = false;}, 1);
  388. if (direction == "right") goto(pageNum-1);
  389. else if (direction == "left") goto(pageNum+1);
  390. },
  391. threshold:50,
  392. excludedElements: ".noSwipe"
  393. });
  394. }
  395. canvas = $element.find("canvas")[0];
  396. ctx = canvas.getContext('2d');
  397. $annotations = $element.find(".pdf-annotations");
  398. state = INIT;
  399. redraw();
  400. }
  401. function zoomIn (focal) {
  402. if (options.disableZoom) return;
  403. if (state != ZOOMEDIN && state != LOADED) return;
  404. state = ZOOMEDIN;
  405. $drag.panzoom('zoom', false, {
  406. increment: 0.25,
  407. animate: true,
  408. focal: focal
  409. });
  410. linksDisabled = false;
  411. }
  412. function zoomOut(focal) {
  413. if (options.disableZoom) return;
  414. if (state != ZOOMEDIN) return;
  415. $drag.panzoom('zoom', true, {
  416. increment: 0.25,
  417. animate: true,
  418. focal: focal
  419. });
  420. linksDisabled = false;
  421. if ($drag.panzoom("getMatrix")[0] == 1) zoomReset();
  422. }
  423. function zoomReset() {
  424. if (options.disableZoom) return;
  425. $drag.panzoom('reset');
  426. linksDisabled = false;
  427. $drag.panzoom("option", "disablePan", true);
  428. state = LOADED;
  429. }
  430. /**
  431. * Asynchronously downloads PDF.
  432. */
  433. function load () {
  434. if (state != INIT) return;
  435. state = LOADING;
  436. PDFJS.getDocument(options.source).then(function (pdfDoc_) {
  437. pdfDoc = pdfDoc_;
  438. totalPages = pdfDoc.numPages;
  439. if (totalPages < 1) return;
  440. state = LOADED;
  441. if (options.loaded) options.loaded()
  442. goto(1);
  443. });
  444. if (options.tabs && $.isArray(options.tabs)) {
  445. var top = [];
  446. var maxOffset = 0;
  447. $.each(options.tabs, function(i, tab) {
  448. if(tab.offset && tab.offset > maxOffset) maxOffset = tab.offset;
  449. });
  450. tabWidth = TAB_WIDTH + TAB_OFFSET_WIDTH * maxOffset;
  451. $.each(options.tabs, function(i, tab) {
  452. var offset = tab.offset || 0;
  453. if (top[offset] === undefined) {
  454. top[offset] = 5 + TOOLBAR_HEIGHT;
  455. }
  456. var $a = $("<a>")
  457. .addClass("tab")
  458. .data("page", tab.page)
  459. .css("margin-left", offset*TAB_OFFSET_WIDTH+"px")
  460. .css("margin-right", (maxOffset-offset)*TAB_OFFSET_WIDTH+"px")
  461. .click(function() {
  462. if (tab.page == pageNumRendering) goto(tab.page-1);
  463. else goto(tab.page)
  464. });
  465. var $span = $("<span>")
  466. .html(tab.title)
  467. .appendTo($a);
  468. if (tab.bottom !== undefined) {
  469. $a.css("bottom", tab.bottom);
  470. } else {
  471. if (tab.top !== undefined) top[offset] = tab.top + TOOLBAR_HEIGHT;
  472. $a.css("top", top[offset]);
  473. }
  474. if (tab.title.length > 2) $a.addClass("large");
  475. if (!tab.color) tab.color = options.tabsColor;
  476. $a.addClass(tab.color);
  477. if (tab.height) {
  478. $a.css("height", tab.height);
  479. $span.css("width", tab.height);
  480. }
  481. $element.find(".pdf-tabs").append($a);
  482. if (tab.bottom === undefined) top[offset] += $a.height() + TAB_SPACING;
  483. });
  484. }
  485. }
  486. /**
  487. * Get page info from document, resize canvas accordingly, and render page.
  488. * @param num Page number.
  489. */
  490. function renderPage () {
  491. if (state != LOADED && state != ZOOMEDIN) return;
  492. if (pageNum == pageNumRendering) return;
  493. zoomReset();
  494. state = RENDERING;
  495. pageNumRendering = pageNum;
  496. updatePageCount();
  497. // Using promise to fetch the page
  498. pdfDoc.getPage(pageNumRendering).then(function(page) {
  499. var viewport = page.getViewport(options.pdfScale*options.quality);
  500. canvas.height = viewport.height;
  501. canvas.width = viewport.width;
  502. $(".pdf-canvas").css("transform", "scale("+(1/options.quality)+")").css("transform-origin", "top left");
  503. // Render PDF page into canvas context
  504. var renderTask = page.render({
  505. canvasContext: ctx,
  506. viewport: viewport
  507. });
  508. if (!options.disableLinks) {
  509. renderAnnotations(page, viewport);
  510. }
  511. // Wait for rendering to finish
  512. renderTask.promise.then(function () {
  513. state = LOADED;
  514. if (pageNumRendering != pageNum) {
  515. // New page rendering is pending
  516. renderPage();
  517. }
  518. });
  519. redraw();
  520. $element.find(".pdf-loading").hide();
  521. $element.find(".pdf-tabs").css("visibility", "visible");
  522. $element.find("canvas").css("visibility", "visible");
  523. if (options.changed) options.changed();
  524. });
  525. }
  526. function redraw() {
  527. if (state == INIT) {
  528. var pdfHeight = options.loadingHeight;
  529. var pdfWidth = options.loadingWidth;
  530. } else {
  531. var pdfHeight = canvas.height / options.quality;
  532. var pdfWidth = canvas.width / options.quality;
  533. }
  534. var winHeight = $element.height();
  535. var winWidth = $element.width();
  536. scale = Math.min( winHeight / (pdfHeight + TOOLBAR_HEIGHT + BORDER_WIDTH*2), winWidth / (pdfWidth + tabWidth*2 + BORDER_WIDTH*2) );
  537. if (scale > 1) scale = 1;
  538. $element.find(".pdf-outerdiv")
  539. .css("transform", "scale("+scale+")")
  540. .css("width", pdfWidth + BORDER_WIDTH*2)
  541. .css("height", pdfHeight + TOOLBAR_HEIGHT + BORDER_WIDTH*2)
  542. .css("padding", "0 "+tabWidth+"px")
  543. .css("left", (winWidth - scale*(pdfWidth + tabWidth*2 + BORDER_WIDTH*2))/2);
  544. $element.find(".pdf-toolbar")
  545. .css("width", pdfWidth)
  546. .css("height", TOOLBAR_HEIGHT)
  547. .css("left", tabWidth + BORDER_WIDTH);
  548. $viewer
  549. .css("width", pdfWidth)
  550. .css("height", pdfHeight)
  551. .css("left", tabWidth)
  552. .css("top", TOOLBAR_HEIGHT)
  553. .css("border-width", BORDER_WIDTH);
  554. $drag
  555. .css("width", pdfWidth)
  556. .css("height", pdfHeight);
  557. if (!options.disableZoom) {
  558. $drag.panzoom('resetDimensions');
  559. }
  560. if (!options.disableSwipe) {
  561. $viewer.swipe("option", "threshold", 75*scale);
  562. }
  563. }
  564. function updatePageCount() {
  565. if (state == EMPTY || state == INIT) return;
  566. $element.find(".pdf-page-count").html(pageNum + " / " + totalPages);
  567. }
  568. function getPageIndex(destRef) {
  569. var defer = $.Deferred();
  570. if (pagesRefMap[destRef.num + ' ' + destRef.gen + ' R']) {
  571. defer.resolve(pagesRefMap[destRef.num + ' ' + destRef.gen + ' R']);
  572. } else {
  573. pdfDoc.getPageIndex(destRef).then(function (pageIndex) {
  574. pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'] = pageIndex + 1;
  575. defer.resolve(pageIndex + 1);
  576. });
  577. }
  578. return defer.promise();
  579. }
  580. function renderAnnotations(page, viewport) {
  581. if (state != RENDERING) return;
  582. $annotations.empty();
  583. page.getAnnotations().then(function (annotationsData) {
  584. viewport = viewport.clone({dontFlip: true});
  585. $.each(annotationsData, function(i, data) {
  586. if (!data || !data.hasHtml || data.subtype !== 'Link' || (!data.dest && !data.url)) return;
  587. var $el = $(PDFJS.AnnotationUtils.getHtmlElement(data, page.commonObjs));
  588. var rect = data.rect;
  589. var view = page.view;
  590. rect = PDFJS.Util.normalizeRect([
  591. rect[0],
  592. view[3] - rect[1] + view[1],
  593. rect[2],
  594. view[3] - rect[3] + view[1]
  595. ]);
  596. $el.css("left", rect[0] + 'px')
  597. .css("top", rect[1] + 'px')
  598. .css("position", 'absolute');
  599. var transform = viewport.transform;
  600. var transformStr = 'matrix(' + transform.join(',') + ')';
  601. $el.css('transform', transformStr);
  602. var transformOriginStr = -rect[0] + 'px ' + -rect[1] + 'px';
  603. $el.css('transformOrigin', transformOriginStr);
  604. var link = $el.find("a")
  605. .on('mousedown', function(e) {
  606. e.preventDefault();
  607. });
  608. if (data.url) {
  609. link.addClass("externalLink")
  610. .attr("href", data.url)
  611. .attr("target", "_blank");
  612. } else if (data.dest) {
  613. link.addClass("internalLink")
  614. .data("dest", data.dest)
  615. .on('click', function(e) {
  616. if (state != LOADED && state != ZOOMEDIN) return false;
  617. if (linksDisabled) return false;
  618. var dest = $(this).data("dest");
  619. if (dest instanceof Array) {
  620. getPageIndex(dest[0]).then( function ( num ) {
  621. if (state != LOADED && state != ZOOMEDIN) return;
  622. goto(num);
  623. });
  624. } else {
  625. pdfDoc.getDestination($(this).data("dest")).then(function(destRefs) {
  626. if (!(destRefs instanceof Array)) return; // invalid destination
  627. getPageIndex(destRefs[0]).then( function ( num ) {
  628. if (state != LOADED && state != ZOOMEDIN) return;
  629. if (linksDisabled) return;
  630. goto(num);
  631. });
  632. });
  633. }
  634. return false;
  635. });
  636. }
  637. $annotations.append($el);
  638. });
  639. });
  640. }
  641. }
  642. })(jQuery );