Unverified Commit c7778409 authored by Robert Knight's avatar Robert Knight Committed by GitHub

Merge pull request #818 from hypothesis/support-newer-pdfjs

Support recent versions of PDF.js
parents 186b59dd d2241159
...@@ -38,13 +38,20 @@ class PDFMetadata { ...@@ -38,13 +38,20 @@ class PDFMetadata {
this._loaded = new Promise(resolve => { this._loaded = new Promise(resolve => {
const finish = () => { const finish = () => {
window.removeEventListener('documentload', finish); window.removeEventListener('documentload', finish);
window.removeEventListener('documentloaded', finish);
resolve(app); resolve(app);
}; };
if (app.documentFingerprint) { if (app.downloadComplete) {
resolve(app); resolve(app);
} else { } else {
// Listen for either the `documentload` (older PDF.js) or
// `documentloaded` (newer PDF.js) events which signal that the document
// has been downloaded and the first page has been rendered.
//
// See https://github.com/mozilla/pdf.js/commit/7bc4bfcc8b7f52b14107f0a551becdf01643c5c2
window.addEventListener('documentload', finish); window.addEventListener('documentload', finish);
window.addEventListener('documentloaded', finish);
} }
}); });
} }
...@@ -61,7 +68,7 @@ class PDFMetadata { ...@@ -61,7 +68,7 @@ class PDFMetadata {
return this._loaded.then(app => { return this._loaded.then(app => {
let uri = getPDFURL(app); let uri = getPDFURL(app);
if (!uri) { if (!uri) {
uri = fingerprintToURN(app.documentFingerprint); uri = fingerprintToURN(app.pdfDocument.fingerprint);
} }
return uri; return uri;
}); });
...@@ -86,7 +93,7 @@ class PDFMetadata { ...@@ -86,7 +93,7 @@ class PDFMetadata {
} }
const link = [ const link = [
{href: fingerprintToURN(app.documentFingerprint)}, {href: fingerprintToURN(app.pdfDocument.fingerprint)},
]; ];
const url = getPDFURL(app); const url = getPDFURL(app);
...@@ -97,7 +104,7 @@ class PDFMetadata { ...@@ -97,7 +104,7 @@ class PDFMetadata {
return { return {
title: title, title: title,
link: link, link: link,
documentFingerprint: app.documentFingerprint, documentFingerprint: app.pdfDocument.fingerprint,
}; };
}); });
} }
......
...@@ -2,27 +2,107 @@ ...@@ -2,27 +2,107 @@
const PDFMetadata = require('../pdf-metadata'); const PDFMetadata = require('../pdf-metadata');
describe('pdf-metadata', function () { /**
it('waits for the PDF to load before returning metadata', function () { * Fake implementation of PDF.js `window.PDFViewerApplication.metadata`.
const fakeApp = {}; */
const pdfMetadata = new PDFMetadata(fakeApp); class FakeMetadata {
/**
* Initialize the metadata dictionary.
*
* @param {Object} metadata - A key/value dictionary of metadata fields.
*/
constructor(metadata) {
this._metadata = metadata;
}
get(key) {
return this._metadata[key];
}
has(key) {
return this._metadata.hasOwnProperty(key);
}
}
/**
* Fake implementation of PDF.js `window.PDFViewerApplication.pdfDocument`.
*/
class FakePDFDocumentProxy {
constructor({ fingerprint }) {
this.fingerprint = fingerprint;
}
}
/**
* Fake implementation of PDF.js `window.PDFViewerApplication` entry point.
*
* This fake only implements the parts that concern document metadata.
*/
class FakePDFViewerApplication {
/**
* Initialize the "PDF viewer" as it would be when loading a document or
* when a document fails to load.
*/
constructor(url = '') {
this.url = url;
this.documentInfo = undefined;
this.metadata = undefined;
this.pdfDocument = null;
}
/**
* Simulate completion of PDF document loading.
*/
finishLoading({ url, fingerprint, metadata, title, eventName = 'documentload' }) {
const event = document.createEvent('Event'); const event = document.createEvent('Event');
event.initEvent('documentload', false, false); event.initEvent(eventName, false, false);
fakeApp.url = 'http://fake.com';
fakeApp.documentFingerprint = 'fakeFingerprint';
window.dispatchEvent(event); window.dispatchEvent(event);
return pdfMetadata.getUri().then(function (uri) { this.url = url;
assert.equal(uri, 'http://fake.com/'); this.downloadComplete = true;
this.documentInfo = {};
if (typeof title !== undefined) {
this.documentInfo.Title = title;
}
if (metadata) {
this.metadata = new FakeMetadata(metadata);
}
this.pdfDocument = new FakePDFDocumentProxy({ fingerprint });
}
}
describe('annotator/plugin/pdf-metadata', function () {
[
// Event dispatched in older PDF.js versions (pre-7bc4bfcc).
'documentload',
// Event dispatched in newer PDF.js versions (post-7bc4bfcc).
'documentloaded',
].forEach(eventName => {
it('waits for the PDF to load before returning metadata', function () {
const fakeApp = new FakePDFViewerApplication;
const pdfMetadata = new PDFMetadata(fakeApp);
fakeApp.finishLoading({
eventName,
url: 'http://fake.com',
fingerprint: 'fakeFingerprint',
});
return pdfMetadata.getUri().then(function (uri) {
assert.equal(uri, 'http://fake.com/');
});
}); });
}); });
it('does not wait for the PDF to load if it has already loaded', function () { it('does not wait for the PDF to load if it has already loaded', function () {
const fakePDFViewerApplication = { const fakePDFViewerApplication = new FakePDFViewerApplication;
fakePDFViewerApplication.finishLoading({
url: 'http://fake.com', url: 'http://fake.com',
documentFingerprint: 'fakeFingerprint', fingerprint: 'fakeFingerprint',
}; });
const pdfMetadata = new PDFMetadata(fakePDFViewerApplication); const pdfMetadata = new PDFMetadata(fakePDFViewerApplication);
return pdfMetadata.getUri().then(function (uri) { return pdfMetadata.getUri().then(function (uri) {
assert.equal(uri, 'http://fake.com/'); assert.equal(uri, 'http://fake.com/');
...@@ -31,18 +111,15 @@ describe('pdf-metadata', function () { ...@@ -31,18 +111,15 @@ describe('pdf-metadata', function () {
describe('metadata sources', function () { describe('metadata sources', function () {
let pdfMetadata; let pdfMetadata;
const fakePDFViewerApplication = { const fakePDFViewerApplication = new FakePDFViewerApplication;
documentFingerprint: 'fakeFingerprint', fakePDFViewerApplication.finishLoading({
documentInfo: { fingerprint: 'fakeFingerprint',
Title: 'fakeTitle', title: 'fakeTitle',
},
metadata: { metadata: {
metadata: { 'dc:title': 'dcFakeTitle',
'dc:title': 'fakeTitle',
},
}, },
url: 'http://fake.com/', url: 'http://fake.com/',
}; });
beforeEach(function () { beforeEach(function () {
pdfMetadata = new PDFMetadata(fakePDFViewerApplication); pdfMetadata = new PDFMetadata(fakePDFViewerApplication);
...@@ -56,10 +133,11 @@ describe('pdf-metadata', function () { ...@@ -56,10 +133,11 @@ describe('pdf-metadata', function () {
}); });
it('returns the fingerprint as a URN when the PDF URL is a local file', function () { it('returns the fingerprint as a URN when the PDF URL is a local file', function () {
const fakePDFViewerApplication = { const fakePDFViewerApplication = new FakePDFViewerApplication;
fakePDFViewerApplication.finishLoading({
url: 'file:///test.pdf', url: 'file:///test.pdf',
documentFingerprint: 'fakeFingerprint', fingerprint: 'fakeFingerprint',
}; });
const pdfMetadata = new PDFMetadata(fakePDFViewerApplication); const pdfMetadata = new PDFMetadata(fakePDFViewerApplication);
return pdfMetadata.getUri().then(function (uri) { return pdfMetadata.getUri().then(function (uri) {
...@@ -68,10 +146,11 @@ describe('pdf-metadata', function () { ...@@ -68,10 +146,11 @@ describe('pdf-metadata', function () {
}); });
it('resolves relative URLs', () => { it('resolves relative URLs', () => {
const fakePDFViewerApplication = { const fakePDFViewerApplication = new FakePDFViewerApplication;
fakePDFViewerApplication.finishLoading({
url: 'index.php?action=download&file_id=wibble', url: 'index.php?action=download&file_id=wibble',
documentFingerprint: 'fakeFingerprint', fingerprint: 'fakeFingerprint',
}; });
const pdfMetadata = new PDFMetadata(fakePDFViewerApplication); const pdfMetadata = new PDFMetadata(fakePDFViewerApplication);
return pdfMetadata.getUri().then(uri => { return pdfMetadata.getUri().then(uri => {
...@@ -85,15 +164,12 @@ describe('pdf-metadata', function () { ...@@ -85,15 +164,12 @@ describe('pdf-metadata', function () {
describe('#getMetadata', function () { describe('#getMetadata', function () {
it('gets the title from the dc:title field', function () { it('gets the title from the dc:title field', function () {
const expectedMetadata = { const expectedMetadata = {
title: 'dcTitle', title: 'dcFakeTitle',
link: [{href: 'urn:x-pdf:' + fakePDFViewerApplication.documentFingerprint}, link: [{href: 'urn:x-pdf:' + fakePDFViewerApplication.pdfDocument.fingerprint},
{href: fakePDFViewerApplication.url}], {href: fakePDFViewerApplication.url}],
documentFingerprint: fakePDFViewerApplication.documentFingerprint, documentFingerprint: fakePDFViewerApplication.pdfDocument.fingerprint,
}; };
fakePDFViewerApplication.metadata.has = sinon.stub().returns(true);
fakePDFViewerApplication.metadata.get = sinon.stub().returns('dcTitle');
return pdfMetadata.getMetadata().then(function (actualMetadata) { return pdfMetadata.getMetadata().then(function (actualMetadata) {
assert.deepEqual(actualMetadata, expectedMetadata); assert.deepEqual(actualMetadata, expectedMetadata);
}); });
...@@ -102,9 +178,9 @@ describe('pdf-metadata', function () { ...@@ -102,9 +178,9 @@ describe('pdf-metadata', function () {
it('gets the title from the documentInfo.Title field', function () { it('gets the title from the documentInfo.Title field', function () {
const expectedMetadata = { const expectedMetadata = {
title: fakePDFViewerApplication.documentInfo.Title, title: fakePDFViewerApplication.documentInfo.Title,
link: [{href: 'urn:x-pdf:' + fakePDFViewerApplication.documentFingerprint}, link: [{href: 'urn:x-pdf:' + fakePDFViewerApplication.pdfDocument.fingerprint},
{href: fakePDFViewerApplication.url}], {href: fakePDFViewerApplication.url}],
documentFingerprint: fakePDFViewerApplication.documentFingerprint, documentFingerprint: fakePDFViewerApplication.pdfDocument.fingerprint,
}; };
fakePDFViewerApplication.metadata.has = sinon.stub().returns(false); fakePDFViewerApplication.metadata.has = sinon.stub().returns(false);
...@@ -116,12 +192,13 @@ describe('pdf-metadata', function () { ...@@ -116,12 +192,13 @@ describe('pdf-metadata', function () {
it('does not save file:// URLs in document metadata', function () { it('does not save file:// URLs in document metadata', function () {
let pdfMetadata; let pdfMetadata;
const fakePDFViewerApplication = { const fakePDFViewerApplication = new FakePDFViewerApplication;
documentFingerprint: 'fakeFingerprint', fakePDFViewerApplication.finishLoading({
fingerprint: 'fakeFingerprint',
url: 'file://fake.pdf', url: 'file://fake.pdf',
}; });
const expectedMetadata = { const expectedMetadata = {
link: [{href: 'urn:x-pdf:' + fakePDFViewerApplication.documentFingerprint}], link: [{href: 'urn:x-pdf:' + fakePDFViewerApplication.pdfDocument.fingerprint}],
}; };
pdfMetadata = new PDFMetadata(fakePDFViewerApplication); pdfMetadata = new PDFMetadata(fakePDFViewerApplication);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment