From 85cf90643f39ee19f51afe02170fa44f6867095c Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Fri, 25 Dec 2015 21:57:08 +0100 Subject: [PATCH] [api-minor] Add support for PageLabels in the API --- src/core/obj.js | 90 ++++++++++++++++++++++++++++++++++++++++ src/core/worker.js | 6 +++ src/display/api.js | 14 +++++++ src/shared/util.js | 36 ++++++++++++++++ test/pdfs/.gitignore | 1 + test/pdfs/bug793632.pdf | Bin 0 -> 23529 bytes test/unit/api_spec.js | 30 ++++++++++++++ 7 files changed, 177 insertions(+) create mode 100644 test/pdfs/bug793632.pdf diff --git a/src/core/obj.js b/src/core/obj.js index b64899f75..923cf18b5 100644 --- a/src/core/obj.js +++ b/src/core/obj.js @@ -263,6 +263,96 @@ var Catalog = (function CatalogClosure() { } return dest; }, + + get pageLabels() { + var obj = null; + try { + obj = this.readPageLabels(); + } catch (ex) { + if (ex instanceof MissingDataException) { + throw ex; + } + warn('Unable to read page labels.'); + } + return shadow(this, 'pageLabels', obj); + }, + readPageLabels: function Catalog_readPageLabels() { + var obj = this.catDict.getRaw('PageLabels'); + if (!obj) { + return null; + } + var pageLabels = new Array(this.numPages); + var style = null; + var prefix = ''; + var start = 1; + + var numberTree = new NumberTree(obj, this.xref); + var nums = numberTree.getAll(); + var currentLabel = '', currentIndex = 1; + + for (var i = 0, ii = this.numPages; i < ii; i++) { + if (nums.hasOwnProperty(i)) { + var labelDict = nums[i]; + assert(isDict(labelDict), 'The PageLabel is not a dictionary.'); + + var type = labelDict.get('Type'); + assert(!type || (isName(type) && type.name === 'PageLabel'), + 'Invalid type in PageLabel dictionary.'); + + var s = labelDict.get('S'); + assert(!s || isName(s), 'Invalid style in PageLabel dictionary.'); + style = (s ? s.name : null); + + prefix = labelDict.get('P') || ''; + assert(isString(prefix), 'Invalid prefix in PageLabel dictionary.'); + + start = labelDict.get('St') || 1; + assert(isInt(start), 'Invalid start in PageLabel dictionary.'); + currentIndex = start; + } + + switch (style) { + case 'D': + currentLabel = currentIndex; + break; + case 'R': + case 'r': + currentLabel = Util.toRoman(currentIndex, style === 'r'); + break; + case 'A': + case 'a': + var LIMIT = 26; // Use only the characters A--Z, or a--z. + var A_UPPER_CASE = 0x41, A_LOWER_CASE = 0x61; + + var baseCharCode = (style === 'a' ? A_LOWER_CASE : A_UPPER_CASE); + var letterIndex = currentIndex - 1; + var character = String.fromCharCode(baseCharCode + + (letterIndex % LIMIT)); + var charBuf = []; + for (var j = 0, jj = (letterIndex / LIMIT) | 0; j <= jj; j++) { + charBuf.push(character); + } + currentLabel = charBuf.join(''); + break; + default: + assert(!style, + 'Invalid style "' + style + '" in PageLabel dictionary.'); + } + pageLabels[i] = prefix + currentLabel; + + currentLabel = ''; + currentIndex++; + } + + // Ignore PageLabels if they correspond to standard page numbering. + for (i = 0, ii = this.numPages; i < ii; i++) { + if (pageLabels[i] !== (i + 1).toString()) { + break; + } + } + return (i === ii ? [] : pageLabels); + }, + get attachments() { var xref = this.xref; var attachments = null, nameTreeRef; diff --git a/src/core/worker.js b/src/core/worker.js index 37f2fc756..f71e42b93 100644 --- a/src/core/worker.js +++ b/src/core/worker.js @@ -451,6 +451,12 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { } ); + handler.on('GetPageLabels', + function wphSetupGetPageLabels(data) { + return pdfManager.ensureCatalog('pageLabels'); + } + ); + handler.on('GetAttachments', function wphSetupGetAttachments(data) { return pdfManager.ensureCatalog('attachments'); diff --git a/src/display/api.js b/src/display/api.js index 9a1ad451a..d94a4bd3a 100644 --- a/src/display/api.js +++ b/src/display/api.js @@ -690,6 +690,16 @@ var PDFDocumentProxy = (function PDFDocumentProxyClosure() { getDestination: function PDFDocumentProxy_getDestination(id) { return this.transport.getDestination(id); }, + /** + * @return {Promise} A promise that is resolved with: an Array containing + * the pageLabels that correspond to the pageIndexes; or null, when no + * pageLabels are present in the PDF file. + * NOTE: If the pageLabels are all identical to standard page numbering, + * i.e. [1, 2, 3, ...], the promise is resolved with an empty Array. + */ + getPageLabels: function PDFDocumentProxy_getPageLabels() { + return this.transport.getPageLabels(); + }, /** * @return {Promise} A promise that is resolved with a lookup table for * mapping named attachments to their content. @@ -1804,6 +1814,10 @@ var WorkerTransport = (function WorkerTransportClosure() { return this.messageHandler.sendWithPromise('GetDestination', { id: id }); }, + getPageLabels: function WorkerTransport_getPageLabels() { + return this.messageHandler.sendWithPromise('GetPageLabels', null); + }, + getAttachments: function WorkerTransport_getAttachments() { return this.messageHandler.sendWithPromise('GetAttachments', null); }, diff --git a/src/shared/util.js b/src/shared/util.js index 23b359227..9a9926667 100644 --- a/src/shared/util.js +++ b/src/shared/util.js @@ -808,6 +808,42 @@ var Util = PDFJS.Util = (function UtilClosure() { return num < 0 ? -1 : 1; }; + var ROMAN_NUMBER_MAP = [ + '', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM', + '', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC', + '', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX' + ]; + /** + * Converts positive integers to (upper case) Roman numerals. + * @param {integer} number - The number that should be converted. + * @param {boolean} lowerCase - Indicates if the result should be converted + * to lower case letters. The default is false. + * @return {string} The resulting Roman number. + */ + Util.toRoman = function Util_toRoman(number, lowerCase) { + assert(isInt(number) && number > 0, + 'The number should be a positive integer.'); + var pos, romanBuf = []; + // Thousands + while (number >= 1000) { + number -= 1000; + romanBuf.push('M'); + } + // Hundreds + pos = (number / 100) | 0; + number %= 100; + romanBuf.push(ROMAN_NUMBER_MAP[pos]); + // Tens + pos = (number / 10) | 0; + number %= 10; + romanBuf.push(ROMAN_NUMBER_MAP[10 + pos]); + // Ones + romanBuf.push(ROMAN_NUMBER_MAP[20 + number]); + + var romanStr = romanBuf.join(''); + return (lowerCase ? romanStr.toLowerCase() : romanStr); + }; + Util.appendToArray = function Util_appendToArray(arr1, arr2) { Array.prototype.push.apply(arr1, arr2); }; diff --git a/test/pdfs/.gitignore b/test/pdfs/.gitignore index ad15775ac..d1c35a4b7 100644 --- a/test/pdfs/.gitignore +++ b/test/pdfs/.gitignore @@ -24,6 +24,7 @@ !arial_unicode_ab_cidfont.pdf !arial_unicode_en_cidfont.pdf !asciihexdecode.pdf +!bug793632.pdf !bug1020858.pdf !bug1050040.pdf !bug1200096.pdf diff --git a/test/pdfs/bug793632.pdf b/test/pdfs/bug793632.pdf new file mode 100644 index 0000000000000000000000000000000000000000..e17e69b6bcbdfc81f2a07821174f0e98f8cf5817 GIT binary patch literal 23529 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