From 3e0c0de985f6cad680b19b1676c71270483a8d16 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Sat, 20 Aug 2011 09:51:44 -0500 Subject: [PATCH] Fixing bookmarks for PDF32000 --- pdf.js | 70 +++++++++++++++++++++++++++++++++++++++++++++------ web/viewer.js | 21 +++++++++------- 2 files changed, 74 insertions(+), 17 deletions(-) diff --git a/pdf.js b/pdf.js index eb9e18086..ef2b0c612 100644 --- a/pdf.js +++ b/pdf.js @@ -2228,6 +2228,26 @@ var Ref = (function() { return constructor; })(); +// The reference is identified by number and generation, +// this structure stores only one instance of the reference. +var RefSet = (function() { + function constructor() { + this.dict = {}; + } + + constructor.prototype = { + has: function(ref) { + return !!this.dict['R' + ref.num + '.' + ref.gen]; + }, + + put: function(ref) { + this.dict['R' + ref.num + '.' + ref.gen] = ref; + } + }; + + return constructor; +})(); + function IsBool(v) { return typeof v == 'boolean'; } @@ -3408,22 +3428,22 @@ var Catalog = (function() { // UTF16BE BOM var i, n = str.length, str2 = ""; for (i = 2; i < n; i+=2) - str2 += String.fromCharCode((str.charCodeAt(i) << 8) | str.charCodeAt(i + 1)); + str2 += String.fromCharCode( + (str.charCodeAt(i) << 8) | str.charCodeAt(i + 1)); str = str2; } return str; } - var obj = this.catDict.get('Outlines'); var root = { items: [] }; if (IsRef(obj)) { obj = this.xref.fetch(obj).get('First'); - var processed = {}; + var processed = new RefSet(); if (IsRef(obj)) { var queue = [{obj: obj, parent: root}]; // to avoid recursion keeping track of the items // in the processed dictionary - processed['R' + obj.num + ',' + obj.gen] = true; + processed.put(obj); while (queue.length > 0) { var i = queue.shift(); var outlineDict = this.xref.fetch(i.obj); @@ -3438,20 +3458,21 @@ var Catalog = (function() { dest: dest, title: convertIfUnicode(outlineDict.get('Title')), color: outlineDict.get('C') || [0, 0, 0], + count: outlineDict.get('Count'), bold: !!(outlineDict.get('F') & 2), italic: !!(outlineDict.get('F') & 1), items: [] }; i.parent.items.push(outlineItem); obj = outlineDict.get('First'); - if (IsRef(obj) && !processed['R' + obj.num + ',' + obj.gen]) { + if (IsRef(obj) && !processed.has(obj)) { queue.push({obj: obj, parent: outlineItem}); - processed['R' + obj.num + ',' + obj.gen] = true; + processed.put(obj); } obj = outlineDict.get('Next'); - if (IsRef(obj) && !processed['R' + obj.num + ',' + obj.gen]) { + if (IsRef(obj) && !processed.has(obj)) { queue.push({obj: obj, parent: i.parent}); - processed['R' + obj.num + ',' + obj.gen] = true; + processed.put(obj); } } } @@ -3488,6 +3509,39 @@ var Catalog = (function() { } } }, + get destinations() { + var xref = this.xref; + var obj = this.catDict.get('Names'); + obj = obj ? xref.fetch(obj) : this.catDict; + obj = obj.get('Dests'); + var dests = {}; + if (obj) { + // reading name tree + var processed = new RefSet(); + processed.put(obj); + var queue = [obj]; + while (queue.length > 0) { + var i, n; + obj = xref.fetch(queue.shift()); + if (obj.has('Kids')) { + var kids = obj.get('Kids'); + for (i = 0, n = kids.length; i < n; i++) { + var kid = kids[i]; + if (processed.has(kid)) + error('invalid destinations'); + queue.push(kid); + processed.put(kid); + } + continue; + } + var names = obj.get('Names'); + for (i = 0, n = names.length; i < n; i += 2) { + dests[names[i]] = xref.fetch(names[i + 1]).get('D'); + } + } + } + return shadow(this, 'destinations', dests); + }, getPage: function(n) { var pageCache = this.pageCache; if (!pageCache) { diff --git a/web/viewer.js b/web/viewer.js index 24afce799..8fe011b29 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -85,16 +85,14 @@ var PDFView = { }, navigateTo: function (dest) { - var i, n = this.pages.length; + if (typeof dest === 'string') + dest = this.destinations[dest]; + // dest array looks like that: var destRef = dest[0]; - // TODO optimize destination page search - for (i = 0; i < n; i++) { - var pageRef = this.pages[i].content.ref; - if (destRef.num == pageRef.num && destRef.gen == pageRef.gen) { - this.page = i + 1; - // TODO scroll to specific region on the page - break; - } + var pageNumber = this.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R']; + if (pageNumber) { + this.page = pageNumber; + // TODO scroll to specific region on the page, the precise scaling required } }, @@ -115,16 +113,21 @@ var PDFView = { document.getElementById('numPages').innerHTML = pagesCount; var pages = this.pages = []; + var pagesRefMap = {}; var thumbnails = this.thumbnails = []; for (var i = 1; i <= pagesCount; i++) { var page = pdf.getPage(i); pages.push(new PageView(container, page, i, page.width, page.height, page.stats, this.navigateTo.bind(this))); thumbnails.push(new ThumbnailView(sidebar, pages[i - 1])); + var pageRef = page.ref; + pagesRefMap[pageRef.num + ' ' + pageRef.gen + ' R'] = i; } this.scale = (scale || kDefaultScale); this.page = parseInt(document.location.hash.substring(1)) || 1; + this.pagesRefMap = pagesRefMap; + this.destinations = pdf.catalog.destinations; }, getVisiblePages: function() {