Brendan Dahl 13 years ago
parent
commit
459e08d1b4
  1. 137
      src/api.js
  2. 3
      src/evaluator.js
  3. 1
      src/fonts.js
  4. 64
      src/obj.js
  5. 16
      src/worker.js
  6. 1
      test/test_manifest.json
  7. 2
      web/viewer.js

137
src/api.js

@ -1,6 +1,16 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/**
* This is the main entry point for loading a PDF and interacting with it.
* NOTE: If a URL is used to fetch the PDF data a standard XMLHttpRequest(XHR)
* is used, which means it must follow the same origin rules that any XHR does
* e.g. No cross domain requests without CORS.
*
* @param {string|TypedAray} source Either a url to a PDF is located or a
* typed array already populated with data.
* @return {Promise} A promise that is resolved with {PDFDocumentProxy} object.
*/
PDFJS.getDocument = function getDocument(source) { PDFJS.getDocument = function getDocument(source) {
var promise = new PDFJS.Promise(); var promise = new PDFJS.Promise();
var transport = new WorkerTransport(promise); var transport = new WorkerTransport(promise);
@ -31,33 +41,74 @@ PDFJS.getDocument = function getDocument(source) {
return promise; return promise;
}; };
/**
* Proxy to a PDFDocument in the worker thread. Also, contains commonly used
* properties that can be read synchronously.
*/
var PDFDocumentProxy = (function() { var PDFDocumentProxy = (function() {
function PDFDocumentProxy(pdfInfo, transport) { function PDFDocumentProxy(pdfInfo, transport) {
this.pdfInfo = pdfInfo; this.pdfInfo = pdfInfo;
this.transport = transport; this.transport = transport;
} }
PDFDocumentProxy.prototype = { PDFDocumentProxy.prototype = {
/**
* @return {number} Total number of pages the PDF contains.
*/
get numPages() { get numPages() {
return this.pdfInfo.numPages; return this.pdfInfo.numPages;
}, },
/**
* @return {string} A unique ID to identify a PDF. Not guaranteed to be
* unique.
*/
get fingerprint() { get fingerprint() {
return this.pdfInfo.fingerprint; return this.pdfInfo.fingerprint;
}, },
/**
* @param {number} The page number to get. The first page is 1.
* @return {Promise} A promise that is resolved with a {PDFPageProxy}
* object.
*/
getPage: function(number) { getPage: function(number) {
return this.transport.getPage(number); return this.transport.getPage(number);
}, },
/**
* @return {Promise} A promise that is resolved with a lookup table for
* mapping named destinations to reference numbers.
*/
getDestinations: function() { getDestinations: function() {
var promise = new PDFJS.Promise(); var promise = new PDFJS.Promise();
var destinations = this.pdfInfo.destinations; var destinations = this.pdfInfo.destinations;
promise.resolve(destinations); promise.resolve(destinations);
return promise; return promise;
}, },
/**
* @return {Promise} A promise that is resolved with an {array} that is a
* tree outline (if it has one) of the PDF. The tree is in the format of:
* [
* {
* title: string,
* bold: boolean,
* italic: boolean,
* color: rgb array,
* dest: dest obj,
* items: array of more items like this
* },
* ...
* ].
*/
getOutline: function() { getOutline: function() {
var promise = new PDFJS.Promise(); var promise = new PDFJS.Promise();
var outline = this.pdfInfo.outline; var outline = this.pdfInfo.outline;
promise.resolve(outline); promise.resolve(outline);
return promise; return promise;
}, },
/**
* @return {Promise} A promise that is resolved with an {object} that has
* info and metadata properties. Info is an {object} filled with anything
* available in the information dictionary and similarly metadata is a
* {Metadata} object with information from the metadata section of the PDF.
*/
getMetadata: function() { getMetadata: function() {
var promise = new PDFJS.Promise(); var promise = new PDFJS.Promise();
var info = this.pdfInfo.info; var info = this.pdfInfo.info;
@ -84,30 +135,69 @@ var PDFPageProxy = (function PDFPageProxyClosure() {
this.objs = transport.objs; this.objs = transport.objs;
} }
PDFPageProxy.prototype = { PDFPageProxy.prototype = {
/**
* @return {number} Page number of the page. First page is 1.
*/
get pageNumber() { get pageNumber() {
return this.pageInfo.pageIndex + 1; return this.pageInfo.pageIndex + 1;
}, },
/**
* @return {number} The number of degrees the page is rotated clockwise.
*/
get rotate() { get rotate() {
return this.pageInfo.rotate; return this.pageInfo.rotate;
}, },
/**
* @return {object} The reference that points to this page. It has 'num' and
* 'gen' properties.
*/
get ref() { get ref() {
return this.pageInfo.ref; return this.pageInfo.ref;
}, },
/**
* @return {array} An array of the visible portion of the PDF page in the
* user space units - [x1, y1, x2, y2].
*/
get view() { get view() {
return this.pageInfo.view; return this.pageInfo.view;
}, },
/**
* @param {number} scale The desired scale of the viewport.
* @param {number} rotate Degrees to rotate the viewport. If omitted this
* defaults to the page rotation.
* @return {PageViewport} Contains 'width' and 'height' properties along
* with transforms required for rendering.
*/
getViewport: function(scale, rotate) { getViewport: function(scale, rotate) {
if (arguments.length < 2) if (arguments.length < 2)
rotate = this.rotate; rotate = this.rotate;
return new PDFJS.PageViewport(this.view, scale, rotate, 0, 0); return new PDFJS.PageViewport(this.view, scale, rotate, 0, 0);
}, },
/**
* @return {Promise} A promise that is resolved with an {array} of the
* annotation objects.
*/
getAnnotations: function() { getAnnotations: function() {
if (this.annotationsPromise)
return this.annotationsPromise;
var promise = new PDFJS.Promise(); var promise = new PDFJS.Promise();
var annotations = this.pageInfo.annotations; this.annotationsPromise = promise;
promise.resolve(annotations); this.transport.getAnnotations(this.pageInfo.pageIndex);
return promise; return promise;
}, },
render: function(renderContext) { /**
* Begins the process of rendering a page to the desired context.
* @param {object} params A parameter object that supports:
* {
* canvasContext(required): A 2D context of a DOM Canvas object.,
* textLayer(optional): An object that has beginLayout, endLayout, and
* appendText functions.
* }.
* @return {Promise} A promise that is resolved when the page finishes
* rendering.
*/
render: function(params) {
var promise = new Promise(); var promise = new Promise();
var stats = this.stats; var stats = this.stats;
stats.time('Overall'); stats.time('Overall');
@ -132,10 +222,10 @@ var PDFPageProxy = (function PDFPageProxyClosure() {
// Once the operatorList and fonts are loaded, do the actual rendering. // Once the operatorList and fonts are loaded, do the actual rendering.
this.displayReadyPromise.then( this.displayReadyPromise.then(
function pageDisplayReadyPromise() { function pageDisplayReadyPromise() {
var gfx = new CanvasGraphics(renderContext.canvasContext, var gfx = new CanvasGraphics(params.canvasContext,
this.objs, renderContext.textLayer); this.objs, params.textLayer);
try { try {
this.display(gfx, renderContext.viewport, complete); this.display(gfx, params.viewport, complete);
} catch (e) { } catch (e) {
complete(e); complete(e);
} }
@ -147,7 +237,9 @@ var PDFPageProxy = (function PDFPageProxyClosure() {
return promise; return promise;
}, },
/**
* For internal use only.
*/
startRenderingFromOperatorList: startRenderingFromOperatorList:
function PDFPageWrapper_startRenderingFromOperatorList(operatorList, function PDFPageWrapper_startRenderingFromOperatorList(operatorList,
fonts) { fonts) {
@ -168,7 +260,9 @@ var PDFPageProxy = (function PDFPageProxyClosure() {
} }
); );
}, },
/**
* For internal use only.
*/
ensureFonts: function PDFPageWrapper_ensureFonts(fonts, callback) { ensureFonts: function PDFPageWrapper_ensureFonts(fonts, callback) {
this.stats.time('Font Loading'); this.stats.time('Font Loading');
// Convert the font names to the corresponding font obj. // Convert the font names to the corresponding font obj.
@ -186,7 +280,9 @@ var PDFPageProxy = (function PDFPageProxyClosure() {
}.bind(this) }.bind(this)
); );
}, },
/**
* For internal use only.
*/
display: function PDFPageWrapper_display(gfx, viewport, callback) { display: function PDFPageWrapper_display(gfx, viewport, callback) {
var stats = this.stats; var stats = this.stats;
stats.time('Rendering'); stats.time('Rendering');
@ -209,6 +305,7 @@ var PDFPageProxy = (function PDFPageProxyClosure() {
gfx.executeOperatorList(operatorList, startIdx, next, stepper); gfx.executeOperatorList(operatorList, startIdx, next, stepper);
if (startIdx == length) { if (startIdx == length) {
gfx.endDrawing(); gfx.endDrawing();
delete this.operatorList;
stats.timeEnd('Rendering'); stats.timeEnd('Rendering');
stats.timeEnd('Overall'); stats.timeEnd('Overall');
if (callback) callback(); if (callback) callback();
@ -216,13 +313,18 @@ var PDFPageProxy = (function PDFPageProxyClosure() {
} }
next(); next();
}, },
/**
* Stub for future feature.
*/
getTextContent: function() { getTextContent: function() {
var promise = new PDFJS.Promise(); var promise = new PDFJS.Promise();
var textContent = 'page text'; // not implemented var textContent = 'page text'; // not implemented
promise.resolve(textContent); promise.resolve(textContent);
return promise; return promise;
}, },
/**
* Stub for future feature.
*/
getOperationList: function() { getOperationList: function() {
var promise = new PDFJS.Promise(); var promise = new PDFJS.Promise();
var operationList = { // not implemented var operationList = { // not implemented
@ -235,7 +337,9 @@ var PDFPageProxy = (function PDFPageProxyClosure() {
}; };
return PDFPageProxy; return PDFPageProxy;
})(); })();
/**
* For internal use only.
*/
var WorkerTransport = (function WorkerTransportClosure() { var WorkerTransport = (function WorkerTransportClosure() {
function WorkerTransport(promise) { function WorkerTransport(promise) {
this.workerReadyPromise = promise; this.workerReadyPromise = promise;
@ -342,6 +446,12 @@ var WorkerTransport = (function WorkerTransportClosure() {
promise.resolve(page); promise.resolve(page);
}, this); }, this);
messageHandler.on('GetAnnotations', function transportAnnotations(data) {
var annotations = data.annotations;
var promise = this.pageCache[data.pageIndex].annotationsPromise;
promise.resolve(annotations);
}, this);
messageHandler.on('RenderPage', function transportRender(data) { messageHandler.on('RenderPage', function transportRender(data) {
var page = this.pageCache[data.pageIndex]; var page = this.pageCache[data.pageIndex];
var depFonts = data.depFonts; var depFonts = data.depFonts;
@ -440,6 +550,11 @@ var WorkerTransport = (function WorkerTransportClosure() {
this.pagePromises[pageIndex] = promise; this.pagePromises[pageIndex] = promise;
this.messageHandler.send('GetPageRequest', { pageIndex: pageIndex }); this.messageHandler.send('GetPageRequest', { pageIndex: pageIndex });
return promise; return promise;
},
getAnnotations: function WorkerTransport_getAnnotations(pageIndex) {
this.messageHandler.send('GetAnnotationsRequest',
{ pageIndex: pageIndex });
} }
}; };
return WorkerTransport; return WorkerTransport;

3
src/evaluator.js

@ -466,7 +466,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
args = []; args = [];
} else if (obj != null) { } else if (obj != null) {
assertWellFormed(args.length <= 33, 'Too many arguments'); assertWellFormed(args.length <= 33, 'Too many arguments');
args.push(obj); args.push(obj instanceof Dict ? obj.getAll() : obj);
} }
} }
@ -862,7 +862,6 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
properties.coded = true; properties.coded = true;
var charProcs = dict.get('CharProcs').getAll(); var charProcs = dict.get('CharProcs').getAll();
var fontResources = dict.get('Resources') || resources; var fontResources = dict.get('Resources') || resources;
properties.resources = fontResources;
properties.charProcOperatorList = {}; properties.charProcOperatorList = {};
for (var key in charProcs) { for (var key in charProcs) {
var glyphStream = charProcs[key]; var glyphStream = charProcs[key];

1
src/fonts.js

@ -766,7 +766,6 @@ var Font = (function FontClosure() {
this.name = name; this.name = name;
this.coded = properties.coded; this.coded = properties.coded;
this.charProcOperatorList = properties.charProcOperatorList; this.charProcOperatorList = properties.charProcOperatorList;
this.resources = properties.resources;
this.sizes = []; this.sizes = [];
var names = name.split('+'); var names = name.split('+');

64
src/obj.js

@ -37,51 +37,55 @@ var Dict = (function DictClosure() {
// xref is optional // xref is optional
function Dict(xref) { function Dict(xref) {
// Map should only be used internally, use functions below to access. // Map should only be used internally, use functions below to access.
this.map = Object.create(null); var map = Object.create(null);
this.xref = xref;
} this.assignXref = function Dict_assingXref(newXref) {
xref = newXref;
};
Dict.prototype = {
// automatically dereferences Ref objects // automatically dereferences Ref objects
get: function Dict_get(key1, key2, key3) { this.get = function Dict_get(key1, key2, key3) {
var value; var value;
var xref = this.xref; if (typeof (value = map[key1]) != 'undefined' || key1 in map ||
if (typeof (value = this.map[key1]) != 'undefined' || key1 in this.map ||
typeof key2 == 'undefined') { typeof key2 == 'undefined') {
return xref ? this.xref.fetchIfRef(value) : value; return xref ? xref.fetchIfRef(value) : value;
} }
if (typeof (value = this.map[key2]) != 'undefined' || key2 in this.map || if (typeof (value = map[key2]) != 'undefined' || key2 in map ||
typeof key3 == 'undefined') { typeof key3 == 'undefined') {
return xref ? this.xref.fetchIfRef(value) : value; return xref ? xref.fetchIfRef(value) : value;
} }
value = this.map[key3] || null; value = map[key3] || null;
return xref ? this.xref.fetchIfRef(value) : value; return xref ? xref.fetchIfRef(value) : value;
}, };
// no dereferencing // no dereferencing
getRaw: function Dict_getRaw(key) { this.getRaw = function Dict_getRaw(key) {
return this.map[key]; return map[key];
}, };
// creates new map and dereferences all Refs // creates new map and dereferences all Refs
getAll: function Dict_getAll() { this.getAll = function Dict_getAll() {
var all = {}; var all = {};
for (var key in this.map) for (var key in map) {
all[key] = this.get(key); var obj = this.get(key);
all[key] = obj instanceof Dict ? obj.getAll() : obj;
}
return all; return all;
}, };
set: function Dict_set(key, value) { this.set = function Dict_set(key, value) {
this.map[key] = value; map[key] = value;
}, };
has: function Dict_has(key) { this.has = function Dict_has(key) {
return key in this.map; return key in map;
}, };
forEach: function Dict_forEach(callback) { this.forEach = function Dict_forEach(callback) {
for (var key in this.map) { for (var key in map) {
callback(key, this.get(key)); callback(key, this.get(key));
} }
} };
}; };
return Dict; return Dict;
@ -299,7 +303,7 @@ var XRef = (function XRefClosure() {
this.entries = []; this.entries = [];
this.xrefstms = {}; this.xrefstms = {};
var trailerDict = this.readXRef(startXRef); var trailerDict = this.readXRef(startXRef);
trailerDict.xref = this; trailerDict.assignXref(this);
this.trailer = trailerDict; this.trailer = trailerDict;
// prepare the XRef cache // prepare the XRef cache
this.cache = []; this.cache = [];

16
src/worker.js

@ -100,20 +100,27 @@ var WorkerMessageHandler = {
handler.send('GetDoc', {pdfInfo: doc}); handler.send('GetDoc', {pdfInfo: doc});
}); });
handler.on('GetPageRequest', function wphSetupTest(data) { handler.on('GetPageRequest', function wphSetupGetPage(data) {
var pageNumber = data.pageIndex + 1; var pageNumber = data.pageIndex + 1;
var pdfPage = pdfModel.getPage(pageNumber); var pdfPage = pdfModel.getPage(pageNumber);
var page = { var page = {
pageIndex: data.pageIndex, pageIndex: data.pageIndex,
rotate: pdfPage.rotate, rotate: pdfPage.rotate,
ref: pdfPage.ref, ref: pdfPage.ref,
view: pdfPage.view, view: pdfPage.view
annotations: pdfPage.getAnnotations()
}; };
handler.send('GetPage', {pageInfo: page}); handler.send('GetPage', {pageInfo: page});
}); });
handler.on('RenderPageRequest', function wphSetupPageRequest(data) { handler.on('GetAnnotationsRequest', function wphSetupGetAnnotations(data) {
var pdfPage = pdfModel.getPage(data.pageIndex + 1);
handler.send('GetAnnotations', {
pageIndex: data.pageIndex,
annotations: pdfPage.getAnnotations()
});
});
handler.on('RenderPageRequest', function wphSetupRenderPage(data) {
var pageNum = data.pageIndex + 1; var pageNum = data.pageIndex + 1;
@ -170,7 +177,6 @@ var WorkerMessageHandler = {
fonts[dep] = true; fonts[dep] = true;
} }
} }
handler.send('RenderPage', { handler.send('RenderPage', {
pageIndex: data.pageIndex, pageIndex: data.pageIndex,
operatorList: operatorList, operatorList: operatorList,

1
test/test_manifest.json

@ -29,6 +29,7 @@
"file": "pdfs/pdf.pdf", "file": "pdfs/pdf.pdf",
"md5": "dbdb23c939d2be09b43126c3c56060c7", "md5": "dbdb23c939d2be09b43126c3c56060c7",
"link": true, "link": true,
"pageLimit": 500,
"rounds": 1, "rounds": 1,
"type": "load" "type": "load"
}, },

2
web/viewer.js

@ -1284,7 +1284,6 @@ window.addEventListener('load', function webViewerLoad(evt) {
var file = PDFJS.isFirefoxExtension ? var file = PDFJS.isFirefoxExtension ?
window.location.toString() : params.file || kDefaultURL; window.location.toString() : params.file || kDefaultURL;
PDFView.open(file, 0);
if (PDFJS.isFirefoxExtension || !window.File || !window.FileReader || if (PDFJS.isFirefoxExtension || !window.File || !window.FileReader ||
!window.FileList || !window.Blob) { !window.FileList || !window.Blob) {
@ -1316,6 +1315,7 @@ window.addEventListener('load', function webViewerLoad(evt) {
var sidebarScrollView = document.getElementById('sidebarScrollView'); var sidebarScrollView = document.getElementById('sidebarScrollView');
sidebarScrollView.addEventListener('scroll', updateThumbViewArea, true); sidebarScrollView.addEventListener('scroll', updateThumbViewArea, true);
PDFView.open(file, 0);
}, true); }, true);
/** /**

Loading…
Cancel
Save