From ba2edeae18739e767f10087df521f6355f917c45 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Tue, 18 Dec 2018 23:26:02 +0100 Subject: [PATCH] [api-minor] Add support, in `getMetadata`, for custom information dictionary entries (issue 5970, issue 10344) (#10346) The custom entries, provided that they exist *and* that their types are safe to include, are exposed through a new `Custom` infoDict entry to clearly separate them from the standard ones. Fixes 5970. Fixes 10344. --- src/core/document.js | 36 ++++++++++++++++++++++++++++-------- test/unit/api_spec.js | 30 ++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 8 deletions(-) diff --git a/src/core/document.js b/src/core/document.js index b9db08ec8..9069a0cd7 100644 --- a/src/core/document.js +++ b/src/core/document.js @@ -14,8 +14,8 @@ */ import { - assert, FormatError, getInheritableProperty, info, isArrayBuffer, isNum, - isSpace, isString, MissingDataException, OPS, shadow, stringToBytes, + assert, FormatError, getInheritableProperty, info, isArrayBuffer, isBool, + isNum, isSpace, isString, MissingDataException, OPS, shadow, stringToBytes, stringToPDFString, Util, warn } from '../shared/util'; import { Catalog, ObjectLoader, XRef } from './obj'; @@ -544,17 +544,37 @@ var PDFDocument = (function PDFDocumentClosure() { info('The document information dictionary is invalid.'); } if (isDict(infoDict)) { - // Only fill the document info with valid entries from the spec. - for (let key in DocumentInfoValidators) { - if (infoDict.has(key)) { - const value = infoDict.get(key); - // Make sure the value conforms to the spec. + // Fill the document info with valid entries from the specification, + // as well as any existing well-formed custom entries. + for (let key of infoDict.getKeys()) { + const value = infoDict.get(key); + + if (DocumentInfoValidators[key]) { + // Make sure the (standard) value conforms to the specification. if (DocumentInfoValidators[key](value)) { docInfo[key] = (typeof value !== 'string' ? value : stringToPDFString(value)); } else { - info('Bad value in document info for "' + key + '"'); + info(`Bad value in document info for "${key}".`); } + } else if (typeof key === 'string') { + // For custom values, only accept white-listed types to prevent + // errors that would occur when trying to send non-serializable + // objects to the main-thread (for example `Dict` or `Stream`). + let customValue; + if (isString(value)) { + customValue = stringToPDFString(value); + } else if (isName(value) || isNum(value) || isBool(value)) { + customValue = value; + } else { + info(`Unsupported value in document info for (custom) "${key}".`); + continue; + } + + if (!docInfo['Custom']) { + docInfo['Custom'] = Object.create(null); + } + docInfo['Custom'][key] = customValue; } } } diff --git a/test/unit/api_spec.js b/test/unit/api_spec.js index ead5e5d79..4ae03fbf0 100644 --- a/test/unit/api_spec.js +++ b/test/unit/api_spec.js @@ -829,6 +829,8 @@ describe('api', function() { var promise = doc.getMetadata(); promise.then(function({ info, metadata, contentDispositionFilename, }) { expect(info['Title']).toEqual('Basic API Test'); + // Custom, non-standard, information dictionary entries. + expect(info['Custom']).toEqual(undefined); // The following are PDF.js specific, non-standard, properties. expect(info['PDFFormatVersion']).toEqual('1.7'); expect(info['IsLinearized']).toEqual(false); @@ -842,6 +844,34 @@ describe('api', function() { done(); }).catch(done.fail); }); + it('gets metadata, with custom info dict entries', function(done) { + var loadingTask = getDocument(buildGetDocumentParams('tracemonkey.pdf')); + + loadingTask.promise.then(function(pdfDocument) { + return pdfDocument.getMetadata(); + }).then(function({ info, metadata, contentDispositionFilename, }) { + expect(info['Creator']).toEqual('TeX'); + expect(info['Producer']).toEqual('pdfeTeX-1.21a'); + expect(info['CreationDate']).toEqual('D:20090401163925-07\'00\''); + // Custom, non-standard, information dictionary entries. + const custom = info['Custom']; + expect(typeof custom === 'object' && custom !== null).toEqual(true); + + expect(custom['PTEX.Fullbanner']).toEqual('This is pdfeTeX, ' + + 'Version 3.141592-1.21a-2.2 (Web2C 7.5.4) kpathsea version 3.5.6'); + // The following are PDF.js specific, non-standard, properties. + expect(info['PDFFormatVersion']).toEqual('1.4'); + expect(info['IsLinearized']).toEqual(false); + expect(info['IsAcroFormPresent']).toEqual(false); + expect(info['IsXFAPresent']).toEqual(false); + + expect(metadata).toEqual(null); + expect(contentDispositionFilename).toEqual(null); + + loadingTask.destroy().then(done); + }).catch(done.fail); + }); + it('gets data', function(done) { var promise = doc.getData(); promise.then(function (data) {