Merge pull request #352 from notmasteryet/outline

Switching to the document outline view
This commit is contained in:
sbarman 2011-08-23 15:00:52 -07:00
commit 488a007f73
6 changed files with 662 additions and 18 deletions

45
pdf.js
View File

@ -3397,6 +3397,9 @@ var Page = (function() {
TODO('other link types'); TODO('other link types');
break; break;
} }
} else if (annotation.has('Dest')) {
// simple destination link
link.dest = annotation.get('Dest').name;
} }
links.push(link); links.push(link);
} }
@ -3437,9 +3440,10 @@ var Catalog = (function() {
return str; return str;
} }
var obj = this.catDict.get('Outlines'); var obj = this.catDict.get('Outlines');
var xref = this.xref;
var root = { items: [] }; var root = { items: [] };
if (IsRef(obj)) { if (IsRef(obj)) {
obj = this.xref.fetch(obj).get('First'); obj = xref.fetch(obj).get('First');
var processed = new RefSet(); var processed = new RefSet();
if (IsRef(obj)) { if (IsRef(obj)) {
var queue = [{obj: obj, parent: root}]; var queue = [{obj: obj, parent: root}];
@ -3448,14 +3452,18 @@ var Catalog = (function() {
processed.put(obj); processed.put(obj);
while (queue.length > 0) { while (queue.length > 0) {
var i = queue.shift(); var i = queue.shift();
var outlineDict = this.xref.fetch(i.obj); var outlineDict = xref.fetch(i.obj);
if (!outlineDict.has('Title')) if (!outlineDict.has('Title'))
error('Invalid outline item'); error('Invalid outline item');
var dest = outlineDict.get('Dest'); var dest = outlineDict.get('A');
if (!dest && outlineDict.get('A')) { if (dest)
var a = this.xref.fetchIfRef(outlineDict.get('A')); dest = xref.fetchIfRef(dest).get('D');
dest = a.get('D'); else if (outlineDict.has('Dest')) {
dest = outlineDict.get('Dest');
if (IsName(dest))
dest = dest.name;
} }
var outlineItem = { var outlineItem = {
dest: dest, dest: dest,
title: convertIfUnicode(outlineDict.get('Title')), 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() { get numPages() {
var obj = this.toplevelPagesDict.get('Count'); var obj = this.toplevelPagesDict.get('Count');
@ -3513,15 +3522,25 @@ var Catalog = (function() {
}, },
get destinations() { get destinations() {
var xref = this.xref; var xref = this.xref;
var dests = {}, nameTreeRef, nameDictionaryRef;
var obj = this.catDict.get('Names'); var obj = this.catDict.get('Names');
obj = obj ? xref.fetch(obj) : this.catDict; if (obj)
obj = obj.get('Dests'); nameTreeRef = xref.fetch(obj).get('Dests');
var dests = {}; else if(this.catDict.has('Dests'))
if (obj) { 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 // reading name tree
var processed = new RefSet(); var processed = new RefSet();
processed.put(obj); processed.put(nameTreeRef);
var queue = [obj]; var queue = [nameTreeRef];
while (queue.length > 0) { while (queue.length > 0) {
var i, n; var i, n;
obj = xref.fetch(queue.shift()); obj = xref.fetch(queue.shift());

202
web/images/nav-outline.svg Normal file
View File

@ -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>

After

Width:  |  Height:  |  Size: 5.3 KiB

283
web/images/nav-thumbs.svg Normal file
View File

@ -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>

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@ -60,10 +60,10 @@ span#info {
/* === Sidebar === */ /* === Sidebar === */
#sidebar { #sidebar {
position: fixed; position: fixed;
width: 200px; width: 350px;
top: 62px; top: 62px;
bottom: 18px; bottom: 18px;
left: -140px; left: -290px;
transition: left 0.25s ease-in-out 1s; transition: left 0.25s ease-in-out 1s;
-moz-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; -webkit-transition: left 0.25s ease-in-out 1s;
@ -78,7 +78,7 @@ span#info {
#sidebarBox { #sidebarBox {
background-color: rgba(0, 0, 0, 0.7); background-color: rgba(0, 0, 0, 0.7);
width: 150px; width: 300px;
height: 100%; height: 100%;
border-top-right-radius: 8px; border-top-right-radius: 8px;
border-bottom-right-radius: 8px; border-bottom-right-radius: 8px;
@ -98,14 +98,73 @@ span#info {
top: 10px; top: 10px;
bottom: 10px; bottom: 10px;
left: 10px; left: 10px;
width: 130px; width: 280px;
} }
.thumbnail { .thumbnail {
width: 104px; width: 104px;
height: 134px; height: 134px;
background-color: white; 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 === */ /* === Content view === */
@ -128,3 +187,10 @@ canvas {
padding: 8px 0px; 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;
}

View File

@ -63,6 +63,17 @@
<div id="sidebarScrollView"> <div id="sidebarScrollView">
<div id="sidebarView"></div> <div id="sidebarView"></div>
</div> </div>
<div id="outlineScrollView" style="display:none">
<div id="outlineView"></div>
</div>
<div id="sidebarControls">
<button id="thumbsSwitch" title="Show Thumbnails" onclick="PDFView.switchSidebarView('thumbs')" data-selected>
<img src="images/nav-thumbs.svg" align="top" height="32" alt="Thumbs" />
</button>
<button id="outlineSwitch" title="Show Document Outline" onclick="PDFView.switchSidebarView('outline')" disabled>
<img src="images/nav-outline.svg" align="top" height="32" alt="Document Outline" />
</button>
</div>
</div> </div>
</div> </div>

View File

@ -129,6 +129,33 @@ var PDFView = {
this.page = parseInt(document.location.hash.substring(1)) || 1; this.page = parseInt(document.location.hash.substring(1)) || 1;
this.pagesRefMap = pagesRefMap; this.pagesRefMap = pagesRefMap;
this.destinations = pdf.catalog.destinations; 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() { 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) { window.addEventListener('load', function(evt) {
var params = document.location.search.substring(1).split('&'); var params = document.location.search.substring(1).split('&');
for (var i = 0; i < params.length; i++) { for (var i = 0; i < params.length; i++) {