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
* it was set, an exception is throw.
*/
var Promise = (function PromiseClosure() {
var Promise = PDFJS.Promise = (function PromiseClosure() {
var EMPTY_PROMISE = {};
/**
@ -297,6 +297,7 @@ var Promise = (function PromiseClosure() {
}
this.callbacks = [];
this.errbacks = [];
this.progressbacks = [];
};
/**
* Builds a promise that is resolved when all the passed in promises are
@ -312,7 +313,7 @@ var Promise = (function PromiseClosure() {
deferred.resolve(results);
return deferred;
}
for (var i = 0; i < unresolved; ++i) {
for (var i = 0, ii = promises.length; i < ii; ++i) {
var promise = promises[i];
promise.then((function(i) {
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) {
if (this.isRejected) {
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) {
error('Requiring callback' + this.name);
}
@ -410,6 +418,9 @@ var Promise = (function PromiseClosure() {
if (errback)
this.errbacks.push(errback);
}
if (progressback)
this.progressbacks.push(progressback);
}
};

View File

@ -11,6 +11,7 @@
<!-- PDFJSSCRIPT_INCLUDE_BUILD -->
<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/api.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/obj.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE -->

View File

@ -318,27 +318,25 @@ var PDFView = {
}
var self = this;
PDFJS.getPdf(
{
url: url,
progress: function getPdfProgress(evt) {
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 = true;
PDFJS.getDocument(url).then(
function getDocumentCallback(pdfDocument) {
self.load(pdfDocument, scale);
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() {
@ -461,7 +459,7 @@ var PDFView = {
PDFView.loadingBar.percent = percent;
},
load: function pdfViewLoad(data, scale) {
load: function pdfViewLoad(pdfDocument, scale) {
function bindOnAfterDraw(pageView, thumbnailView) {
// when page is painted, using the image as thumbnail base
pageView.onAfterDraw = function pdfViewLoadOnAfterDraw() {
@ -489,14 +487,8 @@ var PDFView = {
while (container.hasChildNodes())
container.removeChild(container.lastChild);
var pdf;
try {
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 pagesCount = pdfDocument.numPages;
var id = pdfDocument.fingerprint;
var storedHash = null;
document.getElementById('numPages').textContent = pagesCount;
document.getElementById('pageNumber').max = pagesCount;
@ -514,30 +506,68 @@ var PDFView = {
var pages = this.pages = [];
var pagesRefMap = {};
var thumbnails = this.thumbnails = [];
for (var i = 1; i <= pagesCount; i++) {
var page = pdf.getPage(i);
var pageView = new PageView(container, page, i, page.width, page.height,
page.stats, this.navigateTo.bind(this));
var thumbnailView = new ThumbnailView(sidebar, page, i,
page.width / page.height);
bindOnAfterDraw(pageView, thumbnailView);
var pagePromises = [];
for (var i = 1; i <= pagesCount; i++)
pagePromises.push(pdfDocument.getPage(i));
var self = this;
var pagesPromise = PDFJS.Promise.all(pagePromises);
pagesPromise.then(function(promisedPages) {
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);
thumbnails.push(thumbnailView);
var pageRef = page.ref;
pagesRefMap[pageRef.num + ' ' + pageRef.gen + ' R'] = i;
}
pages.push(pageView);
thumbnails.push(thumbnailView);
var pageRef = page.ref;
pagesRefMap[pageRef.num + ' ' + pageRef.gen + ' R'] = i;
}
this.pagesRefMap = pagesRefMap;
this.destinations = pdf.catalog.destinations;
self.pagesRefMap = pagesRefMap;
});
if (pdf.catalog.documentOutline) {
this.outline = new DocumentOutlineView(pdf.catalog.documentOutline);
var outlineSwitchButton = document.getElementById('outlineSwitch');
outlineSwitchButton.removeAttribute('disabled');
this.switchSidebarView('outline');
}
var destinationsPromise = pdfDocument.getDestinations();
destinationsPromise.then(function(destinations) {
self.destinations = destinations;
});
// 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
// updated if the zoom level stayed the same.
this.currentScale = 0;
@ -558,24 +588,6 @@ var PDFView = {
// Setting the default one.
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) {
@ -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) {
this.id = id;
this.content = content;
this.pdfPage = pdfPage;
var view = this.content.view;
var view = pdfPage.view;
this.x = view.x;
this.y = view.y;
this.width = view.width;
@ -748,7 +760,7 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
div.appendChild(this.loadingIconDiv);
};
function setupAnnotations(content, scale) {
function setupAnnotations(pdfPage, scale) {
function bindLink(link, dest) {
link.href = PDFView.getDestinationHash(dest);
link.onclick = function pageViewSetupLinksOnclick() {
@ -809,29 +821,30 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
return container;
}
var items = content.getAnnotations();
for (var i = 0; i < items.length; i++) {
var item = items[i];
switch (item.type) {
case 'Link':
var link = createElementWithStyle('a', item);
link.href = item.url || '';
if (!item.url)
bindLink(link, ('dest' in item) ? item.dest : null);
div.appendChild(link);
break;
case 'Text':
var comment = createCommentAnnotation(item.name, item);
if (comment)
div.appendChild(comment);
break;
pdfPage.getAnnotations().then(function(items) {
for (var i = 0; i < items.length; i++) {
var item = items[i];
switch (item.type) {
case 'Link':
var link = createElementWithStyle('a', item);
link.href = item.url || '';
if (!item.url)
bindLink(link, ('dest' in item) ? item.dest : null);
div.appendChild(link);
break;
case 'Text':
var comment = createCommentAnnotation(item.name, item);
if (comment)
div.appendChild(comment);
break;
}
}
}
});
}
this.getPagePoint = function pageViewGetPagePoint(x, y) {
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) {
@ -879,8 +892,8 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
}
var boundingRect = [
this.content.rotatePoint(x, y),
this.content.rotatePoint(x + width, y + height)
this.pdfPage.rotatePoint(x, y),
this.pdfPage.rotatePoint(x + width, y + height)
];
if (scale && scale !== PDFView.currentScale)
@ -948,7 +961,7 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
// Rendering area
var self = this;
this.content.startRendering(ctx, function pageViewDrawCallback(error) {
function pageViewDrawCallback(error) {
if (self.loadingIconDiv) {
div.removeChild(self.loadingIconDiv);
delete self.loadingIconDiv;
@ -964,9 +977,22 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
cache.push(self);
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);
};
@ -1397,7 +1423,11 @@ window.addEventListener('change', function webViewerChange(evt) {
for (var i = 0; i < data.length; 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