Initial API implementation

This commit is contained in:
Yury Delendik 2012-04-09 22:20:57 -07:00
parent d8235925ac
commit 737ed84174
4 changed files with 281 additions and 98 deletions

141
src/api.js Normal file
View File

@ -0,0 +1,141 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
(function pdfApiWrapper() {
function PdfPageWrapper(page) {
this.page = page;
}
PdfPageWrapper.prototype = {
get width() {
return this.page.width;
},
get height() {
return this.page.height;
},
get stats() {
return this.page.stats;
},
get ref() {
return this.page.ref;
},
get view() {
return this.page.view;
},
rotatePoint: function(x, y) {
return this.page.rotatePoint(x, y);
},
getAnnotations: function() {
var promise = new PDFJS.Promise();
var annotations = this.page.getAnnotations();
promise.resolve(annotations);
return promise;
},
render: function(renderContext) {
var promise = new PDFJS.Promise();
this.page.startRendering(renderContext.canvasContext,
function complete(error) {
if (error)
promise.reject(error);
else
promise.resolve();
},
renderContext.textLayer);
return promise;
},
getTextContent: function() {
var promise = new PDFJS.Promise();
var textContent = 'page text'; // not implemented
promise.resolve(textContent);
return promise;
},
getOperationList: function() {
var promise = new PDFJS.Promise();
var operationList = { // not implemented
dependencyFontsID: null,
operatorList: null
};
promise.resolve(operationList);
return promise;
}
};
function PdfDocumentWrapper(pdf) {
this.pdf = pdf;
}
PdfDocumentWrapper.prototype = {
get numPages() {
return this.pdf.numPages;
},
get fingerprint() {
return this.pdf.fingerPrint;
},
getPage: function(number) {
var promise = new PDFJS.Promise();
var page = this.pdf.getPage(number);
promise.resolve(new PdfPageWrapper(page));
return promise;
},
getDestinations: function() {
var promise = new PDFJS.Promise();
var destinations = this.pdf.catalog.destinations;
promise.resolve(destinations);
return promise;
},
getOutline: function() {
var promise = new PDFJS.Promise();
var outline = this.pdf.catalog.documentOutline;
promise.resolve(outline);
return promise;
},
getMetadata: function() {
var promise = new PDFJS.Promise();
var info = this.pdf.info;
var metadata = this.pdf.catalog.metadata;
promise.resolve(info, metadata ? new PDFJS.Metadata(metadata) : null);
return promise;
}
};
PDFJS.getDocument = function getDocument(source) {
var promise = new PDFJS.Promise();
if (typeof source === 'string') {
// fetch url
PDFJS.getPdf(
{
url: source,
progress: function getPdfProgress(evt) {
if (evt.lengthComputable)
promise.progress({
loaded: evt.loaded,
total: evt.total
});
},
error: function getPdfError(e) {
promise.reject('Unexpected server response of ' +
e.target.status + '.');
}
},
function getPdfLoad(data) {
var pdf = null;
try {
pdf = new PDFJS.PDFDoc(data);
} catch (e) {
promise.reject('An error occurred while reading the PDF.', e);
}
if (pdf)
promise.resolve(new PdfDocumentWrapper(pdf));
});
} else {
// assuming the source is array, instantiating directly from it
var pdf = null;
try {
pdf = new PDFJS.PDFDoc(source);
} catch (e) {
promise.reject('An error occurred while reading the PDF.', e);
}
if (pdf)
promise.resolve(new PdfDocumentWrapper(pdf));
}
return promise;
};
})();

View File

