Skip to content

Commit

Permalink
Load built-in CMap files using the Fetch API when possible
Browse files Browse the repository at this point in the history
  • Loading branch information
Snuffleupagus committed Feb 27, 2019
1 parent df7756d commit 3dd0477
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 40 deletions.
80 changes: 60 additions & 20 deletions src/display/display_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

import {
assert, CMapCompressionType, removeNullCharacters, stringToBytes,
unreachable, Util, warn
unreachable, URL, Util, warn
} from '../shared/util';

const DEFAULT_LINK_REL = 'noopener noreferrer nofollow';
Expand Down Expand Up @@ -66,19 +66,41 @@ class DOMCMapReaderFactory {
this.isCompressed = isCompressed;
}

fetch({ name, }) {
async fetch({ name, }) {
if (!this.baseUrl) {
return Promise.reject(new Error(
throw new Error(
'The CMap "baseUrl" parameter must be specified, ensure that ' +
'the "cMapUrl" and "cMapPacked" API parameters are provided.'));
'the "cMapUrl" and "cMapPacked" API parameters are provided.');
}
if (!name) {
return Promise.reject(new Error('CMap name must be specified.'));
throw new Error('CMap name must be specified.');
}
const url = this.baseUrl + name + (this.isCompressed ? '.bcmap' : '');
const compressionType = (this.isCompressed ? CMapCompressionType.BINARY :
CMapCompressionType.NONE);

if ((typeof PDFJSDev !== 'undefined' && PDFJSDev.test('MOZCENTRAL')) ||
(isFetchSupported() && isValidFetchUrl(url, document.baseURI))) {
return fetch(url).then(async (response) => {
if (!response.ok) {
throw new Error(response.statusText);
}
let cMapData;
if (this.isCompressed) {
cMapData = new Uint8Array(await response.arrayBuffer());
} else {
cMapData = stringToBytes(await response.text());
}
return { cMapData, compressionType, };
}).catch((reason) => {
throw new Error(`Unable to load ${this.isCompressed ? 'binary ' : ''}` +
`CMap at: ${url}`);
});
}
return new Promise((resolve, reject) => {
let url = this.baseUrl + name + (this.isCompressed ? '.bcmap' : '');

let request = new XMLHttpRequest();
// The Fetch API is not supported.
return new Promise((resolve, reject) => {
const request = new XMLHttpRequest();
request.open('GET', url, true);

if (this.isCompressed) {
Expand All @@ -89,27 +111,24 @@ class DOMCMapReaderFactory {
return;
}
if (request.status === 200 || request.status === 0) {
let data;
let cMapData;
if (this.isCompressed && request.response) {
data = new Uint8Array(request.response);
cMapData = new Uint8Array(request.response);
} else if (!this.isCompressed && request.responseText) {
data = stringToBytes(request.responseText);
cMapData = stringToBytes(request.responseText);
}
if (data) {
resolve({
cMapData: data,
compressionType: this.isCompressed ?
CMapCompressionType.BINARY : CMapCompressionType.NONE,
});
if (cMapData) {
resolve({ cMapData, compressionType, });
return;
}
}
reject(new Error('Unable to load ' +
(this.isCompressed ? 'binary ' : '') +
'CMap at: ' + url));
reject(new Error(request.statusText));
};

request.send(null);
}).catch((reason) => {
throw new Error(`Unable to load ${this.isCompressed ? 'binary ' : ''}` +
`CMap at: ${url}`);
});
}
}
Expand Down Expand Up @@ -428,6 +447,26 @@ class DummyStatTimer {
}
}

function isFetchSupported() {
return (typeof fetch !== 'undefined' &&
typeof Response !== 'undefined' && 'body' in Response.prototype &&
// eslint-disable-next-line no-restricted-globals
typeof ReadableStream !== 'undefined');
}

function isValidFetchUrl(url, baseUrl) {
if (!url) {
return false;
}
try {
const { protocol, } = baseUrl ? new URL(url, baseUrl) : new URL(url);
// The Fetch API only supports the http/https protocols, and not file/ftp.
return (protocol === 'http:' || protocol === 'https:');
} catch (ex) {
return false; // `new URL()` will throw on incorrect data.
}
}

function loadScript(src) {
return new Promise((resolve, reject) => {
let script = document.createElement('script');
Expand All @@ -453,5 +492,6 @@ export {
DOMSVGFactory,
StatTimer,
DummyStatTimer,
isFetchSupported,
loadScript,
};
9 changes: 4 additions & 5 deletions src/pdf.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint-disable no-unused-vars, no-restricted-globals */
/* eslint-disable no-unused-vars */

'use strict';

Expand All @@ -37,8 +37,7 @@ if (typeof PDFJSDev === 'undefined' || PDFJSDev.test('GENERIC')) {
pdfjsDisplayAPI.setPDFNetworkStreamFactory((params) => {
return new PDFNodeStream(params);
});
} else if (typeof Response !== 'undefined' && 'body' in Response.prototype &&
typeof ReadableStream !== 'undefined') {
} else if (pdfjsDisplayDisplayUtils.isFetchSupported()) {
let PDFFetchStream = require('./display/fetch_stream.js').PDFFetchStream;
pdfjsDisplayAPI.setPDFNetworkStreamFactory((params) => {
return new PDFFetchStream(params);
Expand All @@ -65,8 +64,8 @@ if (typeof PDFJSDev === 'undefined' || PDFJSDev.test('GENERIC')) {
return true;
}
};
if (typeof Response !== 'undefined' && 'body' in Response.prototype &&
typeof ReadableStream !== 'undefined' && isChromeWithFetchCredentials()) {
if (pdfjsDisplayDisplayUtils.isFetchSupported() &&
isChromeWithFetchCredentials()) {
PDFFetchStream = require('./display/fetch_stream.js').PDFFetchStream;
}
pdfjsDisplayAPI.setPDFNetworkStreamFactory((params) => {
Expand Down
29 changes: 14 additions & 15 deletions test/unit/test_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,32 +99,31 @@ class NodeCMapReaderFactory {
this.isCompressed = isCompressed;
}

fetch({ name, }) {
async fetch({ name, }) {
if (!this.baseUrl) {
return Promise.reject(new Error(
throw new Error(
'The CMap "baseUrl" parameter must be specified, ensure that ' +
'the "cMapUrl" and "cMapPacked" API parameters are provided.'));
'the "cMapUrl" and "cMapPacked" API parameters are provided.');
}
if (!name) {
return Promise.reject(new Error('CMap name must be specified.'));
throw new Error('CMap name must be specified.');
}
return new Promise((resolve, reject) => {
let url = this.baseUrl + name + (this.isCompressed ? '.bcmap' : '');
const url = this.baseUrl + name + (this.isCompressed ? '.bcmap' : '');
const compressionType = (this.isCompressed ? CMapCompressionType.BINARY :
CMapCompressionType.NONE);

let fs = require('fs');
return new Promise((resolve, reject) => {
const fs = require('fs');
fs.readFile(url, (error, data) => {
if (error || !data) {
reject(new Error('Unable to load ' +
(this.isCompressed ? 'binary ' : '') +
'CMap at: ' + url));
reject(new Error(error));
return;
}
resolve({
cMapData: new Uint8Array(data),
compressionType: this.isCompressed ?
CMapCompressionType.BINARY : CMapCompressionType.NONE,
});
resolve({ cMapData: new Uint8Array(data), compressionType, });
});
}).catch((reason) => {
throw new Error(`Unable to load ${this.isCompressed ? 'binary ' : ''}` +
`CMap at: ${url}`);
});
}
}
Expand Down

0 comments on commit 3dd0477

Please sign in to comment.