Merge branch 'master' into new-ui

Conflicts:
	web/viewer.html
	web/viewer.js
This commit is contained in:
Artur Adib 2012-04-18 15:02:49 -04:00
commit 7e51d2b1f7
31 changed files with 1489 additions and 877 deletions

View File

@ -6,7 +6,7 @@ DEFAULT_TESTS := test_manifest.json
DEFAULT_PYTHON := python2.7
EXTENSION_SRC := ./extensions/
EXTENSION_BASE_VERSION := 4bb289ec499013de66eb421737a4dbb4a9273eda
EXTENSION_BASE_VERSION := f0f0418a9c6637981fe1182b9212c2d592774c7d
FIREFOX_EXTENSION_NAME := pdf.js.xpi
FIREFOX_AMO_EXTENSION_NAME := pdf.js.amo.xpi
CHROME_EXTENSION_NAME := pdf.js.crx
@ -20,6 +20,7 @@ all: bundle
PDF_JS_FILES = \
core.js \
util.js \
api.js \
canvas.js \
obj.js \
function.js \
@ -74,7 +75,8 @@ bundle: | $(BUILD_DIR)
@cd src; \
cat $(PDF_JS_FILES) > all_files.tmp; \
sed '/PDFJSSCRIPT_INCLUDE_ALL/ r all_files.tmp' pdf.js > ../$(BUILD_TARGET); \
sed -i.bak "s/PDFJSSCRIPT_BUNDLE_VER/`git log --format="%h" -n 1`/" ../$(BUILD_TARGET); \
cp ../$(BUILD_TARGET) ../$(BUILD_TARGET).bak; \
sed "s/PDFJSSCRIPT_BUNDLE_VER/`git log --format="%h" -n 1`/" ../$(BUILD_TARGET).bak > ../$(BUILD_TARGET); \
rm -f ../$(BUILD_TARGET).bak; \
rm -f *.tmp; \
cd ..
@ -184,7 +186,7 @@ web: | production extension compiler pages-repo
# and deletions.
pages-repo: | $(BUILD_DIR)
@if [ ! -d "$(GH_PAGES)" ]; then \
git clone -b gh-pages $(REPO) $(GH_PAGES); \
git clone --depth 1 -b gh-pages $(REPO) $(GH_PAGES); \
rm -rf $(GH_PAGES)/*; \
fi;
@mkdir -p $(GH_PAGES)/web;
@ -211,7 +213,7 @@ pages-repo: | $(BUILD_DIR)
# copy of the pdf.js source.
CONTENT_DIR := content
BUILD_NUMBER := `git log --format=oneline $(EXTENSION_BASE_VERSION).. | wc -l | awk '{print $$1}'`
PDFJSSCRIPT_VERSION := 0.2.$(BUILD_NUMBER)
PDFJSSCRIPT_VERSION := 0.3.$(BUILD_NUMBER)
EXTENSION_WEB_FILES = \
web/images \
web/viewer.css \
@ -273,25 +275,34 @@ extension: | production
@cp web/viewer-snippet-firefox-extension.html $(FIREFOX_BUILD_CONTENT)/web/
# Modify the viewer so it does all the extension only stuff.
@cd $(FIREFOX_BUILD_CONTENT)/web; \
sed -i.bak '/PDFJSSCRIPT_INCLUDE_BUNDLE/ r ../build/pdf.js' viewer-snippet-firefox-extension.html; \
sed -i.bak '/PDFJSSCRIPT_REMOVE_CORE/d' viewer.html; \
sed -i.bak '/PDFJSSCRIPT_REMOVE_FIREFOX_EXTENSION/d' viewer.html; \
sed -i.bak '/PDFJSSCRIPT_INCLUDE_FIREFOX_EXTENSION/ r viewer-snippet-firefox-extension.html' viewer.html; \
cp viewer-snippet-firefox-extension.html viewer-snippet-firefox-extension.html.bak; \
sed '/PDFJSSCRIPT_INCLUDE_BUNDLE/ r ../build/pdf.js' viewer-snippet-firefox-extension.html.bak > viewer-snippet-firefox-extension.html; \
cp viewer.html viewer.html.bak; \
sed '/PDFJSSCRIPT_REMOVE_CORE/d' viewer.html.bak > viewer.html; \
cp viewer.html viewer.html.bak; \
sed '/PDFJSSCRIPT_REMOVE_FIREFOX_EXTENSION/d' viewer.html.bak > viewer.html; \
cp viewer.html viewer.html.bak; \
sed '/PDFJSSCRIPT_INCLUDE_FIREFOX_EXTENSION/ r viewer-snippet-firefox-extension.html' viewer.html.bak > viewer.html; \
rm -f *.bak;
# We don't need pdf.js anymore since its inlined
@rm -Rf $(FIREFOX_BUILD_CONTENT)/$(BUILD_DIR)/;
# Update the build version number
@sed -i.bak "s/PDFJSSCRIPT_VERSION/$(PDFJSSCRIPT_VERSION)/" $(FIREFOX_BUILD_DIR)/install.rdf
@sed -i.bak "s/PDFJSSCRIPT_VERSION/$(PDFJSSCRIPT_VERSION)/" $(FIREFOX_BUILD_DIR)/install.rdf.in
@sed -i.bak "s/PDFJSSCRIPT_VERSION/$(PDFJSSCRIPT_VERSION)/" $(FIREFOX_BUILD_DIR)/update.rdf
@sed -i.bak "s/PDFJSSCRIPT_VERSION/$(PDFJSSCRIPT_VERSION)/" $(FIREFOX_BUILD_DIR)/README.mozilla
cp $(FIREFOX_BUILD_DIR)/install.rdf $(FIREFOX_BUILD_DIR)/install.rdf.bak
@sed "s/PDFJSSCRIPT_VERSION/$(PDFJSSCRIPT_VERSION)/" $(FIREFOX_BUILD_DIR)/install.rdf.bak > $(FIREFOX_BUILD_DIR)/install.rdf
cp $(FIREFOX_BUILD_DIR)/install.rdf.in $(FIREFOX_BUILD_DIR)/install.rdf.in.bak
@sed "s/PDFJSSCRIPT_VERSION/$(PDFJSSCRIPT_VERSION)/" $(FIREFOX_BUILD_DIR)/install.rdf.in.bak > $(FIREFOX_BUILD_DIR)/install.rdf.in
cp $(FIREFOX_BUILD_DIR)/update.rdf $(FIREFOX_BUILD_DIR)/update.rdf.bak
@sed "s/PDFJSSCRIPT_VERSION/$(PDFJSSCRIPT_VERSION)/" $(FIREFOX_BUILD_DIR)/update.rdf.bak > $(FIREFOX_BUILD_DIR)/update.rdf
cp $(FIREFOX_BUILD_DIR)/README.mozilla $(FIREFOX_BUILD_DIR)/README.mozilla.bak
@sed "s/PDFJSSCRIPT_VERSION/$(PDFJSSCRIPT_VERSION)/" $(FIREFOX_BUILD_DIR)/README.mozilla.bak > $(FIREFOX_BUILD_DIR)/README.mozilla
@rm -f $(FIREFOX_BUILD_DIR)/*.bak
@find $(FIREFOX_BUILD_DIR) -name ".*" -delete
# Create the xpi
@cd $(FIREFOX_BUILD_DIR); zip -r $(FIREFOX_EXTENSION_NAME) $(FIREFOX_EXTENSION_FILES)
@echo "extension created: " $(FIREFOX_EXTENSION_NAME)
# Build the amo extension too (remove the updateUrl)
@sed -i.bak "/updateURL/d" $(FIREFOX_BUILD_DIR)/install.rdf
cp $(FIREFOX_BUILD_DIR)/install.rdf $(FIREFOX_BUILD_DIR)/install.rdf.bak
@sed "/updateURL/d" $(FIREFOX_BUILD_DIR)/install.rdf.bak > $(FIREFOX_BUILD_DIR)/install.rdf
@rm -f $(FIREFOX_BUILD_DIR)/*.bak
@cd $(FIREFOX_BUILD_DIR); zip -r $(FIREFOX_AMO_EXTENSION_NAME) $(FIREFOX_EXTENSION_FILES)
@echo "AMO extension created: " $(FIREFOX_AMO_EXTENSION_NAME)

View File

@ -9,7 +9,7 @@
var formFields = {};
function setupForm(div, content, scale) {
function setupForm(div, content, viewport) {
function bindInputItem(input, item) {
if (input.name in formFields) {
var value = formFields[input.name];
@ -27,16 +27,20 @@ function setupForm(div, content, scale) {
}
function createElementWithStyle(tagName, item) {
var element = document.createElement(tagName);
element.style.left = (item.x * scale) + 'px';
element.style.top = (item.y * scale) + 'px';
element.style.width = Math.ceil(item.width * scale) + 'px';
element.style.height = Math.ceil(item.height * scale) + 'px';
var rect = Util.normalizeRect(
viewport.convertToViewportRectangle(item.rect));
element.style.left = Math.floor(rect[0]) + 'px';
element.style.top = Math.floor(rect[1]) + 'px';
element.style.width = Math.ceil(rect[2] - rect[0]) + 'px';
element.style.height = Math.ceil(rect[3] - rect[1]) + 'px';
return element;
}
function assignFontStyle(element, item) {
var fontStyles = '';
if ('fontSize' in item)
fontStyles += 'font-size: ' + Math.round(item.fontSize * scale) + 'px;';
if ('fontSize' in item) {
fontStyles += 'font-size: ' + Math.round(item.fontSize *
viewport.fontScale) + 'px;';
}
switch (item.textAlignment) {
case 0:
fontStyles += 'text-align: left;';
@ -51,83 +55,88 @@ function setupForm(div, content, scale) {
element.setAttribute('style', element.getAttribute('style') + fontStyles);
}
var items = content.getAnnotations();
for (var i = 0; i < items.length; i++) {
var item = items[i];
switch (item.type) {
case 'Widget':
if (item.fieldType != 'Tx' && item.fieldType != 'Btn' &&
item.fieldType != 'Ch')
break;
var inputDiv = createElementWithStyle('div', item);
inputDiv.className = 'inputHint';
div.appendChild(inputDiv);
var input;
if (item.fieldType == 'Tx') {
input = createElementWithStyle('input', item);
}
if (item.fieldType == 'Btn') {
input = createElementWithStyle('input', item);
if (item.flags & 32768) {
input.type = 'radio';
// radio button is not supported
} else if (item.flags & 65536) {
input.type = 'button';
// pushbutton is not supported
} else {
input.type = 'checkbox';
content.getAnnotations().then(function(items) {
for (var i = 0; i < items.length; i++) {
var item = items[i];
switch (item.type) {
case 'Widget':
if (item.fieldType != 'Tx' && item.fieldType != 'Btn' &&
item.fieldType != 'Ch')
break;
var inputDiv = createElementWithStyle('div', item);
inputDiv.className = 'inputHint';
div.appendChild(inputDiv);
var input;
if (item.fieldType == 'Tx') {
input = createElementWithStyle('input', item);
}
}
if (item.fieldType == 'Ch') {
input = createElementWithStyle('select', item);
// select box is not supported
}
input.className = 'inputControl';
input.name = item.fullName;
input.title = item.alternativeText;
assignFontStyle(input, item);
bindInputItem(input, item);
div.appendChild(input);
break;
if (item.fieldType == 'Btn') {
input = createElementWithStyle('input', item);
if (item.flags & 32768) {
input.type = 'radio';
// radio button is not supported
} else if (item.flags & 65536) {
input.type = 'button';
// pushbutton is not supported
} else {
input.type = 'checkbox';
}
}
if (item.fieldType == 'Ch') {
input = createElementWithStyle('select', item);
// select box is not supported
}
input.className = 'inputControl';
input.name = item.fullName;
input.title = item.alternativeText;
assignFontStyle(input, item);
bindInputItem(input, item);
div.appendChild(input);
break;
}
}
}
});
}
function renderPage(div, pdf, pageNumber, callback) {
var page = pdf.getPage(pageNumber);
var scale = 1.5;
pdf.getPage(pageNumber).then(function(page) {
var scale = 1.5;
var viewport = page.getViewport(scale);
var pageDisplayWidth = page.width * scale;
var pageDisplayHeight = page.height * scale;
var pageDisplayWidth = viewport.width;
var pageDisplayHeight = viewport.height;
var pageDivHolder = document.createElement('div');
pageDivHolder.className = 'pdfpage';
pageDivHolder.style.width = pageDisplayWidth + 'px';
pageDivHolder.style.height = pageDisplayHeight + 'px';
div.appendChild(pageDivHolder);
var pageDivHolder = document.createElement('div');
pageDivHolder.className = 'pdfpage';
pageDivHolder.style.width = pageDisplayWidth + 'px';
pageDivHolder.style.height = pageDisplayHeight + 'px';
div.appendChild(pageDivHolder);
// Prepare canvas using PDF page dimensions
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
canvas.width = pageDisplayWidth;
canvas.height = pageDisplayHeight;
pageDivHolder.appendChild(canvas);
// Prepare canvas using PDF page dimensions
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
canvas.width = pageDisplayWidth;
canvas.height = pageDisplayHeight;
pageDivHolder.appendChild(canvas);
// Render PDF page into canvas context
page.startRendering(context, callback);
// Render PDF page into canvas context
var renderContext = {
canvasContext: context,
viewport: viewport
};
page.render(renderContext).then(callback);
// Prepare and populate form elements layer
var formDiv = document.createElement('div');
pageDivHolder.appendChild(formDiv);
// Prepare and populate form elements layer
var formDiv = document.createElement('div');
pageDivHolder.appendChild(formDiv);
setupForm(formDiv, page, scale);
setupForm(formDiv, page, viewport);
});
}
PDFJS.getPdf(pdfWithFormsPath, function getPdfForm(data) {
// Instantiate PDFDoc with PDF data
var pdf = new PDFJS.PDFDoc(data);
// Fetch the PDF document from the URL using promices
PDFJS.getDocument(pdfWithFormsPath).then(function getPdfForm(pdf) {
// Rendering all pages starting from first
var viewer = document.getElementById('viewer');
var pageNumber = 1;

View File

@ -6,6 +6,7 @@
<!-- In production, change the content of PDFJS.workerSrc below -->
<script type="text/javascript" src="../../src/core.js"></script>
<script type="text/javascript" src="../../src/util.js"></script>
<script type="text/javascript" src="../../src/api.js"></script>
<script type="text/javascript" src="../../src/canvas.js"></script>
<script type="text/javascript" src="../../src/obj.js"></script>
<script type="text/javascript" src="../../src/function.js"></script>

View File

@ -7,25 +7,31 @@
'use strict';
PDFJS.getPdf('helloworld.pdf', function getPdfHelloWorld(data) {
//
// Instantiate PDFDoc with PDF data
//
var pdf = new PDFJS.PDFDoc(data);
var page = pdf.getPage(1);
var scale = 1.5;
//
// Fetch the PDF document from the URL using promices
//
PDFJS.getDocument('helloworld.pdf').then(function(pdf) {
// Using promise to fetch the page
pdf.getPage(1).then(function(page) {
var scale = 1.5;
var viewport = page.getViewport(scale);
//
// Prepare canvas using PDF page dimensions
//
var canvas = document.getElementById('the-canvas');
var context = canvas.getContext('2d');
canvas.height = page.height * scale;
canvas.width = page.width * scale;
//
// Prepare canvas using PDF page dimensions
//
var canvas = document.getElementById('the-canvas');
var context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
//
// Render PDF page into canvas context
//
page.startRendering(context);
//
// Render PDF page into canvas context
//
var renderContext = {
canvasContext: context,
viewport: viewport
};
page.render(renderContext);
});
});

View File

@ -6,6 +6,7 @@
<!-- In production, change the content of PDFJS.workerSrc below -->
<script type="text/javascript" src="../../src/core.js"></script>
<script type="text/javascript" src="../../src/util.js"></script>
<script type="text/javascript" src="../../src/api.js"></script>
<script type="text/javascript" src="../../src/canvas.js"></script>
<script type="text/javascript" src="../../src/obj.js"></script>
<script type="text/javascript" src="../../src/function.js"></script>

View File

@ -153,7 +153,7 @@ PdfStreamConverter.prototype = {
'resource://pdf.js/web/viewer.html', null, null);
var listener = this.listener;
// Proxy all the requst observer calls, when it gets to onStopRequst
// Proxy all the request observer calls, when it gets to onStopRequest
// we can get the dom window.
var proxy = {
onStartRequest: function() {

View File

@ -79,6 +79,7 @@ target.bundle = function() {
var SRC_FILES =
['core.js',
'util.js',
'api.js',
'canvas.js',
'obj.js',
'function.js',
@ -175,8 +176,8 @@ var EXTENSION_WEB_FILES =
'web/viewer.js',
'web/viewer.html',
'web/viewer-production.html'],
EXTENSION_BASE_VERSION = '4bb289ec499013de66eb421737a4dbb4a9273eda',
EXTENSION_VERSION_PREFIX = '0.2.',
EXTENSION_BASE_VERSION = 'f0f0418a9c6637981fe1182b9212c2d592774c7d',
EXTENSION_VERSION_PREFIX = '0.3.',
EXTENSION_BUILD_NUMBER,
EXTENSION_VERSION;

590
src/api.js Normal file
View File

@ -0,0 +1,590 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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) {
var promise = new PDFJS.Promise();
var transport = new WorkerTransport(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) {
transport.sendData(data);
});
} else {
// assuming the source is array, instantiating directly from it
transport.sendData(source);
}
return promise;
};
/**
* Proxy to a PDFDocument in the worker thread. Also, contains commonly used
* properties that can be read synchronously.
*/
var PDFDocumentProxy = (function() {
function PDFDocumentProxy(pdfInfo, transport) {
this.pdfInfo = pdfInfo;
this.transport = transport;
}
PDFDocumentProxy.prototype = {
/**
* @return {number} Total number of pages the PDF contains.
*/
get numPages() {
return this.pdfInfo.numPages;
},
/**
* @return {string} A unique ID to identify a PDF. Not guaranteed to be
* unique.
*/
get 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) {
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() {
var promise = new PDFJS.Promise();
var destinations = this.pdfInfo.destinations;
promise.resolve(destinations);
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() {
var promise = new PDFJS.Promise();
var outline = this.pdfInfo.outline;
promise.resolve(outline);
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() {
var promise = new PDFJS.Promise();
var info = this.pdfInfo.info;
var metadata = this.pdfInfo.metadata;
promise.resolve({
info: info,
metadata: metadata ? new PDFJS.Metadata(metadata) : null
});
return promise;
},
destroy: function() {
this.transport.destroy();
}
};
return PDFDocumentProxy;
})();
var PDFPageProxy = (function PDFPageProxyClosure() {
function PDFPageProxy(pageInfo, transport) {
this.pageInfo = pageInfo;
this.transport = transport;
this.stats = new StatTimer();
this.stats.enabled = !!globalScope.PDFJS.enableStats;
this.objs = transport.objs;
this.renderInProgress = false;
}
PDFPageProxy.prototype = {
/**
* @return {number} Page number of the page. First page is 1.
*/
get pageNumber() {
return this.pageInfo.pageIndex + 1;
},
/**
* @return {number} The number of degrees the page is rotated clockwise.
*/
get rotate() {
return this.pageInfo.rotate;
},
/**
* @return {object} The reference that points to this page. It has 'num' and
* 'gen' properties.
*/
get 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() {
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) {
if (arguments.length < 2)
rotate = this.rotate;
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() {
if (this.annotationsPromise)
return this.annotationsPromise;
var promise = new PDFJS.Promise();
this.annotationsPromise = promise;
this.transport.getAnnotations(this.pageInfo.pageIndex);
return promise;
},
/**
* 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) {
this.renderInProgress = true;
var promise = new Promise();
var stats = this.stats;
stats.time('Overall');
// If there is no displayReadyPromise yet, then the operatorList was never
// requested before. Make the request and create the promise.
if (!this.displayReadyPromise) {
this.displayReadyPromise = new Promise();
this.destroyed = false;
this.stats.time('Page Request');
this.transport.messageHandler.send('RenderPageRequest', {
pageIndex: this.pageNumber - 1
});
}
var self = this;
function complete(error) {
self.renderInProgress = false;
if (self.destroyed) {
delete self.operatorList;
delete self.displayReadyPromise;
}
if (error)
promise.reject(error);
else
promise.resolve();
};
// Once the operatorList and fonts are loaded, do the actual rendering.
this.displayReadyPromise.then(
function pageDisplayReadyPromise() {
if (self.destroyed) {
complete();
return;
}
var gfx = new CanvasGraphics(params.canvasContext,
this.objs, params.textLayer);
try {
this.display(gfx, params.viewport, complete);
} catch (e) {
complete(e);
}
}.bind(this),
function pageDisplayReadPromiseError(reason) {
complete(reason);
}
);
return promise;
},
/**
* For internal use only.
*/
startRenderingFromOperatorList:
function PDFPageWrapper_startRenderingFromOperatorList(operatorList,
fonts) {
var self = this;
this.operatorList = operatorList;
var displayContinuation = function pageDisplayContinuation() {
// Always defer call to display() to work around bug in
// Firefox error reporting from XHR callbacks.
setTimeout(function pageSetTimeout() {
self.displayReadyPromise.resolve();
});
};
this.ensureFonts(fonts,
function pageStartRenderingFromOperatorListEnsureFonts() {
displayContinuation();
}
);
},
/**
* For internal use only.
*/
ensureFonts: function PDFPageWrapper_ensureFonts(fonts, callback) {
this.stats.time('Font Loading');
// Convert the font names to the corresponding font obj.
for (var i = 0, ii = fonts.length; i < ii; i++) {
fonts[i] = this.objs.objs[fonts[i]].data;
}
// Load all the fonts
FontLoader.bind(
fonts,
function pageEnsureFontsFontObjs(fontObjs) {
this.stats.timeEnd('Font Loading');
callback.call(this);
}.bind(this)
);
},
/**
* For internal use only.
*/
display: function PDFPageWrapper_display(gfx, viewport, callback) {
var stats = this.stats;
stats.time('Rendering');
gfx.beginDrawing(viewport);
var startIdx = 0;
var length = this.operatorList.fnArray.length;
var operatorList = this.operatorList;
var stepper = null;
if (PDFJS.pdfBug && StepperManager.enabled) {
stepper = StepperManager.create(this.pageNumber - 1);
stepper.init(operatorList);
stepper.nextBreakPoint = stepper.getNextBreakPoint();
}
var self = this;
function next() {
startIdx =
gfx.executeOperatorList(operatorList, startIdx, next, stepper);
if (startIdx == length) {
gfx.endDrawing();
stats.timeEnd('Rendering');
stats.timeEnd('Overall');
if (callback) callback();
}
}
next();
},
/**
* Stub for future feature.
*/
getTextContent: function() {
var promise = new PDFJS.Promise();
var textContent = 'page text'; // not implemented
promise.resolve(textContent);
return promise;
},
/**
* Stub for future feature.
*/
getOperationList: function() {
var promise = new PDFJS.Promise();
var operationList = { // not implemented
dependencyFontsID: null,
operatorList: null
};
promise.resolve(operationList);
return promise;
},
/**
* Destroys resources allocated by the page.
*/
destroy: function() {
this.destroyed = true;
if (!this.renderInProgress) {
delete this.operatorList;
delete this.displayReadyPromise;
}
}
};
return PDFPageProxy;
})();
/**
* For internal use only.
*/
var WorkerTransport = (function WorkerTransportClosure() {
function WorkerTransport(promise) {
this.workerReadyPromise = promise;
this.objs = new PDFObjects();
this.pageCache = [];
this.pagePromises = [];
this.fontsLoading = {};
// If worker support isn't disabled explicit and the browser has worker
// support, create a new web worker and test if it/the browser fullfills
// all requirements to run parts of pdf.js in a web worker.
// Right now, the requirement is, that an Uint8Array is still an Uint8Array
// as it arrives on the worker. Chrome added this with version 15.
if (!globalScope.PDFJS.disableWorker && typeof Worker !== 'undefined') {
var workerSrc = PDFJS.workerSrc;
if (typeof workerSrc === 'undefined') {
error('No PDFJS.workerSrc specified');
}
try {
var worker;
if (PDFJS.isFirefoxExtension) {
// The firefox extension can't load the worker from the resource://
// url so we have to inline the script and then use the blob loader.
var bb = new MozBlobBuilder();
bb.append(document.querySelector('#PDFJS_SCRIPT_TAG').textContent);
var blobUrl = window.URL.createObjectURL(bb.getBlob());
worker = new Worker(blobUrl);
} else {
// Some versions of FF can't create a worker on localhost, see:
// https://bugzilla.mozilla.org/show_bug.cgi?id=683280
worker = new Worker(workerSrc);
}
var messageHandler = new MessageHandler('main', worker);
this.messageHandler = messageHandler;
messageHandler.on('test', function transportTest(supportTypedArray) {
if (supportTypedArray) {
this.worker = worker;
this.setupMessageHandler(messageHandler);
} else {
globalScope.PDFJS.disableWorker = true;
this.setupFakeWorker();
}
}.bind(this));
var testObj = new Uint8Array(1);
// Some versions of Opera throw a DATA_CLONE_ERR on
// serializing the typed array.
messageHandler.send('test', testObj);
return;
} catch (e) {
warn('The worker has been disabled.');
}
}
// Either workers are disabled, not supported or have thrown an exception.
// Thus, we fallback to a faked worker.
globalScope.PDFJS.disableWorker = true;
this.setupFakeWorker();
}
WorkerTransport.prototype = {
destroy: function WorkerTransport_destroy() {
if (this.worker)
this.worker.terminate();
this.pageCache = [];
this.pagePromises = [];
},
setupFakeWorker: function WorkerTransport_setupFakeWorker() {
// If we don't use a worker, just post/sendMessage to the main thread.
var fakeWorker = {
postMessage: function WorkerTransport_postMessage(obj) {
fakeWorker.onmessage({data: obj});
},
terminate: function WorkerTransport_terminate() {}
};
var messageHandler = new MessageHandler('main', fakeWorker);
this.setupMessageHandler(messageHandler);
// If the main thread is our worker, setup the handling for the messages
// the main thread sends to it self.
WorkerMessageHandler.setup(messageHandler);
},
setupMessageHandler:
function WorkerTransport_setupMessageHandler(messageHandler) {
this.messageHandler = messageHandler;
messageHandler.on('GetDoc', function transportDoc(data) {
var pdfInfo = data.pdfInfo;
var pdfDocument = new PDFDocumentProxy(pdfInfo, this);
this.pdfDocument = pdfDocument;
this.workerReadyPromise.resolve(pdfDocument);
}, this);
messageHandler.on('GetPage', function transportPage(data) {
var pageInfo = data.pageInfo;
var page = new PDFPageProxy(pageInfo, this);
this.pageCache[pageInfo.pageIndex] = page;
var promise = this.pagePromises[pageInfo.pageIndex];
promise.resolve(page);
}, 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) {
var page = this.pageCache[data.pageIndex];
var depFonts = data.depFonts;
page.stats.timeEnd('Page Request');
page.startRenderingFromOperatorList(data.operatorList, depFonts);
}, this);
messageHandler.on('obj', function transportObj(data) {
var id = data[0];
var type = data[1];
if (this.objs.hasData(id))
return;
switch (type) {
case 'JpegStream':
var imageData = data[2];
loadJpegStream(id, imageData, this.objs);
break;
case 'Image':
var imageData = data[2];
this.objs.resolve(id, imageData);
break;
case 'Font':
var name = data[2];
var file = data[3];
var properties = data[4];
if (file) {
// Rewrap the ArrayBuffer in a stream.
var fontFileDict = new Dict();
file = new Stream(file, 0, file.length, fontFileDict);
}
// At this point, only the font object is created but the font is
// not yet attached to the DOM. This is done in `FontLoader.bind`.
var font = new Font(name, file, properties);
this.objs.resolve(id, font);
break;
default:
error('Got unkown object type ' + type);
}
}, this);
messageHandler.on('PageError', function transportError(data) {
var page = this.pageCache[data.pageNum - 1];
if (page.displayReadyPromise)
page.displayReadyPromise.reject(data.error);
else
error(data.error);
}, this);
messageHandler.on('JpegDecode', function(data, promise) {
var imageData = data[0];
var components = data[1];
if (components != 3 && components != 1)
error('Only 3 component or 1 component can be returned');
var img = new Image();
img.onload = (function messageHandler_onloadClosure() {
var width = img.width;
var height = img.height;
var size = width * height;
var rgbaLength = size * 4;
var buf = new Uint8Array(size * components);
var tmpCanvas = createScratchCanvas(width, height);
var tmpCtx = tmpCanvas.getContext('2d');
tmpCtx.drawImage(img, 0, 0);
var data = tmpCtx.getImageData(0, 0, width, height).data;
if (components == 3) {
for (var i = 0, j = 0; i < rgbaLength; i += 4, j += 3) {
buf[j] = data[i];
buf[j + 1] = data[i + 1];
buf[j + 2] = data[i + 2];
}
} else if (components == 1) {
for (var i = 0, j = 0; i < rgbaLength; i += 4, j++) {
buf[j] = data[i];
}
}
promise.resolve({ data: buf, width: width, height: height});
}).bind(this);
var src = 'data:image/jpeg;base64,' + window.btoa(imageData);
img.src = src;
});
},
sendData: function WorkerTransport_sendData(data) {
this.messageHandler.send('GetDocRequest', data);
},
getPage: function WorkerTransport_getPage(pageNumber, promise) {
var pageIndex = pageNumber - 1;
if (pageIndex in this.pagePromises)
return this.pagePromises[pageIndex];
var promise = new PDFJS.Promise('Page ' + pageNumber);
this.pagePromises[pageIndex] = promise;
this.messageHandler.send('GetPageRequest', { pageIndex: pageIndex });
return promise;
},
getAnnotations: function WorkerTransport_getAnnotations(pageIndex) {
this.messageHandler.send('GetAnnotationsRequest',
{ pageIndex: pageIndex });
}
};
return WorkerTransport;
})();

View File

@ -241,27 +241,10 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
'shadingFill': true
},
beginDrawing: function CanvasGraphics_beginDrawing(mediaBox) {
var cw = this.ctx.canvas.width, ch = this.ctx.canvas.height;
beginDrawing: function CanvasGraphics_beginDrawing(viewport) {
var transform = viewport.transform;
this.ctx.save();
switch (mediaBox.rotate) {
case 0:
this.ctx.transform(1, 0, 0, -1, 0, ch);
break;
case 90:
this.ctx.transform(0, 1, 1, 0, 0, 0);
break;
case 180:
this.ctx.transform(-1, 0, 0, 1, cw, 0);
break;
case 270:
this.ctx.transform(0, -1, -1, 0, cw, ch);
break;
}
// Scale so that canvas units are the same as PDF user space units
this.ctx.scale(cw / mediaBox.width, ch / mediaBox.height);
// Move the media left-top corner to the (0,0) canvas position
this.ctx.translate(-mediaBox.x, -mediaBox.y);
this.ctx.transform.apply(this.ctx, transform);
if (this.textLayer)
this.textLayer.beginLayout();

View File

@ -63,8 +63,6 @@ var Page = (function PageClosure() {
function Page(xref, pageNumber, pageDict, ref) {
this.pageNumber = pageNumber;
this.pageDict = pageDict;
this.stats = new StatTimer();
this.stats.enabled = !!globalScope.PDFJS.enableStats;
this.xref = xref;
this.ref = ref;
@ -100,18 +98,10 @@ var Page = (function PageClosure() {
return shadow(this, 'mediaBox', obj);
},
get view() {
var cropBox = this.inheritPageProp('CropBox');
var view = {
x: 0,
y: 0,
width: this.width,
height: this.height
};
if (!isArray(cropBox) || cropBox.length !== 4)
return shadow(this, 'view', view);
var mediaBox = this.mediaBox;
var offsetX = mediaBox[0], offsetY = mediaBox[1];
var cropBox = this.inheritPageProp('CropBox');
if (!isArray(cropBox) || cropBox.length !== 4)
return shadow(this, 'view', mediaBox);
// From the spec, 6th ed., p.963:
// "The crop, bleed, trim, and art boxes should not ordinarily
@ -119,42 +109,13 @@ var Page = (function PageClosure() {
// effectively reduced to their intersection with the media box."
cropBox = Util.intersect(cropBox, mediaBox);
if (!cropBox)
return shadow(this, 'view', view);
return shadow(this, 'view', mediaBox);
var tl = this.rotatePoint(cropBox[0] - offsetX, cropBox[1] - offsetY);
var br = this.rotatePoint(cropBox[2] - offsetX, cropBox[3] - offsetY);
view.x = Math.min(tl.x, br.x);
view.y = Math.min(tl.y, br.y);
view.width = Math.abs(tl.x - br.x);
view.height = Math.abs(tl.y - br.y);
return shadow(this, 'view', view);
return shadow(this, 'view', cropBox);
},
get annotations() {
return shadow(this, 'annotations', this.inheritPageProp('Annots'));
},
get width() {
var mediaBox = this.mediaBox;
var rotate = this.rotate;
var width;
if (rotate == 0 || rotate == 180) {
width = (mediaBox[2] - mediaBox[0]);
} else {
width = (mediaBox[3] - mediaBox[1]);
}
return shadow(this, 'width', width);
},
get height() {
var mediaBox = this.mediaBox;
var rotate = this.rotate;
var height;
if (rotate == 0 || rotate == 180) {
height = (mediaBox[3] - mediaBox[1]);
} else {
height = (mediaBox[2] - mediaBox[0]);
}
return shadow(this, 'height', height);
},
get rotate() {
var rotate = this.inheritPageProp('Rotate') || 0;
// Normalize rotation so it's a multiple of 90 and between 0 and 270
@ -170,43 +131,19 @@ var Page = (function PageClosure() {
return shadow(this, 'rotate', rotate);
},
startRenderingFromOperatorList:
function Page_startRenderingFromOperatorList(operatorList, fonts) {
var self = this;
this.operatorList = operatorList;
var displayContinuation = function pageDisplayContinuation() {
// Always defer call to display() to work around bug in
// Firefox error reporting from XHR callbacks.
setTimeout(function pageSetTimeout() {
self.displayReadyPromise.resolve();
});
};
this.ensureFonts(fonts,
function pageStartRenderingFromOperatorListEnsureFonts() {
displayContinuation();
}
);
},
getOperatorList: function Page_getOperatorList(handler, dependency) {
if (this.operatorList) {
// content was compiled
return this.operatorList;
}
this.stats.time('Build IR Queue');
var xref = this.xref;
var content = this.content;
var resources = this.resources;
if (isArray(content)) {
// fetching items
var streams = [];
var i, n = content.length;
for (i = 0; i < n; ++i)
content[i] = xref.fetchIfRef(content[i]);
content = new StreamsSequenceStream(content);
streams.push(xref.fetchIfRef(content[i]));
content = new StreamsSequenceStream(streams);
} else if (isStream(content)) {
content.reset();
} else if (!content) {
// replacing non-existent page content with empty one
content = new Stream(new Uint8Array(0));
@ -215,82 +152,9 @@ var Page = (function PageClosure() {
var pe = this.pe = new PartialEvaluator(
xref, handler, 'p' + this.pageNumber + '_');
this.operatorList = pe.getOperatorList(content, resources, dependency);
this.stats.timeEnd('Build IR Queue');
return this.operatorList;
return pe.getOperatorList(content, resources, dependency);
},
ensureFonts: function Page_ensureFonts(fonts, callback) {
this.stats.time('Font Loading');
// Convert the font names to the corresponding font obj.
for (var i = 0, ii = fonts.length; i < ii; i++) {
fonts[i] = this.objs.objs[fonts[i]].data;
}
// Load all the fonts
FontLoader.bind(
fonts,
function pageEnsureFontsFontObjs(fontObjs) {
this.stats.timeEnd('Font Loading');
callback.call(this);
}.bind(this)
);
},
display: function Page_display(gfx, callback) {
var stats = this.stats;
stats.time('Rendering');
var xref = this.xref;
var resources = this.resources;
var mediaBox = this.mediaBox;
assertWellFormed(isDict(resources), 'invalid page resources');
gfx.xref = xref;
gfx.res = resources;
gfx.beginDrawing({ x: mediaBox[0], y: mediaBox[1],
width: this.width,
height: this.height,
rotate: this.rotate });
var startIdx = 0;
var length = this.operatorList.fnArray.length;
var operatorList = this.operatorList;
var stepper = null;
if (PDFJS.pdfBug && StepperManager.enabled) {
stepper = StepperManager.create(this.pageNumber);
stepper.init(operatorList);
stepper.nextBreakPoint = stepper.getNextBreakPoint();
}
var self = this;
function next() {
startIdx =
gfx.executeOperatorList(operatorList, startIdx, next, stepper);
if (startIdx == length) {
gfx.endDrawing();
stats.timeEnd('Rendering');
stats.timeEnd('Overall');
if (callback) callback();
}
}
next();
},
rotatePoint: function Page_rotatePoint(x, y, reverse) {
var rotate = reverse ? (360 - this.rotate) : this.rotate;
switch (rotate) {
case 180:
return {x: this.width - x, y: y};
case 90:
return {x: this.width - y, y: this.height - x};
case 270:
return {x: y, y: x};
case 360:
case 0:
default:
return {x: x, y: this.height - y};
}
},
getLinks: function Page_getLinks() {
var links = [];
var annotations = pageGetAnnotations();
@ -342,15 +206,10 @@ var Page = (function PageClosure() {
if (!isName(subtype))
continue;
var rect = annotation.get('Rect');
var topLeftCorner = this.rotatePoint(rect[0], rect[1]);
var bottomRightCorner = this.rotatePoint(rect[2], rect[3]);
var item = {};
item.type = subtype.name;
item.x = Math.min(topLeftCorner.x, bottomRightCorner.x);
item.y = Math.min(topLeftCorner.y, bottomRightCorner.y);
item.width = Math.abs(topLeftCorner.x - bottomRightCorner.x);
item.height = Math.abs(topLeftCorner.y - bottomRightCorner.y);
item.rect = rect;
switch (subtype.name) {
case 'Link':
var a = annotation.get('A');
@ -424,7 +283,8 @@ var Page = (function PageClosure() {
var title = annotation.get('T');
item.content = stringToPDFString(content || '');
item.title = stringToPDFString(title || '');
item.name = annotation.get('Name').name;
item.name = !annotation.has('Name') ? 'Note' :
annotation.get('Name').name;
break;
default:
TODO('unimplemented annotation type: ' + subtype.name);
@ -433,37 +293,6 @@ var Page = (function PageClosure() {
items.push(item);
}
return items;
},
startRendering: function Page_startRendering(ctx, callback, textLayer) {
var stats = this.stats;
stats.time('Overall');
// If there is no displayReadyPromise yet, then the operatorList was never
// requested before. Make the request and create the promise.
if (!this.displayReadyPromise) {
this.pdf.startRendering(this);
this.displayReadyPromise = new Promise();
}
// Once the operatorList and fonts are loaded, do the actual rendering.
this.displayReadyPromise.then(
function pageDisplayReadyPromise() {
var gfx = new CanvasGraphics(ctx, this.objs, textLayer);
try {
this.display(gfx, callback);
} catch (e) {
if (callback)
callback(e);
else
error(e);
}
}.bind(this),
function pageDisplayReadPromiseError(reason) {
if (callback)
callback(reason);
else
error(reason);
}
);
}
};
@ -471,20 +300,20 @@ var Page = (function PageClosure() {
})();
/**
* The `PDFDocModel` holds all the data of the PDF file. Compared to the
* The `PDFDocument` holds all the data of the PDF file. Compared to the
* `PDFDoc`, this one doesn't have any job management code.
* Right now there exists one PDFDocModel on the main thread + one object
* Right now there exists one PDFDocument on the main thread + one object
* for each worker. If there is no worker support enabled, there are two
* `PDFDocModel` objects on the main thread created.
* `PDFDocument` objects on the main thread created.
*/
var PDFDocModel = (function PDFDocModelClosure() {
function PDFDocModel(arg, callback) {
var PDFDocument = (function PDFDocumentClosure() {
function PDFDocument(arg, callback) {
if (isStream(arg))
init.call(this, arg);
else if (isArrayBuffer(arg))
init.call(this, new Stream(arg));
else
error('PDFDocModel: Unknown argument type');
error('PDFDocument: Unknown argument type');
}
function init(stream) {
@ -510,7 +339,7 @@ var PDFDocModel = (function PDFDocModelClosure() {
return true; /* found */
}
PDFDocModel.prototype = {
PDFDocument.prototype = {
get linearization() {
var length = this.stream.length;
var linearization = false;
@ -571,7 +400,7 @@ var PDFDocModel = (function PDFDocModelClosure() {
},
// Find the header, remove leading garbage and setup the stream
// starting from the header.
checkHeader: function PDFDocModel_checkHeader() {
checkHeader: function PDFDocument_checkHeader() {
var stream = this.stream;
stream.reset();
if (find(stream, '%PDF-', 1024)) {
@ -581,7 +410,7 @@ var PDFDocModel = (function PDFDocModelClosure() {
}
// May not be a PDF file, continue anyway.
},
setup: function PDFDocModel_setup(ownerPassword, userPassword) {
setup: function PDFDocument_setup(ownerPassword, userPassword) {
this.checkHeader();
var xref = new XRef(this.stream,
this.startXRef,
@ -595,7 +424,7 @@ var PDFDocModel = (function PDFDocModelClosure() {
// shadow the prototype getter
return shadow(this, 'numPages', num);
},
getDocumentInfo: function PDFDocModel_getDocumentInfo() {
getDocumentInfo: function PDFDocument_getDocumentInfo() {
var info;
if (this.xref.trailer.has('Info')) {
var infoDict = this.xref.trailer.get('Info');
@ -609,7 +438,7 @@ var PDFDocModel = (function PDFDocModelClosure() {
return shadow(this, 'getDocumentInfo', info);
},
getFingerprint: function PDFDocModel_getFingerprint() {
getFingerprint: function PDFDocument_getFingerprint() {
var xref = this.xref, fileID;
if (xref.trailer.has('ID')) {
fileID = '';
@ -630,251 +459,10 @@ var PDFDocModel = (function PDFDocModelClosure() {
return shadow(this, 'getFingerprint', fileID);
},
getPage: function PDFDocModel_getPage(n) {
getPage: function PDFDocument_getPage(n) {
return this.catalog.getPage(n);
}
};
return PDFDocModel;
return PDFDocument;
})();
var PDFDoc = (function PDFDocClosure() {
function PDFDoc(arg, callback) {
var stream = null;
var data = null;
if (isStream(arg)) {
stream = arg;
data = arg.bytes;
} else if (isArrayBuffer(arg)) {
stream = new Stream(arg);
data = arg;
} else {
error('PDFDoc: Unknown argument type');
}
this.data = data;
this.stream = stream;
this.pdfModel = new PDFDocModel(stream);
this.fingerprint = this.pdfModel.getFingerprint();
this.info = this.pdfModel.getDocumentInfo();
this.catalog = this.pdfModel.catalog;
this.objs = new PDFObjects();
this.pageCache = [];
this.fontsLoading = {};
this.workerReadyPromise = new Promise('workerReady');
// If worker support isn't disabled explicit and the browser has worker
// support, create a new web worker and test if it/the browser fullfills
// all requirements to run parts of pdf.js in a web worker.
// Right now, the requirement is, that an Uint8Array is still an Uint8Array
// as it arrives on the worker. Chrome added this with version 15.
if (!globalScope.PDFJS.disableWorker && typeof Worker !== 'undefined') {
var workerSrc = PDFJS.workerSrc;
if (typeof workerSrc === 'undefined') {
error('No PDFJS.workerSrc specified');
}
try {
var worker;
if (PDFJS.isFirefoxExtension) {
// The firefox extension can't load the worker from the resource://
// url so we have to inline the script and then use the blob loader.
var bb = new MozBlobBuilder();
bb.append(document.querySelector('#PDFJS_SCRIPT_TAG').textContent);
var blobUrl = window.URL.createObjectURL(bb.getBlob());
worker = new Worker(blobUrl);
} else {
// Some versions of FF can't create a worker on localhost, see:
// https://bugzilla.mozilla.org/show_bug.cgi?id=683280
worker = new Worker(workerSrc);
}
var messageHandler = new MessageHandler('main', worker);
messageHandler.on('test', function pdfDocTest(supportTypedArray) {
if (supportTypedArray) {
this.worker = worker;
this.setupMessageHandler(messageHandler);
} else {
globalScope.PDFJS.disableWorker = true;
this.setupFakeWorker();
}
}.bind(this));
var testObj = new Uint8Array(1);
// Some versions of Opera throw a DATA_CLONE_ERR on
// serializing the typed array.
messageHandler.send('test', testObj);
return;
} catch (e) {
warn('The worker has been disabled.');
}
}
// Either workers are disabled, not supported or have thrown an exception.
// Thus, we fallback to a faked worker.
globalScope.PDFJS.disableWorker = true;
this.setupFakeWorker();
}
PDFDoc.prototype = {
setupFakeWorker: function PDFDoc_setupFakeWorker() {
// If we don't use a worker, just post/sendMessage to the main thread.
var fakeWorker = {
postMessage: function PDFDoc_postMessage(obj) {
fakeWorker.onmessage({data: obj});
},
terminate: function PDFDoc_terminate() {}
};
var messageHandler = new MessageHandler('main', fakeWorker);
this.setupMessageHandler(messageHandler);
// If the main thread is our worker, setup the handling for the messages
// the main thread sends to it self.
WorkerMessageHandler.setup(messageHandler);
},
setupMessageHandler: function PDFDoc_setupMessageHandler(messageHandler) {
this.messageHandler = messageHandler;
messageHandler.on('page', function pdfDocPage(data) {
var pageNum = data.pageNum;
var page = this.pageCache[pageNum];
var depFonts = data.depFonts;
page.stats.timeEnd('Page Request');
page.startRenderingFromOperatorList(data.operatorList, depFonts);
}, this);
messageHandler.on('obj', function pdfDocObj(data) {
var id = data[0];
var type = data[1];
switch (type) {
case 'JpegStream':
var imageData = data[2];
loadJpegStream(id, imageData, this.objs);
break;
case 'Image':
var imageData = data[2];
this.objs.resolve(id, imageData);
break;
case 'Font':
var name = data[2];
var file = data[3];
var properties = data[4];
if (file) {
// Rewrap the ArrayBuffer in a stream.
var fontFileDict = new Dict();
file = new Stream(file, 0, file.length, fontFileDict);
}
// At this point, only the font object is created but the font is
// not yet attached to the DOM. This is done in `FontLoader.bind`.
var font = new Font(name, file, properties);
this.objs.resolve(id, font);
break;
default:
error('Got unkown object type ' + type);
}
}, this);
messageHandler.on('page_error', function pdfDocError(data) {
var page = this.pageCache[data.pageNum];
if (page.displayReadyPromise)
page.displayReadyPromise.reject(data.error);
else
error(data.error);
}, this);
messageHandler.on('jpeg_decode', function(data, promise) {
var imageData = data[0];
var components = data[1];
if (components != 3 && components != 1)
error('Only 3 component or 1 component can be returned');
var img = new Image();
img.onload = (function messageHandler_onloadClosure() {
var width = img.width;
var height = img.height;
var size = width * height;
var rgbaLength = size * 4;
var buf = new Uint8Array(size * components);
var tmpCanvas = createScratchCanvas(width, height);
var tmpCtx = tmpCanvas.getContext('2d');
tmpCtx.drawImage(img, 0, 0);
var data = tmpCtx.getImageData(0, 0, width, height).data;
if (components == 3) {
for (var i = 0, j = 0; i < rgbaLength; i += 4, j += 3) {
buf[j] = data[i];
buf[j + 1] = data[i + 1];
buf[j + 2] = data[i + 2];
}
} else if (components == 1) {
for (var i = 0, j = 0; i < rgbaLength; i += 4, j++) {
buf[j] = data[i];
}
}
promise.resolve({ data: buf, width: width, height: height});
}).bind(this);
var src = 'data:image/jpeg;base64,' + window.btoa(imageData);
img.src = src;
});
setTimeout(function pdfDocFontReadySetTimeout() {
messageHandler.send('doc', this.data);
this.workerReadyPromise.resolve(true);
}.bind(this));
},
get numPages() {
return this.pdfModel.numPages;
},
startRendering: function PDFDoc_startRendering(page) {
// The worker might not be ready to receive the page request yet.
this.workerReadyPromise.then(function pdfDocStartRenderingThen() {
page.stats.time('Page Request');
this.messageHandler.send('page_request', page.pageNumber + 1);
}.bind(this));
},
getPage: function PDFDoc_getPage(n) {
if (this.pageCache[n])
return this.pageCache[n];
var page = this.pdfModel.getPage(n);
// Add a reference to the objects such that Page can forward the reference
// to the CanvasGraphics and so on.
page.objs = this.objs;
page.pdf = this;
return (this.pageCache[n] = page);
},
destroy: function PDFDoc_destroy() {
if (this.worker)
this.worker.terminate();
if (this.fontWorker)
this.fontWorker.terminate();
for (var n in this.pageCache)
delete this.pageCache[n];
delete this.data;
delete this.stream;
delete this.pdf;
delete this.catalog;
}
};
return PDFDoc;
})();
globalScope.PDFJS.PDFDoc = PDFDoc;

View File

@ -153,13 +153,14 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
font = xref.fetchIfRef(font) || fontRes.get(fontName);
assertWellFormed(isDict(font));
++self.objIdCounter;
if (!font.translated) {
font.translated = self.translateFont(font, xref, resources,
dependency);
if (font.translated) {
// keep track of each font we translated so the caller can
// load them asynchronously before calling display on a page
loadedName = 'font_' + uniquePrefix + (++self.objIdCounter);
loadedName = 'font_' + uniquePrefix + self.objIdCounter;
font.translated.properties.loadedName = loadedName;
font.loadedName = loadedName;
@ -466,7 +467,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
args = [];
} else if (obj != null) {
assertWellFormed(args.length <= 33, 'Too many arguments');
args.push(obj);
args.push(obj instanceof Dict ? obj.getAll() : obj);
}
}
@ -862,7 +863,6 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
properties.coded = true;
var charProcs = dict.get('CharProcs').getAll();
var fontResources = dict.get('Resources') || resources;
properties.resources = fontResources;
properties.charProcOperatorList = {};
for (var key in charProcs) {
var glyphStream = charProcs[key];

View File

@ -766,7 +766,6 @@ var Font = (function FontClosure() {
this.name = name;
this.coded = properties.coded;
this.charProcOperatorList = properties.charProcOperatorList;
this.resources = properties.resources;
this.sizes = [];
var names = name.split('+');
@ -1727,6 +1726,16 @@ var Font = (function FontClosure() {
properties.glyphNames = glyphNames;
}
function isOS2Valid(os2Table) {
var data = os2Table.data;
// usWinAscent == 0 makes font unreadable by windows
var usWinAscent = (data[74] << 8) | data[75];
if (usWinAscent == 0)
return false;
return true;
}
// Check that required tables are present
var requiredTables = ['OS/2', 'cmap', 'head', 'hhea',
'hmtx', 'maxp', 'name', 'post'];
@ -1734,7 +1743,7 @@ var Font = (function FontClosure() {
var header = readOpenTypeHeader(font);
var numTables = header.numTables;
var cmap, post, maxp, hhea, hmtx, vhea, vmtx, head, loca, glyf;
var cmap, post, maxp, hhea, hmtx, vhea, vmtx, head, loca, glyf, os2;
var tables = [];
for (var i = 0; i < numTables; i++) {
var table = readTableEntry(font);
@ -1752,6 +1761,8 @@ var Font = (function FontClosure() {
hmtx = table;
else if (table.tag == 'head')
head = table;
else if (table.tag == 'OS/2')
os2 = table;
requiredTables.splice(index, 1);
} else {
@ -1767,7 +1778,7 @@ var Font = (function FontClosure() {
tables.push(table);
}
var numTables = header.numTables + requiredTables.length;
var numTables = tables.length + requiredTables.length;
// header and new offsets. Table entry information is appended to the
// end of file. The virtualOffset represents where to put the actual
@ -1781,21 +1792,10 @@ var Font = (function FontClosure() {
// of missing tables
createOpenTypeHeader(header.version, ttf, numTables);
if (requiredTables.indexOf('OS/2') != -1) {
// extract some more font properties from the OpenType head and
// hhea tables; yMin and descent value are always negative
var override = {
unitsPerEm: int16([head.data[18], head.data[19]]),
yMax: int16([head.data[42], head.data[43]]),
yMin: int16([head.data[38], head.data[39]]) - 0x10000,
ascent: int16([hhea.data[4], hhea.data[5]]),
descent: int16([hhea.data[6], hhea.data[7]]) - 0x10000
};
tables.push({
tag: 'OS/2',
data: stringToArray(createOS2Table(properties, null, override))
});
// Invalid OS/2 can break the font for the Windows
if (os2 && !isOS2Valid(os2)) {
tables.splice(tables.indexOf(os2), 1);
os2 = null;
}
// Ensure the [h/v]mtx tables contains the advance width and
@ -2076,6 +2076,23 @@ var Font = (function FontClosure() {
}
this.unicodeIsEnabled = unicodeIsEnabled;
if (!os2) {
// extract some more font properties from the OpenType head and
// hhea tables; yMin and descent value are always negative
var override = {
unitsPerEm: int16([head.data[18], head.data[19]]),
yMax: int16([head.data[42], head.data[43]]),
yMin: int16([head.data[38], head.data[39]]) - 0x10000,
ascent: int16([hhea.data[4], hhea.data[5]]),
descent: int16([hhea.data[6], hhea.data[7]]) - 0x10000
};
tables.push({
tag: 'OS/2',
data: stringToArray(createOS2Table(properties, glyphs, override))
});
}
// Rewrite the 'post' table if needed
if (requiredTables.indexOf('post') != -1) {
tables.push({

View File

@ -15,7 +15,7 @@ var PDFImage = (function PDFImageClosure() {
var colorSpace = dict.get('ColorSpace', 'CS');
colorSpace = ColorSpace.parse(colorSpace, xref, res);
var numComps = colorSpace.numComps;
handler.send('jpeg_decode', [image.getIR(), numComps], function(message) {
handler.send('JpegDecode', [image.getIR(), numComps], function(message) {
var data = message.data;
var stream = new Stream(data, 0, data.length, image.dict);
promise.resolve(stream);

View File

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

View File

@ -249,9 +249,12 @@ var Parser = (function ParserClosure() {
if (name == 'CCITTFaxDecode' || name == 'CCF') {
return new CCITTFaxStream(stream, params);
}
if (name == 'RunLengthDecode') {
if (name == 'RunLengthDecode' || name == 'RL') {
return new RunLengthStream(stream);
}
if (name == 'JBIG2Decode') {
error('JBIG2 image format is not currently supprted.');
}
warn('filter "' + name + '" not supported yet');
return stream;
}

View File

@ -76,7 +76,7 @@ function stringToBytes(str) {
var IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0];
var Util = (function UtilClosure() {
var Util = PDFJS.Util = (function UtilClosure() {
function Util() {}
Util.makeCssRgb = function Util_makeCssRgb(r, g, b) {
@ -97,6 +97,19 @@ var Util = (function UtilClosure() {
return [xt, yt];
};
Util.applyInverseTransform = function Util_applyTransform(p, m) {
var d = m[0] * m[3] - m[1] * m[2];
var xt = (p[0] * m[3] - p[1] * m[2] + m[2] * m[5] - m[4] * m[3]) / d;
var yt = (-p[0] * m[1] + p[1] * m[0] + m[4] * m[1] - m[5] * m[0]) / d;
return [xt, yt];
};
Util.inverseTransform = function Util_inverseTransform(m) {
var d = m[0] * m[3] - m[1] * m[2];
return [m[3] / d, -m[1] / d, -m[2] / d, m[0] / d,
(m[2] * m[5] - m[4] * m[3]) / d, (m[4] * m[1] - m[5] * m[0]) / d];
};
// Apply a generic 3d matrix M on a 3-vector v:
// | a b c | | X |
// | d e f | x | Y |
@ -165,7 +178,7 @@ var Util = (function UtilClosure() {
}
return result;
}
};
Util.sign = function Util_sign(num) {
return num < 0 ? -1 : 1;
@ -174,6 +187,80 @@ var Util = (function UtilClosure() {
return Util;
})();
var PageViewport = PDFJS.PageViewport = (function PageViewportClosure() {
function PageViewport(viewBox, scale, rotate, offsetX, offsetY) {
// creating transform to convert pdf coordinate system to the normal
// canvas like coordinates taking in account scale and rotation
var centerX = (viewBox[2] + viewBox[0]) / 2;
var centerY = (viewBox[3] + viewBox[1]) / 2;
var rotateA, rotateB, rotateC, rotateD;
switch (rotate) {
case -180:
case 180:
rotateA = -1; rotateB = 0; rotateC = 0; rotateD = 1;
break;
case -270:
case 90:
rotateA = 0; rotateB = 1; rotateC = 1; rotateD = 0;
break;
case -90:
case 270:
rotateA = 0; rotateB = -1; rotateC = -1; rotateD = 0;
break;
case 360:
case 0:
default:
rotateA = 1; rotateB = 0; rotateC = 0; rotateD = -1;
break;
}
var offsetCanvasX, offsetCanvasY;
var width, height;
if (rotateA == 0) {
offsetCanvasX = Math.abs(centerY - viewBox[1]) * scale + offsetX;
offsetCanvasY = Math.abs(centerX - viewBox[0]) * scale + offsetY;
width = Math.abs(viewBox[3] - viewBox[1]) * scale;
height = Math.abs(viewBox[2] - viewBox[0]) * scale;
} else {
offsetCanvasX = Math.abs(centerX - viewBox[0]) * scale + offsetX;
offsetCanvasY = Math.abs(centerY - viewBox[1]) * scale + offsetY;
width = Math.abs(viewBox[2] - viewBox[0]) * scale;
height = Math.abs(viewBox[3] - viewBox[1]) * scale;
}
// creating transform for the following operations:
// translate(-centerX, -centerY), rotate and flip vertically,
// scale, and translate(offsetCanvasX, offsetCanvasY)
this.transform = [
rotateA * scale,
rotateB * scale,
rotateC * scale,
rotateD * scale,
offsetCanvasX - rotateA * scale * centerX - rotateC * scale * centerY,
offsetCanvasY - rotateB * scale * centerX - rotateD * scale * centerY
];
this.offsetX = offsetX;
this.offsetY = offsetY;
this.width = width;
this.height = height;
this.fontScale = scale;
}
PageViewport.prototype = {
convertToViewportPoint: function PageViewport_convertToViewportPoint(x, y) {
return Util.applyTransform([x, y], this.transform);
},
convertToViewportRectangle:
function PageViewport_convertToViewportRectangle(rect) {
var tl = Util.applyTransform([rect[0], rect[1]], this.transform);
var br = Util.applyTransform([rect[2], rect[3]], this.transform);
return [tl[0], tl[1], br[0], br[1]];
},
convertToPdfPoint: function PageViewport_convertToPdfPoint(x, y) {
return Util.applyInverseTransform([x, y], this.transform);
}
};
return PageViewport;
})();
var PDFStringTranslateTable = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0x2D8, 0x2C7, 0x2C6, 0x2D9, 0x2DD, 0x2DB, 0x2DA, 0x2DC, 0, 0, 0, 0, 0, 0, 0,
@ -275,7 +362,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 +384,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 +400,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 +464,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 +488,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 +505,9 @@ var Promise = (function PromiseClosure() {
if (errback)
this.errbacks.push(errback);
}
if (progressback)
this.progressbacks.push(progressback);
}
};

View File

@ -85,14 +85,43 @@ var WorkerMessageHandler = {
handler.send('test', data instanceof Uint8Array);
});
handler.on('doc', function wphSetupDoc(data) {
handler.on('GetDocRequest', function wphSetupDoc(data) {
// Create only the model of the PDFDoc, which is enough for
// processing the content of the pdf.
pdfModel = new PDFDocModel(new Stream(data));
pdfModel = new PDFDocument(new Stream(data));
var doc = {
numPages: pdfModel.numPages,
fingerprint: pdfModel.getFingerprint(),
destinations: pdfModel.catalog.destinations,
outline: pdfModel.catalog.documentOutline,
info: pdfModel.getDocumentInfo(),
metadata: pdfModel.catalog.metadata
};
handler.send('GetDoc', {pdfInfo: doc});
});
handler.on('page_request', function wphSetupPageRequest(pageNum) {
pageNum = parseInt(pageNum);
handler.on('GetPageRequest', function wphSetupGetPage(data) {
var pageNumber = data.pageIndex + 1;
var pdfPage = pdfModel.getPage(pageNumber);
var page = {
pageIndex: data.pageIndex,
rotate: pdfPage.rotate,
ref: pdfPage.ref,
view: pdfPage.view
};
handler.send('GetPage', {pageInfo: page});
});
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;
// The following code does quite the same as
@ -130,7 +159,7 @@ var WorkerMessageHandler = {
};
}
handler.send('page_error', {
handler.send('PageError', {
pageNum: pageNum,
error: e
});
@ -148,9 +177,8 @@ var WorkerMessageHandler = {
fonts[dep] = true;
}
}
handler.send('page', {
pageNum: pageNum,
handler.send('RenderPage', {
pageIndex: data.pageIndex,
operatorList: operatorList,
depFonts: Object.keys(fonts)
});

View File

@ -10,7 +10,7 @@
// Disable worker support for running test as
// https://github.com/mozilla/pdf.js/pull/764#issuecomment-2638944
// "firefox-bin: Fatal IO error 12 (Cannot allocate memory) on X server :1."
PDFJS.disableWorker = true;
// PDFJS.disableWorker = true;
var appPath, browser, canvas, currentTaskIdx, manifest, stdout;
var inFlightRequests = 0;
@ -100,13 +100,24 @@ function nextTask() {
getPdf(task.file, function nextTaskGetPdf(data) {
var failure;
function continuation() {
task.pageNum = task.firstPage || 1;
nextPage(task, failure);
}
try {
task.pdfDoc = new PDFJS.PDFDoc(data);
var promise = PDFJS.getDocument(data);
promise.then(function(doc) {
task.pdfDoc = doc;
continuation();
}, function(e) {
failure = 'load PDF doc : ' + e;
continuation();
});
return;
} catch (e) {
failure = 'load PDF doc : ' + exceptionToString(e);
}
task.pageNum = task.firstPage || 1;
nextPage(task, failure);
continuation();
});
}
@ -163,45 +174,45 @@ function nextPage(task, loadError) {
log(' loading page ' + task.pageNum + '/' + task.pdfDoc.numPages +
'... ');
var ctx = canvas.getContext('2d');
page = task.pdfDoc.getPage(task.pageNum);
task.pdfDoc.getPage(task.pageNum).then(function(page) {
var pdfToCssUnitsCoef = 96.0 / 72.0;
var viewport = page.getViewport(pdfToCssUnitsCoef);
canvas.width = viewport.width;
canvas.height = viewport.height;
clear(ctx);
var pdfToCssUnitsCoef = 96.0 / 72.0;
// using mediaBox for the canvas size
var pageWidth = page.width;
var pageHeight = page.height;
canvas.width = pageWidth * pdfToCssUnitsCoef;
canvas.height = pageHeight * pdfToCssUnitsCoef;
clear(ctx);
// using the text layer builder that does nothing to test
// text layer creation operations
var textLayerBuilder = {
beginLayout: function nullTextLayerBuilderBeginLayout() {},
endLayout: function nullTextLayerBuilderEndLayout() {},
appendText: function nullTextLayerBuilderAppendText(text, fontName,
fontSize) {}
};
page.startRendering(
ctx,
function nextPageStartRendering(error) {
var failureMessage = false;
if (error)
failureMessage = 'render : ' + error.message;
snapshotCurrentPage(task, failureMessage);
// using the text layer builder that does nothing to test
// text layer creation operations
var textLayerBuilder = {
beginLayout: function nullTextLayerBuilderBeginLayout() {},
endLayout: function nullTextLayerBuilderEndLayout() {},
appendText: function nullTextLayerBuilderAppendText(text, fontName,
fontSize) {}
};
var renderContext = {
canvasContext: ctx,
textLayer: textLayerBuilder,
viewport: viewport
};
var completeRender = (function(error) {
page.destroy();
snapshotCurrentPage(task, error);
});
page.render(renderContext).then(function() {
completeRender(false);
},
textLayerBuilder
);
function(error) {
completeRender('render : ' + error);
});
},
function(error) {
snapshotCurrentPage(task, 'render : ' + error);
});
} catch (e) {
failure = 'page setup : ' + exceptionToString(e);
snapshotCurrentPage(task, failure);
}
}
if (failure) {
// Skip right to snapshotting if there was a failure, since the
// fonts might be in an inconsistent state.
snapshotCurrentPage(task, failure);
}
}
function snapshotCurrentPage(task, failure) {

View File

@ -31,4 +31,5 @@
!issue1002.pdf
!issue925.pdf
!gradientfill.pdf
!basicapi.pdf
!mixedfonts.pdf

BIN
test/pdfs/basicapi.pdf Normal file

Binary file not shown.

BIN
test/pdfs/mixedfonts.pdf Normal file

Binary file not shown.

View File

@ -16,6 +16,7 @@ BROWSERLOG_FILE = 'browser.log'
REFDIR = 'ref'
TMPDIR = 'tmp'
VERBOSE = False
BROWSER_TIMEOUT = 60
SERVER_HOST = "localhost"
@ -74,7 +75,7 @@ class State:
browsers = [ ]
manifest = { }
taskResults = { }
remaining = 0
remaining = { }
results = { }
done = False
numErrors = 0
@ -83,6 +84,7 @@ class State:
numFBFFailures = 0
numLoadFailures = 0
eqLog = None
lastPost = { }
class Result:
def __init__(self, snapshot, failure, page):
@ -180,6 +182,7 @@ class PDFTestHandler(BaseHTTPRequestHandler):
result = json.loads(self.rfile.read(numBytes))
browser, id, failure, round, page, snapshot = result['browser'], result['id'], result['failure'], result['round'], result['page'], result['snapshot']
State.lastPost[browser] = int(time.time())
taskResults = State.taskResults[browser][id]
taskResults[round].append(Result(snapshot, failure, page))
@ -199,9 +202,16 @@ class PDFTestHandler(BaseHTTPRequestHandler):
self.server.masterMode)
# Please oh please GC this ...
del State.taskResults[browser][id]
State.remaining -= 1
State.remaining[browser] -= 1
State.done = (0 == State.remaining)
checkIfDone()
def checkIfDone():
State.done = True
for key in State.remaining:
if State.remaining[key] != 0:
State.done = False
return
# Applescript hack to quit Chrome on Mac
def tellAppToQuit(path, query):
@ -376,6 +386,8 @@ def setUp(options):
for b in testBrowsers:
State.taskResults[b.name] = { }
State.remaining[b.name] = len(manifestList)
State.lastPost[b.name] = int(time.time())
for item in manifestList:
id, rounds = item['id'], int(item['rounds'])
State.manifest[id] = item
@ -384,8 +396,6 @@ def setUp(options):
taskResults.append([ ])
State.taskResults[b.name][id] = taskResults
State.remaining = len(testBrowsers) * len(manifestList)
return testBrowsers
def startBrowsers(browsers, options):
@ -568,6 +578,12 @@ def runTests(options, browsers):
try:
startBrowsers(browsers, options)
while not State.done:
for b in State.lastPost:
if State.remaining[b] > 0 and int(time.time()) - State.lastPost[b] > BROWSER_TIMEOUT:
print 'TEST-UNEXPECTED-FAIL | test failed', b, "has not responded in", BROWSER_TIMEOUT, "s"
State.numErrors += State.remaining[b]
State.remaining[b] = 0
checkIfDone()
time.sleep(1)
processResults()
finally:

View File

@ -19,7 +19,7 @@
},
{ "id": "intelisa-eq",
"file": "pdfs/intelisa.pdf",
"md5": "83032ccbfdc5a66269b1403971110abe",
"md5": "c1444b7ccd935c0577d094297e1a6448",
"link": true,
"pageLimit": 100,
"rounds": 1,
@ -29,12 +29,13 @@
"file": "pdfs/pdf.pdf",
"md5": "dbdb23c939d2be09b43126c3c56060c7",
"link": true,
"pageLimit": 500,
"rounds": 1,
"type": "load"
},
{ "id": "shavian-load",
"file": "pdfs/shavian.pdf",
"md5": "4fabf0a03e82693007435020bc446f9b",
"md5": "79253352f48b55b7fa28a2586875d8b7",
"link": true,
"rounds": 1,
"type": "load"
@ -241,10 +242,10 @@
"skipPages": [ 16 ],
"type": "load"
},
{ "id": "tcpdf_033",
"file": "pdfs/tcpdf_033.pdf",
"md5": "861294df58d185aae80919173f2732ff",
"link": true,
{ "id": "mixedfonts",
"file": "pdfs/mixedfonts.pdf",
"md5": "a582b83fa1b3a25a6f13803a367c71ec",
"link": false,
"rounds": 1,
"type": "eq"
},

View File

@ -5,6 +5,7 @@
<style type="text/css"></style>
<script type="text/javascript" src="/src/core.js"></script>
<script type="text/javascript" src="/src/util.js"></script>
<script type="text/javascript" src="/src/api.js"></script>
<script type="text/javascript" src="/src/canvas.js"></script>
<script type="text/javascript" src="/src/obj.js"></script>
<script type="text/javascript" src="/src/function.js"></script>

109
test/unit/api_spec.js Normal file
View File

@ -0,0 +1,109 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
'use strict';
describe('api', function() {
// TODO run with worker enabled
PDFJS.disableWorker = true;
var basicApiUrl = '/basicapi.pdf';
function waitsForPromise(promise) {
waitsFor(function() {
return promise.isResolved || promise.isRejected;
}, 250);
}
function expectAfterPromise(promise, successCallback) {
waitsForPromise(promise);
runs(function() {
promise.then(successCallback,
function(error, e) {
// Shouldn't get here.
expect(false).toEqual(true);
});
});
}
describe('PDFJS', function() {
describe('getDocument', function() {
it('creates pdf doc from URL', function() {
console.log('what is');
debugger;
var promise = PDFJS.getDocument(basicApiUrl);
expectAfterPromise(promise, function(data) {
expect(true).toEqual(true);
});
});
/*
it('creates pdf doc from typed array', function() {
// TODO
});
*/
});
});
describe('PDFDocument', function() {
var promise = PDFJS.getDocument(basicApiUrl);
waitsForPromise(promise);
var doc;
runs(function() {
promise.then(function(data) { doc = data; });
});
it('gets number of pages', function() {
expect(doc.numPages).toEqual(3);
});
it('gets fingerprint', function() {
expect(typeof doc.fingerprint).toEqual('string');
});
it('gets page', function() {
var promise = doc.getPage(1);
expectAfterPromise(promise, function(data) {
expect(true).toEqual(true);
});
});
it('gets destinations', function() {
var promise = doc.getDestinations();
expectAfterPromise(promise, function(data) {
// TODO this seems to be broken for the test pdf
});
});
it('gets outline', function() {
var promise = doc.getOutline();
expectAfterPromise(promise, function(outline) {
// Two top level entries.
expect(outline.length).toEqual(2);
// Make sure some basic attributes are set.
expect(outline[1].title).toEqual('Chapter 1');
expect(outline[1].items.length).toEqual(1);
expect(outline[1].items[0].title).toEqual('Paragraph 1.1');
});
});
it('gets metadata', function() {
var promise = doc.getMetadata();
expectAfterPromise(promise, function(metadata) {
expect(metadata.info['Title']).toEqual('Basic API Test');
expect(metadata.metadata.get('dc:title')).toEqual('Basic API Test');
});
});
});
describe('Page', function() {
var promise = new Promise();
PDFJS.getDocument(basicApiUrl).then(function(doc) {
doc.getPage(1).then(function(data) {
promise.resolve(data);
});
});
waitsForPromise(promise);
var page;
runs(function() {
promise.then(function(data) {
page = data;
});
});
it('gets ref', function() {
expect(page.ref).toEqual({num: 15, gen: 0});
});
// TODO rotate
// TODO viewport
// TODO annotaions
// TOOD text content
// TODO operation list
});
});

View File

@ -1,32 +1,39 @@
server: http://localhost:4224
load:
- ../../external/jasmine/jasmine.js
- ../../external/jasmineAdapter/JasmineAdapter.js
- ../../src/obj.js
- ../../src/core.js
- ../../src/util.js
- ../../src/canvas.js
- ../../src/obj.js
- ../../src/function.js
- ../../src/charsets.js
- ../../src/cidmaps.js
- ../../src/colorspace.js
- ../../src/crypto.js
- ../../src/evaluator.js
- ../../src/fonts.js
- ../../src/glyphlist.js
- ../../src/image.js
- ../../src/metrics.js
- ../../src/parser.js
- ../../src/pattern.js
- ../../src/stream.js
- ../../src/worker.js
- ../../src/bidi.js
- ../../external/jpgjs/jpg.js
- ../unit/obj_spec.js
- ../unit/font_spec.js
- ../unit/function_spec.js
- ../unit/crypto_spec.js
- ../unit/stream_spec.js
basepath: ..
load:
- ../external/jasmine/jasmine.js
- ../external/jasmineAdapter/JasmineAdapter.js
- ../src/obj.js
- ../src/core.js
- ../src/util.js
- ../src/api.js
- ../src/canvas.js
- ../src/obj.js
- ../src/function.js
- ../src/charsets.js
- ../src/cidmaps.js
- ../src/colorspace.js
- ../src/crypto.js
- ../src/evaluator.js
- ../src/fonts.js
- ../src/glyphlist.js
- ../src/image.js
- ../src/metrics.js
- ../src/parser.js
- ../src/pattern.js
- ../src/stream.js
- ../src/worker.js
- ../src/bidi.js
- ../src/metadata.js
- ../external/jpgjs/jpg.js
- unit/obj_spec.js
- unit/font_spec.js
- unit/function_spec.js
- unit/crypto_spec.js
- unit/stream_spec.js
- unit/api_spec.js
gateway:
- {matcher: "*.pdf", server: "http://localhost:8888/test/pdfs/"}

View File

@ -234,3 +234,21 @@
console = {log: function() {}};
}
})();
// Check onclick compatibility in Opera
(function checkOnClickCompatibility() {
// workaround for reported Opera bug DSK-354448:
// onclick fires on disabled buttons with opaque content
function ignoreIfTargetDisabled(event) {
if (isDisabled(event.target)) {
event.stopPropagation();
}
}
function isDisabled(node) {
return node.disabled || (node.parentNode && isDisabled(node.parentNode));
}
if (navigator.userAgent.indexOf('Opera') != -1) {
// use browser detection since we cannot feature-check this bug
document.addEventListener('click', ignoreIfTargetDisabled, true);
}
})();

View File

@ -163,29 +163,29 @@ var StepperManager = (function StepperManagerClosure() {
enabled: false,
active: false,
// Stepper specific functions.
create: function create(pageNumber) {
create: function create(pageIndex) {
var debug = document.createElement('div');
debug.id = 'stepper' + pageNumber;
debug.id = 'stepper' + pageIndex;
debug.setAttribute('hidden', true);
debug.className = 'stepper';
stepperDiv.appendChild(debug);
var b = document.createElement('option');
b.textContent = 'Page ' + (pageNumber + 1);
b.value = pageNumber;
b.textContent = 'Page ' + (pageIndex + 1);
b.value = pageIndex;
stepperChooser.appendChild(b);
var initBreakPoints = breakPoints[pageNumber] || [];
var stepper = new Stepper(debug, pageNumber, initBreakPoints);
var initBreakPoints = breakPoints[pageIndex] || [];
var stepper = new Stepper(debug, pageIndex, initBreakPoints);
steppers.push(stepper);
if (steppers.length === 1)
this.selectStepper(pageNumber, false);
this.selectStepper(pageIndex, false);
return stepper;
},
selectStepper: function selectStepper(pageNumber, selectPanel) {
selectStepper: function selectStepper(pageIndex, selectPanel) {
if (selectPanel)
this.manager.selectPanel(1);
for (var i = 0; i < steppers.length; ++i) {
var stepper = steppers[i];
if (stepper.pageNumber == pageNumber)
if (stepper.pageIndex == pageIndex)
stepper.panel.removeAttribute('hidden');
else
stepper.panel.setAttribute('hidden', true);
@ -193,11 +193,11 @@ var StepperManager = (function StepperManagerClosure() {
var options = stepperChooser.options;
for (var i = 0; i < options.length; ++i) {
var option = options[i];
option.selected = option.value == pageNumber;
option.selected = option.value == pageIndex;
}
},
saveBreakPoints: function saveBreakPoints(pageNumber, bps) {
breakPoints[pageNumber] = bps;
saveBreakPoints: function saveBreakPoints(pageIndex, bps) {
breakPoints[pageIndex] = bps;
sessionStorage.setItem('pdfjsBreakPoints', JSON.stringify(breakPoints));
}
};
@ -205,12 +205,12 @@ var StepperManager = (function StepperManagerClosure() {
// The stepper for each page's IRQueue.
var Stepper = (function StepperClosure() {
function Stepper(panel, pageNumber, initialBreakPoints) {
function Stepper(panel, pageIndex, initialBreakPoints) {
this.panel = panel;
this.len;
this.breakPoint = 0;
this.nextBreakPoint = null;
this.pageNumber = pageNumber;
this.pageIndex = pageIndex;
this.breakPoints = initialBreakPoints;
this.currentIdx = -1;
}
@ -256,7 +256,7 @@ var Stepper = (function StepperClosure() {
self.breakPoints.push(x);
else
self.breakPoints.splice(self.breakPoints.indexOf(x), 1);
StepperManager.saveBreakPoints(self.pageNumber, self.breakPoints);
StepperManager.saveBreakPoints(self.pageIndex, self.breakPoints);
}
})(i);
@ -278,7 +278,7 @@ var Stepper = (function StepperClosure() {
return null;
},
breakIt: function breakIt(idx, callback) {
StepperManager.selectStepper(this.pageNumber, true);
StepperManager.selectStepper(this.pageIndex, true);
var self = this;
var dom = document;
self.currentIdx = idx;

46
web/images/text.svg Normal file
View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<svg
xmlns:cc="http://creativecommons.org/ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
height="40"
width="40"
id="svg2995"
version="1.1">
<rect
style="fill:#f1e47b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-opacity:1"
id="rect3009"
width="30.169491"
height="24.576269"
x="4.237288"
y="6.7796612" />
<rect
style="fill:#000000;fill-opacity:1;stroke:none"
id="rect3781"
width="23.38983"
height="1.1864407"
x="7.6271186"
y="11.389831" />
<rect
style="fill:#000000;fill-opacity:1;stroke:none"
id="rect3781-1"
width="23.38983"
height="0.67796612"
x="7.6271191"
y="21.61017" />
<rect
style="fill:#000000;fill-opacity:1;stroke:none"
id="rect3781-7"
width="23.38983"
height="0.67796612"
x="7.4576273"
y="26.152542" />
<rect
style="fill:#000000;fill-opacity:1;stroke:none"
id="rect3781-1-4"
width="23.38983"
height="0.67796612"
x="7.6271186"
y="17.033899" />
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1,6 +1,6 @@
<html>
<head>
<title>PDF viewer</title>
<title>PDF.js viewer</title>
<!-- PDFJSSCRIPT_INCLUDE_FIREFOX_EXTENSION -->
<link rel="stylesheet" href="viewer.css"/>
@ -10,6 +10,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 -->
@ -34,9 +35,8 @@
<script type="text/javascript" src="debugger.js"></script>
<script type="text/javascript" src="viewer.js"></script>
</head>
<body>
<div id="outerContainer">
<div class="toolbar">

View File

@ -32,7 +32,7 @@ var Cache = function cacheCache(size) {
data.splice(i);
data.push(view);
if (data.length > size)
data.shift().update();
data.shift().destroy();
};
};
@ -247,9 +247,9 @@ var PDFView = {
var currentPage = this.pages[this.page - 1];
var pageWidthScale = (window.innerWidth - kScrollbarPadding) /
currentPage.width / kCssUnits;
currentPage.width * currentPage.scale / kCssUnits;
var pageHeightScale = (window.innerHeight - kScrollbarPadding) /
currentPage.height / kCssUnits;
currentPage.height * currentPage.scale / kCssUnits;
if ('page-width' == value)
this.setScale(pageWidthScale, resetAutoSettings);
if ('page-height' == value)
@ -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() {
@ -360,6 +358,8 @@ var PDFView = {
var destRef = dest[0];
var pageNumber = destRef instanceof Object ?
this.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'] : (destRef + 1);
if (pageNumber > this.pages.length)
pageNumber = this.pages.length;
if (pageNumber) {
this.page = pageNumber;
var currentPage = this.pages[pageNumber - 1];
@ -461,7 +461,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 +489,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 +508,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(thumbsView, 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, scale,
page.stats, self.navigateTo.bind(self));
var thumbnailView = new ThumbnailView(thumbsView, page, i);
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('viewOutline');
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('viewOutline');
outlineSwitchButton.removeAttribute('disabled');
self.switchSidebarView('outline');
});
self.setInitialView(storedHash, scale);
});
pdfDocument.getMetadata().then(function(data) {
var info = data.info, metadata = data.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 +590,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) {
@ -649,7 +663,7 @@ var PDFView = {
var windowTop = window.pageYOffset;
for (var i = 1; i <= pages.length; ++i) {
var page = pages[i - 1];
var pageHeight = page.height * page.scale + kBottomMargin;
var pageHeight = page.height + kBottomMargin;
if (currentHeight + pageHeight > windowTop)
break;
@ -673,8 +687,8 @@ var PDFView = {
var view = document.getElementById('thumbnailView');
var currentHeight = kBottomMargin;
var top = view.scrollTop;
var top = view.scrollTop;
for (var i = 1; i <= thumbs.length; ++i) {
var thumb = thumbs[i - 1];
var thumbHeight = thumb.height * thumb.scaleY + kBottomMargin;
@ -685,7 +699,6 @@ var PDFView = {
}
var bottom = top + view.clientHeight;
for (; i <= thumbs.length && currentHeight < bottom; ++i) {
var singleThumb = thumbs[i - 1];
visibleThumbs.push({ id: singleThumb.id, y: currentHeight,
@ -710,16 +723,13 @@ var PDFView = {
}
};
var PageView = function pageView(container, content, id, pageWidth, pageHeight,
var PageView = function pageView(container, pdfPage, id, scale,
stats, navigateTo) {
this.id = id;
this.content = content;
this.pdfPage = pdfPage;
var view = this.content.view;
this.x = view.x;
this.y = view.y;
this.width = view.width;
this.height = view.height;
this.scale = scale || 1.0;
this.viewport = this.pdfPage.getViewport(this.scale);
var anchor = document.createElement('a');
anchor.name = '' + this.id;
@ -731,10 +741,18 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
container.appendChild(anchor);
container.appendChild(div);
this.destroy = function pageViewDestroy() {
this.update();
this.pdfPage.destroy();
};
this.update = function pageViewUpdate(scale) {
this.scale = scale || this.scale;
div.style.width = (this.width * this.scale) + 'px';
div.style.height = (this.height * this.scale) + 'px';
var viewport = this.pdfPage.getViewport(this.scale);
this.viewport = viewport;
div.style.width = viewport.width + 'px';
div.style.height = viewport.height + 'px';
while (div.hasChildNodes())
div.removeChild(div.lastChild);
@ -747,7 +765,21 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
div.appendChild(this.loadingIconDiv);
};
function setupAnnotations(content, scale) {
Object.defineProperty(this, 'width', {
get: function PageView_getWidth() {
return this.viewport.width;
},
enumerable: true
});
Object.defineProperty(this, 'height', {
get: function PageView_getHeight() {
return this.viewport.height;
},
enumerable: true
});
function setupAnnotations(pdfPage, viewport) {
function bindLink(link, dest) {
link.href = PDFView.getDestinationHash(dest);
link.onclick = function pageViewSetupLinksOnclick() {
@ -757,11 +789,13 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
};
}
function createElementWithStyle(tagName, item) {
var rect = viewport.convertToViewportRectangle(item.rect);
rect = PDFJS.Util.normalizeRect(rect);
var element = document.createElement(tagName);
element.style.left = (Math.floor(item.x - view.x) * scale) + 'px';
element.style.top = (Math.floor(item.y - view.y) * scale) + 'px';
element.style.width = Math.ceil(item.width * scale) + 'px';
element.style.height = Math.ceil(item.height * scale) + 'px';
element.style.left = Math.floor(rect[0]) + 'px';
element.style.top = Math.floor(rect[1]) + 'px';
element.style.width = Math.ceil(rect[2] - rect[0]) + 'px';
element.style.height = Math.ceil(rect[3] - rect[1]) + 'px';
return element;
}
function createCommentAnnotation(type, item) {
@ -769,17 +803,20 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
container.className = 'annotComment';
var image = createElementWithStyle('img', item);
var type = item.type;
var rect = viewport.convertToViewportRectangle(item.rect);
rect = PDFJS.Util.normalizeRect(rect);
image.src = kImageDirectory + type.toLowerCase() + '.svg';
image.alt = '[' + type + ' Annotation]';
var content = document.createElement('div');
content.setAttribute('hidden', true);
var title = document.createElement('h1');
var text = document.createElement('p');
var offsetPos = Math.floor(item.x - view.x + item.width);
content.style.left = (offsetPos * scale) + 'px';
content.style.top = (Math.floor(item.y - view.y) * scale) + 'px';
content.style.left = Math.floor(rect[2]) + 'px';
content.style.top = Math.floor(rect[1]) + 'px';
title.textContent = item.title;
if (!item.content) {
if (!item.content && !item.title) {
content.setAttribute('hidden', true);
} else {
var e = document.createElement('span');
@ -792,11 +829,11 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
}
text.appendChild(e);
image.addEventListener('mouseover', function annotationImageOver() {
this.nextSibling.removeAttribute('hidden');
content.removeAttribute('hidden');
}, false);
image.addEventListener('mouseout', function annotationImageOut() {
this.nextSibling.setAttribute('hidden', true);
content.setAttribute('hidden', true);
}, false);
}
@ -808,29 +845,29 @@ 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.viewport.convertToPdfPoint(x, y);
};
this.scrollIntoView = function pageViewScrollIntoView(dest) {
@ -878,8 +915,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.viewport.convertToViewportPoint(x, y),
this.viewport.convertToViewportPoint(x + width, y + height)
];
if (scale && scale !== PDFView.currentScale)
@ -890,18 +927,18 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
setTimeout(function pageViewScrollIntoViewRelayout() {
// letting page to re-layout before scrolling
var scale = PDFView.currentScale;
var x = Math.min(boundingRect[0].x, boundingRect[1].x);
var y = Math.min(boundingRect[0].y, boundingRect[1].y);
var width = Math.abs(boundingRect[0].x - boundingRect[1].x);
var height = Math.abs(boundingRect[0].y - boundingRect[1].y);
var x = Math.min(boundingRect[0][0], boundingRect[1][0]);
var y = Math.min(boundingRect[0][1], boundingRect[1][1]);
var width = Math.abs(boundingRect[0][0] - boundingRect[1][0]);
var height = Math.abs(boundingRect[0][1] - boundingRect[1][1]);
// using temporary div to scroll it into view
var tempDiv = document.createElement('div');
tempDiv.style.position = 'absolute';
tempDiv.style.left = Math.floor(x * scale) + 'px';
tempDiv.style.top = Math.floor(y * scale) + 'px';
tempDiv.style.width = Math.ceil(width * scale) + 'px';
tempDiv.style.height = Math.ceil(height * scale) + 'px';
tempDiv.style.left = Math.floor(x) + 'px';
tempDiv.style.top = Math.floor(y) + 'px';
tempDiv.style.width = Math.ceil(width) + 'px';
tempDiv.style.height = Math.ceil(height) + 'px';
div.appendChild(tempDiv);
tempDiv.scrollIntoView(true);
div.removeChild(tempDiv);
@ -933,21 +970,20 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
}
var textLayer = textLayerDiv ? new TextLayerBuilder(textLayerDiv) : null;
var scale = this.scale;
canvas.width = pageWidth * scale;
canvas.height = pageHeight * scale;
var scale = this.scale, viewport = this.viewport;
canvas.width = viewport.width;
canvas.height = viewport.height;
var ctx = canvas.getContext('2d');
ctx.save();
ctx.fillStyle = 'rgb(255, 255, 255)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.restore();
ctx.translate(-this.x * scale, -this.y * scale);
// 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;
@ -956,16 +992,30 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
if (error)
PDFView.error('An error occurred while rendering the page.', error);
self.stats = content.stats;
self.stats = pdfPage.stats;
self.updateStats();
if (self.onAfterDraw)
self.onAfterDraw();
cache.push(self);
callback();
}, textLayer);
}
setupAnnotations(this.content, this.scale);
var renderContext = {
canvasContext: ctx,
viewport: this.viewport,
textLayer: textLayer
};
this.pdfPage.render(renderContext).then(
function pdfPageRenderCallback() {
pageViewDrawCallback(null);
},
function pdfPageRenderError(error) {
pageViewDrawCallback(error);
}
);
setupAnnotations(this.pdfPage, this.viewport);
div.setAttribute('data-loaded', true);
};
@ -977,7 +1027,7 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
};
};
var ThumbnailView = function thumbnailView(container, page, id, pageRatio) {
var ThumbnailView = function thumbnailView(container, pdfPage, id) {
var anchor = document.createElement('a');
anchor.href = PDFView.getAnchorUrl('#page=' + id);
anchor.onclick = function stopNivigation() {
@ -985,15 +1035,16 @@ var ThumbnailView = function thumbnailView(container, page, id, pageRatio) {
return false;
};
var view = page.view;
this.width = view.width;
this.height = view.height;
var viewport = pdfPage.getViewport(1);
var pageWidth = viewport.width;
var pageHeight = viewport.height;
var pageRatio = pageWidth / pageHeight;
this.id = id;
var canvasWidth = 98;
var canvasHeight = canvasWidth / this.width * this.height;
var scaleX = this.scaleX = (canvasWidth / this.width);
var scaleY = this.scaleY = (canvasHeight / this.height);
var scaleX = this.scaleX = (canvasWidth / pageWidth);
var scaleY = this.scaleY = (canvasHeight / pageHeight);
var div = document.createElement('div');
div.id = 'thumbnailContainer' + id;
@ -1014,7 +1065,7 @@ var ThumbnailView = function thumbnailView(container, page, id, pageRatio) {
canvas.className = 'thumbnailImage';
div.setAttribute('data-loaded', true);
var ring = document.createElement('div');
ring.className = 'thumbnailSelectionRing';
ring.appendChild(canvas);
@ -1023,11 +1074,8 @@ var ThumbnailView = function thumbnailView(container, page, id, pageRatio) {
var ctx = canvas.getContext('2d');
ctx.save();
ctx.fillStyle = 'rgb(255, 255, 255)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
ctx.restore();
var view = page.view;
ctx.translate(-view.x * scaleX, -view.y * scaleY);
return ctx;
}
@ -1042,10 +1090,19 @@ var ThumbnailView = function thumbnailView(container, page, id, pageRatio) {
}
var ctx = getPageDrawContext();
page.startRendering(ctx, function thumbnailViewDrawStartRendering() {
callback();
});
var drawViewport = pdfPage.getViewport(scaleX);
var renderContext = {
canvasContext: ctx,
viewport: drawViewport
};
pdfPage.render(renderContext).then(
function pdfPageRenderCallback() {
callback();
},
function pdfPageRenderError(error) {
callback();
}
);
this.hasImage = true;
};
@ -1235,7 +1292,6 @@ window.addEventListener('load', function webViewerLoad(evt) {
var file = PDFJS.isFirefoxExtension ?
window.location.toString() : params.file || kDefaultURL;
PDFView.open(file, 0);
if (PDFJS.isFirefoxExtension || !window.File || !window.FileReader ||
!window.FileList || !window.Blob) {
@ -1273,6 +1329,8 @@ window.addEventListener('load', function webViewerLoad(evt) {
document.getElementById('sidebarContainer').classList.toggle('hidden');
updateThumbViewArea();
});
PDFView.open(file, 0);
}, true);
/**
@ -1336,14 +1394,14 @@ function updateViewarea() {
var currentPage = PDFView.pages[pageNumber - 1];
var topLeft = currentPage.getPagePoint(window.pageXOffset,
window.pageYOffset - firstPage.y - kViewerTopMargin);
pdfOpenParams += ',' + Math.round(topLeft.x) + ',' + Math.round(topLeft.y);
pdfOpenParams += ',' + Math.round(topLeft[0]) + ',' + Math.round(topLeft[1]);
var store = PDFView.store;
store.set('exists', true);
store.set('page', pageNumber);
store.set('zoom', normalizedScaleValue);
store.set('scrollLeft', Math.round(topLeft.x));
store.set('scrollTop', Math.round(topLeft.y));
store.set('scrollLeft', Math.round(topLeft[0]));
store.set('scrollTop', Math.round(topLeft[1]));
var href = PDFView.getAnchorUrl(pdfOpenParams);
document.getElementById('viewBookmark').href = href;
}
@ -1397,7 +1455,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