From 3aa6e38f01c37851d8815d5d5710532699a2a038 Mon Sep 17 00:00:00 2001 From: notmasteryet <async.processingjs@yahoo.com> Date: Sun, 21 Aug 2011 21:05:10 -0500 Subject: [PATCH] Switching to the document outline view if the document structure is present --- pdf.js | 39 +++-- web/images/nav-outline.svg | 202 ++++++++++++++++++++++++++ web/images/nav-thumbs.svg | 283 +++++++++++++++++++++++++++++++++++++ web/viewer.css | 76 +++++++++- web/viewer.html | 11 ++ web/viewer.js | 63 +++++++++ 6 files changed, 657 insertions(+), 17 deletions(-) create mode 100644 web/images/nav-outline.svg create mode 100644 web/images/nav-thumbs.svg diff --git a/pdf.js b/pdf.js index 0ff3381f5..0d44822dd 100644 --- a/pdf.js +++ b/pdf.js @@ -3395,6 +3395,9 @@ var Page = (function() { TODO('other link types'); break; } + } else if (annotation.has('Dest')) { + // simple destination link + link.dest = annotation.get('Dest').name; } links.push(link); } @@ -3449,11 +3452,12 @@ var Catalog = (function() { var outlineDict = this.xref.fetch(i.obj); if (!outlineDict.has('Title')) error('Invalid outline item'); - var dest = outlineDict.get('Dest'); - if (!dest && outlineDict.get('A')) { - var a = this.xref.fetchIfRef(outlineDict.get('A')); - dest = a.get('D'); - } + var dest = outlineDict.get('A'); + if (dest) + dest = this.xref.fetchIfRef(dest).get('D'); + else if (outlineDict.has('Dest')) + dest = outlineDict.get('Dest').name; + var outlineItem = { dest: dest, title: convertIfUnicode(outlineDict.get('Title')), @@ -3477,7 +3481,8 @@ var Catalog = (function() { } } } - return shadow(this, 'documentOutline', root); + obj = root.items.length > 0 ? root.items : null; + return shadow(this, 'documentOutline', obj); }, get numPages() { var obj = this.toplevelPagesDict.get('Count'); @@ -3511,15 +3516,25 @@ var Catalog = (function() { }, get destinations() { var xref = this.xref; + var dests = {}, nameTreeRef, nameDictionaryRef; var obj = this.catDict.get('Names'); - obj = obj ? xref.fetch(obj) : this.catDict; - obj = obj.get('Dests'); - var dests = {}; - if (obj) { + if (obj) + nameTreeRef = xref.fetch(obj).get('Dests'); + else if(this.catDict.has('Dests')) + nameDictionaryRef = this.catDict.get('Dests'); + + if (nameDictionaryRef) { + // reding simple destination dictionary + obj = xref.fetch(nameDictionaryRef); + obj.forEach(function(key, value) { + dests[key] = xref.fetch(value).get('D'); + }); + } + if (nameTreeRef) { // reading name tree var processed = new RefSet(); - processed.put(obj); - var queue = [obj]; + processed.put(nameTreeRef); + var queue = [nameTreeRef]; while (queue.length > 0) { var i, n; obj = xref.fetch(queue.shift()); diff --git a/web/images/nav-outline.svg b/web/images/nav-outline.svg new file mode 100644 index 000000000..4d4323ce3 --- /dev/null +++ b/web/images/nav-outline.svg @@ -0,0 +1,202 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="48px" + height="48px" + id="svg3007" + version="1.1" + inkscape:version="0.48.1 r9760" + sodipodi:docname="nav-outline.svg"> + <defs + id="defs3009"> + <filter + inkscape:collect="always" + id="filter5333" + x="-0.16623206" + width="1.3324641" + y="-0.030014125" + height="1.0600282"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.47888561" + id="feGaussianBlur5335" /> + </filter> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="11.945051" + inkscape:cx="20.614872" + inkscape:cy="23.423899" + inkscape:current-layer="layer1" + showgrid="true" + inkscape:grid-bbox="true" + inkscape:document-units="px" + inkscape:window-width="1440" + inkscape:window-height="773" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="0" /> + <metadata + id="metadata3012"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + id="layer1" + inkscape:label="Layer 1" + inkscape:groupmode="layer"> + <rect + style="fill:#f0f0f0;fill-rule:evenodd;stroke:#808080;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1;fill-opacity:1" + id="rect3783" + width="46.16272" + height="45.59861" + x="1.0341953" + y="0.99112236" /> + <rect + style="fill:#404040;fill-opacity:1;stroke:none" + id="rect3787" + width="2.8205326" + height="2.7823999" + x="4.2307992" + y="4.093708" /> + <rect + style="fill:#404040;fill-opacity:1;stroke:none" + id="rect5257" + width="24.68285" + height="1.4102663" + x="8.0855274" + y="4.657815" /> + <rect + y="8.4185247" + x="8.4615984" + height="2.7823999" + width="2.8205326" + id="rect5259" + style="fill:#404040;fill-opacity:1;stroke:none" /> + <rect + y="9.0766497" + x="12.410344" + height="1.4102663" + width="30.498053" + id="rect5261" + style="fill:#404040;fill-opacity:1;stroke:none" /> + <rect + style="fill:#404040;fill-opacity:1;stroke:none" + id="rect5263" + width="2.8205326" + height="2.7823999" + x="8.4615984" + y="13.307448" /> + <rect + style="fill:#404040;fill-opacity:1;stroke:none" + id="rect5265" + width="24.972752" + height="1.4102663" + x="12.410344" + y="13.965573" /> + <rect + y="17.444229" + x="4.3248172" + height="2.7823999" + width="2.8205326" + id="rect5267" + style="fill:#404040;fill-opacity:1;stroke:none" /> + <rect + y="18.008337" + x="8.1795454" + height="1.4102663" + width="25.101433" + id="rect5269" + style="fill:#404040;fill-opacity:1;stroke:none" /> + <rect + style="fill:#404040;fill-opacity:1;stroke:none" + id="rect5271" + width="2.8205326" + height="2.7823999" + x="8.5556164" + y="21.769047" /> + <rect + style="fill:#404040;fill-opacity:1;stroke:none" + id="rect5273" + width="28.782515" + height="1.4102663" + x="12.880433" + y="22.427172" /> + <rect + y="26.65797" + x="13.475181" + height="2.7823999" + width="2.8205326" + id="rect5275" + style="fill:#404040;fill-opacity:1;stroke:none" /> + <rect + y="27.316095" + x="17.479" + height="1.4102663" + width="23.681646" + id="rect5277" + style="fill:#404040;fill-opacity:1;stroke:none" /> + <rect + style="fill:#404040;fill-opacity:1;stroke:none" + id="rect5279" + width="2.8205326" + height="2.7823999" + x="8.5130949" + y="31.006269" /> + <rect + style="fill:#404040;fill-opacity:1;stroke:none" + id="rect5281" + width="24.557148" + height="1.4102663" + x="12.592034" + y="31.636858" /> + <rect + y="35.464046" + x="13.475181" + height="2.7823999" + width="2.8205326" + id="rect5283" + style="fill:#404040;fill-opacity:1;stroke:none" /> + <rect + y="36.055695" + x="17.744923" + height="1.4102663" + width="18.577394" + id="rect5285" + style="fill:#404040;fill-opacity:1;stroke:none" /> + <rect + style="fill:#404040;fill-opacity:1;stroke:none" + id="rect5287" + width="2.8205326" + height="2.7823999" + x="13.54166" + y="40.35297" /> + <rect + style="fill:#404040;fill-opacity:1;stroke:none" + id="rect5289" + width="23.080858" + height="1.4102663" + x="17.678442" + y="40.944618" /> + </g> +</svg> diff --git a/web/images/nav-thumbs.svg b/web/images/nav-thumbs.svg new file mode 100644 index 000000000..8737b8cb6 --- /dev/null +++ b/web/images/nav-thumbs.svg @@ -0,0 +1,283 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="48px" + height="48px" + id="svg3007" + version="1.1" + inkscape:version="0.48.1 r9760" + sodipodi:docname="nav-thumbs.svg"> + <defs + id="defs3009"> + <filter + inkscape:collect="always" + id="filter5333" + x="-0.16623206" + width="1.3324641" + y="-0.030014125" + height="1.0600282"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.47888561" + id="feGaussianBlur5335" /> + </filter> + <filter + inkscape:collect="always" + id="filter5966"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.3570515" + id="feGaussianBlur5968" /> + </filter> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="11.945051" + inkscape:cx="9.375932" + inkscape:cy="24.942259" + inkscape:current-layer="layer1" + showgrid="true" + inkscape:grid-bbox="true" + inkscape:document-units="px" + inkscape:window-width="1440" + inkscape:window-height="773" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="0" /> + <metadata + id="metadata3012"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + id="layer1" + inkscape:label="Layer 1" + inkscape:groupmode="layer"> + <rect + style="fill:#484848;fill-rule:evenodd;stroke:#808080;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1;fill-opacity:1" + id="rect3783" + width="46.16272" + height="45.59861" + x="1.0341953" + y="0.99112236" /> + <rect + y="4.7876148" + x="14.359808" + height="12.764274" + width="9.7061672" + id="rect5960" + style="fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter5966)" + transform="matrix(1.0465713,0,0,1.0642851,3.6426579,-2.1141417)" /> + <rect + style="fill:#ffffff;fill-opacity:1;stroke:none" + id="rect5958" + width="9.7061672" + height="12.764274" + x="18.897236" + y="3.1920807" /> + <rect + transform="matrix(1.0465713,0,0,1.0642851,3.6426579,13.043433)" + style="fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter5966)" + id="rect5970" + width="9.7061672" + height="12.764274" + x="14.359808" + y="4.7876148" /> + <rect + y="18.349655" + x="18.897236" + height="12.764274" + width="9.7061672" + id="rect5972" + style="fill:#ffffff;fill-opacity:1;stroke:none" /> + <rect + y="4.7876148" + x="14.359808" + height="12.764274" + width="9.7061672" + id="rect5974" + style="fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter5966)" + transform="matrix(1.0465713,0,0,0.9368834,3.6426579,29.209842)" /> + <rect + style="fill:#ffffff;fill-opacity:1;stroke:none" + id="rect5976" + width="9.7061672" + height="11.833546" + x="18.897236" + y="33.906113" /> + <rect + y="4.905829" + x="19.960924" + height="0.66480595" + width="7.7117486" + id="rect5995" + style="fill:#808080;fill-opacity:1;stroke:none" /> + <rect + style="fill:#808080;fill-opacity:1;stroke:none" + id="rect6177" + width="3.6219761" + height="0.66480595" + x="19.960924" + y="6.0340419" /> + <rect + style="fill:#808080;fill-opacity:1;stroke:none" + id="rect6179" + width="7.7117486" + height="0.66480595" + x="19.960924" + y="7.2562728" /> + <rect + y="8.3844862" + x="19.960924" + height="0.66480595" + width="5.6903667" + id="rect6181" + style="fill:#808080;fill-opacity:1;stroke:none" /> + <rect + y="9.7007341" + x="19.960924" + height="0.66480595" + width="7.7117486" + id="rect6183" + style="fill:#808080;fill-opacity:1;stroke:none" /> + <rect + style="fill:#808080;fill-opacity:1;stroke:none" + id="rect6185" + width="7.7117486" + height="0.66480595" + x="19.960924" + y="10.828948" /> + <rect + y="12.051179" + x="19.960924" + height="0.66480595" + width="7.7117486" + id="rect6187" + style="fill:#808080;fill-opacity:1;stroke:none" /> + <rect + y="14.213587" + x="23.204536" + height="0.66480595" + width="1.2245234" + id="rect6189" + style="fill:#808080;fill-opacity:1;stroke:none" /> + <rect + style="fill:#808080;fill-opacity:1;stroke:none" + id="rect6209" + width="7.7117486" + height="0.66480595" + x="19.772888" + y="19.854652" /> + <rect + y="39.08128" + x="19.913914" + height="0.66480595" + width="3.6219761" + id="rect6211" + style="fill:#808080;fill-opacity:1;stroke:none" /> + <rect + y="22.205095" + x="19.772888" + height="0.66480595" + width="6.6305442" + id="rect6213" + style="fill:#808080;fill-opacity:1;stroke:none" /> + <rect + style="fill:#808080;fill-opacity:1;stroke:none" + id="rect6215" + width="7.7587576" + height="0.66480595" + x="19.866905" + y="37.859051" /> + <rect + style="fill:#808080;fill-opacity:1;stroke:none" + id="rect6217" + width="7.7117486" + height="0.66480595" + x="19.772888" + y="21.029873" /> + <rect + y="25.777771" + x="19.772888" + height="0.66480595" + width="7.7117486" + id="rect6219" + style="fill:#808080;fill-opacity:1;stroke:none" /> + <rect + style="fill:#808080;fill-opacity:1;stroke:none" + id="rect6221" + width="7.7117486" + height="0.66480595" + x="19.772888" + y="27.000002" /> + <rect + style="fill:#808080;fill-opacity:1;stroke:none" + id="rect6223" + width="1.2245234" + height="0.66480595" + x="23.204536" + y="28.974375" /> + <rect + style="fill:#808080;fill-opacity:1;stroke:none" + id="rect6225" + width="3.6219761" + height="0.66480595" + x="19.960922" + y="42.983021" /> + <rect + style="fill:#808080;fill-opacity:1;stroke:none" + id="rect6227" + width="7.7117486" + height="0.66480595" + x="19.913914" + y="36.777847" /> + <rect + y="35.602627" + x="19.913914" + height="0.66480595" + width="7.7117486" + id="rect6231" + style="fill:#808080;fill-opacity:1;stroke:none" /> + <rect + style="fill:#808080;fill-opacity:1;stroke:none" + id="rect6233" + width="7.7117486" + height="0.66480595" + x="19.913914" + y="40.350525" /> + <rect + y="41.572754" + x="19.913914" + height="0.66480595" + width="7.7117486" + id="rect6235" + style="fill:#808080;fill-opacity:1;stroke:none" /> + <rect + style="fill:#0000e6;fill-opacity:0.44444448;stroke:none" + id="rect6237" + width="3.5256658" + height="1.927364" + x="22.077036" + y="23.367346" /> + </g> +</svg> diff --git a/web/viewer.css b/web/viewer.css index 54f648adb..e18d9681e 100644 --- a/web/viewer.css +++ b/web/viewer.css @@ -60,10 +60,10 @@ span#info { /* === Sidebar === */ #sidebar { position: fixed; - width: 200px; + width: 350px; top: 62px; bottom: 18px; - left: -140px; + left: -290px; transition: left 0.25s ease-in-out 1s; -moz-transition: left 0.25s ease-in-out 1s; -webkit-transition: left 0.25s ease-in-out 1s; @@ -78,7 +78,7 @@ span#info { #sidebarBox { background-color: rgba(0, 0, 0, 0.7); - width: 150px; + width: 300px; height: 100%; border-top-right-radius: 8px; border-bottom-right-radius: 8px; @@ -98,14 +98,73 @@ span#info { top: 10px; bottom: 10px; left: 10px; - width: 130px; + width: 280px; } .thumbnail { width: 104px; height: 134px; background-color: white; - margin: 5px; + margin-top: 5px; + margin-bottom: 5px; + margin-left:auto; + margin-right:auto; +} + +#outlineScrollView { + position: absolute; + background-color: #fff; + overflow: auto; + top: 10px; + bottom: 10px; + left: 10px; + width: 280px; +} + +#outlineView { + padding-top: 4px; + padding-bottom: 100px; + padding-left: 6px; + padding-right: 6px; + font-size: smaller; +} + +.outlineItem > .outlineItems { + margin-left: 20px; +} + +.outlineItem > a { + text-decoration: none; + color: black; +} + +.outlineItem > a:hover { + background: #ff0; + box-shadow: 0px 2px 10px #ff0; +} + +#sidebarControls { + position:absolute; + width: 120px; + height: 32px; + left: 15px; + bottom: 35px; +} + +#sidebarControls > button { + box-shadow: 0px 4px 10px #000; + -moz-box-shadow: 0px 4px 10px #000; + -webkit-box-shadow: 0px 4px 10px #000; +} + +#sidebarControls > button[disabled] > img { + opacity: 0.5; +} + +#sidebarControls > button[data-selected] { + box-shadow: 0px 4px 10px #ff0; + -moz-box-shadow: 0px 4px 10px #ff0; + -webkit-box-shadow: 0px 4px 10px #ff0; } /* === Content view === */ @@ -128,3 +187,10 @@ canvas { padding: 8px 0px; } +#sidebarView canvas:hover { + background: #ff0; + box-shadow: 0px 2px 10px #ff0; + -moz-box-shadow: 0px 2px 10px #ff0; + -webkit-box-shadow: 0px 2px 10px #ff0; +} + diff --git a/web/viewer.html b/web/viewer.html index 285dadb01..6232ce4b3 100644 --- a/web/viewer.html +++ b/web/viewer.html @@ -56,6 +56,17 @@ <div id="sidebarScrollView"> <div id="sidebarView"></div> </div> + <div id="outlineScrollView" style="display:none"> + <div id="outlineView"></div> + </div> + <div id="sidebarControls"> + <button id="thumbsSwitch" onclick="PDFView.switchSidebarView('thumbs')" data-selected> + <img src="images/nav-thumbs.svg" align="top" height="32" /> + </button> + <button id="outlineSwitch" onclick="PDFView.switchSidebarView('outline')" disabled> + <img src="images/nav-outline.svg" align="top" height="32" /> + </button> + </div> </div> </div> diff --git a/web/viewer.js b/web/viewer.js index 8fe011b29..95a32128c 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -128,6 +128,33 @@ var PDFView = { this.page = parseInt(document.location.hash.substring(1)) || 1; this.pagesRefMap = pagesRefMap; this.destinations = pdf.catalog.destinations; + if (pdf.catalog.documentOutline) { + this.outline = new DocumentOutlineView(pdf.catalog.documentOutline); + var outlineSwitchButton = document.getElementById('outlineSwitch'); + outlineSwitchButton.removeAttribute('disabled'); + this.switchSidebarView('outline'); + } + }, + + switchSidebarView: function(view) { + var thumbsScrollView = document.getElementById('sidebarScrollView'); + var outlineScrollView = document.getElementById('outlineScrollView'); + var thumbsSwitchButton = document.getElementById('thumbsSwitch'); + var outlineSwitchButton = document.getElementById('outlineSwitch'); + switch(view) { + case 'thumbs': + thumbsScrollView.style.display = 'block'; + outlineScrollView.style.display = 'none'; + thumbsSwitchButton.setAttribute('data-selected', true); + outlineSwitchButton.removeAttribute('data-selected'); + break; + case 'outline': + thumbsScrollView.style.display = 'none'; + outlineScrollView.style.display = 'block'; + thumbsSwitchButton.removeAttribute('data-selected'); + outlineSwitchButton.setAttribute('data-selected', true); + break; + } }, getVisiblePages: function() { @@ -289,6 +316,42 @@ var ThumbnailView = function(container, page) { }; }; +var DocumentOutlineView = function(outline) { + var outlineView = document.getElementById('outlineView'); + + function bindItemLink(domObj, item) { + domObj.href = ''; + domObj.onclick = function(e) { + PDFView.navigateTo(item.dest); + return false; + }; + } + + var queue = [{parent: outlineView, items: outline}]; + while (queue.length > 0) { + var levelData = queue.shift(); + var i, n = levelData.items.length; + for (i = 0; i < n; i++) { + var item = levelData.items[i]; + var div = document.createElement('div'); + div.className = 'outlineItem'; + var a = document.createElement('a'); + bindItemLink(a, item); + a.textContent = item.title; + div.appendChild(a); + + if (item.items.length > 0) { + var itemsDiv = document.createElement('div'); + itemsDiv.className = 'outlineItems'; + div.appendChild(itemsDiv); + queue.push({parent: itemsDiv, items: item.items}); + } + + levelData.parent.appendChild(div); + } + } +}; + window.addEventListener('load', function(evt) { var params = document.location.search.substring(1).split('&'); for (var i = 0; i < params.length; i++) {