mirror of
https://github.com/mozilla/pdf.js.git
synced 2025-04-26 10:08:06 +02:00
Merge pull request #8064 from Snuffleupagus/fetchBuiltInCMap
[api-minor] Refactor fetching of built-in CMaps to utilize a factory on the `display` side instead, to allow users of the API to provide a custom CMap loading factory (e.g. for use with Node.js)
This commit is contained in:
commit
cfaa621a05
13 changed files with 240 additions and 120 deletions
106
src/core/cmap.js
106
src/core/cmap.js
|
@ -36,11 +36,12 @@ var error = sharedUtil.error;
|
|||
var isInt = sharedUtil.isInt;
|
||||
var isString = sharedUtil.isString;
|
||||
var MissingDataException = sharedUtil.MissingDataException;
|
||||
var CMapCompressionType = sharedUtil.CMapCompressionType;
|
||||
var isEOF = corePrimitives.isEOF;
|
||||
var isName = corePrimitives.isName;
|
||||
var isCmd = corePrimitives.isCmd;
|
||||
var isStream = corePrimitives.isStream;
|
||||
var StringStream = coreStream.StringStream;
|
||||
var Stream = coreStream.Stream;
|
||||
var Lexer = coreParser.Lexer;
|
||||
|
||||
var BUILT_IN_CMAPS = [
|
||||
|
@ -423,25 +424,6 @@ var IdentityCMap = (function IdentityCMapClosure() {
|
|||
})();
|
||||
|
||||
var BinaryCMapReader = (function BinaryCMapReaderClosure() {
|
||||
function fetchBinaryData(url) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
var request = new XMLHttpRequest();
|
||||
request.open('GET', url, true);
|
||||
request.responseType = 'arraybuffer';
|
||||
request.onreadystatechange = function () {
|
||||
if (request.readyState === XMLHttpRequest.DONE) {
|
||||
if (!request.response || request.status !== 200 &&
|
||||
request.status !== 0) {
|
||||
reject(new Error('Unable to get binary cMap at: ' + url));
|
||||
} else {
|
||||
resolve(new Uint8Array(request.response));
|
||||
}
|
||||
}
|
||||
};
|
||||
request.send(null);
|
||||
});
|
||||
}
|
||||
|
||||
function hexToInt(a, size) {
|
||||
var n = 0;
|
||||
for (var i = 0; i <= size; i++) {
|
||||
|
@ -561,8 +543,8 @@ var BinaryCMapReader = (function BinaryCMapReaderClosure() {
|
|||
}
|
||||
};
|
||||
|
||||
function processBinaryCMap(url, cMap, extend) {
|
||||
return fetchBinaryData(url).then(function (data) {
|
||||
function processBinaryCMap(data, cMap, extend) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
var stream = new BinaryCMapStream(data);
|
||||
var header = stream.readByte();
|
||||
cMap.vertical = !!(header & 1);
|
||||
|
@ -709,22 +691,23 @@ var BinaryCMapReader = (function BinaryCMapReaderClosure() {
|
|||
}
|
||||
break;
|
||||
default:
|
||||
error('Unknown type: ' + type);
|
||||
break;
|
||||
reject(new Error('processBinaryCMap: Unknown type: ' + type));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (useCMap) {
|
||||
return extend(useCMap);
|
||||
resolve(extend(useCMap));
|
||||
return;
|
||||
}
|
||||
return cMap;
|
||||
resolve(cMap);
|
||||
});
|
||||
}
|
||||
|
||||
function BinaryCMapReader() {}
|
||||
|
||||
BinaryCMapReader.prototype = {
|
||||
read: processBinaryCMap
|
||||
process: processBinaryCMap,
|
||||
};
|
||||
|
||||
return BinaryCMapReader;
|
||||
|
@ -879,7 +862,7 @@ var CMapFactory = (function CMapFactoryClosure() {
|
|||
}
|
||||
}
|
||||
|
||||
function parseCMap(cMap, lexer, builtInCMapParams, useCMap) {
|
||||
function parseCMap(cMap, lexer, fetchBuiltInCMap, useCMap) {
|
||||
var previous;
|
||||
var embededUseCMap;
|
||||
objLoop: while (true) {
|
||||
|
@ -935,14 +918,13 @@ var CMapFactory = (function CMapFactoryClosure() {
|
|||
useCMap = embededUseCMap;
|
||||
}
|
||||
if (useCMap) {
|
||||
return extendCMap(cMap, builtInCMapParams, useCMap);
|
||||
return extendCMap(cMap, fetchBuiltInCMap, useCMap);
|
||||
}
|
||||
return Promise.resolve(cMap);
|
||||
}
|
||||
|
||||
function extendCMap(cMap, builtInCMapParams, useCMap) {
|
||||
return createBuiltInCMap(useCMap, builtInCMapParams).then(
|
||||
function(newCMap) {
|
||||
function extendCMap(cMap, fetchBuiltInCMap, useCMap) {
|
||||
return createBuiltInCMap(useCMap, fetchBuiltInCMap).then(function(newCMap) {
|
||||
cMap.useCMap = newCMap;
|
||||
// If there aren't any code space ranges defined clone all the parent ones
|
||||
// into this cMap.
|
||||
|
@ -965,15 +947,7 @@ var CMapFactory = (function CMapFactoryClosure() {
|
|||
});
|
||||
}
|
||||
|
||||
function parseBinaryCMap(name, builtInCMapParams) {
|
||||
var url = builtInCMapParams.url + name + '.bcmap';
|
||||
var cMap = new CMap(true);
|
||||
return new BinaryCMapReader().read(url, cMap, function (useCMap) {
|
||||
return extendCMap(cMap, builtInCMapParams, useCMap);
|
||||
});
|
||||
}
|
||||
|
||||
function createBuiltInCMap(name, builtInCMapParams) {
|
||||
function createBuiltInCMap(name, fetchBuiltInCMap) {
|
||||
if (name === 'Identity-H') {
|
||||
return Promise.resolve(new IdentityCMap(false, 2));
|
||||
} else if (name === 'Identity-V') {
|
||||
|
@ -982,45 +956,41 @@ var CMapFactory = (function CMapFactoryClosure() {
|
|||
if (BUILT_IN_CMAPS.indexOf(name) === -1) {
|
||||
return Promise.reject(new Error('Unknown cMap name: ' + name));
|
||||
}
|
||||
assert(builtInCMapParams, 'built-in cMap parameters are not provided');
|
||||
assert(fetchBuiltInCMap, 'Built-in CMap parameters are not provided.');
|
||||
|
||||
if (builtInCMapParams.packed) {
|
||||
return parseBinaryCMap(name, builtInCMapParams);
|
||||
}
|
||||
return fetchBuiltInCMap(name).then(function (data) {
|
||||
var cMapData = data.cMapData, compressionType = data.compressionType;
|
||||
var cMap = new CMap(true);
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
var url = builtInCMapParams.url + name;
|
||||
var request = new XMLHttpRequest();
|
||||
request.onreadystatechange = function () {
|
||||
if (request.readyState === XMLHttpRequest.DONE) {
|
||||
if (request.status === 200 || request.status === 0) {
|
||||
var cMap = new CMap(true);
|
||||
var lexer = new Lexer(new StringStream(request.responseText));
|
||||
parseCMap(cMap, lexer, builtInCMapParams, null).then(
|
||||
function (parsedCMap) {
|
||||
resolve(parsedCMap);
|
||||
});
|
||||
} else {
|
||||
reject(new Error('Unable to get cMap at: ' + url));
|
||||
}
|
||||
}
|
||||
};
|
||||
request.open('GET', url, true);
|
||||
request.send(null);
|
||||
if (compressionType === CMapCompressionType.BINARY) {
|
||||
return new BinaryCMapReader().process(cMapData, cMap,
|
||||
function (useCMap) {
|
||||
return extendCMap(cMap, fetchBuiltInCMap, useCMap);
|
||||
});
|
||||
}
|
||||
assert(compressionType === CMapCompressionType.NONE,
|
||||
'TODO: Only BINARY/NONE CMap compression is currently supported.');
|
||||
// Uncompressed CMap.
|
||||
var lexer = new Lexer(new Stream(cMapData));
|
||||
return parseCMap(cMap, lexer, fetchBuiltInCMap, null);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
create: function (encoding, builtInCMapParams, useCMap) {
|
||||
create: function (params) {
|
||||
var encoding = params.encoding;
|
||||
var fetchBuiltInCMap = params.fetchBuiltInCMap;
|
||||
var useCMap = params.useCMap;
|
||||
|
||||
if (isName(encoding)) {
|
||||
return createBuiltInCMap(encoding.name, builtInCMapParams);
|
||||
return createBuiltInCMap(encoding.name, fetchBuiltInCMap);
|
||||
} else if (isStream(encoding)) {
|
||||
var cMap = new CMap();
|
||||
var lexer = new Lexer(encoding);
|
||||
return parseCMap(cMap, lexer, builtInCMapParams, useCMap).then(
|
||||
return parseCMap(cMap, lexer, fetchBuiltInCMap, useCMap).then(
|
||||
function (parsedCMap) {
|
||||
if (parsedCMap.isIdentityCMap) {
|
||||
return createBuiltInCMap(parsedCMap.name, builtInCMapParams);
|
||||
return createBuiltInCMap(parsedCMap.name, fetchBuiltInCMap);
|
||||
}
|
||||
return parsedCMap;
|
||||
});
|
||||
|
|
|
@ -71,13 +71,15 @@ var Page = (function PageClosure() {
|
|||
var DEFAULT_USER_UNIT = 1.0;
|
||||
var LETTER_SIZE_MEDIABOX = [0, 0, 612, 792];
|
||||
|
||||
function Page(pdfManager, xref, pageIndex, pageDict, ref, fontCache) {
|
||||
function Page(pdfManager, xref, pageIndex, pageDict, ref, fontCache,
|
||||
builtInCMapCache) {
|
||||
this.pdfManager = pdfManager;
|
||||
this.pageIndex = pageIndex;
|
||||
this.pageDict = pageDict;
|
||||
this.xref = xref;
|
||||
this.ref = ref;
|
||||
this.fontCache = fontCache;
|
||||
this.builtInCMapCache = builtInCMapCache;
|
||||
this.evaluatorOptions = pdfManager.evaluatorOptions;
|
||||
this.resourcesPromise = null;
|
||||
|
||||
|
@ -248,6 +250,7 @@ var Page = (function PageClosure() {
|
|||
handler, this.pageIndex,
|
||||
this.idFactory,
|
||||
this.fontCache,
|
||||
this.builtInCMapCache,
|
||||
this.evaluatorOptions);
|
||||
|
||||
var dataPromises = Promise.all([contentStreamPromise, resourcesPromise]);
|
||||
|
@ -315,6 +318,7 @@ var Page = (function PageClosure() {
|
|||
handler, self.pageIndex,
|
||||
self.idFactory,
|
||||
self.fontCache,
|
||||
self.builtInCMapCache,
|
||||
self.evaluatorOptions);
|
||||
|
||||
return partialEvaluator.getTextContent(contentStream,
|
||||
|
@ -551,9 +555,10 @@ var PDFDocument = (function PDFDocumentClosure() {
|
|||
this.xref.parse(recoveryMode);
|
||||
var self = this;
|
||||
var pageFactory = {
|
||||
createPage: function (pageIndex, dict, ref, fontCache) {
|
||||
createPage: function (pageIndex, dict, ref, fontCache,
|
||||
builtInCMapCache) {
|
||||
return new Page(self.pdfManager, self.xref, pageIndex, dict, ref,
|
||||
fontCache);
|
||||
fontCache, builtInCMapCache);
|
||||
}
|
||||
};
|
||||
this.catalog = new Catalog(this.pdfManager, this.xref, pageFactory);
|
||||
|
|
|
@ -53,6 +53,7 @@ var UNSUPPORTED_FEATURES = sharedUtil.UNSUPPORTED_FEATURES;
|
|||
var ImageKind = sharedUtil.ImageKind;
|
||||
var OPS = sharedUtil.OPS;
|
||||
var TextRenderingMode = sharedUtil.TextRenderingMode;
|
||||
var CMapCompressionType = sharedUtil.CMapCompressionType;
|
||||
var Util = sharedUtil.Util;
|
||||
var assert = sharedUtil.assert;
|
||||
var createPromiseCapability = sharedUtil.createPromiseCapability;
|
||||
|
@ -112,7 +113,6 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
|||
forceDataSchema: false,
|
||||
maxImageSize: -1,
|
||||
disableFontFace: false,
|
||||
cMapOptions: { url: null, packed: false },
|
||||
disableNativeImageDecoder: false,
|
||||
};
|
||||
|
||||
|
@ -170,14 +170,31 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
|||
};
|
||||
|
||||
function PartialEvaluator(pdfManager, xref, handler, pageIndex,
|
||||
idFactory, fontCache, options) {
|
||||
idFactory, fontCache, builtInCMapCache, options) {
|
||||
this.pdfManager = pdfManager;
|
||||
this.xref = xref;
|
||||
this.handler = handler;
|
||||
this.pageIndex = pageIndex;
|
||||
this.idFactory = idFactory;
|
||||
this.fontCache = fontCache;
|
||||
this.builtInCMapCache = builtInCMapCache;
|
||||
this.options = options || DefaultPartialEvaluatorOptions;
|
||||
|
||||
this.fetchBuiltInCMap = function (name) {
|
||||
var cachedCMap = builtInCMapCache[name];
|
||||
if (cachedCMap) {
|
||||
return Promise.resolve(cachedCMap);
|
||||
}
|
||||
return handler.sendWithPromise('FetchBuiltInCMap', {
|
||||
name: name,
|
||||
}).then(function (data) {
|
||||
if (data.compressionType !== CMapCompressionType.NONE) {
|
||||
// Given the size of uncompressed CMaps, only cache compressed ones.
|
||||
builtInCMapCache[name] = data;
|
||||
}
|
||||
return data;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
// Trying to minimize Date.now() usage and check every 100 time
|
||||
|
@ -1879,9 +1896,11 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
|||
var ucs2CMapName = Name.get(registry + '-' + ordering + '-UCS2');
|
||||
// d) Obtain the CMap with the name constructed in step (c) (available
|
||||
// from the ASN Web site; see the Bibliography).
|
||||
return CMapFactory.create(ucs2CMapName, this.options.cMapOptions,
|
||||
null).then(
|
||||
function (ucs2CMap) {
|
||||
return CMapFactory.create({
|
||||
encoding: ucs2CMapName,
|
||||
fetchBuiltInCMap: this.fetchBuiltInCMap,
|
||||
useCMap: null,
|
||||
}).then(function (ucs2CMap) {
|
||||
var cMap = properties.cMap;
|
||||
toUnicode = [];
|
||||
cMap.forEach(function(charcode, cid) {
|
||||
|
@ -1907,16 +1926,22 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
|||
readToUnicode: function PartialEvaluator_readToUnicode(toUnicode) {
|
||||
var cmapObj = toUnicode;
|
||||
if (isName(cmapObj)) {
|
||||
return CMapFactory.create(cmapObj, this.options.cMapOptions, null).then(
|
||||
function (cmap) {
|
||||
return CMapFactory.create({
|
||||
encoding: cmapObj,
|
||||
fetchBuiltInCMap: this.fetchBuiltInCMap,
|
||||
useCMap: null,
|
||||
}).then(function (cmap) {
|
||||
if (cmap instanceof IdentityCMap) {
|
||||
return new IdentityToUnicodeMap(0, 0xFFFF);
|
||||
}
|
||||
return new ToUnicodeMap(cmap.getMap());
|
||||
});
|
||||
} else if (isStream(cmapObj)) {
|
||||
return CMapFactory.create(cmapObj, this.options.cMapOptions, null).then(
|
||||
function (cmap) {
|
||||
return CMapFactory.create({
|
||||
encoding: cmapObj,
|
||||
fetchBuiltInCMap: this.fetchBuiltInCMap,
|
||||
useCMap: null,
|
||||
}).then(function (cmap) {
|
||||
if (cmap instanceof IdentityCMap) {
|
||||
return new IdentityToUnicodeMap(0, 0xFFFF);
|
||||
}
|
||||
|
@ -2222,7 +2247,6 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
|||
var descriptor = preEvaluatedFont.descriptor;
|
||||
var type = preEvaluatedFont.type;
|
||||
var maxCharIndex = (composite ? 0xFFFF : 0xFF);
|
||||
var cMapOptions = this.options.cMapOptions;
|
||||
var properties;
|
||||
|
||||
if (!descriptor) {
|
||||
|
@ -2352,8 +2376,11 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
|||
if (isName(cidEncoding)) {
|
||||
properties.cidEncoding = cidEncoding.name;
|
||||
}
|
||||
cMapPromise = CMapFactory.create(cidEncoding, cMapOptions, null).then(
|
||||
function (cMap) {
|
||||
cMapPromise = CMapFactory.create({
|
||||
encoding: cidEncoding,
|
||||
fetchBuiltInCMap: this.fetchBuiltInCMap,
|
||||
useCMap: null,
|
||||
}).then(function (cMap) {
|
||||
properties.cMap = cMap;
|
||||
properties.vertical = properties.cMap.vertical;
|
||||
});
|
||||
|
|
|
@ -71,8 +71,8 @@ var Catalog = (function CatalogClosure() {
|
|||
this.xref = xref;
|
||||
this.catDict = xref.getCatalogObj();
|
||||
this.fontCache = new RefSetCache();
|
||||
assert(isDict(this.catDict),
|
||||
'catalog object is not a dictionary');
|
||||
this.builtInCMapCache = Object.create(null);
|
||||
assert(isDict(this.catDict), 'catalog object is not a dictionary');
|
||||
|
||||
// TODO refactor to move getPage() to the PDFDocument.
|
||||
this.pageFactory = pageFactory;
|
||||
|
@ -428,6 +428,7 @@ var Catalog = (function CatalogClosure() {
|
|||
delete font.translated;
|
||||
}
|
||||
this.fontCache.clear();
|
||||
this.builtInCMapCache = Object.create(null);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
|
@ -438,7 +439,8 @@ var Catalog = (function CatalogClosure() {
|
|||
var dict = a[0];
|
||||
var ref = a[1];
|
||||
return this.pageFactory.createPage(pageIndex, dict, ref,
|
||||
this.fontCache);
|
||||
this.fontCache,
|
||||
this.builtInCMapCache);
|
||||
}.bind(this)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -729,15 +729,10 @@ var WorkerMessageHandler = {
|
|||
|
||||
ensureNotTerminated();
|
||||
|
||||
var cMapOptions = {
|
||||
url: data.cMapUrl === undefined ? null : data.cMapUrl,
|
||||
packed: data.cMapPacked === true
|
||||
};
|
||||
var evaluatorOptions = {
|
||||
forceDataSchema: data.disableCreateObjectURL,
|
||||
maxImageSize: data.maxImageSize === undefined ? -1 : data.maxImageSize,
|
||||
disableFontFace: data.disableFontFace,
|
||||
cMapOptions: cMapOptions,
|
||||
disableNativeImageDecoder: data.disableNativeImageDecoder,
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue