/* Copyright 2020 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { PDFObject } from "./pdf_object.js"; import { PrintParams } from "./print_params.js"; import { ZoomType } from "./constants.js"; class InfoProxyHandler { static get(obj, prop) { return obj[prop.toLowerCase()]; } static set(obj, prop, value) { throw new Error(`doc.info.${prop} is read-only`); } } class Doc extends PDFObject { constructor(data) { super(data); this.calculate = true; this.baseURL = data.baseURL || ""; this.calculate = true; this.delay = false; this.dirty = false; this.disclosed = false; this.media = undefined; this.metadata = data.metadata; this.noautocomplete = undefined; this.nocache = undefined; this.spellDictionaryOrder = []; this.spellLanguageOrder = []; this._printParams = null; this._fields = new Map(); this._fieldNames = []; this._event = null; this._author = data.Author || ""; this._creator = data.Creator || ""; this._creationDate = this._getDate(data.CreationDate) || null; this._docID = data.docID || ["", ""]; this._documentFileName = data.filename || ""; this._filesize = data.filesize || 0; this._keywords = data.Keywords || ""; this._layout = data.layout || ""; this._modDate = this._getDate(data.ModDate) || null; this._numFields = 0; this._numPages = data.numPages || 1; this._pageNum = data.pageNum || 0; this._producer = data.Producer || ""; this._subject = data.Subject || ""; this._title = data.Title || ""; this._URL = data.URL || ""; // info has case insensitive properties // and they're are read-only. this._info = new Proxy( { title: this.title, author: this.author, subject: this.subject, keywords: this.keywords, creator: this.creator, producer: this.producer, creationdate: this._creationDate, moddate: this._modDate, trapped: data.Trapped || "Unknown", }, InfoProxyHandler ); this._zoomType = ZoomType.none; this._zoom = data.zoom || 100; } _addField(name, field) { this._fields.set(name, field); this._fieldNames.push(name); this._numFields++; } _getDate(date) { // date format is D:YYYYMMDDHHmmSS[OHH'mm'] if (!date || date.length < 15 || !date.startsWith("D:")) { return date; } date = date.substring(2); const year = date.substring(0, 4); const month = date.substring(4, 6); const day = date.substring(6, 8); const hour = date.substring(8, 10); const minute = date.substring(10, 12); const o = date.charAt(12); let second, offsetPos; if (o === "Z" || o === "+" || o === "-") { second = "00"; offsetPos = 12; } else { second = date.substring(12, 14); offsetPos = 14; } const offset = date.substring(offsetPos).replaceAll("'", ""); return new Date( `${year}-${month}-${day}T${hour}:${minute}:${second}${offset}` ); } get author() { return this._author; } set author(_) { throw new Error("doc.author is read-only"); } get bookmarkRoot() { return undefined; } set bookmarkRoot(_) { throw new Error("doc.bookmarkRoot is read-only"); } get creator() { return this._creator; } set creator(_) { throw new Error("doc.creator is read-only"); } get dataObjects() { return []; } set dataObjects(_) { throw new Error("doc.dataObjects is read-only"); } get docID() { return this._docID; } set docID(_) { throw new Error("doc.docID is read-only"); } get documentFileName() { return this._documentFileName; } set documentFileName(_) { throw new Error("doc.documentFileName is read-only"); } get dynamicXFAForm() { return false; } set dynamicXFAForm(_) { throw new Error("doc.dynamicXFAForm is read-only"); } get external() { return true; } set external(_) { throw new Error("doc.external is read-only"); } get filesize() { return this._filesize; } set filesize(_) { throw new Error("doc.filesize is read-only"); } get hidden() { return false; } set hidden(_) { throw new Error("doc.hidden is read-only"); } get hostContainer() { return undefined; } set hostContainer(_) { throw new Error("doc.hostContainer is read-only"); } get icons() { return undefined; } set icons(_) { throw new Error("doc.icons is read-only"); } get info() { return this._info; } set info(_) { throw new Error("doc.info is read-only"); } get innerAppWindowRect() { return [0, 0, 0, 0]; } set innerAppWindowRect(_) { throw new Error("doc.innerAppWindowRect is read-only"); } get innerDocWindowRect() { return [0, 0, 0, 0]; } set innerDocWindowRect(_) { throw new Error("doc.innerDocWindowRect is read-only"); } get isModal() { return false; } set isModal(_) { throw new Error("doc.isModal is read-only"); } get keywords() { return this._keywords; } set keywords(_) { throw new Error("doc.keywords is read-only"); } get layout() { return this._layout; } set layout(value) { if (typeof value !== "string") { return; } if ( value !== "SinglePage" && value !== "OneColumn" && value !== "TwoColumnLeft" && value !== "TwoPageLeft" && value !== "TwoColumnRight" && value !== "TwoPageRight" ) { value = "SinglePage"; } this._send({ command: "layout", value }); this._layout = value; } get modDate() { return this._modDate; } set modDate(_) { throw new Error("doc.modDate is read-only"); } get mouseX() { return 0; } set mouseX(_) { throw new Error("doc.mouseX is read-only"); } get mouseY() { return 0; } set mouseY(_) { throw new Error("doc.mouseY is read-only"); } get numFields() { return this._numFields; } set numFields(_) { throw new Error("doc.numFields is read-only"); } get numPages() { return this._numPages; } set numPages(_) { throw new Error("doc.numPages is read-only"); } get numTemplates() { return 0; } set numTemplates(_) { throw new Error("doc.numTemplates is read-only"); } get outerAppWindowRect() { return [0, 0, 0, 0]; } set outerAppWindowRect(_) { throw new Error("doc.outerAppWindowRect is read-only"); } get outerDocWindowRect() { return [0, 0, 0, 0]; } set outerDocWindowRect(_) { throw new Error("doc.outerDocWindowRect is read-only"); } get pageNum() { return this._pageNum; } set pageNum(value) { if (typeof value !== "number" || value < 0 || value >= this._numPages) { return; } this._send({ command: "page-num", value }); this._pageNum = value; } get pageWindowRect() { return [0, 0, 0, 0]; } set pageWindowRect(_) { throw new Error("doc.pageWindowRect is read-only"); } get path() { return ""; } set path(_) { throw new Error("doc.path is read-only"); } get permStatusReady() { return true; } set permStatusReady(_) { throw new Error("doc.permStatusReady is read-only"); } get producer() { return this._producer; } set producer(_) { throw new Error("doc.producer is read-only"); } get requiresFullSave() { return false; } set requiresFullSave(_) { throw new Error("doc.requiresFullSave is read-only"); } get securityHandler() { return null; } set securityHandler(_) { throw new Error("doc.securityHandler is read-only"); } get selectedAnnots() { return []; } set selectedAnnots(_) { throw new Error("doc.selectedAnnots is read-only"); } get sounds() { return []; } set sounds(_) { throw new Error("doc.sounds is read-only"); } get subject() { return this._subject; } set subject(_) { throw new Error("doc.subject is read-only"); } get templates() { return []; } set templates(_) { throw new Error("doc.templates is read-only"); } get title() { return this._title; } set title(_) { throw new Error("doc.title is read-only"); } get URL() { return this._URL; } set URL(_) { throw new Error("doc.URL is read-only"); } get viewState() { return undefined; } set viewState(_) { throw new Error("doc.viewState is read-only"); } get xfa() { return this._xfa; } set xfa(_) { throw new Error("doc.xfa is read-only"); } get XFAForeground() { return false; } set XFAForeground(_) { throw new Error("doc.XFAForeground is read-only"); } get zoomType() { return this._zoomType; } set zoomType(type) { if (typeof type !== "string") { return; } switch (type) { case ZoomType.none: this._send({ command: "zoom", value: 1 }); break; case ZoomType.fitP: this._send({ command: "zoom", value: "page-fit" }); break; case ZoomType.fitW: this._send({ command: "zoom", value: "page-width" }); break; case ZoomType.fitH: this._send({ command: "zoom", value: "page-height" }); break; case ZoomType.fitV: this._send({ command: "zoom", value: "auto" }); break; case ZoomType.pref: case ZoomType.refW: break; default: return; } this._zoomType = type; } get zoom() { return this._zoom; } set zoom(value) { if (typeof value !== "number" || value < 8.33 || value > 6400) { return; } this._send({ command: "zoom", value: value / 100 }); } addAnnot() { /* Not implemented */ } addField() { /* Not implemented */ } addIcon() { /* Not implemented */ } addLink() { /* Not implemented */ } addRecipientListCryptFilter() { /* Not implemented */ } addRequirement() { /* Not implemented */ } addScript() { /* Not implemented */ } addThumbnails() { /* Not implemented */ } addWatermarkFromFile() { /* Not implemented */ } addWatermarkFromText() { /* Not implemented */ } addWeblinks() { /* Not implemented */ } bringToFront() { /* Not implemented */ } calculateNow() { this._eventDispatcher.calculateNow(); } closeDoc() { /* Not implemented */ } colorConvertPage() { /* Not implemented */ } createDataObject() { /* Not implemented */ } createTemplate() { /* Not implemented */ } deletePages() { /* Not implemented */ } deleteSound() { /* Not implemented */ } embedDocAsDataObject() { /* Not implemented */ } embedOutputIntent() { /* Not implemented */ } encryptForRecipients() { /* Not implemented */ } encryptUsingPolicy() { /* Not implemented */ } exportAsFDF() { /* Not implemented */ } exportAsFDFStr() { /* Not implemented */ } exportAsText() { /* Not implemented */ } exportAsXFDF() { /* Not implemented */ } exportAsXFDFStr() { /* Not implemented */ } exportDataObject() { /* Not implemented */ } exportXFAData() { /* Not implemented */ } extractPages() { /* Not implemented */ } flattenPages() { /* Not implemented */ } getAnnot() { /* TODO */ } getAnnots() { /* TODO */ } getAnnot3D() { /* Not implemented */ } getAnnots3D() { /* Not implemented */ } getColorConvertAction() { /* Not implemented */ } getDataObject() { /* Not implemented */ } getDataObjectContents() { /* Not implemented */ } getField(cName) { if (typeof cName !== "string") { throw new TypeError("Invalid field name: must be a string"); } const searchedField = this._fields.get(cName); if (searchedField) { return searchedField; } for (const [name, field] of this._fields.entries()) { if (name.includes(cName)) { return field; } } return undefined; } getIcon() { /* Not implemented */ } getLegalWarnings() { /* Not implemented */ } getLinks() { /* Not implemented */ } getNthFieldName(nIndex) { if (typeof nIndex !== "number") { throw new TypeError("Invalid field index: must be a number"); } if (0 <= nIndex && nIndex < this.numFields) { return this._fieldNames[Math.trunc(nIndex)]; } return null; } getNthTemplate() { return null; } getOCGs() { /* Not implemented */ } getOCGOrder() { /* Not implemented */ } getPageBox() { /* TODO */ } getPageLabel() { /* TODO */ } getPageNthWord() { /* TODO or not */ } getPageNthWordQuads() { /* TODO or not */ } getPageNumWords() { /* TODO or not */ } getPageRotation() { /* TODO */ } getPageTransition() { /* Not implemented */ } getPrintParams() { if (!this._printParams) { this._printParams = new PrintParams({ lastPage: this._numPages - 1 }); } return this._printParams; } getSound() { /* Not implemented */ } getTemplate() { /* Not implemented */ } getURL() { /* Not implemented because unsafe */ } gotoNamedDest() { /* TODO */ } importAnFDF() { /* Not implemented */ } importAnXFDF() { /* Not implemented */ } importDataObject() { /* Not implemented */ } importIcon() { /* Not implemented */ } importSound() { /* Not implemented */ } importTextData() { /* Not implemented */ } importXFAData() { /* Not implemented */ } insertPages() { /* Not implemented */ } mailDoc() { /* TODO or not */ } mailForm() { /* TODO or not */ } movePage() { /* Not implemented */ } newPage() { /* Not implemented */ } openDataObject() { /* Not implemented */ } print( bUI = true, nStart = 0, nEnd = -1, bSilent = false, bShrinkToFit = false, bPrintAsImage = false, bReverse = false, bAnnotations = true, printParams = null ) { // TODO: for now just use nStart and nEnd // so need to see how to deal with the other params // (if possible) if (printParams) { nStart = printParams.firstPage; nEnd = printParams.lastPage; } if (typeof nStart === "number") { nStart = Math.max(0, Math.trunc(nStart)); } else { nStart = 0; } if (typeof nEnd === "number") { nEnd = Math.max(0, Math.trunc(nEnd)); } else { nEnd = -1; } this._send({ id: "print", start: nStart, end: nEnd }); } removeDataObject() { /* Not implemented */ } removeField() { /* TODO or not */ } removeIcon() { /* Not implemented */ } removeLinks() { /* Not implemented */ } removeRequirement() { /* Not implemented */ } removeScript() { /* Not implemented */ } removeTemplate() { /* Not implemented */ } removeThumbnails() { /* Not implemented */ } removeWeblinks() { /* Not implemented */ } replacePages() { /* Not implemented */ } resetForm(aFields = null) { let mustCalculate = false; if (aFields) { for (const fieldName of aFields) { const field = this.getField(fieldName); if (field) { field.value = field.defaultValue; mustCalculate = true; } } } else { mustCalculate = this._fields.size !== 0; for (const field of this._fields.values()) { field.value = field.defaultValue; } } if (mustCalculate) { this.calculateNow(); } } saveAs() { /* Not implemented */ } scroll() { /* TODO */ } selectPageNthWord() { /* TODO */ } setAction() { /* TODO */ } setDataObjectContents() { /* Not implemented */ } setOCGOrder() { /* Not implemented */ } setPageAction() { /* TODO */ } setPageBoxes() { /* Not implemented */ } setPageLabels() { /* Not implemented */ } setPageRotations() { /* TODO */ } setPageTabOrder() { /* Not implemented */ } setPageTransitions() { /* Not implemented */ } spawnPageFromTemplate() { /* Not implemented */ } submitForm() { /* TODO or not */ } syncAnnotScan() { /* Not implemented */ } } export { Doc };