diff --git a/src/core/catalog.js b/src/core/catalog.js index 7b959f017f639..fc89ac18877fa 100644 --- a/src/core/catalog.js +++ b/src/core/catalog.js @@ -634,6 +634,19 @@ class Catalog { return shadow(this, "pageLabels", obj); } + get pageLabelDetails() { + let obj = null; + // try { + obj = this._readPageLabelDetails(); + // } catch (ex) { + // if (ex instanceof MissingDataException) { + // throw ex; + // } + // warn("Unable to read page label details."); + // } + return shadow(this, "pageLabelDetails", obj); + } + /** * @private */ @@ -732,7 +745,89 @@ class Catalog { currentLabel = ""; } - pageLabels[i] = prefix + currentLabel; + pageLabels[i] = { + value: prefix + currentLabel, + labelDict, + }; + currentIndex++; + } + return pageLabels; + } + + /** + * @private + */ + _readPageLabelDetails() { + const obj = this._catDict.getRaw("PageLabels"); + + if (!obj) { + return null; + } + + const pageLabels = new Array(this.numPages); + let style = null, + prefix = ""; + + const numberTree = new NumberTree(obj, this.xref); + const numberTreeMap = numberTree.getAll(); + let currentIndex = 1; + + for (let i = 0, ii = this.numPages; i < ii; i++) { + if (numberTreeMap.has(i)) { + const labelDict = numberTreeMap.get(i); + if (!(labelDict instanceof Dict)) { + throw new FormatError("PageLabel is not a dictionary."); + } + + if ( + labelDict.has("Type") && + !isName(labelDict.get("Type"), "PageLabel") + ) { + throw new FormatError("Invalid type in PageLabel dictionary."); + } + + if (labelDict.has("S")) { + const s = labelDict.get("S"); + if (!(s instanceof Name)) { + throw new FormatError("Invalid style in PageLabel dictionary."); + } + style = { + D: "decimal_arabic", + R: "uppercase_roman", + r: "lowercase_roman", + A: "uppercase_latin", + a: "lowercase_latin", + }[s.name]; + } else { + style = "no_style"; + } + + if (labelDict.has("P")) { + const p = labelDict.get("P"); + if (!(p instanceof String)) { + throw new FormatError("Invalid prefix in PageLabel dictionary."); + } + prefix = stringToPDFString(p); + } else { + prefix = ""; + } + + if (labelDict.has("St")) { + const st = labelDict.get("St"); + if (!(Number.isInteger(st) && st >= 1)) { + throw new FormatError("Invalid start in PageLabel dictionary."); + } + currentIndex = st; + } else { + currentIndex = 1; + } + } + + pageLabels[i] = { + firstPageNum: currentIndex, + prefix, + style, + }; currentIndex++; } return pageLabels; diff --git a/src/core/worker.js b/src/core/worker.js index 5ec40561da80c..4ed71b5c8d588 100644 --- a/src/core/worker.js +++ b/src/core/worker.js @@ -471,6 +471,13 @@ class WorkerMessageHandler { return pdfManager.ensureCatalog("pageLabels"); }); + handler.on( + "GetPageLabelDetails", + function wphSetupGetPageLabelDetails(data) { + return pdfManager.ensureCatalog("pageLabelDetails"); + } + ); + handler.on("GetPageLayout", function wphSetupGetPageLayout(data) { return pdfManager.ensureCatalog("pageLayout"); }); diff --git a/src/display/api.js b/src/display/api.js index 241db612e9a5d..e7cd30386f643 100644 --- a/src/display/api.js +++ b/src/display/api.js @@ -820,6 +820,10 @@ class PDFDocumentProxy { return this._transport.getPageLabels(); } + getPageLabelDetails() { + return this._transport.getPageLabelDetails(); + } + /** * @returns {Promise} A promise that is resolved with a {string} * containing the page layout name. @@ -2948,6 +2952,10 @@ class WorkerTransport { return this.messageHandler.sendWithPromise("GetPageLabels", null); } + getPageLabelDetails() { + return this.messageHandler.sendWithPromise("GetPageLabelDetails", null); + } + getPageLayout() { return this.messageHandler.sendWithPromise("GetPageLayout", null); }