diff --git a/pdf.js b/pdf.js index be585046f..6c789d746 100644 --- a/pdf.js +++ b/pdf.js @@ -3397,6 +3397,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); } @@ -3437,9 +3440,10 @@ var Catalog = (function() { return str; } var obj = this.catDict.get('Outlines'); + var xref = this.xref; var root = { items: [] }; if (IsRef(obj)) { - obj = this.xref.fetch(obj).get('First'); + obj = xref.fetch(obj).get('First'); var processed = new RefSet(); if (IsRef(obj)) { var queue = [{obj: obj, parent: root}]; @@ -3448,14 +3452,18 @@ var Catalog = (function() { processed.put(obj); while (queue.length > 0) { var i = queue.shift(); - var outlineDict = this.xref.fetch(i.obj); + var outlineDict = 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 = xref.fetchIfRef(dest).get('D'); + else if (outlineDict.has('Dest')) { + dest = outlineDict.get('Dest'); + if (IsName(dest)) + dest = dest.name; } + var outlineItem = { dest: dest, title: convertIfUnicode(outlineDict.get('Title')), @@ -3479,7 +3487,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'); @@ -3513,15 +3522,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) { + // reading 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 @@ + + + + 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 @@ + + + + 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 0e779c60c..e02b3b9a1 100644 --- a/web/viewer.html +++ b/web/viewer.html @@ -63,6 +63,17 @@
+ + diff --git a/web/viewer.js b/web/viewer.js index 698b8974d..abeaff276 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -129,6 +129,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() { @@ -290,6 +317,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++) {