@ -275,7 +275,7 @@ function isPDFFunction(v) {
* can be set. If any of these happens twice or the data is required before * can be set. If any of these happens twice or the data is required before
* it was set, an exception is throw. * it was set, an exception is throw.
*/ */
var Promise = (function PromiseClosure() { var Promise = PDFJS.Promise = (function PromiseClosure() {
var EMPTY_PROMISE = {}; var EMPTY_PROMISE = {};
/** /**
@ -297,6 +297,7 @@ var Promise = (function PromiseClosure() {
} }
this.callbacks = []; this.callbacks = [];
this.errbacks = []; this.errbacks = [];
this.progressbacks = [];
}; };
/** /**
* Builds a promise that is resolved when all the passed in promises are * Builds a promise that is resolved when all the passed in promises are
@ -312,7 +313,7 @@ var Promise = (function PromiseClosure() {
deferred.resolve(results); deferred.resolve(results);
return deferred; return deferred;
} }
for (var i = 0; i < unresolved; ++i) { for (var i = 0, ii = promises.length; i < ii; ++i) {
var promise = promises[i]; var promise = promises[i];
promise.then((function(i) { promise.then((function(i) {
return function(value) { return function(value) {
@ -376,6 +377,13 @@ var Promise = (function PromiseClosure() {
} }
}, },
progress: function Promise_progress(data) {
var callbacks = this.progressbacks;
for (var i = 0, ii = callbacks.length; i < ii; i++) {
callbacks[i].call(null, data);
}
},
reject: function Promise_reject(reason) { reject: function Promise_reject(reason) {
if (this.isRejected) { if (this.isRejected) {
error('A Promise can be rejected only once ' + this.name); error('A Promise can be rejected only once ' + this.name);
@ -393,7 +401,7 @@ var Promise = (function PromiseClosure() {
} }
}, },
then: function Promise_then(callback, errback) { then: function Promise_then(callback, errback, progressback) {
if (!callback) { if (!callback) {
error('Requiring callback' + this.name); error('Requiring callback' + this.name);
} }
@ -410,6 +418,9 @@ var Promise = (function PromiseClosure() {
if (errback) if (errback)
this.errbacks.push(errback); this.errbacks.push(errback);
} }
if (progressback)
this.progressbacks.push(progressback);
} }
}; };

View File

@ -11,6 +11,7 @@
<!-- PDFJSSCRIPT_INCLUDE_BUILD --> <!-- PDFJSSCRIPT_INCLUDE_BUILD -->
<script type="text/javascript" src="../src/core.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE --> <script type="text/javascript" src="../src/core.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE -->
<script type="text/javascript" src="../src/util.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE --> <script type="text/javascript" src="../src/util.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE -->
<script type="text/javascript" src="../src/api.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE -->
<script type="text/javascript" src="../src/metadata.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE --> <script type="text/javascript" src="../src/metadata.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE -->
<script type="text/javascript" src="../src/canvas.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE --> <script type="text/javascript" src="../src/canvas.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE -->
<script type="text/javascript" src="../src/obj.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE --> <script type="text/javascript" src="../src/obj.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE -->

View File

@ -318,27 +318,25 @@ var PDFView = {
} }
var self = this; var self = this;
PDFJS.getPdf( self.loading = true;
{ PDFJS.getDocument(url).then(
url: url, function getDocumentCallback(pdfDocument) {
progress: function getPdfProgress(evt) { self.load(pdfDocument, scale);
if (evt.lengthComputable)
self.progress(evt.loaded / evt.total);
},
error: function getPdfError(e) {
var loadingIndicator = document.getElementById('loading');
loadingIndicator.textContent = 'Error';
var moreInfo = {
message: 'Unexpected server response of ' + e.target.status + '.'
};
self.error('An error occurred while loading the PDF.', moreInfo);
}
},
function getPdfLoad(data) {
self.loading = true;
self.load(data, scale);
self.loading = false; self.loading = false;
}); },
function getDocumentError(message, exception) {
var loadingIndicator = document.getElementById('loading');
loadingIndicator.textContent = 'Error';
var moreInfo = {
message: message
};
self.error('An error occurred while loading the PDF.', moreInfo);
self.loading = false;
},
function getDocumentProgress(progressData) {
self.progress(progressData.loaded / progressData.total);
}
);
}, },
download: function pdfViewDownload() { download: function pdfViewDownload() {
@ -461,7 +459,7 @@ var PDFView = {
PDFView.loadingBar.percent = percent; PDFView.loadingBar.percent = percent;
}, },
load: function pdfViewLoad(data, scale) { load: function pdfViewLoad(pdfDocument, scale) {
function bindOnAfterDraw(pageView, thumbnailView) { function bindOnAfterDraw(pageView, thumbnailView) {
// when page is painted, using the image as thumbnail base // when page is painted, using the image as thumbnail base
pageView.onAfterDraw = function pdfViewLoadOnAfterDraw() { pageView.onAfterDraw = function pdfViewLoadOnAfterDraw() {
@ -489,14 +487,8 @@ var PDFView = {
while (container.hasChildNodes()) while (container.hasChildNodes())
container.removeChild(container.lastChild); container.removeChild(container.lastChild);
var pdf; var pagesCount = pdfDocument.numPages;
try { var id = pdfDocument.fingerprint;
pdf = new PDFJS.PDFDoc(data);
} catch (e) {
this.error('An error occurred while reading the PDF.', e);
}
var pagesCount = pdf.numPages;
var id = pdf.fingerprint;
var storedHash = null; var storedHash = null;
document.getElementById('numPages').textContent = pagesCount; document.getElementById('numPages').textContent = pagesCount;
document.getElementById('pageNumber').max = pagesCount; document.getElementById('pageNumber').max = pagesCount;
@ -514,30 +506,68 @@ var PDFView = {
var pages = this.pages = []; var pages = this.pages = [];
var pagesRefMap = {}; var pagesRefMap = {};
var thumbnails = this.thumbnails = []; var thumbnails = this.thumbnails = [];
for (var i = 1; i <= pagesCount; i++) { var pagePromises = [];
var page = pdf.getPage(i); for (var i = 1; i <= pagesCount; i++)
var pageView = new PageView(container, page, i, page.width, page.height, pagePromises.push(pdfDocument.getPage(i));
page.stats, this.navigateTo.bind(this)); var self = this;
var thumbnailView = new ThumbnailView(sidebar, page, i, var pagesPromise = PDFJS.Promise.all(pagePromises);
page.width / page.height); pagesPromise.then(function(promisedPages) {
bindOnAfterDraw(pageView, thumbnailView); for (var i = 1; i <= pagesCount; i++) {
var page = promisedPages[i - 1];
var pageView = new PageView(container, page, i, page.width, page.height,
page.stats, self.navigateTo.bind(self));
var thumbnailView = new ThumbnailView(sidebar, page, i,
page.width / page.height);
bindOnAfterDraw(pageView, thumbnailView);
pages.push(pageView); pages.push(pageView);
thumbnails.push(thumbnailView); thumbnails.push(thumbnailView);
var pageRef = page.ref; var pageRef = page.ref;
pagesRefMap[pageRef.num + ' ' + pageRef.gen + ' R'] = i; pagesRefMap[pageRef.num + ' ' + pageRef.gen + ' R'] = i;
} }
this.pagesRefMap = pagesRefMap; self.pagesRefMap = pagesRefMap;
this.destinations = pdf.catalog.destinations; });
if (pdf.catalog.documentOutline) { var destinationsPromise = pdfDocument.getDestinations();
this.outline = new DocumentOutlineView(pdf.catalog.documentOutline); destinationsPromise.then(function(destinations) {
var outlineSwitchButton = document.getElementById('outlineSwitch'); self.destinations = destinations;
outlineSwitchButton.removeAttribute('disabled'); });
this.switchSidebarView('outline');
}
// outline and initial view depends on destinations and pagesRefMap
PDFJS.Promise.all([pagesPromise, destinationsPromise]).then(function() {
pdfDocument.getOutline().then(function(outline) {
if (!outline)
return;
self.outline = new DocumentOutlineView(outline);
var outlineSwitchButton = document.getElementById('outlineSwitch');
outlineSwitchButton.removeAttribute('disabled');
self.switchSidebarView('outline');
});
self.setInitialView(storedHash, scale);
});
pdfDocument.getMetadata().then(function(info, metadata) {
self.documentInfo = info;
self.metadata = metadata;
var pdfTitle;
if (metadata) {
if (metadata.has('dc:title'))
pdfTitle = metadata.get('dc:title');
}
if (!pdfTitle && info && info['Title'])
pdfTitle = info['Title'];
if (pdfTitle)
document.title = pdfTitle + ' - ' + document.title;
});
},
setInitialView: function pdfViewSetInitialView(storedHash, scale) {
// Reset the current scale, as otherwise the page's scale might not get // Reset the current scale, as otherwise the page's scale might not get
// updated if the zoom level stayed the same. // updated if the zoom level stayed the same.
this.currentScale = 0; this.currentScale = 0;
@ -558,24 +588,6 @@ var PDFView = {
// Setting the default one. // Setting the default one.
this.parseScale(kDefaultScale, true); this.parseScale(kDefaultScale, true);
} }
this.metadata = null;
var metadata = pdf.catalog.metadata;
var info = this.documentInfo = pdf.info;
var pdfTitle;
if (metadata) {
this.metadata = metadata = new PDFJS.Metadata(metadata);
if (metadata.has('dc:title'))
pdfTitle = metadata.get('dc:title');
}
if (!pdfTitle && info && info['Title'])
pdfTitle = info['Title'];
if (pdfTitle)
document.title = pdfTitle + ' - ' + document.title;
}, },
setHash: function pdfViewSetHash(hash) { setHash: function pdfViewSetHash(hash) {
@ -711,12 +723,12 @@ var PDFView = {
} }
}; };
var PageView = function pageView(container, content, id, pageWidth, pageHeight, var PageView = function pageView(container, pdfPage, id, pageWidth, pageHeight,
stats, navigateTo) { stats, navigateTo) {
this.id = id; this.id = id;
this.content = content; this.pdfPage = pdfPage;
var view = this.content.view; var view = pdfPage.view;
this.x = view.x; this.x = view.x;
this.y = view.y; this.y = view.y;
this.width = view.width; this.width = view.width;
@ -748,7 +760,7 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
div.appendChild(this.loadingIconDiv); div.appendChild(this.loadingIconDiv);
}; };
function setupAnnotations(content, scale) { function setupAnnotations(pdfPage, scale) {
function bindLink(link, dest) { function bindLink(link, dest) {
link.href = PDFView.getDestinationHash(dest); link.href = PDFView.getDestinationHash(dest);
link.onclick = function pageViewSetupLinksOnclick() { link.onclick = function pageViewSetupLinksOnclick() {
@ -809,29 +821,30 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
return container; return container;
} }
var items = content.getAnnotations(); pdfPage.getAnnotations().then(function(items) {
for (var i = 0; i < items.length; i++) { for (var i = 0; i < items.length; i++) {
var item = items[i]; var item = items[i];
switch (item.type) { switch (item.type) {
case 'Link': case 'Link':
var link = createElementWithStyle('a', item); var link = createElementWithStyle('a', item);
link.href = item.url || ''; link.href = item.url || '';
if (!item.url) if (!item.url)
bindLink(link, ('dest' in item) ? item.dest : null); bindLink(link, ('dest' in item) ? item.dest : null);
div.appendChild(link); div.appendChild(link);
break; break;
case 'Text': case 'Text':
var comment = createCommentAnnotation(item.name, item); var comment = createCommentAnnotation(item.name, item);
if (comment) if (comment)
div.appendChild(comment); div.appendChild(comment);
break; break;
}
} }
} });
} }
this.getPagePoint = function pageViewGetPagePoint(x, y) { this.getPagePoint = function pageViewGetPagePoint(x, y) {
var scale = PDFView.currentScale; var scale = PDFView.currentScale;
return this.content.rotatePoint(x / scale, y / scale); return this.pdfPage.rotatePoint(x / scale, y / scale);
}; };
this.scrollIntoView = function pageViewScrollIntoView(dest) { this.scrollIntoView = function pageViewScrollIntoView(dest) {
@ -879,8 +892,8 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
} }
var boundingRect = [ var boundingRect = [
this.content.rotatePoint(x, y), this.pdfPage.rotatePoint(x, y),
this.content.rotatePoint(x + width, y + height) this.pdfPage.rotatePoint(x + width, y + height)
]; ];
if (scale && scale !== PDFView.currentScale) if (scale && scale !== PDFView.currentScale)
@ -948,7 +961,7 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
// Rendering area // Rendering area
var self = this; var self = this;
this.content.startRendering(ctx, function pageViewDrawCallback(error) { function pageViewDrawCallback(error) {
if (self.loadingIconDiv) { if (self.loadingIconDiv) {
div.removeChild(self.loadingIconDiv); div.removeChild(self.loadingIconDiv);
delete self.loadingIconDiv; delete self.loadingIconDiv;
@ -964,9 +977,22 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
cache.push(self); cache.push(self);
callback(); callback();
}, textLayer); }
setupAnnotations(this.content, this.scale); var renderContext = {
canvasContext: ctx,
textLayer: textLayer
};
this.pdfPage.render(renderContext).then(
function pdfPageRenderCallback() {
pageViewDrawCallback(null);
},
function pdfPageRenderError(error) {
pageViewDrawCallback(error);
}
);
setupAnnotations(this.pdfPage, this.scale);
div.setAttribute('data-loaded', true); div.setAttribute('data-loaded', true);
}; };
@ -1397,7 +1423,11 @@ window.addEventListener('change', function webViewerChange(evt) {
for (var i = 0; i < data.length; i++) for (var i = 0; i < data.length; i++)
uint8Array[i] = data.charCodeAt(i); uint8Array[i] = data.charCodeAt(i);
PDFView.load(uint8Array);
// TODO using blob instead?
PDFJS.getDocument(uint8Array).then(function(pdfDocument) {
PDFView.load(pdfDocument);
});
}; };
// Read as a binary string since "readAsArrayBuffer" is not yet // Read as a binary string since "readAsArrayBuffer" is not yet