From 24f7fce149e6815b5152ad29c82dab42047735f2 Mon Sep 17 00:00:00 2001 From: Modesty Zhang Date: Mon, 29 Apr 2024 15:03:45 -0700 Subject: [PATCH] build: output to both es module and commJS, PDFParser to be named export (#338) * build: output to both es module and commJS, also make PDFParser to be named export * build: remove out of fate rollup scirpt after rewrite * build: simplif rollup, add more tests * build: replace travis with github action, lint readme * build: fix the ci.yml * build: use node 20 for github action run * build: log process.exit code from jest test * build: refactor check-test job for ci.yml * build: simplify ci.yml --- .github/workflows/ci.yml | 26 + .gitignore | 4 +- .npmignore | 3 +- .travis.yml | 3 - base/core/worker.js | 851 ++--- base/shared/util.js | 2062 +++++++------ jest.config.json | 6 + lib/p2jcmd.js | 530 ++-- lib/p2jcmdarg.js | 310 +- lib/pdf.js | 869 +++--- lib/pdfcanvas.js | 1147 ++++--- pkinfo.js => lib/pkinfo.js | 11 +- package-lock.json | 5294 +++++++++++++++++++++++++++++++- package.json | 186 +- pdfparser.cjs | 2610 ---------------- pdfparser.js | 423 +-- readme.md | 1399 +++++---- rollup.config.js | 83 +- rollup/addDestructedImports.js | 46 - rollup/bundle-pdfjs-base.js | 77 + test/_test_.cjs | 218 ++ test/index.js | 217 -- 22 files changed, 9620 insertions(+), 6755 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100755 .travis.yml create mode 100644 jest.config.json rename pkinfo.js => lib/pkinfo.js (50%) delete mode 100644 pdfparser.cjs delete mode 100644 rollup/addDestructedImports.js create mode 100644 rollup/bundle-pdfjs-base.js create mode 100644 test/_test_.cjs delete mode 100644 test/index.js diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..74544677 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,26 @@ +name: Node.js CI/CD + +on: + pull_request: + branches: [ "*" ] + +jobs: + build: + runs-on: ubuntu-latest + outputs: + test-result: ${{ steps.test.outcome }} + + steps: + - uses: actions/checkout@v3 + + - name: Use Node.js 20.x # Updated for Node 20 + uses: actions/setup-node@v3 + with: + node-version: 20.x + + - name: Run ci + run: npm ci + + - name: Run tests + id: test + run: npm test diff --git a/.gitignore b/.gitignore index 40916d43..cba7515f 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,6 @@ node_modules/ target/ .idea .npmrc -package-lock.json +vscode/ +dist/ +lib/pdfjs-code.js diff --git a/.npmignore b/.npmignore index 25d54f7d..9d9ce474 100644 --- a/.npmignore +++ b/.npmignore @@ -7,6 +7,7 @@ .idea node_modules/ target/ - .gitignore test +.vscode/ + diff --git a/.travis.yml b/.travis.yml deleted file mode 100755 index ca5f442d..00000000 --- a/.travis.yml +++ /dev/null @@ -1,3 +0,0 @@ -language: node_js -node_js: - - "14.18.0" diff --git a/base/core/worker.js b/base/core/worker.js index d98ba770..ba0206e5 100755 --- a/base/core/worker.js +++ b/base/core/worker.js @@ -20,431 +20,452 @@ NetworkPdfManager, XRefParseException, isInt, PasswordResponses, MessageHandler, Ref */ -'use strict'; +"use strict"; //MQZ. Oct.11.2012. Add Worker's postMessage API globalScope.postMessage = function WorkerTransport_postMessage(obj) { -// log("Inside globalScope.postMessage:" + JSON.stringify(obj)); + // log("Inside globalScope.postMessage:" + JSON.stringify(obj)); }; -var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { - setup: function wphSetup(handler) { - var pdfManager; - - function loadDocument(recoveryMode) { - var loadDocumentPromise = new Promise(); - - var parseSuccess = function parseSuccess() { - var numPagesPromise = pdfManager.ensureModel('numPages'); - var fingerprintPromise = pdfManager.ensureModel('fingerprint'); - var outlinePromise = pdfManager.ensureCatalog('documentOutline'); - var infoPromise = pdfManager.ensureModel('documentInfo'); - var metadataPromise = pdfManager.ensureCatalog('metadata'); - var encryptedPromise = pdfManager.ensureXRef('encrypt'); - var javaScriptPromise = pdfManager.ensureCatalog('javaScript'); - Promise.all([numPagesPromise, fingerprintPromise, outlinePromise, - infoPromise, metadataPromise, encryptedPromise, - javaScriptPromise]).then( - function onDocReady(results) { - - var doc = { - numPages: results[0], - fingerprint: results[1], - outline: results[2], - info: results[3], - metadata: results[4], - encrypted: !!results[5], - javaScript: results[6] - }; - loadDocumentPromise.resolve(doc); - }, - parseFailure); - }; - - var parseFailure = function parseFailure(e) { - loadDocumentPromise.reject(e); - }; - - pdfManager.ensureModel('checkHeader', []).then(function() { - pdfManager.ensureModel('parseStartXRef', []).then(function() { - pdfManager.ensureModel('parse', [recoveryMode]).then( - parseSuccess, parseFailure); - }, parseFailure); - }, parseFailure); - - return loadDocumentPromise; - } - - function getPdfManager(data) { - var pdfManagerPromise = new Promise(); - - var source = data.source; - var disableRange = data.disableRange; - if (source.data) { - try { - pdfManager = new LocalPdfManager(source.data, source.password); - pdfManagerPromise.resolve(); - } catch (ex) { - pdfManagerPromise.reject(ex); - } - return pdfManagerPromise; - } else if (source.chunkedViewerLoading) { - try { - pdfManager = new NetworkPdfManager(source, handler); - pdfManagerPromise.resolve(); - } catch (ex) { - pdfManagerPromise.reject(ex); - } - return pdfManagerPromise; - } - - var networkManager = new NetworkManager(source.url, { - httpHeaders: source.httpHeaders - }); - var fullRequestXhrId = networkManager.requestFull({ - onHeadersReceived: function onHeadersReceived() { - if (disableRange) { - return; - } - - var fullRequestXhr = networkManager.getRequestXhr(fullRequestXhrId); - if (fullRequestXhr.getResponseHeader('Accept-Ranges') !== 'bytes') { - return; - } - - var contentEncoding = - fullRequestXhr.getResponseHeader('Content-Encoding') || 'identity'; - if (contentEncoding !== 'identity') { - return; - } - - var length = fullRequestXhr.getResponseHeader('Content-Length'); - length = parseInt(length, 10); - if (!isInt(length)) { - return; - } - - // NOTE: by cancelling the full request, and then issuing range - // requests, there will be an issue for sites where you can only - // request the pdf once. However, if this is the case, then the - // server should not be returning that it can support range requests. - networkManager.abortRequest(fullRequestXhrId); - - source.length = length; - try { - pdfManager = new NetworkPdfManager(source, handler); - pdfManagerPromise.resolve(pdfManager); - } catch (ex) { - pdfManagerPromise.reject(ex); - } - }, - - onDone: function onDone(args) { - // the data is array, instantiating directly from it - try { - pdfManager = new LocalPdfManager(args.chunk, source.password); - pdfManagerPromise.resolve(); - } catch (ex) { - pdfManagerPromise.reject(ex); - } - }, - - onError: function onError(status) { - if (status == 404) { - var exception = new MissingPDFException( 'Missing PDF "' + - source.url + '".'); - handler.send('MissingPDF', { exception: exception }); - } else { - handler.send('DocError', 'Unexpected server response (' + - status + ') while retrieving PDF "' + - source.url + '".'); - } - }, - - onProgress: function onProgress(evt) { - handler.send('DocProgress', { - loaded: evt.loaded, - total: evt.lengthComputable ? evt.total : void(0) - }); - } - }); - - return pdfManagerPromise; - } - - handler.on('test', function wphSetupTest(data) { - // check if Uint8Array can be sent to worker - if (!(data instanceof Uint8Array)) { - handler.send('test', false); - return; - } - // making sure postMessage transfers are working - var supportTransfers = data[0] === 255; - handler.postMessageTransfers = supportTransfers; - // check if the response property is supported by xhr - var xhr = new XMLHttpRequest(); - var responseExists = 'response' in xhr; - // check if the property is actually implemented - try { - var dummy = xhr.responseType; - } catch (e) { - responseExists = false; - } - if (!responseExists) { - handler.send('test', false); - return; - } - handler.send('test', { - supportTypedArray: true, - supportTransfers: supportTransfers - }); - }); - - handler.on('GetDocRequest', function wphSetupDoc(data) { - - var onSuccess = function(doc) { - handler.send('GetDoc', { pdfInfo: doc }); - }; - - var onFailure = function(e) { - if (e instanceof PasswordException) { - if (e.code === PasswordResponses.NEED_PASSWORD) { - handler.send('NeedPassword', { - exception: e - }); - } else if (e.code === PasswordResponses.INCORRECT_PASSWORD) { - handler.send('IncorrectPassword', { - exception: e - }); - } - } else if (e instanceof InvalidPDFException) { - handler.send('InvalidPDF', { - exception: e - }); - } else if (e instanceof MissingPDFException) { - handler.send('MissingPDF', { - exception: e - }); - } else { - handler.send('UnknownError', { - exception: new UnknownErrorException(e.message, e.toString()) - }); - } - }; - - PDFJS.maxImageSize = data.maxImageSize === undefined ? - -1 : data.maxImageSize; - PDFJS.disableFontFace = data.disableFontFace; - - getPdfManager(data).then(function pdfManagerReady() { - loadDocument(false).then(onSuccess, function loadFailure(ex) { - // Try again with recoveryMode == true - if (!(ex instanceof XRefParseException)) { - if (ex instanceof PasswordException) { - // after password exception prepare to receive a new password - // to repeat loading - pdfManager.passwordChangedPromise = new Promise(); - pdfManager.passwordChangedPromise.then(pdfManagerReady); - } - - onFailure(ex); - return; - } - - pdfManager.requestLoadedStream(); - pdfManager.onLoadedStream().then(function() { - loadDocument(true).then(onSuccess, onFailure); - }); - }, onFailure); - }, onFailure); - }); - - handler.on('GetPageRequest', function wphSetupGetPage(data) { - var pageIndex = data.pageIndex; - pdfManager.getPage(pageIndex).then(function(page) { - var rotatePromise = pdfManager.ensure(page, 'rotate'); - var refPromise = pdfManager.ensure(page, 'ref'); - var viewPromise = pdfManager.ensure(page, 'view'); - - Promise.all([rotatePromise, refPromise, viewPromise]).then( - function(results) { - var page = { - pageIndex: data.pageIndex, - rotate: results[0], - ref: results[1], - view: results[2] - }; - - handler.send('GetPage', { pageInfo: page }); - }); - }); - }); - - handler.on('GetPageIndex', function wphSetupGetPageIndex(data, promise) { - var ref = new Ref(data.ref.num, data.ref.gen); - pdfManager.pdfModel.catalog.getPageIndex(ref).then(function (pageIndex) { - promise.resolve(pageIndex); - }, promise.reject.bind(promise)); - }); - - handler.on('GetDestinations', - function wphSetupGetDestinations(data, promise) { - pdfManager.ensureCatalog('destinations').then(function(destinations) { - promise.resolve(destinations); - }); - } - ); - - handler.on('GetData', function wphSetupGetData(data, promise) { - pdfManager.requestLoadedStream(); - pdfManager.onLoadedStream().then(function(stream) { - promise.resolve(stream.bytes); - }); - }); - - handler.on('DataLoaded', function wphSetupDataLoaded(data, promise) { - pdfManager.onLoadedStream().then(function(stream) { - promise.resolve({ length: stream.bytes.byteLength }); - }); - }); - - handler.on('UpdatePassword', function wphSetupUpdatePassword(data) { - pdfManager.updatePassword(data); - }); - - handler.on('GetAnnotationsRequest', function wphSetupGetAnnotations(data) { - pdfManager.getPage(data.pageIndex).then(function(page) { - pdfManager.ensure(page, 'getAnnotationsData', []).then( - function(annotationsData) { - handler.send('GetAnnotations', { - pageIndex: data.pageIndex, - annotations: annotationsData - }); - } - ); - }); - }); - - handler.on('RenderPageRequest', function wphSetupRenderPage(data) { - pdfManager.getPage(data.pageIndex).then(function(page) { - - var pageNum = data.pageIndex + 1; -// var start = Date.now(); - // Pre compile the pdf page and fetch the fonts/images. - page.getOperatorList(handler).then(function(operatorList) { - -// log('page=%d - getOperatorList: time=%dms, len=%d', pageNum, -// Date.now() - start, operatorList.fnArray.length); - - }, function(e) { - - var minimumStackMessage = - 'worker.js: while trying to getPage() and getOperatorList()'; - - var wrappedException; - - // Turn the error into an obj that can be serialized - if (typeof e === 'string') { - wrappedException = { - message: e, - stack: minimumStackMessage - }; - } else if (typeof e === 'object') { - wrappedException = { - message: e.message || e.toString(), - stack: e.stack || minimumStackMessage - }; - } else { - wrappedException = { - message: 'Unknown exception type: ' + (typeof e), - stack: minimumStackMessage - }; - } - - handler.send('PageError', { - pageNum: pageNum, - error: wrappedException - }); - }); - }); - }, this); - - handler.on('GetTextContent', function wphExtractText(data, promise) { - pdfManager.getPage(data.pageIndex).then(function(page) { - var pageNum = data.pageIndex + 1; - var start = Date.now(); - page.extractTextContent().then(function(textContent) { - promise.resolve(textContent); - //MQZ 03/17/2016 comment out log - //log('text indexing: page=%d - time=%dms', pageNum, - // Date.now() - start); - }, function (e) { - // Skip errored pages - promise.reject(e); - }); - }); - }); - - handler.on('Cleanup', function wphCleanup(data, promise) { - pdfManager.cleanup(); - promise.resolve(true); - }); - - handler.on('Terminate', function wphTerminate(data, promise) { - pdfManager.terminate(); - promise.resolve(); - }); - } -}; +var WorkerMessageHandler = (PDFJS.WorkerMessageHandler = { + setup: function wphSetup(handler) { + var pdfManager; + + function loadDocument(recoveryMode) { + var loadDocumentPromise = new Promise(); + + var parseSuccess = function parseSuccess() { + var numPagesPromise = pdfManager.ensureModel("numPages"); + var fingerprintPromise = pdfManager.ensureModel("fingerprint"); + var outlinePromise = pdfManager.ensureCatalog("documentOutline"); + var infoPromise = pdfManager.ensureModel("documentInfo"); + var metadataPromise = pdfManager.ensureCatalog("metadata"); + var encryptedPromise = pdfManager.ensureXRef("encrypt"); + var javaScriptPromise = pdfManager.ensureCatalog("javaScript"); + Promise.all([ + numPagesPromise, + fingerprintPromise, + outlinePromise, + infoPromise, + metadataPromise, + encryptedPromise, + javaScriptPromise, + ]).then(function onDocReady(results) { + var doc = { + numPages: results[0], + fingerprint: results[1], + outline: results[2], + info: results[3], + metadata: results[4], + encrypted: !!results[5], + javaScript: results[6], + }; + loadDocumentPromise.resolve(doc); + }, parseFailure); + }; + + var parseFailure = function parseFailure(e) { + loadDocumentPromise.reject(e); + }; + + pdfManager.ensureModel("checkHeader", []).then(function () { + pdfManager.ensureModel("parseStartXRef", []).then(function () { + pdfManager + .ensureModel("parse", [recoveryMode]) + .then(parseSuccess, parseFailure); + }, parseFailure); + }, parseFailure); + + return loadDocumentPromise; + } + + function getPdfManager(data) { + var pdfManagerPromise = new Promise(); + + var source = data.source; + var disableRange = data.disableRange; + if (source.data) { + try { + pdfManager = new LocalPdfManager(source.data, source.password); + pdfManagerPromise.resolve(); + } catch (ex) { + pdfManagerPromise.reject(ex); + } + return pdfManagerPromise; + } else if (source.chunkedViewerLoading) { + try { + pdfManager = new NetworkPdfManager(source, handler); + pdfManagerPromise.resolve(); + } catch (ex) { + pdfManagerPromise.reject(ex); + } + return pdfManagerPromise; + } + + var networkManager = new NetworkManager(source.url, { + httpHeaders: source.httpHeaders, + }); + var fullRequestXhrId = networkManager.requestFull({ + onHeadersReceived: function onHeadersReceived() { + if (disableRange) { + return; + } + + var fullRequestXhr = networkManager.getRequestXhr(fullRequestXhrId); + if (fullRequestXhr.getResponseHeader("Accept-Ranges") !== "bytes") { + return; + } + + var contentEncoding = + fullRequestXhr.getResponseHeader("Content-Encoding") || "identity"; + if (contentEncoding !== "identity") { + return; + } + + var length = fullRequestXhr.getResponseHeader("Content-Length"); + length = parseInt(length, 10); + if (!isInt(length)) { + return; + } + + // NOTE: by cancelling the full request, and then issuing range + // requests, there will be an issue for sites where you can only + // request the pdf once. However, if this is the case, then the + // server should not be returning that it can support range requests. + networkManager.abortRequest(fullRequestXhrId); + + source.length = length; + try { + pdfManager = new NetworkPdfManager(source, handler); + pdfManagerPromise.resolve(pdfManager); + } catch (ex) { + pdfManagerPromise.reject(ex); + } + }, + + onDone: function onDone(args) { + // the data is array, instantiating directly from it + try { + pdfManager = new LocalPdfManager(args.chunk, source.password); + pdfManagerPromise.resolve(); + } catch (ex) { + pdfManagerPromise.reject(ex); + } + }, + + onError: function onError(status) { + if (status == 404) { + var exception = new MissingPDFException( + 'Missing PDF "' + source.url + '".' + ); + handler.send("MissingPDF", { exception: exception }); + } else { + handler.send( + "DocError", + "Unexpected server response (" + + status + + ') while retrieving PDF "' + + source.url + + '".' + ); + } + }, + + onProgress: function onProgress(evt) { + handler.send("DocProgress", { + loaded: evt.loaded, + total: evt.lengthComputable ? evt.total : void 0, + }); + }, + }); + + return pdfManagerPromise; + } + + handler.on("test", function wphSetupTest(data) { + // check if Uint8Array can be sent to worker + if (!(data instanceof Uint8Array)) { + handler.send("test", false); + return; + } + // making sure postMessage transfers are working + var supportTransfers = data[0] === 255; + handler.postMessageTransfers = supportTransfers; + // check if the response property is supported by xhr + var xhr = new XMLHttpRequest(); + var responseExists = "response" in xhr; + // check if the property is actually implemented + try { + var dummy = xhr.responseType; + } catch (e) { + responseExists = false; + } + if (!responseExists) { + handler.send("test", false); + return; + } + handler.send("test", { + supportTypedArray: true, + supportTransfers: supportTransfers, + }); + }); + + handler.on("GetDocRequest", function wphSetupDoc(data) { + var onSuccess = function (doc) { + handler.send("GetDoc", { pdfInfo: doc }); + }; + + var onFailure = function (e) { + if (e instanceof PasswordException) { + if (e.code === PasswordResponses.NEED_PASSWORD) { + handler.send("NeedPassword", { + exception: e, + }); + } else if (e.code === PasswordResponses.INCORRECT_PASSWORD) { + handler.send("IncorrectPassword", { + exception: e, + }); + } + } else if (e instanceof InvalidPDFException) { + handler.send("InvalidPDF", { + exception: e, + }); + } else if (e instanceof MissingPDFException) { + handler.send("MissingPDF", { + exception: e, + }); + } else { + handler.send("UnknownError", { + exception: new UnknownErrorException(e.message, e.toString()), + }); + } + }; + + PDFJS.maxImageSize = + data.maxImageSize === undefined ? -1 : data.maxImageSize; + PDFJS.disableFontFace = data.disableFontFace; + + getPdfManager(data).then(function pdfManagerReady() { + loadDocument(false).then( + onSuccess, + function loadFailure(ex) { + // Try again with recoveryMode == true + if (!(ex instanceof XRefParseException)) { + if (ex instanceof PasswordException) { + // after password exception prepare to receive a new password + // to repeat loading + pdfManager.passwordChangedPromise = new Promise(); + pdfManager.passwordChangedPromise.then(pdfManagerReady); + } + + onFailure(ex); + return; + } + + pdfManager.requestLoadedStream(); + pdfManager.onLoadedStream().then(function () { + loadDocument(true).then(onSuccess, onFailure); + }); + }, + onFailure + ); + }, onFailure); + }); + + handler.on("GetPageRequest", function wphSetupGetPage(data) { + var pageIndex = data.pageIndex; + pdfManager.getPage(pageIndex).then(function (page) { + var rotatePromise = pdfManager.ensure(page, "rotate"); + var refPromise = pdfManager.ensure(page, "ref"); + var viewPromise = pdfManager.ensure(page, "view"); + + Promise.all([rotatePromise, refPromise, viewPromise]).then(function ( + results + ) { + var page = { + pageIndex: data.pageIndex, + rotate: results[0], + ref: results[1], + view: results[2], + }; + + handler.send("GetPage", { pageInfo: page }); + }); + }); + }); + + handler.on("GetPageIndex", function wphSetupGetPageIndex(data, promise) { + var ref = new Ref(data.ref.num, data.ref.gen); + pdfManager.pdfModel.catalog.getPageIndex(ref).then(function (pageIndex) { + promise.resolve(pageIndex); + }, promise.reject.bind(promise)); + }); + + handler.on( + "GetDestinations", + function wphSetupGetDestinations(data, promise) { + pdfManager.ensureCatalog("destinations").then(function (destinations) { + promise.resolve(destinations); + }); + } + ); + + handler.on("GetData", function wphSetupGetData(data, promise) { + pdfManager.requestLoadedStream(); + pdfManager.onLoadedStream().then(function (stream) { + promise.resolve(stream.bytes); + }); + }); + + handler.on("DataLoaded", function wphSetupDataLoaded(data, promise) { + pdfManager.onLoadedStream().then(function (stream) { + promise.resolve({ length: stream.bytes.byteLength }); + }); + }); + + handler.on("UpdatePassword", function wphSetupUpdatePassword(data) { + pdfManager.updatePassword(data); + }); + + handler.on("GetAnnotationsRequest", function wphSetupGetAnnotations(data) { + pdfManager.getPage(data.pageIndex).then(function (page) { + pdfManager + .ensure(page, "getAnnotationsData", []) + .then(function (annotationsData) { + handler.send("GetAnnotations", { + pageIndex: data.pageIndex, + annotations: annotationsData, + }); + }); + }); + }); + + handler.on( + "RenderPageRequest", + function wphSetupRenderPage(data) { + pdfManager.getPage(data.pageIndex).then(function (page) { + var pageNum = data.pageIndex + 1; + // var start = Date.now(); + // Pre compile the pdf page and fetch the fonts/images. + page.getOperatorList(handler).then( + function (operatorList) { + // log('page=%d - getOperatorList: time=%dms, len=%d', pageNum, + // Date.now() - start, operatorList.fnArray.length); + }, + function (e) { + var minimumStackMessage = + "worker.js: while trying to getPage() and getOperatorList()"; + + var wrappedException; + + // Turn the error into an obj that can be serialized + if (typeof e === "string") { + wrappedException = { + message: e, + stack: minimumStackMessage, + }; + } else if (typeof e === "object") { + wrappedException = { + message: e.message || e.toString(), + stack: e.stack || minimumStackMessage, + }; + } else { + wrappedException = { + message: "Unknown exception type: " + typeof e, + stack: minimumStackMessage, + }; + } + + handler.send("PageError", { + pageNum: pageNum, + error: wrappedException, + }); + } + ); + }); + }, + this + ); + + handler.on("GetTextContent", function wphExtractText(data, promise) { + pdfManager.getPage(data.pageIndex).then(function (page) { + var pageNum = data.pageIndex + 1; + var start = Date.now(); + page.extractTextContent().then( + function (textContent) { + promise.resolve(textContent); + //MQZ 03/17/2016 comment out log + //log('text indexing: page=%d - time=%dms', pageNum, + // Date.now() - start); + }, + function (e) { + // Skip errored pages + promise.reject(e); + } + ); + }); + }); + + handler.on("Cleanup", function wphCleanup(data, promise) { + pdfManager.cleanup(); + promise.resolve(true); + }); + + handler.on("Terminate", function wphTerminate(data, promise) { + pdfManager.terminate(); + promise.resolve(); + }); + }, +}); var consoleTimer = {}; var workerConsole = { - log: function log() { - var args = Array.prototype.slice.call(arguments); - globalScope.postMessage({ - action: 'console_log', - data: args - }); - }, - - error: function error() { - var args = Array.prototype.slice.call(arguments); - globalScope.postMessage({ - action: 'console_error', - data: args - }); - throw 'pdf.js execution error'; - }, - - time: function time(name) { - consoleTimer[name] = Date.now(); - }, - - timeEnd: function timeEnd(name) { - var time = consoleTimer[name]; - if (!time) { - error('Unkown timer name ' + name); - } - this.log('Timer:', name, Date.now() - time); - } + log: function log() { + var args = Array.prototype.slice.call(arguments); + globalScope.postMessage({ + action: "console_log", + data: args, + }); + }, + + error: function error() { + var args = Array.prototype.slice.call(arguments); + globalScope.postMessage({ + action: "console_error", + data: args, + }); + throw "pdf.js execution error"; + }, + + time: function time(name) { + consoleTimer[name] = Date.now(); + }, + + timeEnd: function timeEnd(name) { + var time = consoleTimer[name]; + if (!time) { + error("Unkown timer name " + name); + } + this.log("Timer:", name, Date.now() - time); + }, }; // Worker thread? -if (typeof window === 'undefined') { - globalScope.console = workerConsole; - - // Add a logger so we can pass warnings on to the main thread, errors will - // throw an exception which will be forwarded on automatically. - PDFJS.LogManager.addLogger({ - warn: function(msg) { - globalScope.postMessage({ - action: '_warn', - data: msg - }); - } - }); - - var handler = new MessageHandler('worker_processor', this); - WorkerMessageHandler.setup(handler); +if (typeof window === "undefined") { + globalScope.console = workerConsole; + + // Add a logger so we can pass warnings on to the main thread, errors will + // throw an exception which will be forwarded on automatically. + PDFJS.LogManager.addLogger({ + warn: function (msg) { + globalScope.postMessage({ + action: "_warn", + data: msg, + }); + }, + }); + + var handler = new MessageHandler("worker_processor", globalScope); + WorkerMessageHandler.setup(handler); } diff --git a/base/shared/util.js b/base/shared/util.js index 66d24579..c178abe8 100755 --- a/base/shared/util.js +++ b/base/shared/util.js @@ -16,791 +16,819 @@ */ /* globals Cmd, ColorSpace, Dict, Blob, Name, PDFJS, Ref, URL */ -'use strict'; +"use strict"; //MQZ. Oct.10.2012. Moved globalScope definition to lib/pdf.js //var globalScope = (typeof window === 'undefined') ? this : window; -var isWorker = (typeof window == 'undefined'); +var isWorker = typeof window == "undefined"; -var ERRORS = 0, WARNINGS = 1, INFOS = 5; +var ERRORS = 0, + WARNINGS = 1, + INFOS = 5; var verbosity = WARNINGS; var FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0]; var TextRenderingMode = { - FILL: 0, - STROKE: 1, - FILL_STROKE: 2, - INVISIBLE: 3, - FILL_ADD_TO_PATH: 4, - STROKE_ADD_TO_PATH: 5, - FILL_STROKE_ADD_TO_PATH: 6, - ADD_TO_PATH: 7, - FILL_STROKE_MASK: 3, - ADD_TO_PATH_FLAG: 4 + FILL: 0, + STROKE: 1, + FILL_STROKE: 2, + INVISIBLE: 3, + FILL_ADD_TO_PATH: 4, + STROKE_ADD_TO_PATH: 5, + FILL_STROKE_ADD_TO_PATH: 6, + ADD_TO_PATH: 7, + FILL_STROKE_MASK: 3, + ADD_TO_PATH_FLAG: 4, }; // The global PDFJS object exposes the API // In production, it will be declared outside a global wrapper // In development, it will be declared here if (!globalScope.PDFJS) { - globalScope.PDFJS = {}; + globalScope.PDFJS = {}; } globalScope.PDFJS.pdfBug = false; // All the possible operations for an operator list. -var OPS = PDFJS.OPS = { - // Intentionally start from 1 so it is easy to spot bad operators that will be - // 0's. - dependency: 1, - setLineWidth: 2, - setLineCap: 3, - setLineJoin: 4, - setMiterLimit: 5, - setDash: 6, - setRenderingIntent: 7, - setFlatness: 8, - setGState: 9, - save: 10, - restore: 11, - transform: 12, - moveTo: 13, - lineTo: 14, - curveTo: 15, - curveTo2: 16, - curveTo3: 17, - closePath: 18, - rectangle: 19, - stroke: 20, - closeStroke: 21, - fill: 22, - eoFill: 23, - fillStroke: 24, - eoFillStroke: 25, - closeFillStroke: 26, - closeEOFillStroke: 27, - endPath: 28, - clip: 29, - eoClip: 30, - beginText: 31, - endText: 32, - setCharSpacing: 33, - setWordSpacing: 34, - setHScale: 35, - setLeading: 36, - setFont: 37, - setTextRenderingMode: 38, - setTextRise: 39, - moveText: 40, - setLeadingMoveText: 41, - setTextMatrix: 42, - nextLine: 43, - showText: 44, - showSpacedText: 45, - nextLineShowText: 46, - nextLineSetSpacingShowText: 47, - setCharWidth: 48, - setCharWidthAndBounds: 49, - setStrokeColorSpace: 50, - setFillColorSpace: 51, - setStrokeColor: 52, - setStrokeColorN: 53, - setFillColor: 54, - setFillColorN: 55, - setStrokeGray: 56, - setFillGray: 57, - setStrokeRGBColor: 58, - setFillRGBColor: 59, - setStrokeCMYKColor: 60, - setFillCMYKColor: 61, - shadingFill: 62, - beginInlineImage: 63, - beginImageData: 64, - endInlineImage: 65, - paintXObject: 66, - markPoint: 67, - markPointProps: 68, - beginMarkedContent: 69, - beginMarkedContentProps: 70, - endMarkedContent: 71, - beginCompat: 72, - endCompat: 73, - paintFormXObjectBegin: 74, - paintFormXObjectEnd: 75, - beginGroup: 76, - endGroup: 77, - beginAnnotations: 78, - endAnnotations: 79, - beginAnnotation: 80, - endAnnotation: 81, - paintJpegXObject: 82, - paintImageMaskXObject: 83, - paintImageMaskXObjectGroup: 84, - paintImageXObject: 85, - paintInlineImageXObject: 86, - paintInlineImageXObjectGroup: 87 -}; +var OPS = (PDFJS.OPS = { + // Intentionally start from 1 so it is easy to spot bad operators that will be + // 0's. + dependency: 1, + setLineWidth: 2, + setLineCap: 3, + setLineJoin: 4, + setMiterLimit: 5, + setDash: 6, + setRenderingIntent: 7, + setFlatness: 8, + setGState: 9, + save: 10, + restore: 11, + transform: 12, + moveTo: 13, + lineTo: 14, + curveTo: 15, + curveTo2: 16, + curveTo3: 17, + closePath: 18, + rectangle: 19, + stroke: 20, + closeStroke: 21, + fill: 22, + eoFill: 23, + fillStroke: 24, + eoFillStroke: 25, + closeFillStroke: 26, + closeEOFillStroke: 27, + endPath: 28, + clip: 29, + eoClip: 30, + beginText: 31, + endText: 32, + setCharSpacing: 33, + setWordSpacing: 34, + setHScale: 35, + setLeading: 36, + setFont: 37, + setTextRenderingMode: 38, + setTextRise: 39, + moveText: 40, + setLeadingMoveText: 41, + setTextMatrix: 42, + nextLine: 43, + showText: 44, + showSpacedText: 45, + nextLineShowText: 46, + nextLineSetSpacingShowText: 47, + setCharWidth: 48, + setCharWidthAndBounds: 49, + setStrokeColorSpace: 50, + setFillColorSpace: 51, + setStrokeColor: 52, + setStrokeColorN: 53, + setFillColor: 54, + setFillColorN: 55, + setStrokeGray: 56, + setFillGray: 57, + setStrokeRGBColor: 58, + setFillRGBColor: 59, + setStrokeCMYKColor: 60, + setFillCMYKColor: 61, + shadingFill: 62, + beginInlineImage: 63, + beginImageData: 64, + endInlineImage: 65, + paintXObject: 66, + markPoint: 67, + markPointProps: 68, + beginMarkedContent: 69, + beginMarkedContentProps: 70, + endMarkedContent: 71, + beginCompat: 72, + endCompat: 73, + paintFormXObjectBegin: 74, + paintFormXObjectEnd: 75, + beginGroup: 76, + endGroup: 77, + beginAnnotations: 78, + endAnnotations: 79, + beginAnnotation: 80, + endAnnotation: 81, + paintJpegXObject: 82, + paintImageMaskXObject: 83, + paintImageMaskXObjectGroup: 84, + paintImageXObject: 85, + paintInlineImageXObject: 86, + paintInlineImageXObjectGroup: 87, +}); //MQZ.Mar.22 Disabled Operators (to prevent image painting & annotation default appearance) //paintJpegXObject, paintImageMaskXObject, paintImageMaskXObjectGroup, paintImageXObject, paintInlineImageXObject, paintInlineImageXObjectGroup -var NO_OPS = PDFJS.NO_OPS = [82, 83, 84, 85, 86, 87]; -var NO_OPS_RANGE = PDFJS.NO_OPS_RANGE = [78, 79, 80, 81]; //range pairs, all ops with each pair will be skipped. !important! +var NO_OPS = (PDFJS.NO_OPS = [82, 83, 84, 85, 86, 87]); +var NO_OPS_RANGE = (PDFJS.NO_OPS_RANGE = [78, 79, 80, 81]); //range pairs, all ops with each pair will be skipped. !important! // Use only for debugging purposes. This should not be used in any code that is // in mozilla master. -var log = (function() { - var disableLogs = Boolean(Number(process?.env?.PDF2JSON_DISABLE_LOGS ?? "0")); - - if (!disableLogs && 'console' in globalScope && 'log' in globalScope['console']) { - return globalScope['console']['log'].bind(globalScope['console']); - } else { - return function nop() { - }; - } +var log = (function () { + var disableLogs = Boolean(Number(process?.env?.PDF2JSON_DISABLE_LOGS ?? "0")); + + if ( + !disableLogs && + "console" in globalScope && + "log" in globalScope["console"] + ) { + return globalScope["console"]["log"].bind(globalScope["console"]); + } else { + return function nop() {}; + } })(); // A notice for devs that will not trigger the fallback UI. These are good // for things that are helpful to devs, such as warning that Workers were // disabled, which is important to devs but not end users. function info(msg) { - if (verbosity >= INFOS) { - log('Info: ' + msg); - PDFJS.LogManager.notify('info', msg); - } + if (verbosity >= INFOS) { + log("Info: " + msg); + PDFJS.LogManager.notify("info", msg); + } } // Non-fatal warnings that should trigger the fallback UI. function warn(msg) { - if (verbosity >= WARNINGS) { - log('Warning: ' + msg); - PDFJS.LogManager.notify('warn', msg); - } + if (verbosity >= WARNINGS) { + log("Warning: " + msg); + PDFJS.LogManager.notify("warn", msg); + } } // Fatal errors that should trigger the fallback UI and halt execution by // throwing an exception. function error(msg) { - // If multiple arguments were passed, pass them all to the log function. - if (arguments.length > 1) { - var logArguments = ['Error:']; - logArguments.push.apply(logArguments, arguments); - log.apply(null, logArguments); - // Join the arguments into a single string for the lines below. - msg = [].join.call(arguments, ' '); - } else { - //log('Error: ' + msg); - } - //log(backtrace()); - //PDFJS.LogManager.notify('error', msg); - throw new Error(msg); + // If multiple arguments were passed, pass them all to the log function. + if (arguments.length > 1) { + var logArguments = ["Error:"]; + logArguments.push.apply(logArguments, arguments); + log.apply(null, logArguments); + // Join the arguments into a single string for the lines below. + msg = [].join.call(arguments, " "); + } else { + //log('Error: ' + msg); + } + //log(backtrace()); + //PDFJS.LogManager.notify('error', msg); + throw new Error(msg); } // Missing features that should trigger the fallback UI. function TODO(what) { - warn('TODO: ' + what); + warn("TODO: " + what); } function backtrace() { - try { - throw new Error(); - } catch (e) { - return e.stack ? e.stack.split('\n').slice(2).join('\n') : ''; - } + try { + throw new Error(); + } catch (e) { + return e.stack ? e.stack.split("\n").slice(2).join("\n") : ""; + } } function assert(cond, msg) { - if (!cond) - error(msg); + if (!cond) error(msg); } // Combines two URLs. The baseUrl shall be absolute URL. If the url is an // absolute URL, it will be returned as is. function combineUrl(baseUrl, url) { - if (!url) - return baseUrl; - if (url.indexOf(':') >= 0) - return url; - if (url.charAt(0) == '/') { - // absolute path - var i = baseUrl.indexOf('://'); - i = baseUrl.indexOf('/', i + 3); - return baseUrl.substring(0, i) + url; - } else { - // relative path - var pathLength = baseUrl.length, i; - i = baseUrl.lastIndexOf('#'); - pathLength = i >= 0 ? i : pathLength; - i = baseUrl.lastIndexOf('?', pathLength); - pathLength = i >= 0 ? i : pathLength; - var prefixLength = baseUrl.lastIndexOf('/', pathLength); - return baseUrl.substring(0, prefixLength + 1) + url; - } + if (!url) return baseUrl; + if (url.indexOf(":") >= 0) return url; + if (url.charAt(0) == "/") { + // absolute path + var i = baseUrl.indexOf("://"); + i = baseUrl.indexOf("/", i + 3); + return baseUrl.substring(0, i) + url; + } else { + // relative path + var pathLength = baseUrl.length, + i; + i = baseUrl.lastIndexOf("#"); + pathLength = i >= 0 ? i : pathLength; + i = baseUrl.lastIndexOf("?", pathLength); + pathLength = i >= 0 ? i : pathLength; + var prefixLength = baseUrl.lastIndexOf("/", pathLength); + return baseUrl.substring(0, prefixLength + 1) + url; + } } // Validates if URL is safe and allowed, e.g. to avoid XSS. function isValidUrl(url, allowRelative) { - if (!url) { - return false; - } - var colon = url.indexOf(':'); - if (colon < 0) { - return allowRelative; - } - var protocol = url.substring(0, colon); - switch (protocol) { - case 'http': - case 'https': - case 'ftp': - case 'mailto': - return true; - default: - return false; - } + if (!url) { + return false; + } + var colon = url.indexOf(":"); + if (colon < 0) { + return allowRelative; + } + var protocol = url.substring(0, colon); + switch (protocol) { + case "http": + case "https": + case "ftp": + case "mailto": + return true; + default: + return false; + } } PDFJS.isValidUrl = isValidUrl; // In a well-formed PDF, |cond| holds. If it doesn't, subsequent // behavior is undefined. function assertWellFormed(cond, msg) { - if (!cond) - error(msg); + if (!cond) error(msg); } -var LogManager = PDFJS.LogManager = (function LogManagerClosure() { - var loggers = []; - return { - addLogger: function logManager_addLogger(logger) { - loggers.push(logger); - }, - notify: function(type, message) { - for (var i = 0, ii = loggers.length; i < ii; i++) { - var logger = loggers[i]; - if (logger[type]) - logger[type](message); - } - } - }; -})(); +var LogManager = (PDFJS.LogManager = (function LogManagerClosure() { + var loggers = []; + return { + addLogger: function logManager_addLogger(logger) { + loggers.push(logger); + }, + notify: function (type, message) { + for (var i = 0, ii = loggers.length; i < ii; i++) { + var logger = loggers[i]; + if (logger[type]) logger[type](message); + } + }, + }; +})()); function shadow(obj, prop, value) { - Object.defineProperty(obj, prop, { value: value, - enumerable: true, - configurable: true, - writable: false }); - return value; + Object.defineProperty(obj, prop, { + value: value, + enumerable: true, + configurable: true, + writable: false, + }); + return value; } -var PasswordResponses = PDFJS.PasswordResponses = { - NEED_PASSWORD: 1, - INCORRECT_PASSWORD: 2 -}; +var PasswordResponses = (PDFJS.PasswordResponses = { + NEED_PASSWORD: 1, + INCORRECT_PASSWORD: 2, +}); var PasswordException = (function PasswordExceptionClosure() { - function PasswordException(msg, code) { - this.name = 'PasswordException'; - this.message = msg; - this.code = code; - } + function PasswordException(msg, code) { + this.name = "PasswordException"; + this.message = msg; + this.code = code; + } - PasswordException.prototype = new Error(); - PasswordException.constructor = PasswordException; + PasswordException.prototype = new Error(); + PasswordException.constructor = PasswordException; - return PasswordException; + return PasswordException; })(); var UnknownErrorException = (function UnknownErrorExceptionClosure() { - function UnknownErrorException(msg, details) { - this.name = 'UnknownErrorException'; - this.message = msg; - this.details = details; - } + function UnknownErrorException(msg, details) { + this.name = "UnknownErrorException"; + this.message = msg; + this.details = details; + } - UnknownErrorException.prototype = new Error(); - UnknownErrorException.constructor = UnknownErrorException; + UnknownErrorException.prototype = new Error(); + UnknownErrorException.constructor = UnknownErrorException; - return UnknownErrorException; + return UnknownErrorException; })(); var InvalidPDFException = (function InvalidPDFExceptionClosure() { - function InvalidPDFException(msg) { - this.name = 'InvalidPDFException'; - this.message = msg; - } + function InvalidPDFException(msg) { + this.name = "InvalidPDFException"; + this.message = msg; + } - InvalidPDFException.prototype = new Error(); - InvalidPDFException.constructor = InvalidPDFException; + InvalidPDFException.prototype = new Error(); + InvalidPDFException.constructor = InvalidPDFException; - return InvalidPDFException; + return InvalidPDFException; })(); var MissingPDFException = (function MissingPDFExceptionClosure() { - function MissingPDFException(msg) { - this.name = 'MissingPDFException'; - this.message = msg; - } + function MissingPDFException(msg) { + this.name = "MissingPDFException"; + this.message = msg; + } - MissingPDFException.prototype = new Error(); - MissingPDFException.constructor = MissingPDFException; + MissingPDFException.prototype = new Error(); + MissingPDFException.constructor = MissingPDFException; - return MissingPDFException; + return MissingPDFException; })(); var NotImplementedException = (function NotImplementedExceptionClosure() { - function NotImplementedException(msg) { - this.message = msg; - } + function NotImplementedException(msg) { + this.message = msg; + } - NotImplementedException.prototype = new Error(); - NotImplementedException.prototype.name = 'NotImplementedException'; - NotImplementedException.constructor = NotImplementedException; + NotImplementedException.prototype = new Error(); + NotImplementedException.prototype.name = "NotImplementedException"; + NotImplementedException.constructor = NotImplementedException; - return NotImplementedException; + return NotImplementedException; })(); var MissingDataException = (function MissingDataExceptionClosure() { - function MissingDataException(begin, end) { - this.begin = begin; - this.end = end; - this.message = 'Missing data [' + begin + ', ' + end + ')'; - } + function MissingDataException(begin, end) { + this.begin = begin; + this.end = end; + this.message = "Missing data [" + begin + ", " + end + ")"; + } - MissingDataException.prototype = new Error(); - MissingDataException.prototype.name = 'MissingDataException'; - MissingDataException.constructor = MissingDataException; + MissingDataException.prototype = new Error(); + MissingDataException.prototype.name = "MissingDataException"; + MissingDataException.constructor = MissingDataException; - return MissingDataException; + return MissingDataException; })(); var XRefParseException = (function XRefParseExceptionClosure() { - function XRefParseException(msg) { - this.message = msg; - } + function XRefParseException(msg) { + this.message = msg; + } - XRefParseException.prototype = new Error(); - XRefParseException.prototype.name = 'XRefParseException'; - XRefParseException.constructor = XRefParseException; + XRefParseException.prototype = new Error(); + XRefParseException.prototype.name = "XRefParseException"; + XRefParseException.constructor = XRefParseException; - return XRefParseException; + return XRefParseException; })(); - function bytesToString(bytes) { - var str = ''; - var length = bytes.length; - for (var n = 0; n < length; ++n) - str += String.fromCharCode(bytes[n]); - return str; + var str = ""; + var length = bytes.length; + for (var n = 0; n < length; ++n) str += String.fromCharCode(bytes[n]); + return str; } function stringToBytes(str) { - var length = str.length; - var bytes = new Uint8Array(length); - for (var n = 0; n < length; ++n) - bytes[n] = str.charCodeAt(n) & 0xFF; - return bytes; + var length = str.length; + var bytes = new Uint8Array(length); + for (var n = 0; n < length; ++n) bytes[n] = str.charCodeAt(n) & 0xff; + return bytes; } var IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0]; -var Util = PDFJS.Util = (function UtilClosure() { - function Util() {} - - Util.makeCssRgb = function Util_makeCssRgb(rgb) { - return 'rgb(' + rgb[0] + ',' + rgb[1] + ',' + rgb[2] + ')'; - }; - - Util.makeCssCmyk = function Util_makeCssCmyk(cmyk) { - var rgb = ColorSpace.singletons.cmyk.getRgb(cmyk, 0); - return Util.makeCssRgb(rgb); - }; - - // Concatenates two transformation matrices together and returns the result. - Util.transform = function Util_transform(m1, m2) { - return [ - m1[0] * m2[0] + m1[2] * m2[1], - m1[1] * m2[0] + m1[3] * m2[1], - m1[0] * m2[2] + m1[2] * m2[3], - m1[1] * m2[2] + m1[3] * m2[3], - m1[0] * m2[4] + m1[2] * m2[5] + m1[4], - m1[1] * m2[4] + m1[3] * m2[5] + m1[5] - ]; - }; - - // For 2d affine transforms - Util.applyTransform = function Util_applyTransform(p, m) { - var xt = p[0] * m[0] + p[1] * m[2] + m[4]; - var yt = p[0] * m[1] + p[1] * m[3] + m[5]; - return [xt, yt]; - }; - - Util.applyInverseTransform = function Util_applyInverseTransform(p, m) { - var d = m[0] * m[3] - m[1] * m[2]; - var xt = (p[0] * m[3] - p[1] * m[2] + m[2] * m[5] - m[4] * m[3]) / d; - var yt = (-p[0] * m[1] + p[1] * m[0] + m[4] * m[1] - m[5] * m[0]) / d; - return [xt, yt]; - }; - - // Applies the transform to the rectangle and finds the minimum axially - // aligned bounding box. - Util.getAxialAlignedBoundingBox = - function Util_getAxialAlignedBoundingBox(r, m) { - - var p1 = Util.applyTransform(r, m); - var p2 = Util.applyTransform(r.slice(2, 4), m); - var p3 = Util.applyTransform([r[0], r[3]], m); - var p4 = Util.applyTransform([r[2], r[1]], m); - return [ - Math.min(p1[0], p2[0], p3[0], p4[0]), - Math.min(p1[1], p2[1], p3[1], p4[1]), - Math.max(p1[0], p2[0], p3[0], p4[0]), - Math.max(p1[1], p2[1], p3[1], p4[1]) - ]; - }; - - Util.inverseTransform = function Util_inverseTransform(m) { - var d = m[0] * m[3] - m[1] * m[2]; - return [m[3] / d, -m[1] / d, -m[2] / d, m[0] / d, - (m[2] * m[5] - m[4] * m[3]) / d, (m[4] * m[1] - m[5] * m[0]) / d]; - }; - - // Apply a generic 3d matrix M on a 3-vector v: - // | a b c | | X | - // | d e f | x | Y | - // | g h i | | Z | - // M is assumed to be serialized as [a,b,c,d,e,f,g,h,i], - // with v as [X,Y,Z] - Util.apply3dTransform = function Util_apply3dTransform(m, v) { - return [ - m[0] * v[0] + m[1] * v[1] + m[2] * v[2], - m[3] * v[0] + m[4] * v[1] + m[5] * v[2], - m[6] * v[0] + m[7] * v[1] + m[8] * v[2] - ]; - }; - - // This calculation uses Singular Value Decomposition. - // The SVD can be represented with formula A = USV. We are interested in the - // matrix S here because it represents the scale values. - Util.singularValueDecompose2dScale = - function Util_singularValueDecompose2dScale(m) { - - var transpose = [m[0], m[2], m[1], m[3]]; - - // Multiply matrix m with its transpose. - var a = m[0] * transpose[0] + m[1] * transpose[2]; - var b = m[0] * transpose[1] + m[1] * transpose[3]; - var c = m[2] * transpose[0] + m[3] * transpose[2]; - var d = m[2] * transpose[1] + m[3] * transpose[3]; - - // Solve the second degree polynomial to get roots. - var first = (a + d) / 2; - var second = Math.sqrt((a + d) * (a + d) - 4 * (a * d - c * b)) / 2; - var sx = first + second || 1; - var sy = first - second || 1; - - // Scale values are the square roots of the eigenvalues. - return [Math.sqrt(sx), Math.sqrt(sy)]; - }; - - // Normalize rectangle rect=[x1, y1, x2, y2] so that (x1,y1) < (x2,y2) - // For coordinate systems whose origin lies in the bottom-left, this - // means normalization to (BL,TR) ordering. For systems with origin in the - // top-left, this means (TL,BR) ordering. - Util.normalizeRect = function Util_normalizeRect(rect) { - var r = rect.slice(0); // clone rect - if (rect[0] > rect[2]) { - r[0] = rect[2]; - r[2] = rect[0]; - } - if (rect[1] > rect[3]) { - r[1] = rect[3]; - r[3] = rect[1]; - } - return r; - }; - - // Returns a rectangle [x1, y1, x2, y2] corresponding to the - // intersection of rect1 and rect2. If no intersection, returns 'null' - // The rectangle coordinates of rect1, rect2 should be [x1, y1, x2, y2] - Util.intersect = function Util_intersect(rect1, rect2) { - const xLow = Math.max( - Math.min(rect1[0], rect1[2]), - Math.min(rect2[0], rect2[2]) - ); - const xHigh = Math.min( - Math.max(rect1[0], rect1[2]), - Math.max(rect2[0], rect2[2]) - ); - if (xLow > xHigh) { - return null; - } - const yLow = Math.max( - Math.min(rect1[1], rect1[3]), - Math.min(rect2[1], rect2[3]) - ); - const yHigh = Math.min( - Math.max(rect1[1], rect1[3]), - Math.max(rect2[1], rect2[3]) - ); - if (yLow > yHigh) { - return null; - } - - return [xLow, yLow, xHigh, yHigh]; - }; - - Util.sign = function Util_sign(num) { - return num < 0 ? -1 : 1; - }; - - // TODO(mack): Rename appendToArray - Util.concatenateToArray = function concatenateToArray(arr1, arr2) { - Array.prototype.push.apply(arr1, arr2); - }; - - Util.prependToArray = function concatenateToArray(arr1, arr2) { - Array.prototype.unshift.apply(arr1, arr2); - }; - - Util.extendObj = function extendObj(obj1, obj2) { - for (var key in obj2) { - obj1[key] = obj2[key]; - } - }; - - Util.getInheritableProperty = function Util_getInheritableProperty(dict, - name) { - while (dict && !dict.has(name)) { - dict = dict.get('Parent'); - } - if (!dict) { - return null; - } - return dict.get(name); - }; - - Util.inherit = function Util_inherit(sub, base, prototype) { - sub.prototype = Object.create(base.prototype); - sub.prototype.constructor = sub; - for (var prop in prototype) { - sub.prototype[prop] = prototype[prop]; - } - }; - - Util.loadScript = function Util_loadScript(src, callback) { - var script = document.createElement('script'); - var loaded = false; - script.setAttribute('src', src); - if (callback) { - script.onload = function() { - if (!loaded) { - callback(); - } - loaded = true; - }; - } - document.getElementsByTagName('head')[0].appendChild(script); - }; - - return Util; -})(); +var Util = (PDFJS.Util = (function UtilClosure() { + function Util() {} + + Util.makeCssRgb = function Util_makeCssRgb(rgb) { + return "rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ")"; + }; + + Util.makeCssCmyk = function Util_makeCssCmyk(cmyk) { + var rgb = ColorSpace.singletons.cmyk.getRgb(cmyk, 0); + return Util.makeCssRgb(rgb); + }; + + // Concatenates two transformation matrices together and returns the result. + Util.transform = function Util_transform(m1, m2) { + return [ + m1[0] * m2[0] + m1[2] * m2[1], + m1[1] * m2[0] + m1[3] * m2[1], + m1[0] * m2[2] + m1[2] * m2[3], + m1[1] * m2[2] + m1[3] * m2[3], + m1[0] * m2[4] + m1[2] * m2[5] + m1[4], + m1[1] * m2[4] + m1[3] * m2[5] + m1[5], + ]; + }; + + // For 2d affine transforms + Util.applyTransform = function Util_applyTransform(p, m) { + var xt = p[0] * m[0] + p[1] * m[2] + m[4]; + var yt = p[0] * m[1] + p[1] * m[3] + m[5]; + return [xt, yt]; + }; + + Util.applyInverseTransform = function Util_applyInverseTransform(p, m) { + var d = m[0] * m[3] - m[1] * m[2]; + var xt = (p[0] * m[3] - p[1] * m[2] + m[2] * m[5] - m[4] * m[3]) / d; + var yt = (-p[0] * m[1] + p[1] * m[0] + m[4] * m[1] - m[5] * m[0]) / d; + return [xt, yt]; + }; + + // Applies the transform to the rectangle and finds the minimum axially + // aligned bounding box. + Util.getAxialAlignedBoundingBox = function Util_getAxialAlignedBoundingBox( + r, + m + ) { + var p1 = Util.applyTransform(r, m); + var p2 = Util.applyTransform(r.slice(2, 4), m); + var p3 = Util.applyTransform([r[0], r[3]], m); + var p4 = Util.applyTransform([r[2], r[1]], m); + return [ + Math.min(p1[0], p2[0], p3[0], p4[0]), + Math.min(p1[1], p2[1], p3[1], p4[1]), + Math.max(p1[0], p2[0], p3[0], p4[0]), + Math.max(p1[1], p2[1], p3[1], p4[1]), + ]; + }; + + Util.inverseTransform = function Util_inverseTransform(m) { + var d = m[0] * m[3] - m[1] * m[2]; + return [ + m[3] / d, + -m[1] / d, + -m[2] / d, + m[0] / d, + (m[2] * m[5] - m[4] * m[3]) / d, + (m[4] * m[1] - m[5] * m[0]) / d, + ]; + }; + + // Apply a generic 3d matrix M on a 3-vector v: + // | a b c | | X | + // | d e f | x | Y | + // | g h i | | Z | + // M is assumed to be serialized as [a,b,c,d,e,f,g,h,i], + // with v as [X,Y,Z] + Util.apply3dTransform = function Util_apply3dTransform(m, v) { + return [ + m[0] * v[0] + m[1] * v[1] + m[2] * v[2], + m[3] * v[0] + m[4] * v[1] + m[5] * v[2], + m[6] * v[0] + m[7] * v[1] + m[8] * v[2], + ]; + }; + + // This calculation uses Singular Value Decomposition. + // The SVD can be represented with formula A = USV. We are interested in the + // matrix S here because it represents the scale values. + Util.singularValueDecompose2dScale = + function Util_singularValueDecompose2dScale(m) { + var transpose = [m[0], m[2], m[1], m[3]]; + + // Multiply matrix m with its transpose. + var a = m[0] * transpose[0] + m[1] * transpose[2]; + var b = m[0] * transpose[1] + m[1] * transpose[3]; + var c = m[2] * transpose[0] + m[3] * transpose[2]; + var d = m[2] * transpose[1] + m[3] * transpose[3]; + + // Solve the second degree polynomial to get roots. + var first = (a + d) / 2; + var second = Math.sqrt((a + d) * (a + d) - 4 * (a * d - c * b)) / 2; + var sx = first + second || 1; + var sy = first - second || 1; + + // Scale values are the square roots of the eigenvalues. + return [Math.sqrt(sx), Math.sqrt(sy)]; + }; -var PageViewport = PDFJS.PageViewport = (function PageViewportClosure() { - function PageViewport(viewBox, scale, rotation, offsetX, offsetY, dontFlip) { - this.viewBox = viewBox; - this.scale = scale; - this.rotation = rotation; - this.offsetX = offsetX; - this.offsetY = offsetY; - - // creating transform to convert pdf coordinate system to the normal - // canvas like coordinates taking in account scale and rotation - var centerX = (viewBox[2] + viewBox[0]) / 2; - var centerY = (viewBox[3] + viewBox[1]) / 2; - var rotateA, rotateB, rotateC, rotateD; - rotation = rotation % 360; - rotation = rotation < 0 ? rotation + 360 : rotation; - switch (rotation) { - case 180: - rotateA = -1; rotateB = 0; rotateC = 0; rotateD = 1; - break; - case 90: - rotateA = 0; rotateB = 1; rotateC = 1; rotateD = 0; - break; - case 270: - rotateA = 0; rotateB = -1; rotateC = -1; rotateD = 0; - break; - //case 0: - default: - rotateA = 1; rotateB = 0; rotateC = 0; rotateD = -1; - break; - } - - if (dontFlip) { - rotateC = -rotateC; rotateD = -rotateD; - } - - var offsetCanvasX, offsetCanvasY; - var width, height; - if (rotateA === 0) { - offsetCanvasX = Math.abs(centerY - viewBox[1]) * scale + offsetX; - offsetCanvasY = Math.abs(centerX - viewBox[0]) * scale + offsetY; - width = Math.abs(viewBox[3] - viewBox[1]) * scale; - height = Math.abs(viewBox[2] - viewBox[0]) * scale; - } else { - offsetCanvasX = Math.abs(centerX - viewBox[0]) * scale + offsetX; - offsetCanvasY = Math.abs(centerY - viewBox[1]) * scale + offsetY; - width = Math.abs(viewBox[2] - viewBox[0]) * scale; - height = Math.abs(viewBox[3] - viewBox[1]) * scale; - } - // creating transform for the following operations: - // translate(-centerX, -centerY), rotate and flip vertically, - // scale, and translate(offsetCanvasX, offsetCanvasY) - this.transform = [ - rotateA * scale, - rotateB * scale, - rotateC * scale, - rotateD * scale, - offsetCanvasX - rotateA * scale * centerX - rotateC * scale * centerY, - offsetCanvasY - rotateB * scale * centerX - rotateD * scale * centerY - ]; - - this.width = width; - this.height = height; - this.fontScale = scale; - } - PageViewport.prototype = { - clone: function PageViewPort_clone(args) { - args = args || {}; - var scale = 'scale' in args ? args.scale : this.scale; - var rotation = 'rotation' in args ? args.rotation : this.rotation; - return new PageViewport(this.viewBox.slice(), scale, rotation, - this.offsetX, this.offsetY, args.dontFlip); - }, - convertToViewportPoint: function PageViewport_convertToViewportPoint(x, y) { - return Util.applyTransform([x, y], this.transform); - }, - convertToViewportRectangle: - function PageViewport_convertToViewportRectangle(rect) { - var tl = Util.applyTransform([rect[0], rect[1]], this.transform); - var br = Util.applyTransform([rect[2], rect[3]], this.transform); - return [tl[0], tl[1], br[0], br[1]]; - }, - convertToPdfPoint: function PageViewport_convertToPdfPoint(x, y) { - return Util.applyInverseTransform([x, y], this.transform); - } - }; - return PageViewport; -})(); + // Normalize rectangle rect=[x1, y1, x2, y2] so that (x1,y1) < (x2,y2) + // For coordinate systems whose origin lies in the bottom-left, this + // means normalization to (BL,TR) ordering. For systems with origin in the + // top-left, this means (TL,BR) ordering. + Util.normalizeRect = function Util_normalizeRect(rect) { + var r = rect.slice(0); // clone rect + if (rect[0] > rect[2]) { + r[0] = rect[2]; + r[2] = rect[0]; + } + if (rect[1] > rect[3]) { + r[1] = rect[3]; + r[3] = rect[1]; + } + return r; + }; + + // Returns a rectangle [x1, y1, x2, y2] corresponding to the + // intersection of rect1 and rect2. If no intersection, returns 'null' + // The rectangle coordinates of rect1, rect2 should be [x1, y1, x2, y2] + Util.intersect = function Util_intersect(rect1, rect2) { + const xLow = Math.max( + Math.min(rect1[0], rect1[2]), + Math.min(rect2[0], rect2[2]) + ); + const xHigh = Math.min( + Math.max(rect1[0], rect1[2]), + Math.max(rect2[0], rect2[2]) + ); + if (xLow > xHigh) { + return null; + } + const yLow = Math.max( + Math.min(rect1[1], rect1[3]), + Math.min(rect2[1], rect2[3]) + ); + const yHigh = Math.min( + Math.max(rect1[1], rect1[3]), + Math.max(rect2[1], rect2[3]) + ); + if (yLow > yHigh) { + return null; + } + + return [xLow, yLow, xHigh, yHigh]; + }; + + Util.sign = function Util_sign(num) { + return num < 0 ? -1 : 1; + }; + + // TODO(mack): Rename appendToArray + Util.concatenateToArray = function concatenateToArray(arr1, arr2) { + Array.prototype.push.apply(arr1, arr2); + }; + + Util.prependToArray = function concatenateToArray(arr1, arr2) { + Array.prototype.unshift.apply(arr1, arr2); + }; + + Util.extendObj = function extendObj(obj1, obj2) { + for (var key in obj2) { + obj1[key] = obj2[key]; + } + }; + + Util.getInheritableProperty = function Util_getInheritableProperty( + dict, + name + ) { + while (dict && !dict.has(name)) { + dict = dict.get("Parent"); + } + if (!dict) { + return null; + } + return dict.get(name); + }; + + Util.inherit = function Util_inherit(sub, base, prototype) { + sub.prototype = Object.create(base.prototype); + sub.prototype.constructor = sub; + for (var prop in prototype) { + sub.prototype[prop] = prototype[prop]; + } + }; + + Util.loadScript = function Util_loadScript(src, callback) { + var script = document.createElement("script"); + var loaded = false; + script.setAttribute("src", src); + if (callback) { + script.onload = function () { + if (!loaded) { + callback(); + } + loaded = true; + }; + } + document.getElementsByTagName("head")[0].appendChild(script); + }; + + return Util; +})()); + +var PageViewport = (PDFJS.PageViewport = (function PageViewportClosure() { + function PageViewport(viewBox, scale, rotation, offsetX, offsetY, dontFlip) { + this.viewBox = viewBox; + this.scale = scale; + this.rotation = rotation; + this.offsetX = offsetX; + this.offsetY = offsetY; + + // creating transform to convert pdf coordinate system to the normal + // canvas like coordinates taking in account scale and rotation + var centerX = (viewBox[2] + viewBox[0]) / 2; + var centerY = (viewBox[3] + viewBox[1]) / 2; + var rotateA, rotateB, rotateC, rotateD; + rotation = rotation % 360; + rotation = rotation < 0 ? rotation + 360 : rotation; + switch (rotation) { + case 180: + rotateA = -1; + rotateB = 0; + rotateC = 0; + rotateD = 1; + break; + case 90: + rotateA = 0; + rotateB = 1; + rotateC = 1; + rotateD = 0; + break; + case 270: + rotateA = 0; + rotateB = -1; + rotateC = -1; + rotateD = 0; + break; + //case 0: + default: + rotateA = 1; + rotateB = 0; + rotateC = 0; + rotateD = -1; + break; + } + + if (dontFlip) { + rotateC = -rotateC; + rotateD = -rotateD; + } + + var offsetCanvasX, offsetCanvasY; + var width, height; + if (rotateA === 0) { + offsetCanvasX = Math.abs(centerY - viewBox[1]) * scale + offsetX; + offsetCanvasY = Math.abs(centerX - viewBox[0]) * scale + offsetY; + width = Math.abs(viewBox[3] - viewBox[1]) * scale; + height = Math.abs(viewBox[2] - viewBox[0]) * scale; + } else { + offsetCanvasX = Math.abs(centerX - viewBox[0]) * scale + offsetX; + offsetCanvasY = Math.abs(centerY - viewBox[1]) * scale + offsetY; + width = Math.abs(viewBox[2] - viewBox[0]) * scale; + height = Math.abs(viewBox[3] - viewBox[1]) * scale; + } + // creating transform for the following operations: + // translate(-centerX, -centerY), rotate and flip vertically, + // scale, and translate(offsetCanvasX, offsetCanvasY) + this.transform = [ + rotateA * scale, + rotateB * scale, + rotateC * scale, + rotateD * scale, + offsetCanvasX - rotateA * scale * centerX - rotateC * scale * centerY, + offsetCanvasY - rotateB * scale * centerX - rotateD * scale * centerY, + ]; + + this.width = width; + this.height = height; + this.fontScale = scale; + } + PageViewport.prototype = { + clone: function PageViewPort_clone(args) { + args = args || {}; + var scale = "scale" in args ? args.scale : this.scale; + var rotation = "rotation" in args ? args.rotation : this.rotation; + return new PageViewport( + this.viewBox.slice(), + scale, + rotation, + this.offsetX, + this.offsetY, + args.dontFlip + ); + }, + convertToViewportPoint: function PageViewport_convertToViewportPoint(x, y) { + return Util.applyTransform([x, y], this.transform); + }, + convertToViewportRectangle: + function PageViewport_convertToViewportRectangle(rect) { + var tl = Util.applyTransform([rect[0], rect[1]], this.transform); + var br = Util.applyTransform([rect[2], rect[3]], this.transform); + return [tl[0], tl[1], br[0], br[1]]; + }, + convertToPdfPoint: function PageViewport_convertToPdfPoint(x, y) { + return Util.applyInverseTransform([x, y], this.transform); + }, + }; + return PageViewport; +})()); var PDFStringTranslateTable = [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0x2D8, 0x2C7, 0x2C6, 0x2D9, 0x2DD, 0x2DB, 0x2DA, 0x2DC, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2022, 0x2020, 0x2021, 0x2026, 0x2014, - 0x2013, 0x192, 0x2044, 0x2039, 0x203A, 0x2212, 0x2030, 0x201E, 0x201C, - 0x201D, 0x2018, 0x2019, 0x201A, 0x2122, 0xFB01, 0xFB02, 0x141, 0x152, 0x160, - 0x178, 0x17D, 0x131, 0x142, 0x153, 0x161, 0x17E, 0, 0x20AC + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2d8, + 0x2c7, 0x2c6, 0x2d9, 0x2dd, 0x2db, 0x2da, 0x2dc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0x2022, 0x2020, 0x2021, 0x2026, 0x2014, 0x2013, 0x192, + 0x2044, 0x2039, 0x203a, 0x2212, 0x2030, 0x201e, 0x201c, 0x201d, 0x2018, + 0x2019, 0x201a, 0x2122, 0xfb01, 0xfb02, 0x141, 0x152, 0x160, 0x178, 0x17d, + 0x131, 0x142, 0x153, 0x161, 0x17e, 0, 0x20ac, ]; function stringToPDFString(str) { - var i, n = str.length, str2 = ''; - if (str[0] === '\xFE' && str[1] === '\xFF') { - // UTF16BE BOM - for (i = 2; i < n; i += 2) - str2 += String.fromCharCode( - (str.charCodeAt(i) << 8) | str.charCodeAt(i + 1)); - } else { - for (i = 0; i < n; ++i) { - var code = PDFStringTranslateTable[str.charCodeAt(i)]; - str2 += code ? String.fromCharCode(code) : str.charAt(i); - } - } - return str2; + var i, + n = str.length, + str2 = ""; + if (str[0] === "\xFE" && str[1] === "\xFF") { + // UTF16BE BOM + for (i = 2; i < n; i += 2) + str2 += String.fromCharCode( + (str.charCodeAt(i) << 8) | str.charCodeAt(i + 1) + ); + } else { + for (i = 0; i < n; ++i) { + var code = PDFStringTranslateTable[str.charCodeAt(i)]; + str2 += code ? String.fromCharCode(code) : str.charAt(i); + } + } + return str2; } function stringToUTF8String(str) { - return decodeURIComponent(escape(str)); + return decodeURIComponent(escape(str)); } function isEmptyObj(obj) { - for (var key in obj) { - return false; - } - return true; + for (var key in obj) { + return false; + } + return true; } function isBool(v) { - return typeof v == 'boolean'; + return typeof v == "boolean"; } function isInt(v) { - return typeof v == 'number' && ((v | 0) == v); + return typeof v == "number" && (v | 0) == v; } function isNum(v) { - return typeof v == 'number'; + return typeof v == "number"; } function isString(v) { - return typeof v == 'string'; + return typeof v == "string"; } function isNull(v) { - return v === null; + return v === null; } function isName(v) { - return v instanceof Name; + return v instanceof Name; } function isCmd(v, cmd) { - return v instanceof Cmd && (!cmd || v.cmd == cmd); + return v instanceof Cmd && (!cmd || v.cmd == cmd); } function isDict(v, type) { - if (!(v instanceof Dict)) { - return false; - } - if (!type) { - return true; - } - var dictType = v.get('Type'); - return isName(dictType) && dictType.name == type; + if (!(v instanceof Dict)) { + return false; + } + if (!type) { + return true; + } + var dictType = v.get("Type"); + return isName(dictType) && dictType.name == type; } function isArray(v) { - return v instanceof Array; + return v instanceof Array; } function isStream(v) { - return typeof v == 'object' && v !== null && v !== undefined && - ('getBytes' in v); + return ( + typeof v == "object" && v !== null && v !== undefined && "getBytes" in v + ); } function isArrayBuffer(v) { - return typeof v == 'object' && v !== null && v !== undefined && - ('byteLength' in v); + return ( + typeof v == "object" && v !== null && v !== undefined && "byteLength" in v + ); } function isRef(v) { - return v instanceof Ref; + return v instanceof Ref; } function isPDFFunction(v) { - var fnDict; - if (typeof v != 'object') - return false; - else if (isDict(v)) - fnDict = v; - else if (isStream(v)) - fnDict = v.dict; - else - return false; - return fnDict.has('FunctionType'); + var fnDict; + if (typeof v != "object") return false; + else if (isDict(v)) fnDict = v; + else if (isStream(v)) fnDict = v.dict; + else return false; + return fnDict.has("FunctionType"); } /** @@ -812,282 +840,282 @@ function isPDFFunction(v) { * Based off of the work in: * https://bugzilla.mozilla.org/show_bug.cgi?id=810490 */ -var Promise = PDFJS.Promise = (function PromiseClosure() { - var STATUS_PENDING = 0; - var STATUS_RESOLVED = 1; - var STATUS_REJECTED = 2; - - // In an attempt to avoid silent exceptions, unhandled rejections are - // tracked and if they aren't handled in a certain amount of time an - // error is logged. - var REJECTION_TIMEOUT = 500; - - var HandlerManager = { - handlers: [], - running: false, - unhandledRejections: [], - pendingRejectionCheck: false, - - scheduleHandlers: function scheduleHandlers(promise) { - if (promise._status == STATUS_PENDING) { - return; - } - - this.handlers = this.handlers.concat(promise._handlers); - promise._handlers = []; - - if (this.running) { - return; - } - this.running = true; - - setTimeout(this.runHandlers.bind(this), 0); - }, - - runHandlers: function runHandlers() { - while (this.handlers.length > 0) { - var handler = this.handlers.shift(); - - var nextStatus = handler.thisPromise._status; - var nextValue = handler.thisPromise._value; - - try { - if (nextStatus === STATUS_RESOLVED) { - if (typeof(handler.onResolve) == 'function') { - nextValue = handler.onResolve(nextValue); - } - } else if (typeof(handler.onReject) === 'function') { - nextValue = handler.onReject(nextValue); - nextStatus = STATUS_RESOLVED; - - if (handler.thisPromise._unhandledRejection) { - this.removeUnhandeledRejection(handler.thisPromise); - } - } - } catch (ex) { - nextStatus = STATUS_REJECTED; - nextValue = ex; - } - - handler.nextPromise._updateStatus(nextStatus, nextValue); - } - - this.running = false; - }, - - addUnhandledRejection: function addUnhandledRejection(promise) { - this.unhandledRejections.push({ - promise: promise, - time: Date.now() - }); - this.scheduleRejectionCheck(); - }, - - removeUnhandeledRejection: function removeUnhandeledRejection(promise) { - promise._unhandledRejection = false; - for (var i = 0; i < this.unhandledRejections.length; i++) { - if (this.unhandledRejections[i].promise === promise) { - this.unhandledRejections.splice(i); - i--; - } - } - }, - - scheduleRejectionCheck: function scheduleRejectionCheck() { - if (this.pendingRejectionCheck) { - return; - } - this.pendingRejectionCheck = true; - setTimeout(function rejectionCheck() { - this.pendingRejectionCheck = false; - var now = Date.now(); - for (var i = 0; i < this.unhandledRejections.length; i++) { - if (now - this.unhandledRejections[i].time > REJECTION_TIMEOUT) { - var unhandled = this.unhandledRejections[i].promise._value; - var msg = 'Unhandled rejection: ' + unhandled; - if (unhandled.stack) { - msg += '\n' + unhandled.stack; - } - warn(msg); - this.unhandledRejections.splice(i); - i--; - } - } - if (this.unhandledRejections.length) { - this.scheduleRejectionCheck(); - } - }.bind(this), REJECTION_TIMEOUT); - } - }; - - function Promise() { - this._status = STATUS_PENDING; - this._handlers = []; - } - /** - * Builds a promise that is resolved when all the passed in promises are - * resolved. - * @param {array} array of data and/or promises to wait for. - * @return {Promise} New dependant promise. - */ - Promise.all = function Promise_all(promises) { - var deferred = new Promise(); - var unresolved = promises.length; - var results = []; - if (unresolved === 0) { - deferred.resolve(results); - return deferred; - } - function reject(reason) { - if (deferred._status === STATUS_REJECTED) { - return; - } - results = []; - deferred.reject(reason); - } - for (var i = 0, ii = promises.length; i < ii; ++i) { - var promise = promises[i]; - var resolve = (function(i) { - return function(value) { - if (deferred._status === STATUS_REJECTED) { - return; - } - results[i] = value; - unresolved--; - if (unresolved === 0) - deferred.resolve(results); - }; - })(i); - if (Promise.isPromise(promise)) { - promise.then(resolve, reject); - } else { - resolve(promise); - } - } - return deferred; - }; - - /** - * Checks if the value is likely a promise (has a 'then' function). - * @return {boolean} true if x is thenable - */ - Promise.isPromise = function Promise_isPromise(value) { - return value && typeof value.then === 'function'; - }; - - Promise.prototype = { - _status: null, - _value: null, - _handlers: null, - _unhandledRejection: null, - - _updateStatus: function Promise__updateStatus(status, value) { - if (this._status === STATUS_RESOLVED || - this._status === STATUS_REJECTED) { - return; - } - - if (status == STATUS_RESOLVED && - Promise.isPromise(value)) { - value.then(this._updateStatus.bind(this, STATUS_RESOLVED), - this._updateStatus.bind(this, STATUS_REJECTED)); - return; - } - - this._status = status; - this._value = value; - - if (status === STATUS_REJECTED && this._handlers.length === 0) { - this._unhandledRejection = true; - HandlerManager.addUnhandledRejection(this); - } - - HandlerManager.scheduleHandlers(this); - }, - - get isResolved() { - return this._status === STATUS_RESOLVED; - }, - - get isRejected() { - return this._status === STATUS_REJECTED; - }, - - resolve: function Promise_resolve(value) { - this._updateStatus(STATUS_RESOLVED, value); - }, - - reject: function Promise_reject(reason) { - this._updateStatus(STATUS_REJECTED, reason); - }, - - then: function Promise_then(onResolve, onReject) { - var nextPromise = new Promise(); - this._handlers.push({ - thisPromise: this, - onResolve: onResolve, - onReject: onReject, - nextPromise: nextPromise - }); - HandlerManager.scheduleHandlers(this); - return nextPromise; - } - }; - - return Promise; -})(); +var Promise = (PDFJS.Promise = (function PromiseClosure() { + var STATUS_PENDING = 0; + var STATUS_RESOLVED = 1; + var STATUS_REJECTED = 2; + + // In an attempt to avoid silent exceptions, unhandled rejections are + // tracked and if they aren't handled in a certain amount of time an + // error is logged. + var REJECTION_TIMEOUT = 500; + + var HandlerManager = { + handlers: [], + running: false, + unhandledRejections: [], + pendingRejectionCheck: false, + + scheduleHandlers: function scheduleHandlers(promise) { + if (promise._status == STATUS_PENDING) { + return; + } + + this.handlers = this.handlers.concat(promise._handlers); + promise._handlers = []; + + if (this.running) { + return; + } + this.running = true; + + setTimeout(this.runHandlers.bind(this), 0); + }, + + runHandlers: function runHandlers() { + while (this.handlers.length > 0) { + var handler = this.handlers.shift(); + + var nextStatus = handler.thisPromise._status; + var nextValue = handler.thisPromise._value; + + try { + if (nextStatus === STATUS_RESOLVED) { + if (typeof handler.onResolve == "function") { + nextValue = handler.onResolve(nextValue); + } + } else if (typeof handler.onReject === "function") { + nextValue = handler.onReject(nextValue); + nextStatus = STATUS_RESOLVED; + + if (handler.thisPromise._unhandledRejection) { + this.removeUnhandeledRejection(handler.thisPromise); + } + } + } catch (ex) { + nextStatus = STATUS_REJECTED; + nextValue = ex; + } + + handler.nextPromise._updateStatus(nextStatus, nextValue); + } + + this.running = false; + }, + + addUnhandledRejection: function addUnhandledRejection(promise) { + this.unhandledRejections.push({ + promise: promise, + time: Date.now(), + }); + this.scheduleRejectionCheck(); + }, + + removeUnhandeledRejection: function removeUnhandeledRejection(promise) { + promise._unhandledRejection = false; + for (var i = 0; i < this.unhandledRejections.length; i++) { + if (this.unhandledRejections[i].promise === promise) { + this.unhandledRejections.splice(i); + i--; + } + } + }, + + scheduleRejectionCheck: function scheduleRejectionCheck() { + if (this.pendingRejectionCheck) { + return; + } + this.pendingRejectionCheck = true; + setTimeout( + function rejectionCheck() { + this.pendingRejectionCheck = false; + var now = Date.now(); + for (var i = 0; i < this.unhandledRejections.length; i++) { + if (now - this.unhandledRejections[i].time > REJECTION_TIMEOUT) { + var unhandled = this.unhandledRejections[i].promise._value; + var msg = "Unhandled rejection: " + unhandled; + if (unhandled.stack) { + msg += "\n" + unhandled.stack; + } + warn(msg); + this.unhandledRejections.splice(i); + i--; + } + } + if (this.unhandledRejections.length) { + this.scheduleRejectionCheck(); + } + }.bind(this), + REJECTION_TIMEOUT + ); + }, + }; + + function Promise() { + this._status = STATUS_PENDING; + this._handlers = []; + } + /** + * Builds a promise that is resolved when all the passed in promises are + * resolved. + * @param {array} array of data and/or promises to wait for. + * @return {Promise} New dependant promise. + */ + Promise.all = function Promise_all(promises) { + var deferred = new Promise(); + var unresolved = promises.length; + var results = []; + if (unresolved === 0) { + deferred.resolve(results); + return deferred; + } + function reject(reason) { + if (deferred._status === STATUS_REJECTED) { + return; + } + results = []; + deferred.reject(reason); + } + for (var i = 0, ii = promises.length; i < ii; ++i) { + var promise = promises[i]; + var resolve = (function (i) { + return function (value) { + if (deferred._status === STATUS_REJECTED) { + return; + } + results[i] = value; + unresolved--; + if (unresolved === 0) deferred.resolve(results); + }; + })(i); + if (Promise.isPromise(promise)) { + promise.then(resolve, reject); + } else { + resolve(promise); + } + } + return deferred; + }; + + /** + * Checks if the value is likely a promise (has a 'then' function). + * @return {boolean} true if x is thenable + */ + Promise.isPromise = function Promise_isPromise(value) { + return value && typeof value.then === "function"; + }; + + Promise.prototype = { + _status: null, + _value: null, + _handlers: null, + _unhandledRejection: null, + + _updateStatus: function Promise__updateStatus(status, value) { + if ( + this._status === STATUS_RESOLVED || + this._status === STATUS_REJECTED + ) { + return; + } + + if (status == STATUS_RESOLVED && Promise.isPromise(value)) { + value.then( + this._updateStatus.bind(this, STATUS_RESOLVED), + this._updateStatus.bind(this, STATUS_REJECTED) + ); + return; + } + + this._status = status; + this._value = value; + + if (status === STATUS_REJECTED && this._handlers.length === 0) { + this._unhandledRejection = true; + HandlerManager.addUnhandledRejection(this); + } + + HandlerManager.scheduleHandlers(this); + }, + + get isResolved() { + return this._status === STATUS_RESOLVED; + }, + + get isRejected() { + return this._status === STATUS_REJECTED; + }, + + resolve: function Promise_resolve(value) { + this._updateStatus(STATUS_RESOLVED, value); + }, + + reject: function Promise_reject(reason) { + this._updateStatus(STATUS_REJECTED, reason); + }, + + then: function Promise_then(onResolve, onReject) { + var nextPromise = new Promise(); + this._handlers.push({ + thisPromise: this, + onResolve: onResolve, + onReject: onReject, + nextPromise: nextPromise, + }); + HandlerManager.scheduleHandlers(this); + return nextPromise; + }, + }; + + return Promise; +})()); var StatTimer = (function StatTimerClosure() { - function rpad(str, pad, length) { - while (str.length < length) - str += pad; - return str; - } - function StatTimer() { - this.started = {}; - this.times = []; - this.enabled = true; - } - StatTimer.prototype = { - time: function StatTimer_time(name) { - if (!this.enabled) - return; - if (name in this.started) - warn('Timer is already running for ' + name); - this.started[name] = Date.now(); - }, - timeEnd: function StatTimer_timeEnd(name) { - if (!this.enabled) - return; - if (!(name in this.started)) - warn('Timer has not been started for ' + name); - this.times.push({ - 'name': name, - 'start': this.started[name], - 'end': Date.now() - }); - // Remove timer from started so it can be called again. - delete this.started[name]; - }, - toString: function StatTimer_toString() { - var times = this.times; - var out = ''; - // Find the longest name for padding purposes. - var longest = 0; - for (var i = 0, ii = times.length; i < ii; ++i) { - var name = times[i]['name']; - if (name.length > longest) - longest = name.length; - } - for (var i = 0, ii = times.length; i < ii; ++i) { - var span = times[i]; - var duration = span.end - span.start; - out += rpad(span['name'], ' ', longest) + ' ' + duration + 'ms\n'; - } - return out; - } - }; - return StatTimer; + function rpad(str, pad, length) { + while (str.length < length) str += pad; + return str; + } + function StatTimer() { + this.started = {}; + this.times = []; + this.enabled = true; + } + StatTimer.prototype = { + time: function StatTimer_time(name) { + if (!this.enabled) return; + if (name in this.started) warn("Timer is already running for " + name); + this.started[name] = Date.now(); + }, + timeEnd: function StatTimer_timeEnd(name) { + if (!this.enabled) return; + if (!(name in this.started)) + warn("Timer has not been started for " + name); + this.times.push({ + name: name, + start: this.started[name], + end: Date.now(), + }); + // Remove timer from started so it can be called again. + delete this.started[name]; + }, + toString: function StatTimer_toString() { + var times = this.times; + var out = ""; + // Find the longest name for padding purposes. + var longest = 0; + for (var i = 0, ii = times.length; i < ii; ++i) { + var name = times[i]["name"]; + if (name.length > longest) longest = name.length; + } + for (var i = 0, ii = times.length; i < ii; ++i) { + var span = times[i]; + var duration = span.end - span.start; + out += rpad(span["name"], " ", longest) + " " + duration + "ms\n"; + } + return out; + }, + }; + return StatTimer; })(); PDFJS.createBlob = function createBlob(data, contentType) { @@ -1095,133 +1123,142 @@ PDFJS.createBlob = function createBlob(data, contentType) { }; PDFJS.createObjectURL = (function createObjectURLClosure() { - if (typeof URL !== 'undefined' && URL.createObjectURL) { - return function createObjectURL(data, contentType) { - var blob = PDFJS.createBlob(data, contentType); - return URL.createObjectURL(blob); - }; - } - - // Blob/createObjectURL is not available, falling back to data schema. - var digits = - 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; - - return function createObjectURL(data, contentType) { - var buffer = 'data:' + contentType + ';base64,'; - for (var i = 0, ii = data.length; i < ii; i += 3) { - var b1 = data[i] & 0xFF; - var b2 = data[i + 1] & 0xFF; - var b3 = data[i + 2] & 0xFF; - var d1 = b1 >> 2, d2 = ((b1 & 3) << 4) | (b2 >> 4); - var d3 = i + 1 < ii ? ((b2 & 0xF) << 2) | (b3 >> 6) : 64; - var d4 = i + 2 < ii ? (b3 & 0x3F) : 64; - buffer += digits[d1] + digits[d2] + digits[d3] + digits[d4]; - } - return buffer; - }; + if (typeof URL !== "undefined" && URL.createObjectURL) { + return function createObjectURL(data, contentType) { + var blob = PDFJS.createBlob(data, contentType); + return URL.createObjectURL(blob); + }; + } + + // Blob/createObjectURL is not available, falling back to data schema. + var digits = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + + return function createObjectURL(data, contentType) { + var buffer = "data:" + contentType + ";base64,"; + for (var i = 0, ii = data.length; i < ii; i += 3) { + var b1 = data[i] & 0xff; + var b2 = data[i + 1] & 0xff; + var b3 = data[i + 2] & 0xff; + var d1 = b1 >> 2, + d2 = ((b1 & 3) << 4) | (b2 >> 4); + var d3 = i + 1 < ii ? ((b2 & 0xf) << 2) | (b3 >> 6) : 64; + var d4 = i + 2 < ii ? b3 & 0x3f : 64; + buffer += digits[d1] + digits[d2] + digits[d3] + digits[d4]; + } + return buffer; + }; })(); function MessageHandler(name, comObj) { - this.name = name; - this.comObj = comObj; - this.callbackIndex = 1; - this.postMessageTransfers = true; - var callbacks = this.callbacks = {}; - var ah = this.actionHandler = {}; - - ah['console_log'] = [function ahConsoleLog(data) { - log.apply(null, data); - }]; - // If there's no console available, console_error in the - // action handler will do nothing. - if ('console' in globalScope) { - ah['console_error'] = [function ahConsoleError(data) { - globalScope['console'].error.apply(null, data); - }]; - } else { - ah['console_error'] = [function ahConsoleError(data) { - log.apply(null, data); - }]; - } - ah['_warn'] = [function ah_Warn(data) { - warn(data); - }]; - - if (typeof comObj === 'object') { + this.name = name; + this.comObj = comObj; + this.callbackIndex = 1; + this.postMessageTransfers = true; + var callbacks = (this.callbacks = {}); + var ah = (this.actionHandler = {}); + + ah["console_log"] = [ + function ahConsoleLog(data) { + log.apply(null, data); + }, + ]; + // If there's no console available, console_error in the + // action handler will do nothing. + if ("console" in globalScope) { + ah["console_error"] = [ + function ahConsoleError(data) { + globalScope["console"].error.apply(null, data); + }, + ]; + } else { + ah["console_error"] = [ + function ahConsoleError(data) { + log.apply(null, data); + }, + ]; + } + ah["_warn"] = [ + function ah_Warn(data) { + warn(data); + }, + ]; + + if (typeof comObj === "object") { comObj.onmessage = function messageHandlerComObjOnMessage(event) { var data = event.data; if (data.isReply) { - var callbackId = data.callbackId; - if (data.callbackId in callbacks) { - var callback = callbacks[callbackId]; - delete callbacks[callbackId]; - callback(data.data); - } else { - error('Cannot resolve callback ' + callbackId); - } + var callbackId = data.callbackId; + if (data.callbackId in callbacks) { + var callback = callbacks[callbackId]; + delete callbacks[callbackId]; + callback(data.data); + } else { + error("Cannot resolve callback " + callbackId); + } } else if (data.action in ah) { - var action = ah[data.action]; - if (data.callbackId) { - var promise = new Promise(); - promise.then(function(resolvedData) { - comObj.postMessage({ - isReply: true, - callbackId: data.callbackId, - data: resolvedData + var action = ah[data.action]; + if (data.callbackId) { + var promise = new Promise(); + promise.then(function (resolvedData) { + comObj.postMessage({ + isReply: true, + callbackId: data.callbackId, + data: resolvedData, + }); }); - }); - action[0].call(action[1], data.data, promise); - } else { - action[0].call(action[1], data.data); - } + action[0].call(action[1], data.data, promise); + } else { + action[0].call(action[1], data.data); + } } else { - error('Unkown action from worker: ' + data.action); + error("Unkown action from worker: " + data.action); } }; } } MessageHandler.prototype = { - on: function messageHandlerOn(actionName, handler, scope) { - var ah = this.actionHandler; - if (ah[actionName]) { - error('There is already an actionName called "' + actionName + '"'); - } - ah[actionName] = [handler, scope]; - }, - /** - * Sends a message to the comObj to invoke the action with the supplied data. - * @param {String} actionName Action to call. - * @param {JSON} data JSON data to send. - * @param {function} [callback] Optional callback that will handle a reply. - * @param {Array} [transfers] Optional list of transfers/ArrayBuffers - */ - send: function messageHandlerSend(actionName, data, callback, transfers) { - var message = { - action: actionName, - data: data - }; - if (callback) { - var callbackId = this.callbackIndex++; - this.callbacks[callbackId] = callback; - message.callbackId = callbackId; - } - if (transfers && this.postMessageTransfers) { - this.comObj.postMessage(message, transfers); - } else { - this.comObj.postMessage(message); - } - } + on: function messageHandlerOn(actionName, handler, scope) { + var ah = this.actionHandler; + if (ah[actionName]) { + error('There is already an actionName called "' + actionName + '"'); + } + ah[actionName] = [handler, scope]; + }, + /** + * Sends a message to the comObj to invoke the action with the supplied data. + * @param {String} actionName Action to call. + * @param {JSON} data JSON data to send. + * @param {function} [callback] Optional callback that will handle a reply. + * @param {Array} [transfers] Optional list of transfers/ArrayBuffers + */ + send: function messageHandlerSend(actionName, data, callback, transfers) { + var message = { + action: actionName, + data: data, + }; + if (callback) { + var callbackId = this.callbackIndex++; + this.callbacks[callbackId] = callback; + message.callbackId = callbackId; + } + if (transfers && this.postMessageTransfers) { + this.comObj.postMessage(message, transfers); + } else { + this.comObj.postMessage(message); + } + }, }; function loadJpegStream(id, imageUrl, objs) { - var img = new Image(); - img.onload = (function loadJpegStream_onloadClosure() { - objs.resolve(id, img); - }); -// img.src = imageUrl; - //MQZ. Apr.09.2013 calls windows.btoa safely - img.src = 'data:image/jpeg;base64,' + img.btoa(imageUrl); + var img = new Image(); + img.onload = function loadJpegStream_onloadClosure() { + objs.resolve(id, img); + }; + // img.src = imageUrl; + //MQZ. Apr.09.2013 calls windows.btoa safely + img.src = "data:image/jpeg;base64," + img.btoa(imageUrl); } //MQZ Oct.18.2013 expose util methods @@ -1229,20 +1266,15 @@ nodeUtil.p2jlog = log; nodeUtil.p2jinfo = info; nodeUtil.p2jwarn = warn; nodeUtil.p2jerror = error; -nodeUtil.verbosity = function(verbo) { - if (isNaN(verbo)) { - verbosity = WARNINGS; - } - else { - if (verbo <= ERRORS) { - verbosity = ERRORS; - } - else if (verbo >= INFOS) { - verbosity = INFOS; - } - else - verbosity = verbo; - } +nodeUtil.verbosity = function (verbo) { + if (isNaN(verbo)) { + verbosity = WARNINGS; + } else { + if (verbo <= ERRORS) { + verbosity = ERRORS; + } else if (verbo >= INFOS) { + verbosity = INFOS; + } else verbosity = verbo; + } }; nodeUtil.verbosity(); - diff --git a/jest.config.json b/jest.config.json new file mode 100644 index 00000000..2457cddc --- /dev/null +++ b/jest.config.json @@ -0,0 +1,6 @@ +{ + "testMatch": ["**/test/_test_.*"], + "testEnvironment": "node", + "bail": false, + "testFailureExitCode": 1 +} diff --git a/lib/p2jcmd.js b/lib/p2jcmd.js index 08c39dd5..5a59c3d0 100644 --- a/lib/p2jcmd.js +++ b/lib/p2jcmd.js @@ -2,94 +2,118 @@ import nodeUtil from "util"; import fs from "fs"; import path from "path"; -import {ParserStream, StringifyStream} from "./parserstream.js"; -import PDFParser from "../pdfparser.js"; - -import { pkInfo, _PARSER_SIG as _PRO_TIMER } from "../pkinfo.js"; +import { + PDFParser, + ParserStream, + StringifyStream, + pkInfo, + _PARSER_SIG as _PRO_TIMER, +} from "../dist/pdfparser.js"; import { yargs } from "./p2jcmdarg.js"; const argv = yargs.argv; -const ONLY_SHOW_VERSION = ('v' in argv); -const ONLY_SHOW_HELP = ('h' in argv); -const VERBOSITY_LEVEL = (('s' in argv) ? 0 : 5); -const HAS_INPUT_DIR_OR_FILE = ('f' in argv); +const ONLY_SHOW_VERSION = "v" in argv; +const ONLY_SHOW_HELP = "h" in argv; +const VERBOSITY_LEVEL = "s" in argv ? 0 : 5; +const HAS_INPUT_DIR_OR_FILE = "f" in argv; -const PROCESS_RAW_TEXT_CONTENT = ('c' in argv); -const PROCESS_FIELDS_CONTENT = ('t' in argv); -const PROCESS_MERGE_BROKEN_TEXT_BLOCKS = ('m' in argv); -const PROCESS_WITH_STREAM = ('r' in argv); +const PROCESS_RAW_TEXT_CONTENT = "c" in argv; +const PROCESS_FIELDS_CONTENT = "t" in argv; +const PROCESS_MERGE_BROKEN_TEXT_BLOCKS = "m" in argv; +const PROCESS_WITH_STREAM = "r" in argv; const INPUT_DIR_OR_FILE = argv.f; class PDFProcessor { - inputDir = null; - inputFile = null; - inputPath = null; - - outputDir = null; - outputFile = null; - outputPath = null; - - pdfParser = null; - curCLI = null; - - // constructor - constructor(inputDir, inputFile, curCLI) { - // public, this instance copies - this.inputDir = path.normalize(inputDir); - this.inputFile = inputFile; - this.inputPath = path.join(this.inputDir, this.inputFile); - - this.outputDir = path.normalize(argv.o || inputDir); - this.outputFile = null; - this.outputPath = null; - - this.pdfParser = null; - this.curCLI = curCLI; - } - - //private methods + inputDir = null; + inputFile = null; + inputPath = null; + + outputDir = null; + outputFile = null; + outputPath = null; + + pdfParser = null; + curCLI = null; + + // constructor + constructor(inputDir, inputFile, curCLI) { + // public, this instance copies + this.inputDir = path.normalize(inputDir); + this.inputFile = inputFile; + this.inputPath = path.join(this.inputDir, this.inputFile); + + this.outputDir = path.normalize(argv.o || inputDir); + this.outputFile = null; + this.outputPath = null; + + this.pdfParser = null; + this.curCLI = curCLI; + } + + //private methods #generateMergedTextBlocksStream() { - return new Promise( (resolve, reject) => { - const outputStream = ParserStream.createOutputStream(this.outputPath.replace(".json", ".merged.json"), resolve, reject); - this.pdfParser.getMergedTextBlocksStream().pipe(new StringifyStream()).pipe(outputStream); + return new Promise((resolve, reject) => { + const outputStream = ParserStream.createOutputStream( + this.outputPath.replace(".json", ".merged.json"), + resolve, + reject + ); + this.pdfParser + .getMergedTextBlocksStream() + .pipe(new StringifyStream()) + .pipe(outputStream); }); } - #generateRawTextContentStream() { - return new Promise( (resolve, reject) => { - const outputStream = ParserStream.createOutputStream(this.outputPath.replace(".json", ".content.txt"), resolve, reject); + #generateRawTextContentStream() { + return new Promise((resolve, reject) => { + const outputStream = ParserStream.createOutputStream( + this.outputPath.replace(".json", ".content.txt"), + resolve, + reject + ); this.pdfParser.getRawTextContentStream().pipe(outputStream); }); - } + } - #generateFieldsTypesStream() { - return new Promise( (resolve, reject) => { - const outputStream = ParserStream.createOutputStream(this.outputPath.replace(".json", ".fields.json"), resolve, reject); - this.pdfParser.getAllFieldsTypesStream().pipe(new StringifyStream()).pipe(outputStream); + #generateFieldsTypesStream() { + return new Promise((resolve, reject) => { + const outputStream = ParserStream.createOutputStream( + this.outputPath.replace(".json", ".fields.json"), + resolve, + reject + ); + this.pdfParser + .getAllFieldsTypesStream() + .pipe(new StringifyStream()) + .pipe(outputStream); }); } #processAdditionalStreams() { - const outputTasks = []; - if (PROCESS_FIELDS_CONTENT) {//needs to generate fields.json file - outputTasks.push(this.#generateFieldsTypesStream()); - } - if (PROCESS_RAW_TEXT_CONTENT) {//needs to generate content.txt file - outputTasks.push(this.#generateRawTextContentStream()); - } - if (PROCESS_MERGE_BROKEN_TEXT_BLOCKS) {//needs to generate json file with merged broken text blocks - outputTasks.push(this.#generateMergedTextBlocksStream()); - } + const outputTasks = []; + if (PROCESS_FIELDS_CONTENT) { + //needs to generate fields.json file + outputTasks.push(this.#generateFieldsTypesStream()); + } + if (PROCESS_RAW_TEXT_CONTENT) { + //needs to generate content.txt file + outputTasks.push(this.#generateRawTextContentStream()); + } + if (PROCESS_MERGE_BROKEN_TEXT_BLOCKS) { + //needs to generate json file with merged broken text blocks + outputTasks.push(this.#generateMergedTextBlocksStream()); + } return Promise.allSettled(outputTasks); } #onPrimarySuccess(resolve, reject) { this.curCLI.addResultCount(); this.#processAdditionalStreams() - .then( retVal => resolve(retVal)) - .catch( err => reject(err) ); + .then((retVal) => resolve(retVal)) + .catch((err) => reject(err)); } #onPrimaryError(err, reject) { @@ -98,28 +122,39 @@ class PDFProcessor { } #parseOnePDFStream() { - return new Promise( (resolve, reject) => { + return new Promise((resolve, reject) => { this.pdfParser = new PDFParser(null, PROCESS_RAW_TEXT_CONTENT); - this.pdfParser.on("pdfParser_dataError", evtData => this.#onPrimaryError(evtData.parserError, reject)); - + this.pdfParser.on("pdfParser_dataError", (evtData) => + this.#onPrimaryError(evtData.parserError, reject) + ); + const outputStream = fs.createWriteStream(this.outputPath); - outputStream.on('finish', () => this.#onPrimarySuccess(resolve, reject)); - outputStream.on('error', err => this.#onPrimaryError(err, reject)); - - nodeUtil.p2jinfo("Transcoding Stream " + this.inputFile + " to - " + this.outputPath); - const inputStream = fs.createReadStream(this.inputPath, {bufferSize: 64 * 1024}); - inputStream.pipe(this.pdfParser.createParserStream()).pipe(new StringifyStream()).pipe(outputStream); + outputStream.on("finish", () => this.#onPrimarySuccess(resolve, reject)); + outputStream.on("error", (err) => this.#onPrimaryError(err, reject)); + + nodeUtil.p2jinfo( + "Transcoding Stream " + this.inputFile + " to - " + this.outputPath + ); + const inputStream = fs.createReadStream(this.inputPath, { + bufferSize: 64 * 1024, + }); + inputStream + .pipe(this.pdfParser.createParserStream()) + .pipe(new StringifyStream()) + .pipe(outputStream); }); - }; + } #parseOnePDF() { - return new Promise( (resolve, reject) => { + return new Promise((resolve, reject) => { this.pdfParser = new PDFParser(null, PROCESS_RAW_TEXT_CONTENT); - this.pdfParser.on("pdfParser_dataError", evtData => this.#onPrimaryError(evtData.parserError, reject)); + this.pdfParser.on("pdfParser_dataError", (evtData) => + this.#onPrimaryError(evtData.parserError, reject) + ); - this.pdfParser.on("pdfParser_dataReady", evtData => { - fs.writeFile(this.outputPath, JSON.stringify(evtData), err => { - if(err) { + this.pdfParser.on("pdfParser_dataReady", (evtData) => { + fs.writeFile(this.outputPath, JSON.stringify(evtData), (err) => { + if (err) { this.#onPrimaryError(err, reject); } else { this.#onPrimarySuccess(resolve, reject); @@ -127,213 +162,232 @@ class PDFProcessor { }); }); - nodeUtil.p2jinfo("Transcoding File " + this.inputFile + " to - " + this.outputPath); + nodeUtil.p2jinfo( + "Transcoding File " + this.inputFile + " to - " + this.outputPath + ); this.pdfParser.loadPDF(this.inputPath, VERBOSITY_LEVEL); }); } - //public methods - validateParams() { - let retVal = null; - - if (!fs.existsSync(this.inputDir)) - retVal = "Input error: input directory doesn't exist - " + this.inputDir + "."; - else if (!fs.existsSync(this.inputPath)) - retVal = "Input error: input file doesn't exist - " + this.inputPath + "."; - else if (!fs.existsSync(this.outputDir)) - retVal = "Input error: output directory doesn't exist - " + this.outputDir + "."; - - if (retVal != null) { - this.curCLI.addResultCount(retVal); - return retVal; - } - - const inExtName = path.extname(this.inputFile).toLowerCase(); - if (inExtName !== '.pdf') - retVal = "Input error: input file name doesn't have pdf extention - " + this.inputFile + "."; - else { - this.outputFile = path.basename(this.inputPath, inExtName) + ".json"; - this.outputPath = path.normalize(this.outputDir + "/" + this.outputFile); - if (fs.existsSync(this.outputPath)) - nodeUtil.p2jwarn("Output file will be replaced - " + this.outputPath); - else { - let fod = fs.openSync(this.outputPath, "wx"); - if (!fod) - retVal = "Input error: can not write to " + this.outputPath; - else { - fs.closeSync(fod); - fs.unlinkSync(this.outputPath); - } - } - } - - return retVal; - } - - destroy() { - this.inputDir = null; - this.inputFile = null; - this.inputPath = null; - this.outputDir = null; - this.outputPath = null; - - if (this.pdfParser) { - this.pdfParser.destroy(); - } - this.pdfParser = null; - this.curCLI = null; - } - - processFile() { + //public methods + validateParams() { + let retVal = null; + + if (!fs.existsSync(this.inputDir)) + retVal = + "Input error: input directory doesn't exist - " + this.inputDir + "."; + else if (!fs.existsSync(this.inputPath)) + retVal = + "Input error: input file doesn't exist - " + this.inputPath + "."; + else if (!fs.existsSync(this.outputDir)) + retVal = + "Input error: output directory doesn't exist - " + this.outputDir + "."; + + if (retVal != null) { + this.curCLI.addResultCount(retVal); + return retVal; + } + + const inExtName = path.extname(this.inputFile).toLowerCase(); + if (inExtName !== ".pdf") + retVal = + "Input error: input file name doesn't have pdf extention - " + + this.inputFile + + "."; + else { + this.outputFile = path.basename(this.inputPath, inExtName) + ".json"; + this.outputPath = path.normalize(this.outputDir + "/" + this.outputFile); + if (fs.existsSync(this.outputPath)) + nodeUtil.p2jwarn("Output file will be replaced - " + this.outputPath); + else { + let fod = fs.openSync(this.outputPath, "wx"); + if (!fod) retVal = "Input error: can not write to " + this.outputPath; + else { + fs.closeSync(fod); + fs.unlinkSync(this.outputPath); + } + } + } + + return retVal; + } + + destroy() { + this.inputDir = null; + this.inputFile = null; + this.inputPath = null; + this.outputDir = null; + this.outputPath = null; + + if (this.pdfParser) { + this.pdfParser.destroy(); + } + this.pdfParser = null; + this.curCLI = null; + } + + processFile() { return new Promise((resolve, reject) => { const validateMsg = this.validateParams(); if (!!validateMsg) { reject(validateMsg); - } - else { - const parserFunc = PROCESS_WITH_STREAM ? this.#parseOnePDFStream : this.#parseOnePDF; - parserFunc.call(this) - .then( value => resolve(value) ) - .catch( err => reject(err) ); + } else { + const parserFunc = PROCESS_WITH_STREAM + ? this.#parseOnePDFStream + : this.#parseOnePDF; + parserFunc + .call(this) + .then((value) => resolve(value)) + .catch((err) => reject(err)); } }); - } + } - getOutputFile = function() { - return path.join(this.outputDir, this.outputFile); - } + getOutputFile = function () { + return path.join(this.outputDir, this.outputFile); + }; } export default class PDFCLI { - inputCount = 0; - successCount = 0; - failedCount = 0; - warningCount = 0; - statusMsgs = []; - - // constructor - constructor() { - this.inputCount = 0; - this.successCount = 0; - this.failedCount = 0; - this.warningCount = 0; - this.statusMsgs = []; - } - - initialize() { - nodeUtil.verbosity(VERBOSITY_LEVEL); - let retVal = true; - try { - if (ONLY_SHOW_VERSION) { - console.log(pkInfo.version); - retVal = false; - } - else if (ONLY_SHOW_HELP) { - yargs.showHelp(); - retVal = false; - } - else if (!HAS_INPUT_DIR_OR_FILE) { - yargs.showHelp(); - console.error("-f is required to specify input directory or file."); - retVal = false; - } - } - catch(e) { - console.error("Exception: " + e.message); - retVal = false; - } - return retVal; - } - - async start() { - if (!this.initialize()) - return; + inputCount = 0; + successCount = 0; + failedCount = 0; + warningCount = 0; + statusMsgs = []; + + // constructor + constructor() { + this.inputCount = 0; + this.successCount = 0; + this.failedCount = 0; + this.warningCount = 0; + this.statusMsgs = []; + } + + initialize() { + nodeUtil.verbosity(VERBOSITY_LEVEL); + let retVal = true; + try { + if (ONLY_SHOW_VERSION) { + console.log(pkInfo.version); + retVal = false; + } else if (ONLY_SHOW_HELP) { + yargs.showHelp(); + retVal = false; + } else if (!HAS_INPUT_DIR_OR_FILE) { + yargs.showHelp(); + console.error("-f is required to specify input directory or file."); + retVal = false; + } + } catch (e) { + console.error("Exception: " + e.message); + retVal = false; + } + return retVal; + } + + async start() { + if (!this.initialize()) return; console.log(_PRO_TIMER); console.time(_PRO_TIMER); - + try { - const inputStatus = fs.statSync(INPUT_DIR_OR_FILE); - if (inputStatus.isFile()) { + const inputStatus = fs.statSync(INPUT_DIR_OR_FILE); + if (inputStatus.isFile()) { this.inputCount = 1; - await this.processOneFile(path.dirname(INPUT_DIR_OR_FILE), path.basename(INPUT_DIR_OR_FILE)); - } - else if (inputStatus.isDirectory()) { - await this.processOneDirectory(path.normalize(INPUT_DIR_OR_FILE)); - } - } - catch(e) { - console.error("Exception: ", e); - } - finally { + await this.processOneFile( + path.dirname(INPUT_DIR_OR_FILE), + path.basename(INPUT_DIR_OR_FILE) + ); + } else if (inputStatus.isDirectory()) { + await this.processOneDirectory(path.normalize(INPUT_DIR_OR_FILE)); + } + } catch (e) { + console.error("Exception: ", e); + } finally { this.complete(); } - } - - complete() { - if (this.statusMsgs.length > 0) - console.log(this.statusMsgs); - console.log(`${this.inputCount} input files\t${this.successCount} success\t${this.failedCount} fail\t${this.warningCount} warning`); - process.nextTick( () => { - console.timeEnd(_PRO_TIMER); - // process.exit((this.inputCount === this.successCount) ? 0 : 1); - }); - } - - processOneFile(inputDir, inputFile) { + } + + complete() { + if (this.statusMsgs.length > 0) console.log(this.statusMsgs); + console.log( + `${this.inputCount} input files\t${this.successCount} success\t${this.failedCount} fail\t${this.warningCount} warning` + ); + process.nextTick(() => { + console.timeEnd(_PRO_TIMER); + // process.exit((this.inputCount === this.successCount) ? 0 : 1); + }); + } + + processOneFile(inputDir, inputFile) { return new Promise((resolve, reject) => { const p2j = new PDFProcessor(inputDir, inputFile, this); - p2j.processFile() - .then( retVal => { - this.addStatusMsg(null, `${path.join(inputDir, inputFile)} => ${p2j.getOutputFile()}`); - retVal.forEach(ret => this.addStatusMsg(null, `+ ${ret.value}`)); + p2j + .processFile() + .then((retVal) => { + this.addStatusMsg( + null, + `${path.join(inputDir, inputFile)} => ${p2j.getOutputFile()}` + ); + retVal.forEach((ret) => this.addStatusMsg(null, `+ ${ret.value}`)); resolve(retVal); }) - .catch(error => { - this.addStatusMsg(error, `${path.join(inputDir, inputFile)} => ${error}`); + .catch((error) => { + this.addStatusMsg( + error, + `${path.join(inputDir, inputFile)} => ${error}` + ); reject(error); }) - .finally(() => p2j.destroy()); + .finally(() => p2j.destroy()); }); - } + } - processFiles(inputDir, files) { + processFiles(inputDir, files) { const allPromises = []; - files.forEach( (file, idx) => allPromises.push(this.processOneFile(inputDir, file)) ); + files.forEach((file, idx) => + allPromises.push(this.processOneFile(inputDir, file)) + ); return Promise.allSettled(allPromises); - } + } - processOneDirectory(inputDir) { - return new Promise((resolve, reject) => { + processOneDirectory(inputDir) { + return new Promise((resolve, reject) => { fs.readdir(inputDir, (err, files) => { if (err) { this.addStatusMsg(true, `[${inputDir}] - ${err.toString()}`); reject(err); - } - else { - const _iChars = "!@#$%^&*()+=[]\\\';,/{}|\":<>?~`.-_ "; - const pdfFiles = files.filter( file => file.slice(-4).toLowerCase() === '.pdf' && _iChars.indexOf(file.substring(0,1)) < 0 ); + } else { + const _iChars = "!@#$%^&*()+=[]\\';,/{}|\":<>?~`.-_ "; + const pdfFiles = files.filter( + (file) => + file.slice(-4).toLowerCase() === ".pdf" && + _iChars.indexOf(file.substring(0, 1)) < 0 + ); this.inputCount = pdfFiles.length; if (this.inputCount > 0) { this.processFiles(inputDir, pdfFiles) - .then( value => resolve(value) ) - .catch( err => reject(err) ); - } - else { + .then((value) => resolve(value)) + .catch((err) => reject(err)); + } else { this.addStatusMsg(true, `[${inputDir}] - No PDF files found`); resolve(); } } }); }); - } + } - addStatusMsg(error, oneMsg) { - this.statusMsgs.push(error ? `✗ Error : ${oneMsg}` : `✓ Success : ${oneMsg}`); - } + addStatusMsg(error, oneMsg) { + this.statusMsgs.push( + error ? `✗ Error : ${oneMsg}` : `✓ Success : ${oneMsg}` + ); + } - addResultCount(error) { - (error ? this.failedCount++ : this.successCount++); - } + addResultCount(error) { + error ? this.failedCount++ : this.successCount++; + } } diff --git a/lib/p2jcmdarg.js b/lib/p2jcmdarg.js index 12ddbc4f..aaf4c0e0 100644 --- a/lib/p2jcmdarg.js +++ b/lib/p2jcmdarg.js @@ -1,148 +1,174 @@ -import { pkInfo, _PARSER_SIG as _PRO_TIMER } from "../pkinfo.js"; +import { pkInfo, _PARSER_SIG as _PRO_TIMER } from "../dist/pdfparser.js"; class CLIArgParser { - args = []; - #aliases = {}; - - #usage = ""; - #argv = null; - - // constructor - constructor(args) { - if (Array.isArray(args)) - this.args = args; - } - - usage(usageMsg) { - this.#usage = usageMsg + '\n\nOptions:\n'; - return this; - } - - alias(key, name, description) { - this.#aliases[key] = {name, description}; - return this; - } - - showHelp() { - let helpMsg = this.#usage; - for (const [key, value] of Object.entries(this.#aliases)) { - helpMsg += `-${key},--${value.name}\t ${value.description}\n`; - } - console.log(helpMsg); - } - - get argv() { - return this.#argv ? this.#argv : this.#parseArgv(); - } - - static isNumber (x) { - if (typeof x === 'number') - return true; - if (/^0x[0-9a-f]+$/i.test(x)) - return true; - return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(x); - } - - #setArg(key, val, argv) { - const value = CLIArgParser.isNumber(val) ? Number(val) : val; - this.#setKey(argv, key.split('.'), value); - - const aliasKey = (key in this.#aliases) ? [this.#aliases[key].name] : []; - if (aliasKey.length < 1) { - for (const [akey, avalue] of Object.entries(this.#aliases)) { - if (key === avalue.name) { - aliasKey.push(akey); - break; - } - } - } - aliasKey.forEach(x => this.#setKey(argv, x.split('.'), value)); - } - - #setKey(obj, keys, value) { - let o = obj; - for (let i = 0; i < keys.length-1; i++) { - let key = keys[i]; - if (key === '__proto__') return; - if (o[key] === undefined) o[key] = {}; - if (o[key] === Object.prototype || o[key] === Number.prototype - || o[key] === String.prototype) o[key] = {}; - if (o[key] === Array.prototype) o[key] = []; - o = o[key]; - } - - let key = keys[keys.length - 1]; - if (key === '__proto__') return; - if (o === Object.prototype || o === Number.prototype - || o === String.prototype) o = {}; - if (o === Array.prototype) o = []; - if (o[key] === undefined) { - o[key] = value; - } - else if (Array.isArray(o[key])) { - o[key].push(value); - } - else { - o[key] = [ o[key], value ]; - } - } - - #parseArgv() { - let aliases=this.#aliases, args = this.args; - let argv = {}; - - for (let i = 0; i < args.length; i++) { - let arg = args[i]; - - if (/^--.+/.test(arg)) { - let key = arg.match(/^--(.+)/)[1]; - let next = args[i + 1]; - if (next !== undefined && !/^-/.test(next)) { - this.#setArg(key, next, argv); - i++; - } - else if (/^(true|false)$/.test(next)) { - this.#setArg(key, next === 'true', argv); - i++; - } - else { - this.#setArg(key, true, argv); - } - } - else if (/^-[^-]+/.test(arg)) { - let key = arg.slice(-1)[0]; - if (key !== '-') { - if (args[i+1] && !/^(-|--)[^-]/.test(args[i+1])) { - this.#setArg(key, args[i+1], argv); - i++; - } - else if (args[i+1] && /^(true|false)$/.test(args[i+1])) { - this.#setArg(key, args[i+1] === 'true', argv); - i++; - } - else { - this.#setArg(key, true, argv); - } - } - } - else { - console.warn("Unknow CLI options:", arg); - } - } - - this.#argv = argv; - return argv; - } + args = []; + #aliases = {}; + + #usage = ""; + #argv = null; + + // constructor + constructor(args) { + if (Array.isArray(args)) this.args = args; + } + + usage(usageMsg) { + this.#usage = usageMsg + "\n\nOptions:\n"; + return this; + } + + alias(key, name, description) { + this.#aliases[key] = { name, description }; + return this; + } + + showHelp() { + let helpMsg = this.#usage; + for (const [key, value] of Object.entries(this.#aliases)) { + helpMsg += `-${key},--${value.name}\t ${value.description}\n`; + } + console.log(helpMsg); + } + + get argv() { + return this.#argv ? this.#argv : this.#parseArgv(); + } + + static isNumber(x) { + if (typeof x === "number") return true; + if (/^0x[0-9a-f]+$/i.test(x)) return true; + return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(x); + } + + #setArg(key, val, argv) { + const value = CLIArgParser.isNumber(val) ? Number(val) : val; + this.#setKey(argv, key.split("."), value); + + const aliasKey = key in this.#aliases ? [this.#aliases[key].name] : []; + if (aliasKey.length < 1) { + for (const [akey, avalue] of Object.entries(this.#aliases)) { + if (key === avalue.name) { + aliasKey.push(akey); + break; + } + } + } + aliasKey.forEach((x) => this.#setKey(argv, x.split("."), value)); + } + + #setKey(obj, keys, value) { + let o = obj; + for (let i = 0; i < keys.length - 1; i++) { + let key = keys[i]; + if (key === "__proto__") return; + if (o[key] === undefined) o[key] = {}; + if ( + o[key] === Object.prototype || + o[key] === Number.prototype || + o[key] === String.prototype + ) + o[key] = {}; + if (o[key] === Array.prototype) o[key] = []; + o = o[key]; + } + + let key = keys[keys.length - 1]; + if (key === "__proto__") return; + if ( + o === Object.prototype || + o === Number.prototype || + o === String.prototype + ) + o = {}; + if (o === Array.prototype) o = []; + if (o[key] === undefined) { + o[key] = value; + } else if (Array.isArray(o[key])) { + o[key].push(value); + } else { + o[key] = [o[key], value]; + } + } + + #parseArgv() { + let aliases = this.#aliases, + args = this.args; + let argv = {}; + + for (let i = 0; i < args.length; i++) { + let arg = args[i]; + + if (/^--.+/.test(arg)) { + let key = arg.match(/^--(.+)/)[1]; + let next = args[i + 1]; + if (next !== undefined && !/^-/.test(next)) { + this.#setArg(key, next, argv); + i++; + } else if (/^(true|false)$/.test(next)) { + this.#setArg(key, next === "true", argv); + i++; + } else { + this.#setArg(key, true, argv); + } + } else if (/^-[^-]+/.test(arg)) { + let key = arg.slice(-1)[0]; + if (key !== "-") { + if (args[i + 1] && !/^(-|--)[^-]/.test(args[i + 1])) { + this.#setArg(key, args[i + 1], argv); + i++; + } else if (args[i + 1] && /^(true|false)$/.test(args[i + 1])) { + this.#setArg(key, args[i + 1] === "true", argv); + i++; + } else { + this.#setArg(key, true, argv); + } + } + } else { + console.warn("Unknow CLI options:", arg); + } + } + + this.#argv = argv; + return argv; + } } export const yargs = new CLIArgParser(process.argv.slice(2)) .usage(`\n${_PRO_TIMER}\n\nUsage: ${pkInfo.name} -f|--file [-o|output_dir]`) - .alias('v', 'version', 'Display version.') - .alias('h', 'help', 'Display brief help information.') - .alias('f', 'file', '(required) Full path of input PDF file or a directory to scan for all PDF files.\n\t\t When specifying a PDF file name, it must end with .PDF, otherwise it would be treated as a input directory.') - .alias('o', 'output', '(optional) Full path of output directory, must already exist.\n\t\t Current JSON file in the output folder will be replaced when file name is same.') - .alias('s', 'silent', '(optional) when specified, will only log errors, otherwise verbose.') - .alias('t', 'fieldTypes', '(optional) when specified, will generate .fields.json that includes fields ids and types.') - .alias('c', 'content', '(optional) when specified, will generate .content.txt that includes text content from PDF.') - .alias('m', 'merge', '(optional) when specified, will generate .merged.json that includes auto-merged broken text blocks from PDF.') - .alias('r', 'stream', '(optional) when specified, will process and parse with buffer/object transform stream rather than file system.'); + .alias("v", "version", "Display version.") + .alias("h", "help", "Display brief help information.") + .alias( + "f", + "file", + "(required) Full path of input PDF file or a directory to scan for all PDF files.\n\t\t When specifying a PDF file name, it must end with .PDF, otherwise it would be treated as a input directory." + ) + .alias( + "o", + "output", + "(optional) Full path of output directory, must already exist.\n\t\t Current JSON file in the output folder will be replaced when file name is same." + ) + .alias( + "s", + "silent", + "(optional) when specified, will only log errors, otherwise verbose." + ) + .alias( + "t", + "fieldTypes", + "(optional) when specified, will generate .fields.json that includes fields ids and types." + ) + .alias( + "c", + "content", + "(optional) when specified, will generate .content.txt that includes text content from PDF." + ) + .alias( + "m", + "merge", + "(optional) when specified, will generate .merged.json that includes auto-merged broken text blocks from PDF." + ) + .alias( + "r", + "stream", + "(optional) when specified, will process and parse with buffer/object transform stream rather than file system." + ); diff --git a/lib/pdf.js b/lib/pdf.js index d29e73f6..758cf12b 100644 --- a/lib/pdf.js +++ b/lib/pdf.js @@ -1,480 +1,409 @@ -import nodeUtil from 'util'; -import fs from 'fs'; -import path from 'path'; -import { fileURLToPath } from 'url'; - -import { EventEmitter } from 'events'; -import { Blob } from 'buffer'; -import { DOMParser } from '@xmldom/xmldom'; - -import PDFCanvas from './pdfcanvas.js'; -import PDFUnit from './pdfunit.js'; -import PDFField from './pdffield.js'; -import PDFAnno from './pdfanno.js'; -import Image from './pdfimage.js'; -import PDFFont from './pdffont.js'; -import PTIXmlParser from './ptixmlinject.js'; - -import { pkInfo, _PARSER_SIG } from '../pkinfo.js'; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); - -const _pdfjsFiles = [ - 'shared/util.js', - 'shared/colorspace.js', - 'shared/pattern.js', - 'shared/function.js', - 'shared/annotation.js', - - 'core/core.js', - 'core/obj.js', - 'core/charsets.js', - 'core/crypto.js', - 'core/evaluator.js', - 'core/fonts.js', - 'core/font_renderer.js', - 'core/glyphlist.js', - 'core/image.js', - 'core/metrics.js', - 'core/parser.js', - 'core/stream.js', - 'core/worker.js', - 'core/jpx.js', - 'core/jbig2.js', - 'core/bidi.js', - 'core/jpg.js', - 'core/chunked_stream.js', - 'core/pdf_manager.js', - 'core/cmap.js', - 'core/cidmaps.js', - - 'display/canvas.js', - 'display/font_loader.js', - 'display/metadata.js', - 'display/api.js', -]; - -//////replacing HTML5 canvas with PDFCanvas (in-memory canvas) -function createScratchCanvas(width, height) { - return new PDFCanvas({}, width, height); -} +import fs from "fs"; +import nodeUtil from "util"; +import { EventEmitter } from "events"; -const PDFJS = {}; -const globalScope = { console }; +import { _PARSER_SIG } from "./pkinfo.js"; +import PDFField from "./pdffield.js"; +import PDFFont from "./pdffont.js"; +import PDFUnit from "./pdfunit.js"; +import PTIXmlParser from "./ptixmlinject.js"; -const baseDir = `${__dirname}/../base/`; -const _baseCode = _pdfjsFiles.reduce( - (preContent, fileName, idx, arr) => - (preContent += fs.readFileSync(baseDir + fileName, 'utf8')), - '' -); -eval(_baseCode); +import { PDFJS } from "./pdfjs-code.js"; // created via `npm run build` ////////////////////////////////start of helper classes class PDFPageParser { - //static - static RenderingStates = { - INITIAL: 0, - RUNNING: 1, - PAUSED: 2, - FINISHED: 3, - }; - - //public - id = -1; - pdfPage = null; - ptiParser = null; - scale = 0; - viewport = null; - renderingState = -1; - - Fields = null; - Boxsets = null; - ctxCanvas = null; - - #_addField(field) { - if (!PDFField.isFormElement(field)) { - nodeUtil.p2jwarn('NOT valid form element', field); - return; - } - - const oneField = new PDFField( - field, - this.viewport, - this.Fields, - this.Boxsets - ); - oneField.processField(); - } - - // constructor - constructor(pdfPage, id, scale, ptiParser) { - // public, this instance copies - this.id = id; - this.pdfPage = pdfPage; - this.ptiParser = ptiParser; - - this.scale = scale || 1.0; - - //leave out the 2nd parameter in order to use page's default rotation (for both portrait and landscape form) - this.viewport = this.pdfPage.getViewport(this.scale); - - this.renderingState = PDFPageParser.RenderingStates.INITIAL; - - //form elements other than radio buttons and check boxes - this.Fields = []; - //form elements: radio buttons and check boxes - this.Boxsets = []; - this.ctxCanvas = {}; - } - - get width() { - return PDFUnit.toFormX(this.viewport.width); - } - get height() { - return PDFUnit.toFormY(this.viewport.height); - } - get HLines() { - return this.ctxCanvas.HLines; - } - get VLines() { - return this.ctxCanvas.VLines; - } - get Fills() { - return this.ctxCanvas.Fills; - } - get Texts() { - return this.ctxCanvas.Texts; - } - - destroy() { - this.pdfPage.destroy(); - this.pdfPage = null; - - this.ptiParser = null; - this.Fields = null; - this.Boxsets = null; - this.ctxCanvas = null; - } - - getPagePoint(x, y) { - return this.viewport.convertToPdfPoint(x, y); - } - - parsePage(callback, errorCallBack) { - if (this.renderingState !== PDFPageParser.RenderingStates.INITIAL) { - errorCallBack('Must be in new state before drawing'); - return; - } - - this.renderingState = PDFPageParser.RenderingStates.RUNNING; - - const canvas = createScratchCanvas(1, 1); - const ctx = canvas.getContext('2d'); - - function pageViewDrawCallback(error) { - this.renderingState = PDFPageParser.RenderingStates.FINISHED; - - if (error) { - console.error(error); - errorCallBack(`Error: Page ${this.id + 1}: ${error.message}`); - } else { - if (this.ptiParser) { - const extraFields = this.ptiParser.getFields( - parseInt(this.id) + 1 - ); - extraFields.forEach((field) => this.#_addField(field)); - } - - this.ctxCanvas = ctx.canvas; - this.stats = this.pdfPage.stats; - - nodeUtil.p2jinfo(`Success: Page ${this.id + 1}`); - callback(); - } - } - - const renderContext = { - canvasContext: ctx, - viewport: this.viewport, - }; - - this.pdfPage.render(renderContext).then( - (data) => { - this.pdfPage.getAnnotations().then( - (fields) => { - fields.forEach((field) => this.#_addField(field)); - pageViewDrawCallback.call(this, null); - }, - (err) => errorCallBack('pdfPage.getAnnotations error:' + err) - ); - }, - (err) => pageViewDrawCallback.call(this, err) - ); - } + //static + static RenderingStates = { + INITIAL: 0, + RUNNING: 1, + PAUSED: 2, + FINISHED: 3, + }; + + //public + id = -1; + pdfPage = null; + ptiParser = null; + scale = 0; + viewport = null; + renderingState = -1; + + Fields = null; + Boxsets = null; + ctxCanvas = null; + + #_addField(field) { + if (!PDFField.isFormElement(field)) { + nodeUtil.p2jwarn("NOT valid form element", field); + return; + } + + const oneField = new PDFField( + field, + this.viewport, + this.Fields, + this.Boxsets + ); + oneField.processField(); + } + + // constructor + constructor(pdfPage, id, scale, ptiParser) { + // public, this instance copies + this.id = id; + this.pdfPage = pdfPage; + this.ptiParser = ptiParser; + + this.scale = scale || 1.0; + + //leave out the 2nd parameter in order to use page's default rotation (for both portrait and landscape form) + this.viewport = this.pdfPage.getViewport(this.scale); + + this.renderingState = PDFPageParser.RenderingStates.INITIAL; + + //form elements other than radio buttons and check boxes + this.Fields = []; + //form elements: radio buttons and check boxes + this.Boxsets = []; + this.ctxCanvas = {}; + } + + get width() { + return PDFUnit.toFormX(this.viewport.width); + } + get height() { + return PDFUnit.toFormY(this.viewport.height); + } + get HLines() { + return this.ctxCanvas.HLines; + } + get VLines() { + return this.ctxCanvas.VLines; + } + get Fills() { + return this.ctxCanvas.Fills; + } + get Texts() { + return this.ctxCanvas.Texts; + } + + destroy() { + this.pdfPage.destroy(); + this.pdfPage = null; + + this.ptiParser = null; + this.Fields = null; + this.Boxsets = null; + this.ctxCanvas = null; + } + + getPagePoint(x, y) { + return this.viewport.convertToPdfPoint(x, y); + } + + parsePage(callback, errorCallBack) { + if (this.renderingState !== PDFPageParser.RenderingStates.INITIAL) { + errorCallBack("Must be in new state before drawing"); + return; + } + + this.renderingState = PDFPageParser.RenderingStates.RUNNING; + + const canvas = createScratchCanvas(1, 1); + const ctx = canvas.getContext("2d"); + + function pageViewDrawCallback(error) { + this.renderingState = PDFPageParser.RenderingStates.FINISHED; + + if (error) { + console.error(error); + errorCallBack(`Error: Page ${this.id + 1}: ${error.message}`); + } else { + if (this.ptiParser) { + const extraFields = this.ptiParser.getFields(parseInt(this.id) + 1); + extraFields.forEach((field) => this.#_addField(field)); + } + + this.ctxCanvas = ctx.canvas; + this.stats = this.pdfPage.stats; + + nodeUtil.p2jinfo(`Success: Page ${this.id + 1}`); + callback(); + } + } + + const renderContext = { + canvasContext: ctx, + viewport: this.viewport, + }; + + this.pdfPage.render(renderContext).then( + (data) => { + this.pdfPage.getAnnotations().then( + (fields) => { + fields.forEach((field) => this.#_addField(field)); + pageViewDrawCallback.call(this, null); + }, + (err) => errorCallBack("pdfPage.getAnnotations error:" + err) + ); + }, + (err) => pageViewDrawCallback.call(this, err) + ); + } } ////////////////////////////////Start of Node.js Module export default class PDFJSClass extends EventEmitter { - pdfDocument = null; - pages = null; - rawTextContents = null; - - needRawText = null; - - // constructor - constructor(needRawText) { - super(); - - // public, this instance copies - this.pdfDocument = null; - this.pages = []; - this.rawTextContents = []; - - this.needRawText = needRawText; - } - - raiseErrorEvent(errMsg) { - console.error(errMsg); - process.nextTick(() => this.emit('pdfjs_parseDataError', errMsg)); - // this.emit("error", errMsg); - return errMsg; - } - - raiseReadyEvent(data) { - process.nextTick(() => this.emit('pdfjs_parseDataReady', data)); - return data; - } - - parsePDFData(arrayBuffer, password) { - this.pdfDocument = null; - - const parameters = { password: password, data: arrayBuffer }; - PDFJS.getDocument(parameters).then( - (pdfDocument) => this.load(pdfDocument, 1), - (error) => this.raiseErrorEvent(error) - ); - } - - tryLoadFieldInfoXML(pdfFilePath) { - const _sufInfo = '_fieldInfo.xml'; - const fieldInfoXMLPath = pdfFilePath.replace('.pdf', _sufInfo); - if ( - fieldInfoXMLPath.indexOf(_sufInfo) < 1 || - !fs.existsSync(fieldInfoXMLPath) - ) { - return; - } - nodeUtil.p2jinfo('About to load fieldInfo XML : ' + fieldInfoXMLPath); - - this.ptiParser = new PTIXmlParser(); - this.ptiParser.parseXml(fieldInfoXMLPath, (err) => { - if (err) { - nodeUtil.p2jwarn('fieldInfo XML Error: ' + JSON.stringify(err)); - this.ptiParser = null; - } else { - nodeUtil.p2jinfo('fieldInfo XML loaded.'); - } - }); - } - - load(pdfDocument, scale) { - this.pdfDocument = pdfDocument; - - return this.loadMetaData().then( - () => this.loadPages(), - (error) => this.raiseErrorEvent('loadMetaData error: ' + error) - ); - } - - loadMetaData() { - return this.pdfDocument.getMetadata().then( - (data) => { - this.documentInfo = data.info; - this.metadata = data.metadata?.metadata ?? {}; - this.parseMetaData(); - }, - (error) => - this.raiseErrorEvent('pdfDocument.getMetadata error: ' + error) - ); - } - - parseMetaData() { - const meta = { - Transcoder: _PARSER_SIG, - Meta: { ...this.documentInfo, Metadata: this.metadata }, - }; - this.raiseReadyEvent(meta); - this.emit('readable', meta); - } - - loadPages() { - const pagesCount = this.pdfDocument.numPages; - const pagePromises = []; - for (let i = 1; i <= pagesCount; i++) - pagePromises.push(this.pdfDocument.getPage(i)); - - const pagesPromise = PDFJS.Promise.all(pagePromises); - - nodeUtil.p2jinfo('PDF loaded. pagesCount = ' + pagesCount); - - return pagesPromise.then( - (promisedPages) => this.parsePage(promisedPages, 0, 1.5), - (error) => this.raiseErrorEvent('pagesPromise error: ' + error) - ); - } - - parsePage(promisedPages, id, scale) { - nodeUtil.p2jinfo('start to parse page:' + (id + 1)); - - const pdfPage = promisedPages[id]; - const pageParser = new PDFPageParser(pdfPage, id, scale, this.ptiParser); - - function continueOnNextPage() { - nodeUtil.p2jinfo('complete parsing page:' + (id + 1)); - if (id === this.pdfDocument.numPages - 1) { - this.raiseReadyEvent({ Pages: this.pages }); - //v1.1.2: signal end of parsed data with null - process.nextTick(() => this.raiseReadyEvent(null)); - this.emit('data', null); - } else { - process.nextTick(() => this.parsePage(promisedPages, ++id, scale)); - } - } - - pageParser.parsePage( - (data) => { - const page = { - Width: pageParser.width, - Height: pageParser.height, - HLines: pageParser.HLines, - VLines: pageParser.VLines, - Fills: pageParser.Fills, - //needs to keep current default output format, text content will output to a separate file if '-c' command line argument is set - // Content:pdfPage.getTextContent(), - Texts: pageParser.Texts, - Fields: pageParser.Fields, - Boxsets: pageParser.Boxsets, - }; - - this.pages.push(page); - this.emit('data', page); - - if (this.needRawText) { - pdfPage.getTextContent().then( - (textContent) => { - this.rawTextContents.push(textContent); - nodeUtil.p2jinfo( - 'complete parsing raw text content:' + (id + 1) - ); - continueOnNextPage.call(this); - }, - (error) => - this.raiseErrorEvent( - 'pdfPage.getTextContent error: ' + error - ) - ); - } else { - continueOnNextPage.call(this); - } - }, - (errMsg) => this.raiseErrorEvent(errMsg) - ); - } - - getRawTextContent() { - let retVal = ''; - if (!this.needRawText) return retVal; - - this.rawTextContents.forEach((textContent, index) => { - let prevText = null; - textContent.bidiTexts.forEach((textObj, idx) => { - if (prevText) { - if (Math.abs(textObj.y - prevText.y) <= 9) { - prevText.str += textObj.str; - } else { - retVal += prevText.str + '\r\n'; - prevText = textObj; - } - } else { - prevText = textObj; - } - }); - if (prevText) { - retVal += prevText.str; - } - retVal += - '\r\n----------------Page (' + - index + - ') Break----------------\r\n'; - }); - - return retVal; - } - - getAllFieldsTypes() { - return PDFField.getAllFieldsTypes({ Pages: this.pages || [] }); - } - - getMergedTextBlocksIfNeeded() { - for (let p = 0; p < this.pages.length; p++) { - let prevText = null; - let page = this.pages[p]; - - page.Texts.sort(PDFFont.compareBlockPos); - page.Texts = page.Texts.filter((t, j) => { - let isDup = - j > 0 && PDFFont.areDuplicateBlocks(page.Texts[j - 1], t); - if (isDup) { - nodeUtil.p2jinfo( - 'skipped: dup text block: ' + decodeURIComponent(t.R[0].T) - ); - } - return !isDup; - }); - - for (let i = 0; i < page.Texts.length; i++) { - let text = page.Texts[i]; - - if (prevText) { - if ( - PDFFont.areAdjacentBlocks(prevText, text) && - PDFFont.haveSameStyle(prevText, text) - ) { - let preT = decodeURIComponent(prevText.R[0].T); - let curT = decodeURIComponent(text.R[0].T); - - prevText.R[0].T += text.R[0].T; - prevText.w += text.w; - text.merged = true; - - let mergedText = decodeURIComponent(prevText.R[0].T); - nodeUtil.p2jinfo( - `merged text block: ${preT} + ${curT} => ${mergedText}` - ); - prevText = null; //yeah, only merge two blocks for now - } else { - prevText = text; - } - } else { - prevText = text; - } - } - - page.Texts = page.Texts.filter((t) => !t.merged); - } - - return { Pages: this.pages }; - } - - destroy() { - this.removeAllListeners(); - - if (this.pdfDocument) this.pdfDocument.destroy(); - this.pdfDocument = null; - - this.pages = null; - this.rawTextContents = null; - } + pdfDocument = null; + pages = null; + rawTextContents = null; + + needRawText = null; + + // constructor + constructor(needRawText) { + super(); + + // public, this instance copies + this.pdfDocument = null; + this.pages = []; + this.rawTextContents = []; + + this.needRawText = needRawText; + } + + raiseErrorEvent(errMsg) { + console.error(errMsg); + process.nextTick(() => this.emit("pdfjs_parseDataError", errMsg)); + // this.emit("error", errMsg); + return errMsg; + } + + raiseReadyEvent(data) { + process.nextTick(() => this.emit("pdfjs_parseDataReady", data)); + return data; + } + + parsePDFData(arrayBuffer, password) { + this.pdfDocument = null; + + const parameters = { password: password, data: arrayBuffer }; + PDFJS.getDocument(parameters).then( + (pdfDocument) => this.load(pdfDocument, 1), + (error) => this.raiseErrorEvent(error) + ); + } + + tryLoadFieldInfoXML(pdfFilePath) { + const _sufInfo = "_fieldInfo.xml"; + const fieldInfoXMLPath = pdfFilePath.replace(".pdf", _sufInfo); + if ( + fieldInfoXMLPath.indexOf(_sufInfo) < 1 || + !fs.existsSync(fieldInfoXMLPath) + ) { + return; + } + nodeUtil.p2jinfo("About to load fieldInfo XML : " + fieldInfoXMLPath); + + this.ptiParser = new PTIXmlParser(); + this.ptiParser.parseXml(fieldInfoXMLPath, (err) => { + if (err) { + nodeUtil.p2jwarn("fieldInfo XML Error: " + JSON.stringify(err)); + this.ptiParser = null; + } else { + nodeUtil.p2jinfo("fieldInfo XML loaded."); + } + }); + } + + load(pdfDocument, scale) { + this.pdfDocument = pdfDocument; + + return this.loadMetaData().then( + () => this.loadPages(), + (error) => this.raiseErrorEvent("loadMetaData error: " + error) + ); + } + + loadMetaData() { + return this.pdfDocument.getMetadata().then( + (data) => { + this.documentInfo = data.info; + this.metadata = data.metadata?.metadata ?? {}; + this.parseMetaData(); + }, + (error) => this.raiseErrorEvent("pdfDocument.getMetadata error: " + error) + ); + } + + parseMetaData() { + const meta = { + Transcoder: _PARSER_SIG, + Meta: { ...this.documentInfo, Metadata: this.metadata }, + }; + this.raiseReadyEvent(meta); + this.emit("readable", meta); + } + + loadPages() { + const pagesCount = this.pdfDocument.numPages; + const pagePromises = []; + for (let i = 1; i <= pagesCount; i++) + pagePromises.push(this.pdfDocument.getPage(i)); + + const pagesPromise = PDFJS.Promise.all(pagePromises); + + nodeUtil.p2jinfo("PDF loaded. pagesCount = " + pagesCount); + + return pagesPromise.then( + (promisedPages) => this.parsePage(promisedPages, 0, 1.5), + (error) => this.raiseErrorEvent("pagesPromise error: " + error) + ); + } + + parsePage(promisedPages, id, scale) { + nodeUtil.p2jinfo("start to parse page:" + (id + 1)); + + const pdfPage = promisedPages[id]; + const pageParser = new PDFPageParser(pdfPage, id, scale, this.ptiParser); + + function continueOnNextPage() { + nodeUtil.p2jinfo("complete parsing page:" + (id + 1)); + if (id === this.pdfDocument.numPages - 1) { + this.raiseReadyEvent({ Pages: this.pages }); + //v1.1.2: signal end of parsed data with null + process.nextTick(() => this.raiseReadyEvent(null)); + this.emit("data", null); + } else { + process.nextTick(() => this.parsePage(promisedPages, ++id, scale)); + } + } + + pageParser.parsePage( + (data) => { + const page = { + Width: pageParser.width, + Height: pageParser.height, + HLines: pageParser.HLines, + VLines: pageParser.VLines, + Fills: pageParser.Fills, + //needs to keep current default output format, text content will output to a separate file if '-c' command line argument is set + // Content:pdfPage.getTextContent(), + Texts: pageParser.Texts, + Fields: pageParser.Fields, + Boxsets: pageParser.Boxsets, + }; + + this.pages.push(page); + this.emit("data", page); + + if (this.needRawText) { + pdfPage.getTextContent().then( + (textContent) => { + this.rawTextContents.push(textContent); + nodeUtil.p2jinfo("complete parsing raw text content:" + (id + 1)); + continueOnNextPage.call(this); + }, + (error) => + this.raiseErrorEvent("pdfPage.getTextContent error: " + error) + ); + } else { + continueOnNextPage.call(this); + } + }, + (errMsg) => this.raiseErrorEvent(errMsg) + ); + } + + getRawTextContent() { + let retVal = ""; + if (!this.needRawText) return retVal; + + this.rawTextContents.forEach((textContent, index) => { + let prevText = null; + textContent.bidiTexts.forEach((textObj, idx) => { + if (prevText) { + if (Math.abs(textObj.y - prevText.y) <= 9) { + prevText.str += textObj.str; + } else { + retVal += prevText.str + "\r\n"; + prevText = textObj; + } + } else { + prevText = textObj; + } + }); + if (prevText) { + retVal += prevText.str; + } + retVal += + "\r\n----------------Page (" + index + ") Break----------------\r\n"; + }); + + return retVal; + } + + getAllFieldsTypes() { + return PDFField.getAllFieldsTypes({ Pages: this.pages || [] }); + } + + getMergedTextBlocksIfNeeded() { + for (let p = 0; p < this.pages.length; p++) { + let prevText = null; + let page = this.pages[p]; + + page.Texts.sort(PDFFont.compareBlockPos); + page.Texts = page.Texts.filter((t, j) => { + let isDup = j > 0 && PDFFont.areDuplicateBlocks(page.Texts[j - 1], t); + if (isDup) { + nodeUtil.p2jinfo( + "skipped: dup text block: " + decodeURIComponent(t.R[0].T) + ); + } + return !isDup; + }); + + for (let i = 0; i < page.Texts.length; i++) { + let text = page.Texts[i]; + + if (prevText) { + if ( + PDFFont.areAdjacentBlocks(prevText, text) && + PDFFont.haveSameStyle(prevText, text) + ) { + let preT = decodeURIComponent(prevText.R[0].T); + let curT = decodeURIComponent(text.R[0].T); + + prevText.R[0].T += text.R[0].T; + prevText.w += text.w; + text.merged = true; + + let mergedText = decodeURIComponent(prevText.R[0].T); + nodeUtil.p2jinfo( + `merged text block: ${preT} + ${curT} => ${mergedText}` + ); + prevText = null; //yeah, only merge two blocks for now + } else { + prevText = text; + } + } else { + prevText = text; + } + } + + page.Texts = page.Texts.filter((t) => !t.merged); + } + + return { Pages: this.pages }; + } + + destroy() { + this.removeAllListeners(); + + if (this.pdfDocument) this.pdfDocument.destroy(); + this.pdfDocument = null; + + this.pages = null; + this.rawTextContents = null; + } } diff --git a/lib/pdfcanvas.js b/lib/pdfcanvas.js index 4283c4f5..ecae8436 100644 --- a/lib/pdfcanvas.js +++ b/lib/pdfcanvas.js @@ -1,7 +1,7 @@ -import nodeUtil from 'util'; -import PDFLine from './pdfline.js'; -import PDFFill from './pdffill.js'; -import PDFFont from './pdffont.js'; +import nodeUtil from "util"; +import PDFLine from "./pdfline.js"; +import PDFFill from "./pdffill.js"; +import PDFFont from "./pdffont.js"; // alias some functions to make (compiled) code shorter const { round: mr, sin: ms, cos: mc, abs, sqrt } = Math; @@ -9,157 +9,157 @@ const { round: mr, sin: ms, cos: mc, abs, sqrt } = Math; // precompute "00" to "FF" const dec2hex = []; for (let i = 0; i < 16; i++) { - for (let j = 0; j < 16; j++) { - dec2hex[i * 16 + j] = i.toString(16) + j.toString(16); - } + for (let j = 0; j < 16; j++) { + dec2hex[i * 16 + j] = i.toString(16) + j.toString(16); + } } function createMatrixIdentity() { - return [ - [1, 0, 0], - [0, 1, 0], - [0, 0, 1], - ]; + return [ + [1, 0, 0], + [0, 1, 0], + [0, 0, 1], + ]; } function matrixMultiply(m1, m2) { - let result = createMatrixIdentity(); + let result = createMatrixIdentity(); - for (let x = 0; x < 3; x++) { - for (let y = 0; y < 3; y++) { - let sum = 0; + for (let x = 0; x < 3; x++) { + for (let y = 0; y < 3; y++) { + let sum = 0; - for (let z = 0; z < 3; z++) { - sum += m1[x][z] * m2[z][y]; - } + for (let z = 0; z < 3; z++) { + sum += m1[x][z] * m2[z][y]; + } - result[x][y] = sum; - } - } - return result; + result[x][y] = sum; + } + } + return result; } function copyState(o1, o2) { - o2.fillStyle = o1.fillStyle; - o2.lineCap = o1.lineCap; - o2.lineJoin = o1.lineJoin; - o2.lineWidth = o1.lineWidth; - o2.miterLimit = o1.miterLimit; - o2.shadowBlur = o1.shadowBlur; - o2.shadowColor = o1.shadowColor; - o2.shadowOffsetX = o1.shadowOffsetX; - o2.shadowOffsetY = o1.shadowOffsetY; - o2.strokeStyle = o1.strokeStyle; - o2.globalAlpha = o1.globalAlpha; - o2.arcScaleX_ = o1.arcScaleX_; - o2.arcScaleY_ = o1.arcScaleY_; - o2.lineScale_ = o1.lineScale_; - o2.dashArray = o1.dashArray; + o2.fillStyle = o1.fillStyle; + o2.lineCap = o1.lineCap; + o2.lineJoin = o1.lineJoin; + o2.lineWidth = o1.lineWidth; + o2.miterLimit = o1.miterLimit; + o2.shadowBlur = o1.shadowBlur; + o2.shadowColor = o1.shadowColor; + o2.shadowOffsetX = o1.shadowOffsetX; + o2.shadowOffsetY = o1.shadowOffsetY; + o2.strokeStyle = o1.strokeStyle; + o2.globalAlpha = o1.globalAlpha; + o2.arcScaleX_ = o1.arcScaleX_; + o2.arcScaleY_ = o1.arcScaleY_; + o2.lineScale_ = o1.lineScale_; + o2.dashArray = o1.dashArray; } function processStyle(styleString) { - let str, - alpha = 1; - - styleString = String(styleString); - if (styleString.substring(0, 3) == 'rgb') { - let start = styleString.indexOf('(', 3); - let end = styleString.indexOf(')', start + 1); - let guts = styleString.substring(start + 1, end).split(','); - - str = '#'; - for (let i = 0; i < 3; i++) { - str += dec2hex[Number(guts[i])]; - } - - if (guts.length == 4 && styleString.substring(3, 4) == 'a') { - alpha = guts[3]; - } - } else { - str = styleString; - } - - return { color: str, alpha: alpha }; + let str, + alpha = 1; + + styleString = String(styleString); + if (styleString.substring(0, 3) == "rgb") { + let start = styleString.indexOf("(", 3); + let end = styleString.indexOf(")", start + 1); + let guts = styleString.substring(start + 1, end).split(","); + + str = "#"; + for (let i = 0; i < 3; i++) { + str += dec2hex[Number(guts[i])]; + } + + if (guts.length == 4 && styleString.substring(3, 4) == "a") { + alpha = guts[3]; + } + } else { + str = styleString; + } + + return { color: str, alpha: alpha }; } function processLineCap(lineCap) { - switch (lineCap) { - case 'butt': - return 'flat'; - case 'round': - return 'round'; - case 'square': - default: - return 'square'; - } + switch (lineCap) { + case "butt": + return "flat"; + case "round": + return "round"; + case "square": + default: + return "square"; + } } // Helper function that takes the already fixed cordinates. function bezierCurveToHelper(self, cp1, cp2, p) { - self.currentPath_.push({ - type: 'bezierCurveTo', - cp1x: cp1.x, - cp1y: cp1.y, - cp2x: cp2.x, - cp2y: cp2.y, - x: p.x, - y: p.y, - }); - self.currentX_ = p.x; - self.currentY_ = p.y; + self.currentPath_.push({ + type: "bezierCurveTo", + cp1x: cp1.x, + cp1y: cp1.y, + cp2x: cp2.x, + cp2y: cp2.y, + x: p.x, + y: p.y, + }); + self.currentX_ = p.x; + self.currentY_ = p.y; } function matrixIsFinite(m) { - for (let j = 0; j < 3; j++) { - for (let k = 0; k < 2; k++) { - if (!isFinite(m[j][k]) || isNaN(m[j][k])) { - return false; - } - } - } - return true; + for (let j = 0; j < 3; j++) { + for (let k = 0; k < 2; k++) { + if (!isFinite(m[j][k]) || isNaN(m[j][k])) { + return false; + } + } + } + return true; } function setM(ctx, m, updateLineScale) { - if (!matrixIsFinite(m)) { - return; - } - ctx.m_ = m; - - if (updateLineScale) { - // Get the line scale. - // Determinant of this.m_ means how much the area is enlarged by the - // transformation. So its square root can be used as a scale factor - // for width. - let det = m[0][0] * m[1][1] - m[0][1] * m[1][0]; - ctx.lineScale_ = sqrt(abs(det)); - } + if (!matrixIsFinite(m)) { + return; + } + ctx.m_ = m; + + if (updateLineScale) { + // Get the line scale. + // Determinant of this.m_ means how much the area is enlarged by the + // transformation. So its square root can be used as a scale factor + // for width. + let det = m[0][0] * m[1][1] - m[0][1] * m[1][0]; + ctx.lineScale_ = sqrt(abs(det)); + } } class CanvasPattern_ { - constructor() {} + constructor() {} } // Gradient / Pattern Stubs class CanvasGradient_ { - constructor(aType) { - this.type_ = aType; - this.x0_ = 0; - this.y0_ = 0; - this.r0_ = 0; - this.x1_ = 0; - this.y1_ = 0; - this.r1_ = 0; - this.colors_ = []; - } - addColorStop(aOffset, aColor) { - aColor = processStyle(aColor); - this.colors_.push({ - offset: aOffset, - color: aColor.color, - alpha: aColor.alpha, - }); - } + constructor(aType) { + this.type_ = aType; + this.x0_ = 0; + this.y0_ = 0; + this.r0_ = 0; + this.x1_ = 0; + this.y1_ = 0; + this.r1_ = 0; + this.colors_ = []; + } + addColorStop(aOffset, aColor) { + aColor = processStyle(aColor); + this.colors_.push({ + offset: aOffset, + color: aColor.color, + alpha: aColor.alpha, + }); + } } /** @@ -169,462 +169,453 @@ class CanvasGradient_ { * be associated with */ export default class CanvasRenderingContext2D_ { - constructor(canvasTarget, scaledWidth, scaledHeight) { - this.m_ = createMatrixIdentity(); - - this.mStack_ = []; - this.aStack_ = []; - this.currentPath_ = []; - - // Canvas context properties - this.strokeStyle = '#000'; - this.fillStyle = '#000'; - - this.lineWidth = 1; - this.lineJoin = 'miter'; - this.lineCap = 'butt'; - this.dashArray = []; - this.miterLimit = 1; - this.globalAlpha = 1; - - if (!('HLines' in canvasTarget) || !Array.isArray(canvasTarget.HLines)) - canvasTarget.HLines = []; - if (!('VLines' in canvasTarget) || !Array.isArray(canvasTarget.VLines)) - canvasTarget.VLines = []; - if (!('Fills' in canvasTarget) || !Array.isArray(canvasTarget.Fills)) - canvasTarget.Fills = []; - if (!('Texts' in canvasTarget) || !Array.isArray(canvasTarget.Texts)) - canvasTarget.Texts = []; - - this.canvas = canvasTarget; - - this.width = scaledWidth; - this.height = scaledHeight; - - this.arcScaleX_ = 1; - this.arcScaleY_ = 1; - this.lineScale_ = 1; - - this.currentFont = null; - } - - //private helper methods - #drawPDFLine(p1, p2, lineWidth, color) { - let dashedLine = - Array.isArray(this.dashArray) && this.dashArray.length > 1; - let pL = new PDFLine( - p1.x, - p1.y, - p2.x, - p2.y, - lineWidth, - color, - dashedLine - ); - pL.processLine(this.canvas); - } - - #drawPDFFill(cp, min, max, color) { - let width = max.x - min.x; - let height = max.y - min.y; - let pF = new PDFFill(cp.x, cp.y, width, height, color); - pF.processFill(this.canvas); - } - - #needRemoveRect(x, y, w, h) { - let retVal = Math.abs(w - Math.abs(h)) < 1 && w < 13; - if (retVal) { - nodeUtil.p2jinfo('Skipped: tiny rect: w=' + w + ', h=' + h); - } - return retVal; - } - - getContext(ctxType) { - return ctxType === '2d' ? this : null; - } - - setLineDash(lineDash) { - this.dashArray = lineDash; - } - - getLineDash() { - return this.dashArray; - } - - fillText(text, x, y, maxWidth, fontSize) { - if (!text || (!text.length === 1 && text.trim().length < 1)) return; - let p = this.getCoords_(x, y); - - let a = processStyle(this.fillStyle || this.strokeStyle); - let color = !!a ? a.color : '#000000'; - - this.currentFont.processText( - p, - text, - maxWidth, - color, - fontSize, - this.canvas, - this.m_ - ); - } - - strokeText(text, x, y, maxWidth) { - //MQZ. 10/23/2012, yeah, no hollow text for now - this.fillText(text, x, y, maxWidth); - } - - measureText(text) { - console.warn('to be implemented: contextPrototype.measureText - ', text); - let chars = text.length || 1; - return { width: chars * (this.currentFont.spaceWidth || 5) }; - } - - setFont(fontObj) { - if (!!this.currentFont && typeof this.currentFont.clean === 'function') { - this.currentFont.clean(); - this.currentFont = null; - } - - this.currentFont = new PDFFont(fontObj); - } - - clearRect() { - console.warn('to be implemented: contextPrototype.clearRect'); - } - - beginPath() { - // TODO: Branch current matrix so that save/restore has no effect - // as per safari docs. - this.currentPath_ = []; - } - - moveTo(aX, aY) { - let p = this.getCoords_(aX, aY); - this.currentPath_.push({ type: 'moveTo', x: p.x, y: p.y }); - this.currentX_ = p.x; - this.currentY_ = p.y; - } - - lineTo(aX, aY) { - let p = this.getCoords_(aX, aY); - this.currentPath_.push({ type: 'lineTo', x: p.x, y: p.y }); - - this.currentX_ = p.x; - this.currentY_ = p.y; - } - - bezierCurveTo(aCP1x, aCP1y, aCP2x, aCP2y, aX, aY) { - let p = this.getCoords_(aX, aY); - let cp1 = this.getCoords_(aCP1x, aCP1y); - let cp2 = this.getCoords_(aCP2x, aCP2y); - bezierCurveToHelper(this, cp1, cp2, p); - } - - quadraticCurveTo(aCPx, aCPy, aX, aY) { - // the following is lifted almost directly from - // http://developer.mozilla.org/en/docs/Canvas_tutorial:Drawing_shapes - - let cp = this.getCoords_(aCPx, aCPy); - let p = this.getCoords_(aX, aY); - - let cp1 = { - x: this.currentX_ + (2.0 / 3.0) * (cp.x - this.currentX_), - y: this.currentY_ + (2.0 / 3.0) * (cp.y - this.currentY_), - }; - let cp2 = { - x: cp1.x + (p.x - this.currentX_) / 3.0, - y: cp1.y + (p.y - this.currentY_) / 3.0, - }; - - bezierCurveToHelper(this, cp1, cp2, p); - } - - arc(aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise) { - let arcType = aClockwise ? 'at' : 'wa'; - - let xStart = aX + mc(aStartAngle) * aRadius; - let yStart = aY + ms(aStartAngle) * aRadius; - - let xEnd = aX + mc(aEndAngle) * aRadius; - let yEnd = aY + ms(aEndAngle) * aRadius; - - // IE won't render arches drawn counter clockwise if xStart == xEnd. - if (xStart == xEnd && !aClockwise) { - xStart += 0.125; // Offset xStart by 1/80 of a pixel. Use something - // that can be represented in binary - } - - let p = this.getCoords_(aX, aY); - let pStart = this.getCoords_(xStart, yStart); - let pEnd = this.getCoords_(xEnd, yEnd); - - this.currentPath_.push({ - type: arcType, - x: p.x, - y: p.y, - radius: aRadius, - xStart: pStart.x, - yStart: pStart.y, - xEnd: pEnd.x, - yEnd: pEnd.y, - }); - } - - rect(aX, aY, aWidth, aHeight) { - if (this.#needRemoveRect(aX, aY, aWidth, aHeight)) { - return; //try to remove the rectangle behind radio buttons and checkboxes - } - - this.moveTo(aX, aY); - this.lineTo(aX + aWidth, aY); - this.lineTo(aX + aWidth, aY + aHeight); - this.lineTo(aX, aY + aHeight); - this.closePath(); - } - - strokeRect(aX, aY, aWidth, aHeight) { - if (this.#needRemoveRect(aX, aY, aWidth, aHeight)) { - return; //try to remove the rectangle behind radio buttons and checkboxes - } - - let oldPath = this.currentPath_; - this.beginPath(); - - this.moveTo(aX, aY); - this.lineTo(aX + aWidth, aY); - this.lineTo(aX + aWidth, aY + aHeight); - this.lineTo(aX, aY + aHeight); - this.closePath(); - this.stroke(); - - this.currentPath_ = oldPath; - } - - fillRect(aX, aY, aWidth, aHeight) { - if (this.#needRemoveRect(aX, aY, aWidth, aHeight)) { - return; //try to remove the rectangle behind radio buttons and checkboxes - } - - let oldPath = this.currentPath_; - this.beginPath(); - - this.moveTo(aX, aY); - this.lineTo(aX + aWidth, aY); - this.lineTo(aX + aWidth, aY + aHeight); - this.lineTo(aX, aY + aHeight); - this.closePath(); - this.fill(); - - this.currentPath_ = oldPath; - } - - createLinearGradient(aX0, aY0, aX1, aY1) { - let gradient = new CanvasGradient_('gradient'); - gradient.x0_ = aX0; - gradient.y0_ = aY0; - gradient.x1_ = aX1; - gradient.y1_ = aY1; - return gradient; - } - - createRadialGradient(aX0, aY0, aR0, aX1, aY1, aR1) { - let gradient = new CanvasGradient_('gradientradial'); - gradient.x0_ = aX0; - gradient.y0_ = aY0; - gradient.r0_ = aR0; - gradient.x1_ = aX1; - gradient.y1_ = aY1; - gradient.r1_ = aR1; - return gradient; - } - - drawImage(image, var_args) { - //MQZ. no image drawing support for now - } - - getImageData(x, y, w, h) { - //MQZ. returns empty data buffer for now - return { - width: w, - height: h, - data: new Uint8Array(w * h * 4), - }; - } - - stroke(aFill) { - if (this.currentPath_.length < 2) { - return; - } - - let a = processStyle(aFill ? this.fillStyle : this.strokeStyle); - let color = a.color; - // let opacity = a.alpha * this.globalAlpha; - let lineWidth = this.lineScale_ * this.lineWidth; - - let min = { x: null, y: null }; - let max = { x: null, y: null }; - - for (let i = 0; i < this.currentPath_.length; i++) { - let p = this.currentPath_[i]; - - switch (p.type) { - case 'moveTo': - break; - case 'lineTo': - if (!aFill) { - //lines - if (i > 0) { - this.#drawPDFLine( - this.currentPath_[i - 1], - p, - lineWidth, - color - ); - } - } - break; - case 'close': - if (!aFill) { - //lines - if (i > 0) { - this.#drawPDFLine( - this.currentPath_[i - 1], - this.currentPath_[0], - lineWidth, - color - ); - } - } - p = null; - break; - case 'bezierCurveTo': - break; - case 'at': - case 'wa': - break; - } - - // Figure out dimensions so we can set fills' coordinates correctly - if (aFill && p) { - if (min.x == null || p.x < min.x) { - min.x = p.x; - } - if (max.x == null || p.x > max.x) { - max.x = p.x; - } - if (min.y == null || p.y < min.y) { - min.y = p.y; - } - if (max.y == null || p.y > max.y) { - max.y = p.y; - } - } - } - - if (aFill) { - //fill - this.#drawPDFFill(min, min, max, color); - } - } - - fill() { - this.stroke(true); - } - - closePath() { - this.currentPath_.push({ type: 'close' }); - } - - /** - * @private - */ - getCoords_(aX, aY) { - let m = this.m_; - return { - x: aX * m[0][0] + aY * m[1][0] + m[2][0], - y: aX * m[0][1] + aY * m[1][1] + m[2][1], - }; - } - - save() { - let o = {}; - copyState(this, o); - this.aStack_.push(o); - this.mStack_.push(this.m_); - this.m_ = matrixMultiply(createMatrixIdentity(), this.m_); - } - - restore() { - copyState(this.aStack_.pop(), this); - this.m_ = this.mStack_.pop(); - } - - translate(aX, aY) { - let m1 = [ - [1, 0, 0], - [0, 1, 0], - [aX, aY, 1], - ]; - - setM(this, matrixMultiply(m1, this.m_), false); - } - - rotate(aRot) { - let c = mc(aRot); - let s = ms(aRot); - - let m1 = [ - [c, s, 0], - [-s, c, 0], - [0, 0, 1], - ]; - - setM(this, matrixMultiply(m1, this.m_), false); - } - - scale(aX, aY) { - this.arcScaleX_ *= aX; - this.arcScaleY_ *= aY; - let m1 = [ - [aX, 0, 0], - [0, aY, 0], - [0, 0, 1], - ]; - - setM(this, matrixMultiply(m1, this.m_), true); - } - - transform(m11, m12, m21, m22, dx, dy) { - let m1 = [ - [m11, m12, 0], - [m21, m22, 0], - [dx, dy, 1], - ]; - - setM(this, matrixMultiply(m1, this.m_), true); - } - - setTransform(m11, m12, m21, m22, dx, dy) { - let m = [ - [m11, m12, 0], - [m21, m22, 0], - [dx, dy, 1], - ]; - - setM(this, m, true); - } - - /******** STUBS ********/ - clip() { - // TODO: Implement - } - - arcTo() { - // TODO: Implement - } - - createPattern() { - return new CanvasPattern_(); - } + constructor(canvasTarget, scaledWidth, scaledHeight) { + this.m_ = createMatrixIdentity(); + + this.mStack_ = []; + this.aStack_ = []; + this.currentPath_ = []; + + // Canvas context properties + this.strokeStyle = "#000"; + this.fillStyle = "#000"; + + this.lineWidth = 1; + this.lineJoin = "miter"; + this.lineCap = "butt"; + this.dashArray = []; + this.miterLimit = 1; + this.globalAlpha = 1; + + if (!("HLines" in canvasTarget) || !Array.isArray(canvasTarget.HLines)) + canvasTarget.HLines = []; + if (!("VLines" in canvasTarget) || !Array.isArray(canvasTarget.VLines)) + canvasTarget.VLines = []; + if (!("Fills" in canvasTarget) || !Array.isArray(canvasTarget.Fills)) + canvasTarget.Fills = []; + if (!("Texts" in canvasTarget) || !Array.isArray(canvasTarget.Texts)) + canvasTarget.Texts = []; + + this.canvas = canvasTarget; + + this.width = scaledWidth; + this.height = scaledHeight; + + this.arcScaleX_ = 1; + this.arcScaleY_ = 1; + this.lineScale_ = 1; + + this.currentFont = null; + } + + //private helper methods + #drawPDFLine(p1, p2, lineWidth, color) { + let dashedLine = Array.isArray(this.dashArray) && this.dashArray.length > 1; + let pL = new PDFLine(p1.x, p1.y, p2.x, p2.y, lineWidth, color, dashedLine); + pL.processLine(this.canvas); + } + + #drawPDFFill(cp, min, max, color) { + let width = max.x - min.x; + let height = max.y - min.y; + let pF = new PDFFill(cp.x, cp.y, width, height, color); + pF.processFill(this.canvas); + } + + #needRemoveRect(x, y, w, h) { + let retVal = Math.abs(w - Math.abs(h)) < 1 && w < 13; + if (retVal) { + nodeUtil.p2jinfo("Skipped: tiny rect: w=" + w + ", h=" + h); + } + return retVal; + } + + getContext(ctxType) { + return ctxType === "2d" ? this : null; + } + + setLineDash(lineDash) { + this.dashArray = lineDash; + } + + getLineDash() { + return this.dashArray; + } + + fillText(text, x, y, maxWidth, fontSize) { + if (!text || (!text.length === 1 && text.trim().length < 1)) return; + let p = this.getCoords_(x, y); + + let a = processStyle(this.fillStyle || this.strokeStyle); + let color = !!a ? a.color : "#000000"; + + this.currentFont.processText( + p, + text, + maxWidth, + color, + fontSize, + this.canvas, + this.m_ + ); + } + + strokeText(text, x, y, maxWidth) { + //MQZ. 10/23/2012, yeah, no hollow text for now + this.fillText(text, x, y, maxWidth); + } + + measureText(text) { + console.warn("to be implemented: contextPrototype.measureText - ", text); + let chars = text.length || 1; + return { width: chars * (this.currentFont.spaceWidth || 5) }; + } + + setFont(fontObj) { + if (!!this.currentFont && typeof this.currentFont.clean === "function") { + this.currentFont.clean(); + this.currentFont = null; + } + + this.currentFont = new PDFFont(fontObj); + } + + clearRect() { + console.warn("to be implemented: contextPrototype.clearRect"); + } + + beginPath() { + // TODO: Branch current matrix so that save/restore has no effect + // as per safari docs. + this.currentPath_ = []; + } + + moveTo(aX, aY) { + let p = this.getCoords_(aX, aY); + this.currentPath_.push({ type: "moveTo", x: p.x, y: p.y }); + this.currentX_ = p.x; + this.currentY_ = p.y; + } + + lineTo(aX, aY) { + let p = this.getCoords_(aX, aY); + this.currentPath_.push({ type: "lineTo", x: p.x, y: p.y }); + + this.currentX_ = p.x; + this.currentY_ = p.y; + } + + bezierCurveTo(aCP1x, aCP1y, aCP2x, aCP2y, aX, aY) { + let p = this.getCoords_(aX, aY); + let cp1 = this.getCoords_(aCP1x, aCP1y); + let cp2 = this.getCoords_(aCP2x, aCP2y); + bezierCurveToHelper(this, cp1, cp2, p); + } + + quadraticCurveTo(aCPx, aCPy, aX, aY) { + // the following is lifted almost directly from + // http://developer.mozilla.org/en/docs/Canvas_tutorial:Drawing_shapes + + let cp = this.getCoords_(aCPx, aCPy); + let p = this.getCoords_(aX, aY); + + let cp1 = { + x: this.currentX_ + (2.0 / 3.0) * (cp.x - this.currentX_), + y: this.currentY_ + (2.0 / 3.0) * (cp.y - this.currentY_), + }; + let cp2 = { + x: cp1.x + (p.x - this.currentX_) / 3.0, + y: cp1.y + (p.y - this.currentY_) / 3.0, + }; + + bezierCurveToHelper(this, cp1, cp2, p); + } + + arc(aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise) { + let arcType = aClockwise ? "at" : "wa"; + + let xStart = aX + mc(aStartAngle) * aRadius; + let yStart = aY + ms(aStartAngle) * aRadius; + + let xEnd = aX + mc(aEndAngle) * aRadius; + let yEnd = aY + ms(aEndAngle) * aRadius; + + // IE won't render arches drawn counter clockwise if xStart == xEnd. + if (xStart == xEnd && !aClockwise) { + xStart += 0.125; // Offset xStart by 1/80 of a pixel. Use something + // that can be represented in binary + } + + let p = this.getCoords_(aX, aY); + let pStart = this.getCoords_(xStart, yStart); + let pEnd = this.getCoords_(xEnd, yEnd); + + this.currentPath_.push({ + type: arcType, + x: p.x, + y: p.y, + radius: aRadius, + xStart: pStart.x, + yStart: pStart.y, + xEnd: pEnd.x, + yEnd: pEnd.y, + }); + } + + rect(aX, aY, aWidth, aHeight) { + if (this.#needRemoveRect(aX, aY, aWidth, aHeight)) { + return; //try to remove the rectangle behind radio buttons and checkboxes + } + + this.moveTo(aX, aY); + this.lineTo(aX + aWidth, aY); + this.lineTo(aX + aWidth, aY + aHeight); + this.lineTo(aX, aY + aHeight); + this.closePath(); + } + + strokeRect(aX, aY, aWidth, aHeight) { + if (this.#needRemoveRect(aX, aY, aWidth, aHeight)) { + return; //try to remove the rectangle behind radio buttons and checkboxes + } + + let oldPath = this.currentPath_; + this.beginPath(); + + this.moveTo(aX, aY); + this.lineTo(aX + aWidth, aY); + this.lineTo(aX + aWidth, aY + aHeight); + this.lineTo(aX, aY + aHeight); + this.closePath(); + this.stroke(); + + this.currentPath_ = oldPath; + } + + fillRect(aX, aY, aWidth, aHeight) { + if (this.#needRemoveRect(aX, aY, aWidth, aHeight)) { + return; //try to remove the rectangle behind radio buttons and checkboxes + } + + let oldPath = this.currentPath_; + this.beginPath(); + + this.moveTo(aX, aY); + this.lineTo(aX + aWidth, aY); + this.lineTo(aX + aWidth, aY + aHeight); + this.lineTo(aX, aY + aHeight); + this.closePath(); + this.fill(); + + this.currentPath_ = oldPath; + } + + createLinearGradient(aX0, aY0, aX1, aY1) { + let gradient = new CanvasGradient_("gradient"); + gradient.x0_ = aX0; + gradient.y0_ = aY0; + gradient.x1_ = aX1; + gradient.y1_ = aY1; + return gradient; + } + + createRadialGradient(aX0, aY0, aR0, aX1, aY1, aR1) { + let gradient = new CanvasGradient_("gradientradial"); + gradient.x0_ = aX0; + gradient.y0_ = aY0; + gradient.r0_ = aR0; + gradient.x1_ = aX1; + gradient.y1_ = aY1; + gradient.r1_ = aR1; + return gradient; + } + + drawImage(image, var_args) { + //MQZ. no image drawing support for now + } + + getImageData(x, y, w, h) { + //MQZ. returns empty data buffer for now + return { + width: w, + height: h, + data: new Uint8Array(w * h * 4), + }; + } + + stroke(aFill) { + if (this.currentPath_.length < 2) { + return; + } + + let a = processStyle(aFill ? this.fillStyle : this.strokeStyle); + let color = a.color; + // let opacity = a.alpha * this.globalAlpha; + let lineWidth = this.lineScale_ * this.lineWidth; + + let min = { x: null, y: null }; + let max = { x: null, y: null }; + + for (let i = 0; i < this.currentPath_.length; i++) { + let p = this.currentPath_[i]; + + switch (p.type) { + case "moveTo": + break; + case "lineTo": + if (!aFill) { + //lines + if (i > 0) { + this.#drawPDFLine(this.currentPath_[i - 1], p, lineWidth, color); + } + } + break; + case "close": + if (!aFill) { + //lines + if (i > 0) { + this.#drawPDFLine( + this.currentPath_[i - 1], + this.currentPath_[0], + lineWidth, + color + ); + } + } + p = null; + break; + case "bezierCurveTo": + break; + case "at": + case "wa": + break; + } + + // Figure out dimensions so we can set fills' coordinates correctly + if (aFill && p) { + if (min.x == null || p.x < min.x) { + min.x = p.x; + } + if (max.x == null || p.x > max.x) { + max.x = p.x; + } + if (min.y == null || p.y < min.y) { + min.y = p.y; + } + if (max.y == null || p.y > max.y) { + max.y = p.y; + } + } + } + + if (aFill) { + //fill + this.#drawPDFFill(min, min, max, color); + } + } + + fill() { + this.stroke(true); + } + + closePath() { + this.currentPath_.push({ type: "close" }); + } + + /** + * @private + */ + getCoords_(aX, aY) { + let m = this.m_; + return { + x: aX * m[0][0] + aY * m[1][0] + m[2][0], + y: aX * m[0][1] + aY * m[1][1] + m[2][1], + }; + } + + save() { + let o = {}; + copyState(this, o); + this.aStack_.push(o); + this.mStack_.push(this.m_); + this.m_ = matrixMultiply(createMatrixIdentity(), this.m_); + } + + restore() { + copyState(this.aStack_.pop(), this); + this.m_ = this.mStack_.pop(); + } + + translate(aX, aY) { + let m1 = [ + [1, 0, 0], + [0, 1, 0], + [aX, aY, 1], + ]; + + setM(this, matrixMultiply(m1, this.m_), false); + } + + rotate(aRot) { + let c = mc(aRot); + let s = ms(aRot); + + let m1 = [ + [c, s, 0], + [-s, c, 0], + [0, 0, 1], + ]; + + setM(this, matrixMultiply(m1, this.m_), false); + } + + scale(aX, aY) { + this.arcScaleX_ *= aX; + this.arcScaleY_ *= aY; + let m1 = [ + [aX, 0, 0], + [0, aY, 0], + [0, 0, 1], + ]; + + setM(this, matrixMultiply(m1, this.m_), true); + } + + transform(m11, m12, m21, m22, dx, dy) { + let m1 = [ + [m11, m12, 0], + [m21, m22, 0], + [dx, dy, 1], + ]; + + setM(this, matrixMultiply(m1, this.m_), true); + } + + setTransform(m11, m12, m21, m22, dx, dy) { + let m = [ + [m11, m12, 0], + [m21, m22, 0], + [dx, dy, 1], + ]; + + setM(this, m, true); + } + + /******** STUBS ********/ + clip() { + // TODO: Implement + } + + arcTo() { + // TODO: Implement + } + + createPattern() { + return new CanvasPattern_(); + } +} + +// replacing HTML5 canvas with PDFCanvas (in-memory canvas) +export function createScratchCanvas(width, height) { + return new CanvasRenderingContext2D_({}, width, height); } diff --git a/pkinfo.js b/lib/pkinfo.js similarity index 50% rename from pkinfo.js rename to lib/pkinfo.js index d92f1651..8a77548a 100644 --- a/pkinfo.js +++ b/lib/pkinfo.js @@ -1,9 +1,10 @@ -import path from 'path'; -import {fileURLToPath} from 'url'; +import path from "path"; +import { fileURLToPath } from "url"; import fs from "fs"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); - -export const pkInfo = JSON.parse(fs.readFileSync(`${__dirname}/package.json`, 'utf8')); -export const _PARSER_SIG = `${pkInfo.name}@${pkInfo.version} [${pkInfo.homepage}]`; \ No newline at end of file +export const pkInfo = JSON.parse( + fs.readFileSync(`${__dirname}/../package.json`, "utf8") +); +export const _PARSER_SIG = `${pkInfo.name}@${pkInfo.version} [${pkInfo.homepage}]`; diff --git a/package-lock.json b/package-lock.json index 757934c7..94b61b61 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,57 +1,5241 @@ { - "name": "pdf2json", - "version": "3.0.4", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "pdf2json", - "version": "3.0.4", - "bundleDependencies": [ - "@xmldom/xmldom" - ], - "license": "Apache-2.0", - "dependencies": { - "@xmldom/xmldom": "^0.8.8" - }, - "bin": { - "pdf2json": "bin/pdf2json.js" - }, - "devDependencies": { - "@types/node": "^20.3.1" - }, - "engines": { - "node": ">=18.12.1", - "npm": ">=8.19.2" - } - }, - "node_modules/@types/node": { - "version": "20.3.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.1.tgz", - "integrity": "sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg==", - "dev": true - }, - "node_modules/@xmldom/xmldom": { - "version": "0.8.8", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.8.tgz", - "integrity": "sha512-0LNz4EY8B/8xXY86wMrQ4tz6zEHZv9ehFMJPm8u2gq5lQ71cfRKdaKyxfJAx5aUoyzx0qzgURblTisPGgz3d+Q==", - "inBundle": true, - "engines": { - "node": ">=10.0.0" - } - } - }, - "dependencies": { - "@types/node": { - "version": "20.3.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.1.tgz", - "integrity": "sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg==", - "dev": true - }, - "@xmldom/xmldom": { - "version": "0.8.8", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.8.tgz", - "integrity": "sha512-0LNz4EY8B/8xXY86wMrQ4tz6zEHZv9ehFMJPm8u2gq5lQ71cfRKdaKyxfJAx5aUoyzx0qzgURblTisPGgz3d+Q==" - } - } + "name": "pdf2json", + "version": "3.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "pdf2json", + "version": "3.1.0", + "bundleDependencies": [ + "@xmldom/xmldom" + ], + "license": "Apache-2.0", + "dependencies": { + "@xmldom/xmldom": "^0.8.10" + }, + "bin": { + "pdf2json": "bin/pdf2json.js" + }, + "devDependencies": { + "@rollup/plugin-commonjs": "^25.0.4", + "@rollup/plugin-node-resolve": "^15.2.1", + "@rollup/plugin-replace": "^5.0.2", + "@types/node": "^20.12.7", + "jest": "^29.7.0", + "rollup": "^4.17.0", + "rollup-plugin-inject": "^3.0.2", + "rollup-plugin-node-builtins": "^2.1.2", + "rollup-plugin-sourcemaps": "^0.6.3" + }, + "engines": { + "node": ">=18.12.1", + "npm": ">=8.19.2" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", + "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.24.2", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.4.tgz", + "integrity": "sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.5.tgz", + "integrity": "sha512-tVQRucExLQ02Boi4vdPp49svNGcfL2GhdTCT9aldhXgCJVAI21EtRfBettiuLUwce/7r6bFdgs6JFkcdTiFttA==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.24.2", + "@babel/generator": "^7.24.5", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.24.5", + "@babel/helpers": "^7.24.5", + "@babel/parser": "^7.24.5", + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.5", + "@babel/types": "^7.24.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.5.tgz", + "integrity": "sha512-x32i4hEXvr+iI0NEoEfDKzlemF8AmtOP8CcrRaEcpzysWuoEb1KknpcvMsHKPONoKZiDuItklgWhB18xEhr9PA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.5", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.24.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz", + "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.5.tgz", + "integrity": "sha512-9GxeY8c2d2mdQUP1Dye0ks3VDyIMS98kt/llQ2nUId8IsWqTF0l1LkSX0/uP7l7MCDrzXS009Hyhe2gzTiGW8A==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.24.3", + "@babel/helper-simple-access": "^7.24.5", + "@babel/helper-split-export-declaration": "^7.24.5", + "@babel/helper-validator-identifier": "^7.24.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.5.tgz", + "integrity": "sha512-xjNLDopRzW2o6ba0gKbkZq5YWEBaK3PCyTOY1K2P/O07LGMhMqlMXPxwN4S5/RhWuCobT8z0jrlKGlYmeR1OhQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.5.tgz", + "integrity": "sha512-uH3Hmf5q5n7n8mz7arjUlDOCbttY/DW4DYhE6FUsjKJ/oYC1kQQUvwEQWxRwUpX9qQKRXeqLwWxrqilMrf32sQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.5.tgz", + "integrity": "sha512-5CHncttXohrHk8GWOFCcCl4oRD9fKosWlIRgWm4ql9VYioKm52Mk2xsmoohvm7f3JoiLSM5ZgJuRaf5QZZYd3Q==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", + "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz", + "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.5.tgz", + "integrity": "sha512-CiQmBMMpMQHwM5m01YnrM6imUG1ebgYJ+fAIW4FZe6m4qHTPaRHti+R8cggAwkdz4oXhtO4/K9JWlh+8hIfR2Q==", + "dev": true, + "dependencies": { + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.5", + "@babel/types": "^7.24.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.5.tgz", + "integrity": "sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.24.5", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.5.tgz", + "integrity": "sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.1.tgz", + "integrity": "sha512-2eCtxZXf+kbkMIsXS4poTvT4Yu5rXiRa+9xGVT56raghjmBTKMpFNc9R4IDiB4emao9eO22Ox7CxuJG7BgExqA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.1.tgz", + "integrity": "sha512-Yhnmvy5HZEnHUty6i++gcfH1/l68AHnItFHnaCv6hn9dNh0hQvvQJsxpi4BMBFN5DLeHBuucT/0DgzXif/OyRw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", + "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.23.5", + "@babel/parser": "^7.24.0", + "@babel/types": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.5.tgz", + "integrity": "sha512-7aaBLeDQ4zYcUFDUD41lJc1fG8+5IU9DaNSJAgal866FGvmD5EbWQgnEC6kO1gGLsX0esNkfnJSndbTXA3r7UA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.24.2", + "@babel/generator": "^7.24.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.24.5", + "@babel/parser": "^7.24.5", + "@babel/types": "^7.24.5", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.5.tgz", + "integrity": "sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.24.1", + "@babel/helper-validator-identifier": "^7.24.5", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/reporters/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@jest/reporters/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@jest/reporters/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@rollup/plugin-commonjs": { + "version": "25.0.7", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.7.tgz", + "integrity": "sha512-nEvcR+LRjEjsaSsc4x3XZfCCvZIaSMenZu/OiwOKGN2UhQpAYI7ru7czFvyWbErlpoGjnSX3D5Ch5FcMA3kRWQ==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "commondir": "^1.0.1", + "estree-walker": "^2.0.2", + "glob": "^8.0.3", + "is-reference": "1.2.1", + "magic-string": "^0.30.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.68.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "15.2.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz", + "integrity": "sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-builtin-module": "^3.2.1", + "is-module": "^1.0.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.78.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-replace": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-5.0.5.tgz", + "integrity": "sha512-rYO4fOi8lMaTg/z5Jb+hKnrHHVn8j2lwkqwyS4kTRhKyWOLf2wST2sWXr4WzWiTcoHTp2sTjqUbqIj2E39slKQ==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "magic-string": "^0.30.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", + "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.17.1.tgz", + "integrity": "sha512-P6Wg856Ou/DLpR+O0ZLneNmrv7QpqBg+hK4wE05ijbC/t349BRfMfx+UFj5Ha3fCFopIa6iSZlpdaB4agkWp2Q==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.17.1.tgz", + "integrity": "sha512-piwZDjuW2WiHr05djVdUkrG5JbjnGbtx8BXQchYCMfib/nhjzWoiScelZ+s5IJI7lecrwSxHCzW026MWBL+oJQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.17.1.tgz", + "integrity": "sha512-LsZXXIsN5Q460cKDT4Y+bzoPDhBmO5DTr7wP80d+2EnYlxSgkwdPfE3hbE+Fk8dtya+8092N9srjBTJ0di8RIA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.17.1.tgz", + "integrity": "sha512-S7TYNQpWXB9APkxu/SLmYHezWwCoZRA9QLgrDeml+SR2A1LLPD2DBUdUlvmCF7FUpRMKvbeeWky+iizQj65Etw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.17.1.tgz", + "integrity": "sha512-Lq2JR5a5jsA5um2ZoLiXXEaOagnVyCpCW7xvlcqHC7y46tLwTEgUSTM3a2TfmmTMmdqv+jknUioWXlmxYxE9Yw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.17.1.tgz", + "integrity": "sha512-9BfzwyPNV0IizQoR+5HTNBGkh1KXE8BqU0DBkqMngmyFW7BfuIZyMjQ0s6igJEiPSBvT3ZcnIFohZ19OqjhDPg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.17.1.tgz", + "integrity": "sha512-e2uWaoxo/rtzA52OifrTSXTvJhAXb0XeRkz4CdHBK2KtxrFmuU/uNd544Ogkpu938BzEfvmWs8NZ8Axhw33FDw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.17.1.tgz", + "integrity": "sha512-ekggix/Bc/d/60H1Mi4YeYb/7dbal1kEDZ6sIFVAE8pUSx7PiWeEh+NWbL7bGu0X68BBIkgF3ibRJe1oFTksQQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.17.1.tgz", + "integrity": "sha512-UGV0dUo/xCv4pkr/C8KY7XLFwBNnvladt8q+VmdKrw/3RUd3rD0TptwjisvE2TTnnlENtuY4/PZuoOYRiGp8Gw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.17.1.tgz", + "integrity": "sha512-gEYmYYHaehdvX46mwXrU49vD6Euf1Bxhq9pPb82cbUU9UT2NV+RSckQ5tKWOnNXZixKsy8/cPGtiUWqzPuAcXQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.17.1.tgz", + "integrity": "sha512-xeae5pMAxHFp6yX5vajInG2toST5lsCTrckSRUFwNgzYqnUjNBcQyqk1bXUxX5yhjWFl2Mnz3F8vQjl+2FRIcw==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.17.1.tgz", + "integrity": "sha512-AsdnINQoDWfKpBzCPqQWxSPdAWzSgnYbrJYtn6W0H2E9It5bZss99PiLA8CgmDRfvKygt20UpZ3xkhFlIfX9zQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.17.1.tgz", + "integrity": "sha512-KoB4fyKXTR+wYENkIG3fFF+5G6N4GFvzYx8Jax8BR4vmddtuqSb5oQmYu2Uu067vT/Fod7gxeQYKupm8gAcMSQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.17.1.tgz", + "integrity": "sha512-J0d3NVNf7wBL9t4blCNat+d0PYqAx8wOoY+/9Q5cujnafbX7BmtYk3XvzkqLmFECaWvXGLuHmKj/wrILUinmQg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.17.1.tgz", + "integrity": "sha512-xjgkWUwlq7IbgJSIxvl516FJ2iuC/7ttjsAxSPpC9kkI5iQQFHKyEN5BjbhvJ/IXIZ3yIBcW5QDlWAyrA+TFag==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.17.1.tgz", + "integrity": "sha512-0QbCkfk6cnnVKWqqlC0cUrrUMDMfu5ffvYMTUHf+qMN2uAb3MKP31LPcwiMXBNsvoFGs/kYdFOsuLmvppCopXA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", + "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/node": { + "version": "20.12.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz", + "integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/resolve": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", + "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", + "dev": true + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true + }, + "node_modules/@types/yargs": { + "version": "17.0.32", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", + "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true + }, + "node_modules/@xmldom/xmldom": { + "version": "0.8.10", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", + "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", + "inBundle": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/abstract-leveldown": { + "version": "0.12.4", + "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-0.12.4.tgz", + "integrity": "sha512-TOod9d5RDExo6STLMGa+04HGkl+TlMfbDnTyN93/ETJ9DpQ0DaYLqcMZlbXvdc4W3vVo1Qrl+WhSp8zvDsJ+jA==", + "dev": true, + "dependencies": { + "xtend": "~3.0.0" + } + }, + "node_modules/abstract-leveldown/node_modules/xtend": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-3.0.0.tgz", + "integrity": "sha512-sp/sT9OALMjRW1fKDlPeuSZlDQpkqReA0pyJukniWbTGoEKefHxhGJynE3PNhUMlcM8qWIjPwecwCw4LArS5Eg==", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "dev": true, + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/asn1.js/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true, + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/bl": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/bl/-/bl-0.8.2.tgz", + "integrity": "sha512-pfqikmByp+lifZCS0p6j6KreV6kNU6Apzpm2nKOk+94cZb/jvle55+JxWiByUQ0Wo/+XnDXEy5MxxKMb6r0VIw==", + "dev": true, + "dependencies": { + "readable-stream": "~1.0.26" + } + }, + "node_modules/bl/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "dev": true + }, + "node_modules/bl/node_modules/readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/bl/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "dev": true + }, + "node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", + "dev": true + }, + "node_modules/browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "dependencies": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "node_modules/browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/browserify-fs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browserify-fs/-/browserify-fs-1.0.0.tgz", + "integrity": "sha512-8LqHRPuAEKvyTX34R6tsw4bO2ro6j9DmlYBhiYWHRM26Zv2cBw1fJOU0NeUQ0RkXkPn/PFBjhA0dm4AgaBurTg==", + "dev": true, + "dependencies": { + "level-filesystem": "^1.0.1", + "level-js": "^2.1.3", + "levelup": "^0.18.2" + } + }, + "node_modules/browserify-rsa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", + "dev": true, + "dependencies": { + "bn.js": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "node_modules/browserify-sign": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.3.tgz", + "integrity": "sha512-JWCZW6SKhfhjJxO8Tyiiy+XYB7cqd2S5/+WeYHsKdNKFlCBhKbblba1A/HN/90YwtxKc8tCErjffZl++UNmGiw==", + "dev": true, + "dependencies": { + "bn.js": "^5.2.1", + "browserify-rsa": "^4.1.0", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.5", + "hash-base": "~3.0", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.7", + "readable-stream": "^2.3.8", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/browserslist": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", + "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-es6": { + "version": "4.9.3", + "resolved": "https://registry.npmjs.org/buffer-es6/-/buffer-es6-4.9.3.tgz", + "integrity": "sha512-Ibt+oXxhmeYJSsCkODPqNpPmyegefiD8rfutH1NYGhMZQhSp95Rz7haemgnJ6dxa6LT+JLLbtgOMORRluwKktw==", + "dev": true + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", + "dev": true + }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001614", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001614.tgz", + "integrity": "sha512-jmZQ1VpmlRwHgdP1/uiKzgiAuGOfLEJsYFP4+GBou/QQ4U6IOJCB4NP1c+1p9RGLpwObcT94jA5/uO+F1vBbog==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz", + "integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==", + "dev": true + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clone": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/clone/-/clone-0.1.19.tgz", + "integrity": "sha512-IO78I0y6JcSpEPHzK4obKdsL7E7oLdRVDVOLwr2Hkbjsb+Eoz0dxW6tef0WizoKu0gLC4oZSZuEF4U2K6w1WQw==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "node_modules/create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + } + }, + "node_modules/create-ecdh/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "dependencies": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + }, + "engines": { + "node": "*" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/dedent": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", + "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", + "dev": true, + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/deferred-leveldown": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-0.2.0.tgz", + "integrity": "sha512-+WCbb4+ez/SZ77Sdy1iadagFiVzMB89IKOBhglgnUkVxOxRWmmFsz8UDSNWh4Rhq+3wr/vMFlYj+rdEwWUDdng==", + "dev": true, + "dependencies": { + "abstract-leveldown": "~0.12.1" + } + }, + "node_modules/des.js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", + "integrity": "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "node_modules/diffie-hellman/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/electron-to-chromium": { + "version": "1.4.751", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.751.tgz", + "integrity": "sha512-2DEPi++qa89SMGRhufWTiLmzqyuGmNF3SK4+PQetW1JKiZdEpF4XQonJXJCzyuYSA6mauiMhbyVhqYAP45Hvfw==", + "dev": true + }, + "node_modules/elliptic": { + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.5.tgz", + "integrity": "sha512-7EjbcmUm17NQFu4Pmgmq2olYMj8nwMnpcddByChSUjArp8F5DQWcIcpriwO4ZToLNAJig0yiyjswfyGNje/ixw==", + "dev": true, + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/foreach": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.6.tgz", + "integrity": "sha512-k6GAGDyqLe9JaebCsFCoudPPWfihKu8pylYXRlqP1J7ms39iPoTtk2fviNglIeQEwdh0bQeKJ01ZPyuyQvKzwg==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/fwd-stream": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fwd-stream/-/fwd-stream-1.0.4.tgz", + "integrity": "sha512-q2qaK2B38W07wfPSQDKMiKOD5Nzv2XyuvQlrmh1q0pxyHNanKHq8lwQ6n9zHucAwA5EbzRJKEgds2orn88rYTg==", + "dev": true, + "dependencies": { + "readable-stream": "~1.0.26-4" + } + }, + "node_modules/fwd-stream/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "dev": true + }, + "node_modules/fwd-stream/node_modules/readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/fwd-stream/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "dev": true + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha512-EeeoJKjTyt868liAlVmcv2ZsUfGHlE3Q+BICOXcZiwN3osr5Q/zFGYmTJpoIzuaSTAwndFy+GqhEwlU4L3j4Ow==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "dev": true, + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/idb-wrapper": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/idb-wrapper/-/idb-wrapper-1.7.2.tgz", + "integrity": "sha512-zfNREywMuf0NzDo9mVsL0yegjsirJxHpKHvWcyRozIqQy89g0a3U+oBPOCN4cc0oCiOuYgZHimzaW/R46G1Mpg==", + "dev": true + }, + "node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha512-i0G7hLJ1z0DE8dsqJa2rycj9dBmNKgXBvotXtZYXakU9oivfB9Uj2ZBC27qqef2U58/ZLwalxa1X/RDCdkHtVg==", + "dev": true + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/is/-/is-0.2.7.tgz", + "integrity": "sha512-ajQCouIvkcSnl2iRdK70Jug9mohIHVX9uKpoWnl115ov0R5mzBvRrXxrnHbsA+8AdwCwc/sfw7HXmd4I5EJBdQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-builtin-module": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "dev": true, + "dependencies": { + "builtin-modules": "^3.3.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "dev": true + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-object": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/is-object/-/is-object-0.1.2.tgz", + "integrity": "sha512-GkfZZlIZtpkFrqyAXPQSRBMsaHAw+CgoKe2HXAkjd/sfoI9+hS8PT4wg2rJxdQyUKr7N2vHJbg7/jQtE5l5vBQ==", + "dev": true + }, + "node_modules/is-reference": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", + "dev": true, + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/isbuffer": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/isbuffer/-/isbuffer-0.0.0.tgz", + "integrity": "sha512-xU+NoHp+YtKQkaM2HsQchYn0sltxMxew0HavMfHbjnucBoTSGbw745tL+Z7QBANleWM1eEQMenEpi174mIeS4g==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.2.tgz", + "integrity": "sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-config/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/jest-config/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-config/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/jest-runtime/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-runtime/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-snapshot/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/level-blobs": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/level-blobs/-/level-blobs-0.1.7.tgz", + "integrity": "sha512-n0iYYCGozLd36m/Pzm206+brIgXP8mxPZazZ6ZvgKr+8YwOZ8/PPpYC5zMUu2qFygRN8RO6WC/HH3XWMW7RMVg==", + "dev": true, + "dependencies": { + "level-peek": "1.0.6", + "once": "^1.3.0", + "readable-stream": "^1.0.26-4" + } + }, + "node_modules/level-blobs/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "dev": true + }, + "node_modules/level-blobs/node_modules/readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/level-blobs/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "dev": true + }, + "node_modules/level-filesystem": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/level-filesystem/-/level-filesystem-1.2.0.tgz", + "integrity": "sha512-PhXDuCNYpngpxp3jwMT9AYBMgOvB6zxj3DeuIywNKmZqFj2djj9XfT2XDVslfqmo0Ip79cAd3SBy3FsfOZPJ1g==", + "dev": true, + "dependencies": { + "concat-stream": "^1.4.4", + "errno": "^0.1.1", + "fwd-stream": "^1.0.4", + "level-blobs": "^0.1.7", + "level-peek": "^1.0.6", + "level-sublevel": "^5.2.0", + "octal": "^1.0.0", + "once": "^1.3.0", + "xtend": "^2.2.0" + } + }, + "node_modules/level-fix-range": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/level-fix-range/-/level-fix-range-1.0.2.tgz", + "integrity": "sha512-9llaVn6uqBiSlBP+wKiIEoBa01FwEISFgHSZiyec2S0KpyLUkGR4afW/FCZ/X8y+QJvzS0u4PGOlZDdh1/1avQ==", + "dev": true + }, + "node_modules/level-hooks": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/level-hooks/-/level-hooks-4.5.0.tgz", + "integrity": "sha512-fxLNny/vL/G4PnkLhWsbHnEaRi+A/k8r5EH/M77npZwYL62RHi2fV0S824z3QdpAk6VTgisJwIRywzBHLK4ZVA==", + "dev": true, + "dependencies": { + "string-range": "~1.2" + } + }, + "node_modules/level-js": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/level-js/-/level-js-2.2.4.tgz", + "integrity": "sha512-lZtjt4ZwHE00UMC1vAb271p9qzg8vKlnDeXfIesH3zL0KxhHRDjClQLGLWhyR0nK4XARnd4wc/9eD1ffd4PshQ==", + "dev": true, + "dependencies": { + "abstract-leveldown": "~0.12.0", + "idb-wrapper": "^1.5.0", + "isbuffer": "~0.0.0", + "ltgt": "^2.1.2", + "typedarray-to-buffer": "~1.0.0", + "xtend": "~2.1.2" + } + }, + "node_modules/level-js/node_modules/xtend": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", + "integrity": "sha512-vMNKzr2rHP9Dp/e1NQFnLQlwlhp9L/LfvnsVdHxN1f+uggyVI3i08uD14GPvCToPkdsRfyPqIyYGmIk58V98ZQ==", + "dev": true, + "dependencies": { + "object-keys": "~0.4.0" + }, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/level-peek": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/level-peek/-/level-peek-1.0.6.tgz", + "integrity": "sha512-TKEzH5TxROTjQxWMczt9sizVgnmJ4F3hotBI48xCTYvOKd/4gA/uY0XjKkhJFo6BMic8Tqjf6jFMLWeg3MAbqQ==", + "dev": true, + "dependencies": { + "level-fix-range": "~1.0.2" + } + }, + "node_modules/level-sublevel": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/level-sublevel/-/level-sublevel-5.2.3.tgz", + "integrity": "sha512-tO8jrFp+QZYrxx/Gnmjawuh1UBiifpvKNAcm4KCogesWr1Nm2+ckARitf+Oo7xg4OHqMW76eAqQ204BoIlscjA==", + "dev": true, + "dependencies": { + "level-fix-range": "2.0", + "level-hooks": ">=4.4.0 <5", + "string-range": "~1.2.1", + "xtend": "~2.0.4" + } + }, + "node_modules/level-sublevel/node_modules/level-fix-range": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/level-fix-range/-/level-fix-range-2.0.0.tgz", + "integrity": "sha512-WrLfGWgwWbYPrHsYzJau+5+te89dUbENBg3/lsxOs4p2tYOhCHjbgXxBAj4DFqp3k/XBwitcRXoCh8RoCogASA==", + "dev": true, + "dependencies": { + "clone": "~0.1.9" + } + }, + "node_modules/level-sublevel/node_modules/object-keys": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.2.0.tgz", + "integrity": "sha512-XODjdR2pBh/1qrjPcbSeSgEtKbYo7LqYNq64/TPuCf7j9SfDD3i21yatKoIy39yIWNvVM59iutfQQpCv1RfFzA==", + "deprecated": "Please update to the latest object-keys", + "dev": true, + "dependencies": { + "foreach": "~2.0.1", + "indexof": "~0.0.1", + "is": "~0.2.6" + } + }, + "node_modules/level-sublevel/node_modules/xtend": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.0.6.tgz", + "integrity": "sha512-fOZg4ECOlrMl+A6Msr7EIFcON1L26mb4NY5rurSkOex/TWhazOrg6eXD/B0XkuiYcYhQDWLXzQxLMVJ7LXwokg==", + "dev": true, + "dependencies": { + "is-object": "~0.1.2", + "object-keys": "~0.2.0" + }, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/levelup": { + "version": "0.18.6", + "resolved": "https://registry.npmjs.org/levelup/-/levelup-0.18.6.tgz", + "integrity": "sha512-uB0auyRqIVXx+hrpIUtol4VAPhLRcnxcOsd2i2m6rbFIDarO5dnrupLOStYYpEcu8ZT087Z9HEuYw1wjr6RL6Q==", + "dev": true, + "dependencies": { + "bl": "~0.8.1", + "deferred-leveldown": "~0.2.0", + "errno": "~0.1.1", + "prr": "~0.0.0", + "readable-stream": "~1.0.26", + "semver": "~2.3.1", + "xtend": "~3.0.0" + } + }, + "node_modules/levelup/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "dev": true + }, + "node_modules/levelup/node_modules/prr": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/prr/-/prr-0.0.0.tgz", + "integrity": "sha512-LmUECmrW7RVj6mDWKjTXfKug7TFGdiz9P18HMcO4RHL+RW7MCOGNvpj5j47Rnp6ne6r4fZ2VzyUWEpKbg+tsjQ==", + "dev": true + }, + "node_modules/levelup/node_modules/readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/levelup/node_modules/semver": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-2.3.2.tgz", + "integrity": "sha512-abLdIKCosKfpnmhS52NCTjO4RiLspDfsn37prjzGrp9im5DPJOgh82Os92vtwGh6XdQryKI/7SREZnV+aqiXrA==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/levelup/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "dev": true + }, + "node_modules/levelup/node_modules/xtend": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-3.0.0.tgz", + "integrity": "sha512-sp/sT9OALMjRW1fKDlPeuSZlDQpkqReA0pyJukniWbTGoEKefHxhGJynE3PNhUMlcM8qWIjPwecwCw4LArS5Eg==", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/ltgt": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz", + "integrity": "sha512-AI2r85+4MquTw9ZYqabu4nMwy9Oftlfa/e/52t9IjtfG+mGBbTNdAoZ3RQKLHR6r0wQnwZnPIEh/Ya6XTWAKNA==", + "dev": true + }, + "node_modules/magic-string": { + "version": "0.30.10", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", + "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-dir/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "dependencies": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "bin": { + "miller-rabin": "bin/miller-rabin" + } + }, + "node_modules/miller-rabin/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", + "dev": true + }, + "node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/object-keys": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", + "integrity": "sha512-ncrLw+X55z7bkl5PnUvHwFK9FcGuFYo9gtjws2XtSzL+aZ8tm830P60WJ0dSmFVaSalWieW5MD7kEdnXda9yJw==", + "dev": true + }, + "node_modules/octal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/octal/-/octal-1.0.0.tgz", + "integrity": "sha512-nnda7W8d+A3vEIY+UrDQzzboPf1vhs4JYVhff5CDkq9QNoZY7Xrxeo/htox37j9dZf7yNHevZzqtejWgy1vCqQ==", + "dev": true + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-asn1": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.7.tgz", + "integrity": "sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==", + "dev": true, + "dependencies": { + "asn1.js": "^4.10.1", + "browserify-aes": "^1.2.0", + "evp_bytestokey": "^1.0.3", + "hash-base": "~3.0", + "pbkdf2": "^3.1.2", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "dev": true, + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/process-es6": { + "version": "0.11.6", + "resolved": "https://registry.npmjs.org/process-es6/-/process-es6-0.11.6.tgz", + "integrity": "sha512-GYBRQtL4v3wgigq10Pv58jmTbFXlIiTbSfgnNqZLY0ldUPqy1rRxDI5fCjoCpnM6TqmHQI8ydzTBXW86OYc0gA==", + "dev": true + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "dev": true + }, + "node_modules/public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/public-encrypt/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ] + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "dependencies": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/rollup": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.17.1.tgz", + "integrity": "sha512-0gG94inrUtg25sB2V/pApwiv1lUb0bQ25FPNuzO89Baa+B+c0ccaaBKM5zkZV/12pUUdH+lWCSm9wmHqyocuVQ==", + "dev": true, + "dependencies": { + "@types/estree": "1.0.5" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.17.1", + "@rollup/rollup-android-arm64": "4.17.1", + "@rollup/rollup-darwin-arm64": "4.17.1", + "@rollup/rollup-darwin-x64": "4.17.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.17.1", + "@rollup/rollup-linux-arm-musleabihf": "4.17.1", + "@rollup/rollup-linux-arm64-gnu": "4.17.1", + "@rollup/rollup-linux-arm64-musl": "4.17.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.17.1", + "@rollup/rollup-linux-riscv64-gnu": "4.17.1", + "@rollup/rollup-linux-s390x-gnu": "4.17.1", + "@rollup/rollup-linux-x64-gnu": "4.17.1", + "@rollup/rollup-linux-x64-musl": "4.17.1", + "@rollup/rollup-win32-arm64-msvc": "4.17.1", + "@rollup/rollup-win32-ia32-msvc": "4.17.1", + "@rollup/rollup-win32-x64-msvc": "4.17.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/rollup-plugin-inject": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rollup-plugin-inject/-/rollup-plugin-inject-3.0.2.tgz", + "integrity": "sha512-ptg9PQwzs3orn4jkgXJ74bfs5vYz1NCZlSQMBUA0wKcGp5i5pA1AO3fOUEte8enhGUC+iapTCzEWw2jEFFUO/w==", + "deprecated": "This package has been deprecated and is no longer maintained. Please use @rollup/plugin-inject.", + "dev": true, + "dependencies": { + "estree-walker": "^0.6.1", + "magic-string": "^0.25.3", + "rollup-pluginutils": "^2.8.1" + } + }, + "node_modules/rollup-plugin-inject/node_modules/estree-walker": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", + "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", + "dev": true + }, + "node_modules/rollup-plugin-inject/node_modules/magic-string": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", + "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "dev": true, + "dependencies": { + "sourcemap-codec": "^1.4.8" + } + }, + "node_modules/rollup-plugin-node-builtins": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/rollup-plugin-node-builtins/-/rollup-plugin-node-builtins-2.1.2.tgz", + "integrity": "sha512-bxdnJw8jIivr2yEyt8IZSGqZkygIJOGAWypXvHXnwKAbUcN4Q/dGTx7K0oAJryC/m6aq6tKutltSeXtuogU6sw==", + "dev": true, + "dependencies": { + "browserify-fs": "^1.0.0", + "buffer-es6": "^4.9.2", + "crypto-browserify": "^3.11.0", + "process-es6": "^0.11.2" + } + }, + "node_modules/rollup-plugin-sourcemaps": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/rollup-plugin-sourcemaps/-/rollup-plugin-sourcemaps-0.6.3.tgz", + "integrity": "sha512-paFu+nT1xvuO1tPFYXGe+XnQvg4Hjqv/eIhG8i5EspfYYPBKL57X7iVbfv55aNVASg3dzWvES9dmWsL2KhfByw==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^3.0.9", + "source-map-resolve": "^0.6.0" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "@types/node": ">=10.0.0", + "rollup": ">=0.31.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/rollup-plugin-sourcemaps/node_modules/@rollup/pluginutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", + "dev": true, + "dependencies": { + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" + }, + "engines": { + "node": ">= 8.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0" + } + }, + "node_modules/rollup-plugin-sourcemaps/node_modules/@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", + "dev": true + }, + "node_modules/rollup-plugin-sourcemaps/node_modules/estree-walker": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", + "dev": true + }, + "node_modules/rollup-pluginutils": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", + "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", + "dev": true, + "dependencies": { + "estree-walker": "^0.6.1" + } + }, + "node_modules/rollup-pluginutils/node_modules/estree-walker": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", + "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", + "dev": true + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-resolve": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz", + "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==", + "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", + "dev": true, + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "deprecated": "Please use @jridgewell/sourcemap-codec instead", + "dev": true + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-range": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/string-range/-/string-range-1.2.2.tgz", + "integrity": "sha512-tYft6IFi8SjplJpxCUxyqisD3b+R2CSkomrtJYCkvuf1KuCAWgz7YXt4O0jip7efpfCemwHEzTEAO8EuOYgh3w==", + "dev": true + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "dev": true + }, + "node_modules/typedarray-to-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-1.0.4.tgz", + "integrity": "sha512-vjMKrfSoUDN8/Vnqitw2FmstOfuJ73G6CrSEKnf11A6RmasVxHqfeBcnTb6RsL4pTMuV5Zsv9IiHRphMZyckUw==", + "dev": true + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/v8-to-istanbul": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", + "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/xtend": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.2.0.tgz", + "integrity": "sha512-SLt5uylT+4aoXxXuwtQp5ZnMMzhDb1Xkg4pEqc00WUJCQifPfV9Ub1VrNhp9kXkrjZD2I2Hl8WnjP37jzZLPZw==", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } } diff --git a/package.json b/package.json index f3a34f49..1a0e9594 100644 --- a/package.json +++ b/package.json @@ -1,96 +1,94 @@ { - "name": "pdf2json", - "version": "3.0.5", - "description": "PDF file parser that converts PDF binaries to text based JSON, powered by porting a fork of PDF.JS to Node.js", - "keywords": [ - "pdf", - "pdf parser", - "pdf2json", - "convert pdf to json", - "server side PDF parser", - "port pdf.js to node.js", - "PDF binary to text", - "commandline utility to parse pdf to json", - "JSON", - "javascript", - "PDF canvas", - "pdf.js fork" - ], - "author": { - "name": "Modesty Zhang", - "email": "modestyz@hotmail.com", - "url": "http://www.codeproject.com/script/Articles/MemberArticles.aspx?amid=62372" - }, - "homepage": "https://github.com/modesty/pdf2json", - "repository": { - "type": "git", - "url": "git://github.com/modesty/pdf2json.git" - }, - "main": "./pdfparser.cjs", - "module": "./pdfparser.js", - "typings": "./pdfparser.d.ts", - "scripts": { - "test": "cd ./test && sh p2j.forms.sh", - "test:folder": "node ./test/index.cjs", - "test-misc": "cd ./test && sh p2j.one.sh misc . \"Expected: 7 success, 3 fail exception with stack trace\" ", - "parse": "node --trace-deprecation --trace-warnings pdf2json.js -f ./test/pdf/fd/form/F1040.pdf -o ./test/target/fd/form", - "parse-s": "node --trace-deprecation --trace-warnings pdf2json.js -f ./test/pdf/fd/form/F1040.pdf -o ./test/target/fd/form -s", - "parse-t": "node --trace-deprecation --trace-warnings pdf2json.js -f ./test/pdf/fd/form/F1040.pdf -o ./test/target/fd/form -s -t", - "parse-c": "node --trace-deprecation --trace-warnings pdf2json.js -f ./test/pdf/fd/form/F1040.pdf -o ./test/target/fd/form -s -t -c", - "parse-m": "node --trace-deprecation --trace-warnings pdf2json.js -f ./test/pdf/fd/form/F1040.pdf -o ./test/target/fd/form -s -t -c -m", - "parse-r": "node --trace-deprecation --trace-warnings pdf2json.js -f ./test/pdf/fd/form/F1040.pdf -o ./test/target/fd/form -t -c -m -r", - "parse-fd": "node --trace-deprecation --trace-warnings pdf2json.js -f ./test/pdf/fd/form/ -o ./test/target/fd/form -t -c -m -r", - "parse-tb": "node --trace-deprecation --trace-warnings pdf2json.js -f ./test/pdf/misc/i242_testingWithTable.pdf -o ./test/target/misc", - "parse-tc": "node --trace-deprecation --trace-warnings pdf2json.js -f ./test/pdf/misc/i293_pdfpac.pdf -o ./test/target/misc", - "parse-rectFix": "node --trace-deprecation --trace-warnings pdf2json.js -f ./test/pdf/misc/pr298_rect_fix_from_upstream.pdf -o ./test/target/misc", - "parse-e": "node --trace-deprecation --trace-warnings pdf2json.js -f ./test/pdf/misc/i43_encrypted.pdf -o ./test/target/misc", - "parse-e2": "node --trace-deprecation --trace-warnings pdf2json.js -f ./test/pdf/misc/i243_problem_file_anon.pdf -o ./test/target/misc", - "parse-e3": "node --trace-deprecation --trace-warnings pdf2json.js -f ./test/pdf/misc/i200_test.pdf -o ./test/target/misc", - "build:rollup": "rollup -c ./rollup.config.js", - "build:add-destructured-imports-to-cjs": "node rollup/addDestructedImports.js", - "build": "npm run build:rollup && npm run build:add-destructured-imports-to-cjs" - }, - "engines": { - "node": ">=18.12.1", - "npm": ">=8.19.2" - }, - "type": "module", - "bin": { - "pdf2json": "./bin/pdf2json.js" - }, - "dependencies": { - "@xmldom/xmldom": "^0.8.8" - }, - "devDependencies": { - "@rollup/plugin-commonjs": "^25.0.4", - "@rollup/plugin-node-resolve": "^15.2.1", - "@rollup/plugin-replace": "^5.0.2", - "@types/node": "^20.3.1", - "lodash": "^4.17.21", - "rollup": "^3.29.2", - "rollup-plugin-node-builtins": "^2.1.2", - "vows": "^0.8.3" - }, - "bundledDependencies": [ - "@xmldom/xmldom" - ], - "maintainers": [ - { - "name": "Modesty Zhang", - "email": "modestyz@hotmail.com", - "url": "http://www.codeproject.com/script/Articles/MemberArticles.aspx?amid=62372" - } - ], - "contributors": [], - "bugs": { - "url": "http://github.com/modesty/pdf2json/issues" - }, - "exports": { - ".": { - "import": "./pdfparser.js", - "require": "./pdfparser.cjs" - } - }, - "license": "Apache-2.0", - "readme": "https://github.com/modesty/pdf2json/blob/master/readme.md" + "name": "pdf2json", + "version": "3.1.0", + "description": "PDF file parser that converts PDF binaries to text based JSON, powered by porting a fork of PDF.JS to Node.js", + "keywords": [ + "pdf", + "pdf parser", + "pdf2json", + "convert pdf to json", + "server side PDF parser", + "port pdf.js to node.js", + "PDF to text", + "PDF text extractor", + "PDF binary to text", + "PDF form extractor", + "command line utility to parse pdf to json", + "JSON", + "javascript", + "PDF canvas" + ], + "author": { + "name": "Modesty Zhang", + "email": "modestyz@hotmail.com", + "url": "http://www.codeproject.com/script/Articles/MemberArticles.aspx?amid=62372" + }, + "homepage": "https://github.com/modesty/pdf2json", + "repository": { + "type": "git", + "url": "git://github.com/modesty/pdf2json.git" + }, + "main": "dist/pdfparser.js", + "module": "dist/pdfparser.js", + "typings": "./pdfparser.d.ts", + "scripts": { + "pretest": "npm run build", + "test": "jest --config ./jest.config.json", + "test:forms": "cd ./test && sh p2j.forms.sh", + "test:misc": "cd ./test && sh p2j.one.sh misc . \"Expected: 7 success, 3 fail exception with stack trace\" ", + "parse": "npm run build && node --trace-deprecation --trace-warnings pdf2json.js -f ./test/pdf/fd/form/F1040.pdf -o ./test/target/fd/form", + "parse-s": "npm run build && node --trace-deprecation --trace-warnings pdf2json.js -f ./test/pdf/fd/form/F1040.pdf -o ./test/target/fd/form -s", + "parse-t": "npm run build && node --trace-deprecation --trace-warnings pdf2json.js -f ./test/pdf/fd/form/F1040.pdf -o ./test/target/fd/form -s -t", + "parse-c": "npm run build && node --trace-deprecation --trace-warnings pdf2json.js -f ./test/pdf/fd/form/F1040.pdf -o ./test/target/fd/form -s -t -c", + "parse-m": "npm run build && node --trace-deprecation --trace-warnings pdf2json.js -f ./test/pdf/fd/form/F1040.pdf -o ./test/target/fd/form -s -t -c -m", + "parse-r": "npm run build && node --trace-deprecation --trace-warnings pdf2json.js -f ./test/pdf/fd/form/F1040.pdf -o ./test/target/fd/form -t -c -m -r", + "parse-fd": "npm run build && node --trace-deprecation --trace-warnings pdf2json.js -f ./test/pdf/fd/form/ -o ./test/target/fd/form -t -c -m -r", + "parse-tb": "npm run build && node --trace-deprecation --trace-warnings pdf2json.js -f ./test/pdf/misc/i242_testingWithTable.pdf -o ./test/target/misc", + "parse-tc": "npm run build && node --trace-deprecation --trace-warnings pdf2json.js -f ./test/pdf/misc/i293_pdfpac.pdf -o ./test/target/misc", + "parse-rectFix": "npm run build && node --trace-deprecation --trace-warnings pdf2json.js -f ./test/pdf/misc/pr298_rect_fix_from_upstream.pdf -o ./test/target/misc", + "parse-e": "npm run build && node --trace-deprecation --trace-warnings pdf2json.js -f ./test/pdf/misc/i43_encrypted.pdf -o ./test/target/misc", + "parse-e2": "npm run build && node --trace-deprecation --trace-warnings pdf2json.js -f ./test/pdf/misc/i243_problem_file_anon.pdf -o ./test/target/misc", + "parse-e3": "npm run build && node --trace-deprecation --trace-warnings pdf2json.js -f ./test/pdf/misc/i200_test.pdf -o ./test/target/misc", + "build:rollup": "rollup -c ./rollup.config.js", + "build:bundle-pdfjs-base": "node rollup/bundle-pdfjs-base.js", + "build": "npm run build:bundle-pdfjs-base && npm run build:rollup" + }, + "engines": { + "node": ">=18.12.1", + "npm": ">=8.19.2" + }, + "type": "module", + "bin": { + "pdf2json": "./bin/pdf2json.js" + }, + "dependencies": { + "@xmldom/xmldom": "^0.8.10" + }, + "devDependencies": { + "@types/node": "^20.12.7", + "@rollup/plugin-commonjs": "^25.0.4", + "@rollup/plugin-node-resolve": "^15.2.1", + "@rollup/plugin-replace": "^5.0.2", + "rollup": "^4.17.0", + "rollup-plugin-node-builtins": "^2.1.2", + "rollup-plugin-inject": "^3.0.2", + "rollup-plugin-sourcemaps": "^0.6.3", + "jest": "^29.7.0" + }, + "bundledDependencies": [ + "@xmldom/xmldom" + ], + "maintainers": [ + { + "name": "Modesty Zhang", + "email": "modestyz@hotmail.com", + "url": "http://www.codeproject.com/script/Articles/MemberArticles.aspx?amid=62372" + } + ], + "contributors": [], + "bugs": { + "url": "http://github.com/modesty/pdf2json/issues" + }, + "license": "Apache-2.0", + "readme": "https://github.com/modesty/pdf2json/blob/master/readme.md" } diff --git a/pdfparser.cjs b/pdfparser.cjs deleted file mode 100644 index fde9066b..00000000 --- a/pdfparser.cjs +++ /dev/null @@ -1,2610 +0,0 @@ -'use strict'; - -var fs = require('fs'); -var nodeUtil = require('util'); -var promises = require('fs/promises'); -var events = require('events'); -var path = require('path'); -var url = require('url'); -var { Blob } = require('buffer'); -var xmldom = require('@xmldom/xmldom'); -var DOMParser = xmldom.DOMParser; -var stream = require('stream'); - -var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null; -const kColors = [ - '#000000', // 0 - '#ffffff', // 1 - '#4c4c4c', // 2 - '#808080', // 3 - '#999999', // 4 - '#c0c0c0', // 5 - '#cccccc', // 6 - '#e5e5e5', // 7 - '#f2f2f2', // 8 - '#008000', // 9 - '#00ff00', // 10 - '#bfffa0', // 11 - '#ffd629', // 12 - '#ff99cc', // 13 - '#004080', // 14 - '#9fc0e1', // 15 - '#5580ff', // 16 - '#a9c9fa', // 17 - '#ff0080', // 18 - '#800080', // 19 - '#ffbfff', // 20 - '#e45b21', // 21 - '#ffbfaa', // 22 - '#008080', // 23 - '#ff0000', // 24 - '#fdc59f', // 25 - '#808000', // 26 - '#bfbf00', // 27 - '#824100', // 28 - '#007256', // 29 - '#008000', // 30 - '#000080', // Last + 1 - '#008080', // Last + 2 - '#800080', // Last + 3 - '#ff0000', // Last + 4 - '#0000ff', // Last + 5 - '#008000' // Last + 6 -]; - -const kFontFaces = [ - "quicktype,arial,helvetica,sans-serif", // 00 - QuickType - sans-serif variable font - "quicktype condensed,arial narrow,arial,helvetica,sans-serif", // 01 - QuickType Condensed - thin sans-serif variable font - "quicktypepi,quicktypeiipi", // 02 - QuickType Pi - "quicktype mono,courier new,courier,monospace", // 03 - QuickType Mono - san-serif fixed font - "ocr-a,courier new,courier,monospace", // 04 - OCR-A - OCR readable san-serif fixed font - "ocr b mt,courier new,courier,monospace" // 05 - OCR-B MT - OCR readable san-serif fixed font - ]; - - const kFontStyles = [ - // Face Size Bold Italic StyleID(Comment) - // ----- ---- ---- ----- ----------------- - [0, 6, 0, 0], //00 - [0, 8, 0, 0], //01 - [0, 10, 0, 0], //02 - [0, 12, 0, 0], //03 - [0, 14, 0, 0], //04 - [0, 18, 0, 0], //05 - [0, 6, 1, 0], //06 - [0, 8, 1, 0], //07 - [0, 10, 1, 0], //08 - [0, 12, 1, 0], //09 - [0, 14, 1, 0], //10 - [0, 18, 1, 0], //11 - [0, 6, 0, 1], //12 - [0, 8, 0, 1], //13 - [0, 10, 0, 1], //14 - [0, 12, 0, 1], //15 - [0, 14, 0, 1], //16 - [0, 18, 0, 1], //17 - [0, 6, 1, 1], //18 - [0, 8, 1, 1], //19 - [0, 10, 1, 1], //20 - [0, 12, 1, 1], //21 - [0, 14, 1, 1], //22 - [0, 18, 1, 1], //23 - [1, 6, 0, 0], //24 - [1, 8, 0, 0], //25 - [1, 10, 0, 0], //26 - [1, 12, 0, 0], //27 - [1, 14, 0, 0], //28 - [1, 18, 0, 0], //29 - [1, 6, 1, 0], //30 - [1, 8, 1, 0], //31 - [1, 10, 1, 0], //32 - [1, 12, 1, 0], //33 - [1, 14, 1, 0], //34 - [1, 18, 1, 0], //35 - [1, 6, 0, 1], //36 - [1, 8, 0, 1], //37 - [1, 10, 0, 1], //38 - [1, 12, 0, 1], //39 - [1, 14, 0, 1], //40 - [1, 18, 0, 1], //41 - [2, 8, 0, 0], //42 - [2, 10, 0, 0], //43 - [2, 12, 0, 0], //44 - [2, 14, 0, 0], //45 - [2, 18, 0, 0], //46 - [3, 8, 0, 0], //47 - [3, 10, 0, 0], //48 - [3, 12, 0, 0], //49 - [4, 12, 0, 0], //50 - [0, 9, 0, 0], //51 - [0, 9, 1, 0], //52 - [0, 9, 0, 1], //53 - [0, 9, 1, 1], //54 - [1, 9, 0, 0], //55 - [1, 9, 1, 0], //56 - [1, 9, 1, 1], //57 - [4, 10, 0, 0], //58 - [5, 10, 0, 0], //59 - [5, 12, 0, 0] //60 -]; - -const dpi = 96.0; -const gridXPerInch = 4.0; -const gridYPerInch = 4.0; - -const _pixelXPerGrid = dpi/gridXPerInch; -const _pixelYPerGrid = dpi/gridYPerInch; -const _pixelPerPoint = dpi/72; - -class PDFUnit { - static toFixedFloat(fNum) { - return parseFloat(fNum.toFixed(3)); - } - - static colorCount() { - return kColors.length; - } - - static toPixelX(formX) { - return Math.round(formX * _pixelXPerGrid); - } - - static toPixelY(formY) { - return Math.round(formY * _pixelYPerGrid); - } - - static pointToPixel(point) {// Point unit (1/72 an inch) to pixel units - return point * _pixelPerPoint; - } - - static getColorByIndex(clrId) { - return kColors[clrId]; - } - - static toFormPoint(viewportX, viewportY) { - return [(viewportX / _pixelXPerGrid), (viewportY / _pixelYPerGrid)]; - } - - static toFormX(viewportX) { - return PDFUnit.toFixedFloat(viewportX / _pixelXPerGrid); - } - - static toFormY(viewportY) { - return PDFUnit.toFixedFloat(viewportY / _pixelYPerGrid); - } - - static findColorIndex(color) { - if (color.length === 4) - color += "000"; - //MQZ. 07/29/2013: if color is not in dictionary, just return -1. The caller (pdffont, pdffill) will set the actual color - return kColors.indexOf(color); - } - - static dateToIso8601(date) { - // PDF spec p.160 - if (date.slice(0, 2) === 'D:') { // D: prefix is optional - date = date.slice(2); - } - let tz = 'Z'; - let idx = date.search(/[Z+-]/); // timezone is optional - if (idx >= 0) { - tz = date.slice(idx); - if (tz !== 'Z') { // timezone format OHH'mm' - tz = tz.slice(0, 3) + ':' + tz.slice(4, 6); - } - date = date.slice(0, idx); - } - let yr = date.slice(0, 4); // everything after year is optional - let mth = date.slice(4, 6) || '01'; - let day = date.slice(6, 8) || '01'; - let hr = date.slice(8, 10) || '00'; - let min = date.slice(10, 12) || '00'; - let sec = date.slice(12, 14) || '00'; - return yr + '-' + mth + '-' + day + 'T' + hr + ':' + min + ':' + sec + tz; - } -} - -class PDFLine { - constructor(x1, y1, x2, y2, lineWidth, color, dashed) { - this.x1 = x1; - this.y1 = y1; - this.x2 = x2; - this.y2 = y2; - this.lineWidth = lineWidth || 1.0; - this.color = color; - this.dashed = dashed; - } - - #setStartPoint(oneLine, x, y) { - oneLine.x = PDFUnit.toFormX(x); - oneLine.y = PDFUnit.toFormY(y); - } - - processLine(targetData) { - const xDelta = Math.abs(this.x2 - this.x1); - const yDelta = Math.abs(this.y2 - this.y1); - const minDelta = this.lineWidth; - - let oneLine = {x:0, y:0, w: PDFUnit.toFixedFloat(this.lineWidth), l:0}; - - //MQZ Aug.28.2013, adding color support, using color dictionary and default to black - const clrId = PDFUnit.findColorIndex(this.color); - const colorObj = (clrId > 0 && clrId < PDFUnit.colorCount()) ? {clr: clrId} : {oc: this.color}; - oneLine = {...oneLine, ...colorObj}; - - //MQZ Aug.29 dashed line support - if (this.dashed) { - oneLine = oneLine = {...oneLine, dsh: 1}; - } - - if ((yDelta < this.lineWidth) && (xDelta > minDelta)) { //HLine - if (this.lineWidth < 4 && (xDelta / this.lineWidth < 4)) { - nodeUtil.p2jinfo("Skipped: short thick HLine: lineWidth = " + this.lineWidth + ", xDelta = " + xDelta); - return; //skip short thick lines, like PA SPP lines behinds checkbox - } - - oneLine.l = PDFUnit.toFormX(xDelta); - if (this.x1 > this.x2) - this.#setStartPoint(oneLine, this.x2, this.y2); - else - this.#setStartPoint(oneLine, this.x1, this.y1); - targetData.HLines.push(oneLine); - } - else if ((xDelta < this.lineWidth) && (yDelta > minDelta)) {//VLine - if (this.lineWidth < 4 && (yDelta / this.lineWidth < 4)) { - nodeUtil.p2jinfo("Skipped: short thick VLine: lineWidth = " + this.lineWidth + ", yDelta = " + yDelta); - return; //skip short think lines, like PA SPP lines behinds checkbox - } - - oneLine.l = PDFUnit.toFormY(yDelta); - if (this.y1 > this.y2) - this.#setStartPoint(oneLine, this.x2, this.y2); - else - this.#setStartPoint(oneLine, this.x1, this.y1); - targetData.VLines.push(oneLine); - } - } -} - -class PDFFill{ - // constructor - constructor(x, y, width, height, color) { - this.x = x; - this.y = y; - this.width = width; - this.height = height; - this.color = color; - } - - processFill(targetData) { - //MQZ.07/29/2013: when color is not in color dictionary, set the original color (oc) - const clrId = PDFUnit.findColorIndex(this.color); - const colorObj = (clrId > 0 && clrId < PDFUnit.colorCount()) ? {clr: clrId} : {oc: this.color}; - - const oneFill = {x:PDFUnit.toFormX(this.x), - y:PDFUnit.toFormY(this.y), - w:PDFUnit.toFormX(this.width), - h:PDFUnit.toFormY(this.height), - ...colorObj}; - - - if (oneFill.w < 2 && oneFill.h < 2) { - nodeUtil.p2jinfo("Skipped: tiny fill: " + oneFill.w + " x " + oneFill.h); - return; //skip short thick lines, like PA SPP lines behinds checkbox - } - - targetData.Fills.push(oneFill); - } -} - -const _boldSubNames = ['bd', 'bold', 'demi', 'black']; -const _stdFonts = [ - 'arial', - 'helvetica', - 'sans-serif ', - 'courier ', - 'monospace ', - 'ocr ', -]; -const DISTANCE_DELTA = 0.1; - -class PDFFont { - #initTypeName() { - let typeName = this.fontObj.name || this.fontObj.fallbackName; - if (!typeName) { - typeName = kFontFaces[0]; //default font family name - } - typeName = typeName.toLowerCase(); - return typeName; - } - - #initSubType() { - let subType = this.typeName; - let bold = false; - - let nameArray = this.typeName.split('+'); - if (Array.isArray(nameArray) && nameArray.length > 1) { - subType = nameArray[1].split('-'); - if (Array.isArray(subType) && subType.length > 1) { - let subName = subType[1].toLowerCase(); - bold = _boldSubNames.indexOf(subName) >= 0; - subType = subType[0]; - } - } - return { subType, bold }; - } - - #initSymbol() { - let isSymbol = - this.typeName.indexOf('symbol') > 0 || - kFontFaces[2].indexOf(this.subType) >= 0; - if (this.fontObj.isSymbolicFont) { - let mFonts = _stdFonts.filter( - (oneName) => this.typeName.indexOf(oneName) >= 0 - ); - - if (mFonts.length > 0) { - this.fontObj.isSymbolicFont = false; //lots of Arial-based font is detected as symbol in VA forms (301, 76-c, etc.) reset the flag for now - nodeUtil.p2jinfo( - 'Reset: isSymbolicFont (false) for ' + this.fontObj.name - ); - } - } else { - if (isSymbol) { - this.fontObj.isSymbolicFont = true; //text pdf: va_ind_760c - nodeUtil.p2jinfo( - 'Reset: isSymbolicFont (true) for ' + this.fontObj.name - ); - } - } - return isSymbol; - } - - #initSpaceWidth() { - let spaceWidth = this.fontObj.spaceWidth; - if (!spaceWidth) { - var spaceId = Array.isArray(this.fontObj.toFontChar) - ? this.fontObj.toFontChar.indexOf(32) - : -1; - spaceWidth = - spaceId >= 0 && Array.isArray(this.fontObj.widths) - ? this.fontObj.widths[spaceId] - : 250; - } - spaceWidth = PDFUnit.toFormX(spaceWidth) / 32; - return spaceWidth; - } - - // constructor - constructor(fontObj) { - this.fontObj = fontObj; - - this.typeName = this.#initTypeName(); - - const { subType, bold } = this.#initSubType(); - this.subType = subType; - this.bold = bold; - - this.isSymbol = this.#initSymbol(); - this.spaceWidth = this.#initSpaceWidth(); - - this.fontSize = 1; - this.faceIdx = 0; - this.italic = false; - this.fontStyleId = -1; - } - - /** sort text blocks by y then x */ - static compareBlockPos(t1, t2) { - if (t1.y < t2.y - DISTANCE_DELTA) { - return -1; - } - if (Math.abs(t1.y - t2.y) <= DISTANCE_DELTA) { - if (t1.x < t2.x - DISTANCE_DELTA) { - return -1; - } - if (Math.abs(t1.x - t2.x) <= DISTANCE_DELTA) { - return 0; - } - } - return 1; - } - - static haveSameStyle(t1, t2) { - let retVal = t1.R[0].S === t2.R[0].S; - if (retVal && t1.R[0].S < 0) { - for (let i = 0; i < t1.R[0].TS.length; i++) { - if (t1.R[0].TS[i] !== t2.R[0].TS[i]) { - retVal = false; - break; - } - } - } - if (retVal) { - // make sure both block are not rotated - retVal = - typeof t1.R[0].RA === 'undefined' && - typeof t2.R[0].RA === 'undefined'; - } - - return retVal; - } - - static getSpaceThreshHold(t1) { - return (PDFFont.getFontSize(t1) / 12) * t1.sw; - } - - static areAdjacentBlocks(t1, t2) { - const isInSameLine = Math.abs(t1.y - t2.y) <= DISTANCE_DELTA; - const isDistanceSmallerThanASpace = - t2.x - t1.x - t1.w < PDFFont.getSpaceThreshHold(t1); - - return isInSameLine && isDistanceSmallerThanASpace; - } - - static getFontSize(textBlock) { - const sId = textBlock.R[0].S; - return sId < 0 ? textBlock.R[0].TS[1] : kFontStyles[sId][1]; - } - - static areDuplicateBlocks(t1, t2) { - return ( - t1.x == t2.x && - t1.y == t2.y && - t1.R[0].T == t2.R[0].T && - PDFFont.haveSameStyle(t1, t2) - ); - } - - // private - #setFaceIndex() { - const fontObj = this.fontObj; - - this.bold = fontObj.bold; - if (!this.bold) { - this.bold = - this.typeName.indexOf('bold') >= 0 || - this.typeName.indexOf('black') >= 0; - } - this.italic = fontObj.italic; // fix https://github.com/modesty/pdf2json/issues/42 - // Extended the fix for https://github.com/modesty/pdf2json/issues/42 - if (!this.italic) { - this.italic = - this.typeName.indexOf('italic') >= 0 || - this.typeName.indexOf('oblique') >= 0; - } - // Added detection of hybrid dual bolditalic fonts - if ( - (!this.bold || !this.italic) && - this.typeName.indexOf('boldobl') >= 0 - ) { - this.bold = true; - this.italic = true; - } - - let typeName = this.subType; - if (fontObj.isSerifFont) { - if (kFontFaces[1].indexOf(typeName) >= 0) this.faceIdx = 1; - } else if (kFontFaces[2].indexOf(this.subType) >= 0) { - this.faceIdx = 2; - } else if (fontObj.isMonospace) { - this.faceIdx = 3; - - if (kFontFaces[4].indexOf(typeName) >= 0) this.faceIdx = 4; - else if (kFontFaces[5].indexOf(typeName) >= 0) this.faceIdx = 5; - } else if (fontObj.isSymbolicFont) { - this.faceIdx = 2; - } - - if (this.faceIdx == 0) { - if (this.typeName.indexOf('narrow') > 0) this.faceIdx = 1; - } - - // nodeUtil.p2jinfo"typeName = " + typeName + " => faceIdx = " + this.faceIdx); - } - - #getFontStyleIndex(fontSize) { - this.#setFaceIndex(); - - //MQZ Feb.28.2013. Adjust bold text fontsize to work around word spacing issue - this.fontSize = this.bold && fontSize > 12 ? fontSize + 1 : fontSize; - - let fsa = [ - this.faceIdx, - this.fontSize, - this.bold ? 1 : 0, - this.italic ? 1 : 0, - ]; - let retVal = -1; - - kFontStyles.forEach(function (element, index, list) { - if (retVal === -1) { - if ( - element[0] === fsa[0] && - element[1] === fsa[1] && - element[2] === fsa[2] && - element[3] === fsa[3] - ) { - retVal = index; - } - } - }); - - return retVal; - } - - #processSymbolicFont(str) { - let retVal = str; - - if (!str || str.length !== 1) return retVal; - - if (!this.fontObj.isSymbolicFont || !this.isSymbol) { - if (retVal == 'C' || retVal == 'G') { - //prevent symbolic encoding from the client - retVal = ' ' + retVal + ' '; //sample: va_ind_760c - } - return retVal; - } - - switch (str.charCodeAt(0)) { - case 20: - retVal = '\u2713'; - break; //check mark - case 70: - retVal = this.fontObj.type === 'CIDFontType0' ? '\u26A0' : '\u007D'; - break; //exclaimation in triangle OR right curly bracket - case 71: - retVal = '\u25b6'; - break; //right triangle - case 97: - retVal = '\u25b6'; - break; //right triangle - case 99: - retVal = this.isSymbol ? '\u2022' : '\u25b2'; - break; //up triangle. set to Bullet Dot for VA SchSCR - case 100: - retVal = '\u25bc'; - break; //down triangle - case 103: - retVal = '\u27A8'; - break; //right arrow. sample: va_ind_760pff and pmt - case 106: - retVal = ''; - break; //VA 301: string j character by the checkbox, hide it for now - case 114: - retVal = '\u2022'; - break; //Bullet dot - case 115: - retVal = '\u25b2'; - break; //up triangle - case 116: - retVal = '\u2022'; - break; //Bullet dot - case 118: - retVal = '\u2022'; - break; //Bullet dot - default: - nodeUtil.p2jinfo( - this.fontObj.type + - ' - SymbolicFont - (' + - this.fontObj.name + - ') : ' + - str.charCodeAt(0) + - '::' + - str.charCodeAt(1) + - ' => ' + - retVal - ); - } - - return retVal; - } - - #textRotationAngle(matrix2D) { - let retVal = 0; - if (matrix2D[0][0] === 0 && matrix2D[1][1] === 0) { - if (matrix2D[0][1] != 0 && matrix2D[1][0] != 0) { - if (matrix2D[0][1] / matrix2D[1][0] + 1 < 0.0001) retVal = 90; - } - } else if (matrix2D[0][0] !== 0 && matrix2D[1][1] !== 0) { - let r1 = Math.atan(-matrix2D[0][1] / matrix2D[0][0]); - let r2 = Math.atan(matrix2D[1][0] / matrix2D[1][1]); - if (Math.abs(r1) > 0.0001 && r1 - r2 < 0.0001) { - retVal = (r1 * 180) / Math.PI; - } - } - return retVal; - } - - // public instance methods - processText(p, str, maxWidth, color, fontSize, targetData, matrix2D) { - const text = this.#processSymbolicFont(str); - if (!text) { - return; - } - this.fontStyleId = this.#getFontStyleIndex(fontSize); - - // when this.fontStyleId === -1, it means the text style doesn't match any entry in the dictionary - // adding TS to better describe text style [fontFaceId, fontSize, 1/0 for bold, 1/0 for italic]; - const TS = [ - this.faceIdx, - this.fontSize, - this.bold ? 1 : 0, - this.italic ? 1 : 0, - ]; - - const clrId = PDFUnit.findColorIndex(color); - const colorObj = - clrId >= 0 && clrId < PDFUnit.colorCount() - ? { clr: clrId } - : { oc: color }; - - let textRun = { - T: this.flash_encode(text), - S: this.fontStyleId, - TS: TS, - }; - const rAngle = this.#textRotationAngle(matrix2D); - if (rAngle != 0) { - nodeUtil.p2jinfo(str + ': rotated ' + rAngle + ' degree.'); - textRun = { ...textRun, RA: rAngle }; - } - - const oneText = { - x: PDFUnit.toFormX(p.x) - 0.25, - y: PDFUnit.toFormY(p.y) - 0.75, - w: PDFUnit.toFixedFloat(maxWidth), - ...colorObj, //MQZ.07/29/2013: when color is not in color dictionary, set the original color (oc) - sw: this.spaceWidth, //font space width, use to merge adjacent text blocks - A: 'left', - R: [textRun], - }; - - targetData.Texts.push(oneText); - } - - flash_encode(str) { - let retVal = encodeURIComponent(str); - retVal = retVal.replace('%C2%96', '-'); - retVal = retVal.replace('%C2%91', '%27'); - retVal = retVal.replace('%C2%92', '%27'); - retVal = retVal.replace('%C2%82', '%27'); - retVal = retVal.replace('%C2%93', '%22'); - retVal = retVal.replace('%C2%94', '%22'); - retVal = retVal.replace('%C2%84', '%22'); - retVal = retVal.replace('%C2%8B', '%C2%AB'); - retVal = retVal.replace('%C2%9B', '%C2%BB'); - - return retVal; - } - - clean() { - this.fontObj = null; - delete this.fontObj; - } -} - -// alias some functions to make (compiled) code shorter -const { round: mr, sin: ms, cos: mc, abs, sqrt } = Math; - -// precompute "00" to "FF" -const dec2hex = []; -for (let i = 0; i < 16; i++) { - for (let j = 0; j < 16; j++) { - dec2hex[i * 16 + j] = i.toString(16) + j.toString(16); - } -} - -function createMatrixIdentity() { - return [ - [1, 0, 0], - [0, 1, 0], - [0, 0, 1], - ]; -} - -function matrixMultiply(m1, m2) { - let result = createMatrixIdentity(); - - for (let x = 0; x < 3; x++) { - for (let y = 0; y < 3; y++) { - let sum = 0; - - for (let z = 0; z < 3; z++) { - sum += m1[x][z] * m2[z][y]; - } - - result[x][y] = sum; - } - } - return result; -} - -function copyState(o1, o2) { - o2.fillStyle = o1.fillStyle; - o2.lineCap = o1.lineCap; - o2.lineJoin = o1.lineJoin; - o2.lineWidth = o1.lineWidth; - o2.miterLimit = o1.miterLimit; - o2.shadowBlur = o1.shadowBlur; - o2.shadowColor = o1.shadowColor; - o2.shadowOffsetX = o1.shadowOffsetX; - o2.shadowOffsetY = o1.shadowOffsetY; - o2.strokeStyle = o1.strokeStyle; - o2.globalAlpha = o1.globalAlpha; - o2.arcScaleX_ = o1.arcScaleX_; - o2.arcScaleY_ = o1.arcScaleY_; - o2.lineScale_ = o1.lineScale_; - o2.dashArray = o1.dashArray; -} - -function processStyle(styleString) { - let str, - alpha = 1; - - styleString = String(styleString); - if (styleString.substring(0, 3) == 'rgb') { - let start = styleString.indexOf('(', 3); - let end = styleString.indexOf(')', start + 1); - let guts = styleString.substring(start + 1, end).split(','); - - str = '#'; - for (let i = 0; i < 3; i++) { - str += dec2hex[Number(guts[i])]; - } - - if (guts.length == 4 && styleString.substring(3, 4) == 'a') { - alpha = guts[3]; - } - } else { - str = styleString; - } - - return { color: str, alpha: alpha }; -} - -function processLineCap(lineCap) { - switch (lineCap) { - case 'butt': - return 'flat'; - case 'round': - return 'round'; - case 'square': - default: - return 'square'; - } -} - -// Helper function that takes the already fixed cordinates. -function bezierCurveToHelper(self, cp1, cp2, p) { - self.currentPath_.push({ - type: 'bezierCurveTo', - cp1x: cp1.x, - cp1y: cp1.y, - cp2x: cp2.x, - cp2y: cp2.y, - x: p.x, - y: p.y, - }); - self.currentX_ = p.x; - self.currentY_ = p.y; -} - -function matrixIsFinite(m) { - for (let j = 0; j < 3; j++) { - for (let k = 0; k < 2; k++) { - if (!isFinite(m[j][k]) || isNaN(m[j][k])) { - return false; - } - } - } - return true; -} - -function setM(ctx, m, updateLineScale) { - if (!matrixIsFinite(m)) { - return; - } - ctx.m_ = m; - - if (updateLineScale) { - // Get the line scale. - // Determinant of this.m_ means how much the area is enlarged by the - // transformation. So its square root can be used as a scale factor - // for width. - let det = m[0][0] * m[1][1] - m[0][1] * m[1][0]; - ctx.lineScale_ = sqrt(abs(det)); - } -} - -class CanvasPattern_ { - constructor() {} -} - -// Gradient / Pattern Stubs -class CanvasGradient_ { - constructor(aType) { - this.type_ = aType; - this.x0_ = 0; - this.y0_ = 0; - this.r0_ = 0; - this.x1_ = 0; - this.y1_ = 0; - this.r1_ = 0; - this.colors_ = []; - } - addColorStop(aOffset, aColor) { - aColor = processStyle(aColor); - this.colors_.push({ - offset: aOffset, - color: aColor.color, - alpha: aColor.alpha, - }); - } -} - -/** - * This class implements CanvasRenderingContext2D interface as described by - * the WHATWG. - * @param {HTMLElement} surfaceElement The element that the 2D context should - * be associated with - */ -class CanvasRenderingContext2D_ { - constructor(canvasTarget, scaledWidth, scaledHeight) { - this.m_ = createMatrixIdentity(); - - this.mStack_ = []; - this.aStack_ = []; - this.currentPath_ = []; - - // Canvas context properties - this.strokeStyle = '#000'; - this.fillStyle = '#000'; - - this.lineWidth = 1; - this.lineJoin = 'miter'; - this.lineCap = 'butt'; - this.dashArray = []; - this.miterLimit = 1; - this.globalAlpha = 1; - - if (!('HLines' in canvasTarget) || !Array.isArray(canvasTarget.HLines)) - canvasTarget.HLines = []; - if (!('VLines' in canvasTarget) || !Array.isArray(canvasTarget.VLines)) - canvasTarget.VLines = []; - if (!('Fills' in canvasTarget) || !Array.isArray(canvasTarget.Fills)) - canvasTarget.Fills = []; - if (!('Texts' in canvasTarget) || !Array.isArray(canvasTarget.Texts)) - canvasTarget.Texts = []; - - this.canvas = canvasTarget; - - this.width = scaledWidth; - this.height = scaledHeight; - - this.arcScaleX_ = 1; - this.arcScaleY_ = 1; - this.lineScale_ = 1; - - this.currentFont = null; - } - - //private helper methods - #drawPDFLine(p1, p2, lineWidth, color) { - let dashedLine = - Array.isArray(this.dashArray) && this.dashArray.length > 1; - let pL = new PDFLine( - p1.x, - p1.y, - p2.x, - p2.y, - lineWidth, - color, - dashedLine - ); - pL.processLine(this.canvas); - } - - #drawPDFFill(cp, min, max, color) { - let width = max.x - min.x; - let height = max.y - min.y; - let pF = new PDFFill(cp.x, cp.y, width, height, color); - pF.processFill(this.canvas); - } - - #needRemoveRect(x, y, w, h) { - let retVal = Math.abs(w - Math.abs(h)) < 1 && w < 13; - if (retVal) { - nodeUtil.p2jinfo('Skipped: tiny rect: w=' + w + ', h=' + h); - } - return retVal; - } - - getContext(ctxType) { - return ctxType === '2d' ? this : null; - } - - setLineDash(lineDash) { - this.dashArray = lineDash; - } - - getLineDash() { - return this.dashArray; - } - - fillText(text, x, y, maxWidth, fontSize) { - if (!text || (!text.length === 1 && text.trim().length < 1)) return; - let p = this.getCoords_(x, y); - - let a = processStyle(this.fillStyle || this.strokeStyle); - let color = !!a ? a.color : '#000000'; - - this.currentFont.processText( - p, - text, - maxWidth, - color, - fontSize, - this.canvas, - this.m_ - ); - } - - strokeText(text, x, y, maxWidth) { - //MQZ. 10/23/2012, yeah, no hollow text for now - this.fillText(text, x, y, maxWidth); - } - - measureText(text) { - console.warn('to be implemented: contextPrototype.measureText - ', text); - let chars = text.length || 1; - return { width: chars * (this.currentFont.spaceWidth || 5) }; - } - - setFont(fontObj) { - if (!!this.currentFont && typeof this.currentFont.clean === 'function') { - this.currentFont.clean(); - this.currentFont = null; - } - - this.currentFont = new PDFFont(fontObj); - } - - clearRect() { - console.warn('to be implemented: contextPrototype.clearRect'); - } - - beginPath() { - // TODO: Branch current matrix so that save/restore has no effect - // as per safari docs. - this.currentPath_ = []; - } - - moveTo(aX, aY) { - let p = this.getCoords_(aX, aY); - this.currentPath_.push({ type: 'moveTo', x: p.x, y: p.y }); - this.currentX_ = p.x; - this.currentY_ = p.y; - } - - lineTo(aX, aY) { - let p = this.getCoords_(aX, aY); - this.currentPath_.push({ type: 'lineTo', x: p.x, y: p.y }); - - this.currentX_ = p.x; - this.currentY_ = p.y; - } - - bezierCurveTo(aCP1x, aCP1y, aCP2x, aCP2y, aX, aY) { - let p = this.getCoords_(aX, aY); - let cp1 = this.getCoords_(aCP1x, aCP1y); - let cp2 = this.getCoords_(aCP2x, aCP2y); - bezierCurveToHelper(this, cp1, cp2, p); - } - - quadraticCurveTo(aCPx, aCPy, aX, aY) { - // the following is lifted almost directly from - // http://developer.mozilla.org/en/docs/Canvas_tutorial:Drawing_shapes - - let cp = this.getCoords_(aCPx, aCPy); - let p = this.getCoords_(aX, aY); - - let cp1 = { - x: this.currentX_ + (2.0 / 3.0) * (cp.x - this.currentX_), - y: this.currentY_ + (2.0 / 3.0) * (cp.y - this.currentY_), - }; - let cp2 = { - x: cp1.x + (p.x - this.currentX_) / 3.0, - y: cp1.y + (p.y - this.currentY_) / 3.0, - }; - - bezierCurveToHelper(this, cp1, cp2, p); - } - - arc(aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise) { - let arcType = aClockwise ? 'at' : 'wa'; - - let xStart = aX + mc(aStartAngle) * aRadius; - let yStart = aY + ms(aStartAngle) * aRadius; - - let xEnd = aX + mc(aEndAngle) * aRadius; - let yEnd = aY + ms(aEndAngle) * aRadius; - - // IE won't render arches drawn counter clockwise if xStart == xEnd. - if (xStart == xEnd && !aClockwise) { - xStart += 0.125; // Offset xStart by 1/80 of a pixel. Use something - // that can be represented in binary - } - - let p = this.getCoords_(aX, aY); - let pStart = this.getCoords_(xStart, yStart); - let pEnd = this.getCoords_(xEnd, yEnd); - - this.currentPath_.push({ - type: arcType, - x: p.x, - y: p.y, - radius: aRadius, - xStart: pStart.x, - yStart: pStart.y, - xEnd: pEnd.x, - yEnd: pEnd.y, - }); - } - - rect(aX, aY, aWidth, aHeight) { - if (this.#needRemoveRect(aX, aY, aWidth, aHeight)) { - return; //try to remove the rectangle behind radio buttons and checkboxes - } - - this.moveTo(aX, aY); - this.lineTo(aX + aWidth, aY); - this.lineTo(aX + aWidth, aY + aHeight); - this.lineTo(aX, aY + aHeight); - this.closePath(); - } - - strokeRect(aX, aY, aWidth, aHeight) { - if (this.#needRemoveRect(aX, aY, aWidth, aHeight)) { - return; //try to remove the rectangle behind radio buttons and checkboxes - } - - let oldPath = this.currentPath_; - this.beginPath(); - - this.moveTo(aX, aY); - this.lineTo(aX + aWidth, aY); - this.lineTo(aX + aWidth, aY + aHeight); - this.lineTo(aX, aY + aHeight); - this.closePath(); - this.stroke(); - - this.currentPath_ = oldPath; - } - - fillRect(aX, aY, aWidth, aHeight) { - if (this.#needRemoveRect(aX, aY, aWidth, aHeight)) { - return; //try to remove the rectangle behind radio buttons and checkboxes - } - - let oldPath = this.currentPath_; - this.beginPath(); - - this.moveTo(aX, aY); - this.lineTo(aX + aWidth, aY); - this.lineTo(aX + aWidth, aY + aHeight); - this.lineTo(aX, aY + aHeight); - this.closePath(); - this.fill(); - - this.currentPath_ = oldPath; - } - - createLinearGradient(aX0, aY0, aX1, aY1) { - let gradient = new CanvasGradient_('gradient'); - gradient.x0_ = aX0; - gradient.y0_ = aY0; - gradient.x1_ = aX1; - gradient.y1_ = aY1; - return gradient; - } - - createRadialGradient(aX0, aY0, aR0, aX1, aY1, aR1) { - let gradient = new CanvasGradient_('gradientradial'); - gradient.x0_ = aX0; - gradient.y0_ = aY0; - gradient.r0_ = aR0; - gradient.x1_ = aX1; - gradient.y1_ = aY1; - gradient.r1_ = aR1; - return gradient; - } - - drawImage(image, var_args) { - //MQZ. no image drawing support for now - } - - getImageData(x, y, w, h) { - //MQZ. returns empty data buffer for now - return { - width: w, - height: h, - data: new Uint8Array(w * h * 4), - }; - } - - stroke(aFill) { - if (this.currentPath_.length < 2) { - return; - } - - let a = processStyle(aFill ? this.fillStyle : this.strokeStyle); - let color = a.color; - // let opacity = a.alpha * this.globalAlpha; - let lineWidth = this.lineScale_ * this.lineWidth; - - let min = { x: null, y: null }; - let max = { x: null, y: null }; - - for (let i = 0; i < this.currentPath_.length; i++) { - let p = this.currentPath_[i]; - - switch (p.type) { - case 'moveTo': - break; - case 'lineTo': - if (!aFill) { - //lines - if (i > 0) { - this.#drawPDFLine( - this.currentPath_[i - 1], - p, - lineWidth, - color - ); - } - } - break; - case 'close': - if (!aFill) { - //lines - if (i > 0) { - this.#drawPDFLine( - this.currentPath_[i - 1], - this.currentPath_[0], - lineWidth, - color - ); - } - } - p = null; - break; - case 'bezierCurveTo': - break; - case 'at': - case 'wa': - break; - } - - // Figure out dimensions so we can set fills' coordinates correctly - if (aFill && p) { - if (min.x == null || p.x < min.x) { - min.x = p.x; - } - if (max.x == null || p.x > max.x) { - max.x = p.x; - } - if (min.y == null || p.y < min.y) { - min.y = p.y; - } - if (max.y == null || p.y > max.y) { - max.y = p.y; - } - } - } - - if (aFill) { - //fill - this.#drawPDFFill(min, min, max, color); - } - } - - fill() { - this.stroke(true); - } - - closePath() { - this.currentPath_.push({ type: 'close' }); - } - - /** - * @private - */ - getCoords_(aX, aY) { - let m = this.m_; - return { - x: aX * m[0][0] + aY * m[1][0] + m[2][0], - y: aX * m[0][1] + aY * m[1][1] + m[2][1], - }; - } - - save() { - let o = {}; - copyState(this, o); - this.aStack_.push(o); - this.mStack_.push(this.m_); - this.m_ = matrixMultiply(createMatrixIdentity(), this.m_); - } - - restore() { - copyState(this.aStack_.pop(), this); - this.m_ = this.mStack_.pop(); - } - - translate(aX, aY) { - let m1 = [ - [1, 0, 0], - [0, 1, 0], - [aX, aY, 1], - ]; - - setM(this, matrixMultiply(m1, this.m_), false); - } - - rotate(aRot) { - let c = mc(aRot); - let s = ms(aRot); - - let m1 = [ - [c, s, 0], - [-s, c, 0], - [0, 0, 1], - ]; - - setM(this, matrixMultiply(m1, this.m_), false); - } - - scale(aX, aY) { - this.arcScaleX_ *= aX; - this.arcScaleY_ *= aY; - let m1 = [ - [aX, 0, 0], - [0, aY, 0], - [0, 0, 1], - ]; - - setM(this, matrixMultiply(m1, this.m_), true); - } - - transform(m11, m12, m21, m22, dx, dy) { - let m1 = [ - [m11, m12, 0], - [m21, m22, 0], - [dx, dy, 1], - ]; - - setM(this, matrixMultiply(m1, this.m_), true); - } - - setTransform(m11, m12, m21, m22, dx, dy) { - let m = [ - [m11, m12, 0], - [m21, m22, 0], - [dx, dy, 1], - ]; - - setM(this, m, true); - } - - /******** STUBS ********/ - clip() { - // TODO: Implement - } - - arcTo() { - // TODO: Implement - } - - createPattern() { - return new CanvasPattern_(); - } -} - -const kFBANotOverridable = 0x00000400; // indicates the field is read only by the user -const kFBARequired = 0x00000010; // indicates the field is required -const kMinHeight = 20; - -class PDFField { - static tabIndex = 0; - - static isWidgetSupported(field) { - let retVal = false; - - switch(field.fieldType) { - case 'Tx': retVal = true; break; //text input - case 'Btn': - if (field.fieldFlags & 32768) { - field.fieldType = 'Rd'; //radio button - } - else if (field.fieldFlags & 65536) { - field.fieldType = 'Btn'; //push button - } - else { - field.fieldType = 'Cb'; //checkbox - } - retVal = true; - break; - case 'Ch': retVal = true; break; //drop down - case 'Sig': retVal = true; break; //signature - default: - nodeUtil.p2jwarn("Unsupported: field.fieldType of " + field.fieldType); - break; - } - - return retVal; - } - - static isFormElement(field) { - let retVal = false; - switch(field.subtype) { - case 'Widget': retVal = PDFField.isWidgetSupported(field); break; - default: - nodeUtil.p2jwarn("Unsupported: field.type of " + field.subtype); - break; - } - return retVal; - } - - // constructor - constructor(field, viewport, Fields, Boxsets) { - this.field = field; - this.viewport = viewport; - this.Fields = Fields; - this.Boxsets = Boxsets; - } - - // Normalize rectangle rect=[x1, y1, x2, y2] so that (x1,y1) < (x2,y2) - // For coordinate systems whose origin lies in the bottom-left, this - // means normalization to (BL,TR) ordering. For systems with origin in the - // top-left, this means (TL,BR) ordering. - static #normalizeRect(rect) { - const r = rect.slice(0); // clone rect - if (rect[0] > rect[2]) { - r[0] = rect[2]; - r[2] = rect[0]; - } - if (rect[1] > rect[3]) { - r[1] = rect[3]; - r[3] = rect[1]; - } - return r; - } - - #getFieldPosition(field) { - let viewPort = this.viewport; - let fieldRect = viewPort.convertToViewportRectangle(field.rect); - let rect = PDFField.#normalizeRect(fieldRect); - - let height = rect[3] - rect[1]; - if (field.fieldType === 'Tx') { - if (height > kMinHeight + 2) { - rect[1] += 2; - height -= 2; - } - } - else if (field.fieldType !== 'Ch') { //checkbox, radio button, and link button - rect[1] -= 3; - } - - height = (height >= kMinHeight) ? height : kMinHeight; - - return { - x: PDFUnit.toFormX(rect[0]), - y: PDFUnit.toFormY(rect[1]), - w: PDFUnit.toFormX(rect[2] - rect[0]), - h: PDFUnit.toFormY(height) - }; - } - - #getFieldBaseData(field) { - let attributeMask = 0; - //PDF Spec p.676 TABLE 8.70 Field flags common to all field types - if (field.fieldFlags & 0x00000001) { - attributeMask |= kFBANotOverridable; - } - if (field.fieldFlags & 0x00000002) { - attributeMask |= kFBARequired; - } - - let anData = { - id: { Id: field.fullName, EN: 0}, - TI: field.TI, - AM: attributeMask - }; - //PDF Spec p.675: add TU (AlternativeText) fields to provide accessibility info - if (field.alternativeText && field.alternativeText.length > 1) { - anData.TU = field.alternativeText; - } - - if (field.alternativeID && field.alternativeID.length > 1) { - anData.TM = field.alternativeID; - } - - return Object.assign(anData, this.#getFieldPosition(field)); - } - - #addAlpha(field) { - const anData = Object.assign({ - style: 48, - T: { - Name: field.TName || "alpha", - TypeInfo: {} - } - }, this.#getFieldBaseData(field)); - - if (field.MV) { //field attributes: arbitrary mask value - anData.MV = field.MV; - } - if (field.fieldValue) { - anData.V = field.fieldValue; //read-only field value, like "self-prepared" - } - - this.Fields.push(anData); - } - - #addCheckBox(box) { - const anData = Object.assign({ - style: 48, - T: { - Name: "box", - TypeInfo: {} - } - }, this.#getFieldBaseData(box)); - if(box.fieldValue) { - anData.checked = box.fieldValue !== 'Off'; - } - - this.Boxsets.push({boxes:[anData]}); - } - - #addRadioButton(box) { - const anData = Object.assign({ - style: 48, - T: { - Name: "box", - TypeInfo: {} - } - }, this.#getFieldBaseData(box)); - - anData.id.Id = box.value; - if ('checked' in box) { - anData.checked = box.checked; - } - - const rdGroup = this.Boxsets.filter(boxset => ('id' in boxset) && ('Id' in boxset.id) && (boxset.id.Id === box.fullName))[0]; - if ((!!rdGroup) && ('boxes' in rdGroup)) { - rdGroup.boxes.push(anData); - } - else { - this.Boxsets.push({boxes:[anData], id: { Id: box.fullName, EN: 0}}); - } - } - - #addLinkButton(field) { - const anData = Object.assign({ - style: 48, - T: { - Name: "link" - }, - FL: { - form: {Id: field.FL} - } - }, this.#getFieldBaseData(field)); - - this.Fields.push(anData); - } - - #addSelect(field) { - const anData = Object.assign({ - style: 48, - T: { - Name: "alpha", - TypeInfo: {} - } - }, this.#getFieldBaseData(field)); - - anData.w -= 0.5; //adjust combobox width - anData.PL = {V: [], D: []}; - field.value.forEach( (ele, idx) => { - if (Array.isArray(ele)) { - anData.PL.D.push(ele[0]); - anData.PL.V.push(ele[1]); - } else { - anData.PL.D.push(ele); - anData.PL.V.push(ele); - } - }); - - // add field value to the object - if (field.fieldValue) { - anData.V = field.fieldValue; - } - this.Fields.push(anData); - }; - - #addSignature(field) { - const anData = Object.assign({ - style: 48, - T: { - Name: "signature", - TypeInfo: {} - } - }, this.#getFieldBaseData(field)); - - if (field.Sig) { - anData.Sig = {}; - if (field.Sig.Name) anData.Sig.Name = field.Sig.Name; - if (field.Sig.M) anData.Sig.M = PDFUnit.dateToIso8601(field.Sig.M); - if (field.Sig.Location) anData.Sig.Location = field.Sig.Location; - if (field.Sig.Reason) anData.Sig.Reason = field.Sig.Reason; - if (field.Sig.ContactInfo) anData.Sig.ContactInfo = field.Sig.ContactInfo; - } - - this.Fields.push(anData); - } - - // public instance methods - processField() { - this.field.TI = PDFField.tabIndex++; - - switch(this.field.fieldType) { - case 'Tx': this.#addAlpha(this.field); break; - case 'Cb': this.#addCheckBox(this.field); break; - case 'Rd': this.#addRadioButton(this.field);break; - case 'Btn':this.#addLinkButton(this.field); break; - case 'Ch': this.#addSelect(this.field); break; - case 'Sig': this.#addSignature(this.field); break; - } - - this.clean(); - } - - clean() { - delete this.field; - delete this.viewport; - delete this.Fields; - delete this.Boxsets; - } - - //static public method to generate fieldsType object based on parser result - static getAllFieldsTypes(data) { - const isFieldReadOnly = field => { - return (field.AM & kFBANotOverridable) ? true : false; - }; - - const getFieldBase = field => { - return {id: field.id.Id, type: field.T.Name, calc: isFieldReadOnly(field), value: field.V || ""}; - }; - - let retVal = []; - data.Pages.forEach( page => { - page.Boxsets.forEach( boxsets => { - if (boxsets.boxes.length > 1) { //radio button - boxsets.boxes.forEach( box => { - retVal.push({id: boxsets.id.Id, type: "radio", calc: isFieldReadOnly(box), value: box.id.Id}); - }); - } - else { //checkbox - retVal.push(getFieldBase(boxsets.boxes[0])); - } - }); - - page.Fields.forEach(field => retVal.push(getFieldBase(field))); - - }); - return retVal; - } -} - -//BEGIN - MQZ 9/19/2012. Helper functions to parse acroForm elements -function setupRadioButton(annotation, item) { - let asName = ''; - //PDF Spec p.689: parent item's DV holds the item's value that is selected by default - let po = annotation.get('Parent'); - if (po) { - po.forEach(function(key, val){ - if (key === 'DV') { - asName = val.name || ''; - } - else if (key === 'TU') { - //radio buttons use the alternative text from the parent - item.alternativeText = val; - } else if( key == 'TM') { - item.alternativeID = val; - } - }); - } - - //PDF Spec p.606: get appearance dictionary - let ap = annotation.get('AP'); - //PDF Spec p.614 get normal appearance - let nVal = ap.get('N'); - //PDF Spec p.689 - nVal.forEach(function (key, value) { - if (key.toLowerCase() != "off") { - //value if selected - item.value = key; //export value - item.checked = (key === asName); //initial selection state - } - }); - - if (!item.value) - item.value = "off"; -} - -function setupPushButton(annotation, item) { - //button label: PDF Spec p.640 - let mk = annotation.get('MK'); - if(mk) { - item.value = mk.get('CA') || ''; - } - - //button action: url when mouse up: PDF Spec:p.642 - item.FL = ""; - let ap = annotation.get('A'); - if (ap) { - let sp = ap.get('S'); - item.FL = ap.get(sp.name); - } -} - -function setupCheckBox(annotation, item) { - //PDF Spec p.606: get appearance dictionary - let ap = annotation.get('AP'); - //PDF Spec p.614 get normal appearance - let nVal = ap.get('N'); - - //PDF Spec p.689 - let i = 0; - nVal.forEach(function (key, value) { - i++; - if (i == 1) //initial selection state - item.value = key; - }); -} - -function setupDropDown(annotation, item) { - //PDF Spec p.688 - item.value = annotation.get('Opt') || []; -} - -function setupFieldAttributes(annotation, item) { - //MQZ. Jan.03.2013. additional-actions dictionary - //PDF Spec P.648. 8.5.2. Trigger Events - let aa = annotation.get('AA'); - if (!aa) { - return; - } - - //PDF Spec p.651 get format dictionary - let nVal = aa.get('F'); - if (!nVal) { - nVal = aa.get('K'); - if (!nVal) - return; - } - - nVal.forEach(function (key, value) { - if (key === "JS") { - processFieldAttribute(value, item); - } - }); -} - -const AFSpecial_Format = ['zip', 'zip', 'phone', 'ssn', '']; -// let AFNumber_Format = ['nDec', 'sepStyle', 'negStyle', 'currStyle', 'strCurrency', 'bCurrencyPrepend']; -//– nDec is the number of places after the decimal point; -//– sepStyle is an integer denoting whether to use a separator or not. If sepStyle=0, use commas. If sepStyle=1, do not separate. -//– negStyle is the formatting used for negative numbers: 0 = MinusBlack, 1 = Red, 2 = ParensBlack, 3 = ParensRed -//– currStyle is the currency style - not used -//- strCurrency is the currency symbol -//– bCurrencyPrepend -// let AFDate_FormatEx = ["m/d", "m/d/yy", "mm/dd/yy", "mm/yy", "d-mmm", "d-mmm-yy", "dd-mmm-yy", "yymm-dd", "mmm-yy", "mmmm-yy", "mmm d, yyyy", "mmmm d, yyyy", "m/d/yy h:MM tt", "m/d/yy HH:MM"]; - -function processFieldAttribute(jsFuncName, item) { - if (item.hasOwnProperty('TName')) - return; - - if(!jsFuncName.split) - return; - - let vParts = jsFuncName.split('('); - if (vParts.length !== 2) - return; - - let funcName = vParts[0]; - let funcParam = vParts[1].split(')')[0]; - - switch (funcName) { - case 'AFSpecial_Format': - item.TName = AFSpecial_Format[Number(funcParam)]; - break; - case 'AFNumber_Format': -// nfs = funcParam.split(','); -//set the Money fields to use the Number type with no decimal places after, no commas, and bCurrencyPrepend is set as true; (o use a negative sign (fits the PDF layout and our print formatting as well). -// if (nfs[0] === '0' && nfs[1] === '1' && nfs[5]) -// item.TName = 'money'; -// else - item.TName = 'number'; - break; - case 'AFDate_FormatEx': - item.TName = 'date'; - item.MV = funcParam.replace(/^'+|^"+|'+$|"+$/g,''); //mask value - break; - case 'AFSpecial_KeystrokeEx': //special format: "arbitrary mask" - let maskValue = funcParam.replace(/^'+|^"+|'+$|"+$/g,''); //mask value - if ((!!maskValue) && maskValue.length > 0 && maskValue.length < 64) { - item.TName = 'mask'; //fixed length input - item.MV = maskValue; - } - break; - case 'AFPercent_Format': - item.TName = 'percent'; //funcParam => 2, 0, will specified how many decimal places - break; - } -} - -function setupSignature(annotation, item) { - //PDF Spec p.695: field value is signature dict if signed - let sig = annotation.get('V'); - if (!sig) return; - - //PDF Spec p.728: get signature information - item.Sig = {}; - let name = sig.get('Name'); - if (name) item.Sig.Name = name; - let time = sig.get('M'); - if (time) item.Sig.M = time; - let location = sig.get('Location'); - if (location) item.Sig.Location = location; - let reason = sig.get('Reason'); - if (reason) item.Sig.Reason = reason; - let contactInfo = sig.get('ContactInfo'); - if (contactInfo) item.Sig.ContactInfo = contactInfo; -} - -//END - MQZ 9/19/2012. Helper functions to parse acroForm elements - -class PDFAnno { - static processAnnotation(annotation, item) { - if (item.fieldType == 'Btn') { //PDF Spec p.675 - if (item.fieldFlags & 32768) { - setupRadioButton(annotation, item); - } - else if (item.fieldFlags & 65536) { - setupPushButton(annotation, item); - } - else { - setupCheckBox(annotation, item); - } - } - else if (item.fieldType == 'Ch') { - setupDropDown(annotation, item); - } - else if (item.fieldType == 'Tx') { - setupFieldAttributes(annotation, item); - } - else if (item.fieldType === 'Sig') { - setupSignature(annotation, item); - } - else { - nodeUtil.p2jwarn("Unknown fieldType: ", item); - } - } -} - -class PDFImage { - #_src = ''; - #_onload = null; - - set onload(val) { - this.#_onload = typeof val === 'function' ? val : null; - } - - get onload() { - return this.#_onload; - } - - set src(val) { - this.#_src = val; - if (this.#_onload) this.#_onload(); - } - - get src() { - return this.#_src; - } - - btoa(val) { - if (typeof window === 'undefined') { - return (new Buffer.from(val, 'ascii')).toString('base64'); - } - else if (typeof window.btoa === 'function') - return window.btoa(val); - - return ""; - } - -} - -class PTIXmlParser { - xmlData = null; - ptiPageArray = []; - - // constructor - constructor() { - this.xmlData = null; - this.ptiPageArray = []; - } - - parseXml(filePath, callback) { - fs.readFile(filePath, 'utf8', (err, data) => { - if (err) { - callback(err); - } - else { - this.xmlData = data; - - var parser = new xmldom.DOMParser(); - var dom = parser.parseFromString(this.xmlData); - var root = dom.documentElement; - - var xmlFields = root.getElementsByTagName("field"); - var fields = []; - - for(var i=0;i - (preContent += fs.readFileSync(baseDir + fileName, 'utf8')), - '' -); -eval(_baseCode); - -////////////////////////////////start of helper classes -class PDFPageParser { - //static - static RenderingStates = { - INITIAL: 0, - RUNNING: 1, - PAUSED: 2, - FINISHED: 3, - }; - - //public - id = -1; - pdfPage = null; - ptiParser = null; - scale = 0; - viewport = null; - renderingState = -1; - - Fields = null; - Boxsets = null; - ctxCanvas = null; - - #_addField(field) { - if (!PDFField.isFormElement(field)) { - nodeUtil.p2jwarn('NOT valid form element', field); - return; - } - - const oneField = new PDFField( - field, - this.viewport, - this.Fields, - this.Boxsets - ); - oneField.processField(); - } - - // constructor - constructor(pdfPage, id, scale, ptiParser) { - // public, this instance copies - this.id = id; - this.pdfPage = pdfPage; - this.ptiParser = ptiParser; - - this.scale = scale || 1.0; - - //leave out the 2nd parameter in order to use page's default rotation (for both portrait and landscape form) - this.viewport = this.pdfPage.getViewport(this.scale); - - this.renderingState = PDFPageParser.RenderingStates.INITIAL; - - //form elements other than radio buttons and check boxes - this.Fields = []; - //form elements: radio buttons and check boxes - this.Boxsets = []; - this.ctxCanvas = {}; - } - - get width() { - return PDFUnit.toFormX(this.viewport.width); - } - get height() { - return PDFUnit.toFormY(this.viewport.height); - } - get HLines() { - return this.ctxCanvas.HLines; - } - get VLines() { - return this.ctxCanvas.VLines; - } - get Fills() { - return this.ctxCanvas.Fills; - } - get Texts() { - return this.ctxCanvas.Texts; - } - - destroy() { - this.pdfPage.destroy(); - this.pdfPage = null; - - this.ptiParser = null; - this.Fields = null; - this.Boxsets = null; - this.ctxCanvas = null; - } - - getPagePoint(x, y) { - return this.viewport.convertToPdfPoint(x, y); - } - - parsePage(callback, errorCallBack) { - if (this.renderingState !== PDFPageParser.RenderingStates.INITIAL) { - errorCallBack('Must be in new state before drawing'); - return; - } - - this.renderingState = PDFPageParser.RenderingStates.RUNNING; - - const canvas = createScratchCanvas(1, 1); - const ctx = canvas.getContext('2d'); - - function pageViewDrawCallback(error) { - this.renderingState = PDFPageParser.RenderingStates.FINISHED; - - if (error) { - console.error(error); - errorCallBack(`Error: Page ${this.id + 1}: ${error.message}`); - } else { - if (this.ptiParser) { - const extraFields = this.ptiParser.getFields( - parseInt(this.id) + 1 - ); - extraFields.forEach((field) => this.#_addField(field)); - } - - this.ctxCanvas = ctx.canvas; - this.stats = this.pdfPage.stats; - - nodeUtil.p2jinfo(`Success: Page ${this.id + 1}`); - callback(); - } - } - - const renderContext = { - canvasContext: ctx, - viewport: this.viewport, - }; - - this.pdfPage.render(renderContext).then( - (data) => { - this.pdfPage.getAnnotations().then( - (fields) => { - fields.forEach((field) => this.#_addField(field)); - pageViewDrawCallback.call(this, null); - }, - (err) => errorCallBack('pdfPage.getAnnotations error:' + err) - ); - }, - (err) => pageViewDrawCallback.call(this, err) - ); - } -} - -////////////////////////////////Start of Node.js Module -class PDFJSClass extends events.EventEmitter { - pdfDocument = null; - pages = null; - rawTextContents = null; - - needRawText = null; - - // constructor - constructor(needRawText) { - super(); - - // public, this instance copies - this.pdfDocument = null; - this.pages = []; - this.rawTextContents = []; - - this.needRawText = needRawText; - } - - raiseErrorEvent(errMsg) { - console.error(errMsg); - process.nextTick(() => this.emit('pdfjs_parseDataError', errMsg)); - // this.emit("error", errMsg); - return errMsg; - } - - raiseReadyEvent(data) { - process.nextTick(() => this.emit('pdfjs_parseDataReady', data)); - return data; - } - - parsePDFData(arrayBuffer, password) { - this.pdfDocument = null; - - const parameters = { password: password, data: arrayBuffer }; - PDFJS.getDocument(parameters).then( - (pdfDocument) => this.load(pdfDocument, 1), - (error) => this.raiseErrorEvent(error) - ); - } - - tryLoadFieldInfoXML(pdfFilePath) { - const _sufInfo = '_fieldInfo.xml'; - const fieldInfoXMLPath = pdfFilePath.replace('.pdf', _sufInfo); - if ( - fieldInfoXMLPath.indexOf(_sufInfo) < 1 || - !fs.existsSync(fieldInfoXMLPath) - ) { - return; - } - nodeUtil.p2jinfo('About to load fieldInfo XML : ' + fieldInfoXMLPath); - - this.ptiParser = new PTIXmlParser(); - this.ptiParser.parseXml(fieldInfoXMLPath, (err) => { - if (err) { - nodeUtil.p2jwarn('fieldInfo XML Error: ' + JSON.stringify(err)); - this.ptiParser = null; - } else { - nodeUtil.p2jinfo('fieldInfo XML loaded.'); - } - }); - } - - load(pdfDocument, scale) { - this.pdfDocument = pdfDocument; - - return this.loadMetaData().then( - () => this.loadPages(), - (error) => this.raiseErrorEvent('loadMetaData error: ' + error) - ); - } - - loadMetaData() { - return this.pdfDocument.getMetadata().then( - (data) => { - this.documentInfo = data.info; - this.metadata = data.metadata?.metadata ?? {}; - this.parseMetaData(); - }, - (error) => - this.raiseErrorEvent('pdfDocument.getMetadata error: ' + error) - ); - } - - parseMetaData() { - const meta = { - Transcoder: _PARSER_SIG, - Meta: { ...this.documentInfo, Metadata: this.metadata }, - }; - this.raiseReadyEvent(meta); - this.emit('readable', meta); - } - - loadPages() { - const pagesCount = this.pdfDocument.numPages; - const pagePromises = []; - for (let i = 1; i <= pagesCount; i++) - pagePromises.push(this.pdfDocument.getPage(i)); - - const pagesPromise = PDFJS.Promise.all(pagePromises); - - nodeUtil.p2jinfo('PDF loaded. pagesCount = ' + pagesCount); - - return pagesPromise.then( - (promisedPages) => this.parsePage(promisedPages, 0, 1.5), - (error) => this.raiseErrorEvent('pagesPromise error: ' + error) - ); - } - - parsePage(promisedPages, id, scale) { - nodeUtil.p2jinfo('start to parse page:' + (id + 1)); - - const pdfPage = promisedPages[id]; - const pageParser = new PDFPageParser(pdfPage, id, scale, this.ptiParser); - - function continueOnNextPage() { - nodeUtil.p2jinfo('complete parsing page:' + (id + 1)); - if (id === this.pdfDocument.numPages - 1) { - this.raiseReadyEvent({ Pages: this.pages }); - //v1.1.2: signal end of parsed data with null - process.nextTick(() => this.raiseReadyEvent(null)); - this.emit('data', null); - } else { - process.nextTick(() => this.parsePage(promisedPages, ++id, scale)); - } - } - - pageParser.parsePage( - (data) => { - const page = { - Width: pageParser.width, - Height: pageParser.height, - HLines: pageParser.HLines, - VLines: pageParser.VLines, - Fills: pageParser.Fills, - //needs to keep current default output format, text content will output to a separate file if '-c' command line argument is set - // Content:pdfPage.getTextContent(), - Texts: pageParser.Texts, - Fields: pageParser.Fields, - Boxsets: pageParser.Boxsets, - }; - - this.pages.push(page); - this.emit('data', page); - - if (this.needRawText) { - pdfPage.getTextContent().then( - (textContent) => { - this.rawTextContents.push(textContent); - nodeUtil.p2jinfo( - 'complete parsing raw text content:' + (id + 1) - ); - continueOnNextPage.call(this); - }, - (error) => - this.raiseErrorEvent( - 'pdfPage.getTextContent error: ' + error - ) - ); - } else { - continueOnNextPage.call(this); - } - }, - (errMsg) => this.raiseErrorEvent(errMsg) - ); - } - - getRawTextContent() { - let retVal = ''; - if (!this.needRawText) return retVal; - - this.rawTextContents.forEach((textContent, index) => { - let prevText = null; - textContent.bidiTexts.forEach((textObj, idx) => { - if (prevText) { - if (Math.abs(textObj.y - prevText.y) <= 9) { - prevText.str += textObj.str; - } else { - retVal += prevText.str + '\r\n'; - prevText = textObj; - } - } else { - prevText = textObj; - } - }); - if (prevText) { - retVal += prevText.str; - } - retVal += - '\r\n----------------Page (' + - index + - ') Break----------------\r\n'; - }); - - return retVal; - } - - getAllFieldsTypes() { - return PDFField.getAllFieldsTypes({ Pages: this.pages || [] }); - } - - getMergedTextBlocksIfNeeded() { - for (let p = 0; p < this.pages.length; p++) { - let prevText = null; - let page = this.pages[p]; - - page.Texts.sort(PDFFont.compareBlockPos); - page.Texts = page.Texts.filter((t, j) => { - let isDup = - j > 0 && PDFFont.areDuplicateBlocks(page.Texts[j - 1], t); - if (isDup) { - nodeUtil.p2jinfo( - 'skipped: dup text block: ' + decodeURIComponent(t.R[0].T) - ); - } - return !isDup; - }); - - for (let i = 0; i < page.Texts.length; i++) { - let text = page.Texts[i]; - - if (prevText) { - if ( - PDFFont.areAdjacentBlocks(prevText, text) && - PDFFont.haveSameStyle(prevText, text) - ) { - let preT = decodeURIComponent(prevText.R[0].T); - let curT = decodeURIComponent(text.R[0].T); - - prevText.R[0].T += text.R[0].T; - prevText.w += text.w; - text.merged = true; - - let mergedText = decodeURIComponent(prevText.R[0].T); - nodeUtil.p2jinfo( - `merged text block: ${preT} + ${curT} => ${mergedText}` - ); - prevText = null; //yeah, only merge two blocks for now - } else { - prevText = text; - } - } else { - prevText = text; - } - } - - page.Texts = page.Texts.filter((t) => !t.merged); - } - - return { Pages: this.pages }; - } - - destroy() { - this.removeAllListeners(); - - if (this.pdfDocument) this.pdfDocument.destroy(); - this.pdfDocument = null; - - this.pages = null; - this.rawTextContents = null; - } -} - -class ParserStream extends stream.Transform { - static createContentStream(jsonObj) { - const rStream = new stream.Readable({objectMode: true}); - rStream.push(jsonObj); - rStream.push(null); - return rStream; - } - - static createOutputStream(outputPath, resolve, reject) { - const outputStream = fs.createWriteStream(outputPath); - outputStream.on('finish', () => resolve(outputPath)); - outputStream.on('error', err => reject(err) ); - return outputStream; - } - - #pdfParser = null; - #chunks = []; - #parsedData = {Pages:[]}; - #_flush_callback = null; - - constructor(pdfParser, options) { - super(options); - this.#pdfParser = pdfParser; - - this.#chunks = []; - - // this.#pdfParser.on("pdfParser_dataReady", evtData => { - // this.push(evtData); - // this.#_flush_callback(); - // this.emit('end', null); - // }); - this.#pdfParser.on("readable", meta => this.#parsedData = {...meta, Pages:[]}); - this.#pdfParser.on("data", page => { - if (!page) { - this.push(this.#parsedData); - this.#_flush_callback(); - } - else - this.#parsedData.Pages.push(page); - }); - } - - //implements transform stream - _transform(chunk, enc, callback) { - this.#chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, enc)); - callback(); - } - - _flush(callback) { - this.#_flush_callback = callback; - this.#pdfParser.parseBuffer(Buffer.concat(this.#chunks)); - } - - _destroy() { - super.removeAllListeners(); - this.#pdfParser = null; - this.#chunks = []; - } -} - - -class StringifyStream extends stream.Transform { - constructor(options) { - super(options); - - this._readableState.objectMode = false; - this._writableState.objectMode = true; - } - - _transform(obj, encoding, callback){ - this.push(JSON.stringify(obj)); - callback(); - } -} - -class PDFParser extends events.EventEmitter { // inherit from event emitter - //public static - static get colorDict() {return kColors; } - static get fontFaceDict() { return kFontFaces; } - static get fontStyleDict() { return kFontStyles; } - - //private static - static #maxBinBufferCount = 10; - static #binBuffer = {}; - - //private - #password = ""; - - #context = null; // service context object, only used in Web Service project; null in command line - - #pdfFilePath = null; //current PDF file to load and parse, null means loading/parsing not started - #pdfFileMTime = null; // last time the current pdf was modified, used to recognize changes and ignore cache - #data = null; //if file read success, data is PDF content; if failed, data is "err" object - #PDFJS = null; //will be initialized in constructor - #processFieldInfoXML = false;//disable additional _fieldInfo.xml parsing and merging (do NOT set to true) - - // constructor - constructor(context, needRawText, password) { - //call constructor for super class - super(); - - // private - // service context object, only used in Web Service project; null in command line - this.#context = context; - - this.#pdfFilePath = null; //current PDF file to load and parse, null means loading/parsing not started - this.#pdfFileMTime = null; // last time the current pdf was modified, used to recognize changes and ignore cache - this.#data = null; //if file read success, data is PDF content; if failed, data is "err" object - this.#processFieldInfoXML = false;//disable additional _fieldInfo.xml parsing and merging (do NOT set to true) - - this.#PDFJS = new PDFJSClass(needRawText); - this.#password = password; - } - - //private methods, needs to invoked by [funcName].call(this, ...) - #onPDFJSParseDataReady(data) { - if (!data) { //v1.1.2: data===null means end of parsed data - nodeUtil.p2jinfo("PDF parsing completed."); - this.emit("pdfParser_dataReady", this.#data); - } - else { - this.#data = {...this.#data, ...data}; - } - } - - #onPDFJSParserDataError(err) { - this.#data = null; - this.emit("pdfParser_dataError", {"parserError": err}); - // this.emit("error", err); - } - - #startParsingPDF(buffer) { - this.#data = {}; - - this.#PDFJS.on("pdfjs_parseDataReady", data => this.#onPDFJSParseDataReady(data)); - this.#PDFJS.on("pdfjs_parseDataError", err => this.#onPDFJSParserDataError(err)); - - //v1.3.0 the following Readable Stream-like events are replacement for the top two custom events - this.#PDFJS.on("readable", meta => this.emit("readable", meta)); - this.#PDFJS.on("data", data => this.emit("data", data)); - this.#PDFJS.on("error", err => this.#onPDFJSParserDataError(err)); - - this.#PDFJS.parsePDFData(buffer || PDFParser.#binBuffer[this.binBufferKey], this.#password); - } - - #processBinaryCache() { - if (this.binBufferKey in PDFParser.#binBuffer) { - this.#startParsingPDF(); - return true; - } - - const allKeys = Object.keys(PDFParser.#binBuffer); - if (allKeys.length > PDFParser.#maxBinBufferCount) { - const idx = this.id % PDFParser.#maxBinBufferCount; - const key = allKeys[idx]; - PDFParser.#binBuffer[key] = null; - delete PDFParser.#binBuffer[key]; - - nodeUtil.p2jinfo("re-cycled cache for " + key); - } - - return false; - } - - //public getter - get data() { return this.#data; } - get binBufferKey() { return this.#pdfFilePath + this.#pdfFileMTime; } - - //public APIs - createParserStream() { - return new ParserStream(this, {objectMode: true, bufferSize: 64 * 1024}); - } - - async loadPDF(pdfFilePath, verbosity) { - nodeUtil.verbosity(verbosity || 0); - nodeUtil.p2jinfo("about to load PDF file " + pdfFilePath); - - this.#pdfFilePath = pdfFilePath; - - try { - this.#pdfFileMTime = fs.statSync(pdfFilePath).mtimeMs; - if (this.#processFieldInfoXML) { - this.#PDFJS.tryLoadFieldInfoXML(pdfFilePath); - } - - if (this.#processBinaryCache()) - return; - - PDFParser.#binBuffer[this.binBufferKey] = await promises.readFile(pdfFilePath); - nodeUtil.p2jinfo(`Load OK: ${pdfFilePath}`); - this.#startParsingPDF(); - } - catch(err) { - nodeUtil.p2jerror(`Load Failed: ${pdfFilePath} - ${err}`); - this.emit("pdfParser_dataError", err); - } - } - - // Introduce a way to directly process buffers without the need to write it to a temporary file - parseBuffer(pdfBuffer) { - this.#startParsingPDF(pdfBuffer); - } - - getRawTextContent() { return this.#PDFJS.getRawTextContent(); } - getRawTextContentStream() { return ParserStream.createContentStream(this.getRawTextContent()); } - - getAllFieldsTypes() { return this.#PDFJS.getAllFieldsTypes(); }; - getAllFieldsTypesStream() { return ParserStream.createContentStream(this.getAllFieldsTypes()); } - - getMergedTextBlocksIfNeeded() { return this.#PDFJS.getMergedTextBlocksIfNeeded(); } - getMergedTextBlocksStream() { return ParserStream.createContentStream(this.getMergedTextBlocksIfNeeded()) } - - destroy() { // invoked with stream transform process - super.removeAllListeners(); - - //context object will be set in Web Service project, but not in command line utility - if (this.#context) { - this.#context.destroy(); - this.#context = null; - } - - this.#pdfFilePath = null; - this.#pdfFileMTime = null; - this.#data = null; - this.#processFieldInfoXML = false;//disable additional _fieldInfo.xml parsing and merging (do NOT set to true) - - this.#PDFJS.destroy(); - this.#PDFJS = null; - } -} - -module.exports = PDFParser; diff --git a/pdfparser.js b/pdfparser.js index e88b6775..27929ad4 100644 --- a/pdfparser.js +++ b/pdfparser.js @@ -4,150 +4,167 @@ import { readFile } from "fs/promises"; import { EventEmitter } from "events"; import PDFJS from "./lib/pdf.js"; -import {ParserStream} from "./lib/parserstream.js"; -import {kColors, kFontFaces, kFontStyles} from "./lib/pdfconst.js"; +import { ParserStream, StringifyStream } from "./lib/parserstream.js"; +import { kColors, kFontFaces, kFontStyles } from "./lib/pdfconst.js"; +import { pkInfo, _PARSER_SIG } from "./lib/pkinfo.js"; /** * Class representing a PDF Parser. * @extends EventEmitter */ -export default class PDFParser extends EventEmitter { - /** - * Static method to retrieve color dictionary. - * @returns {object} Color dictionary - */ - static get colorDict() { return kColors; } - - /** - * Static method to retrieve font face dictionary. - * @returns {object} Font face dictionary - */ - static get fontFaceDict() { return kFontFaces; } - - /** - * Static method to retrieve font style dictionary. - * @returns {object} Font style dictionary - */ - static get fontStyleDict() { return kFontStyles; } - - static #maxBinBufferCount = 10; - static #binBuffer = {}; - - #password = ""; - #context = null; // service context object, only used in Web Service project; null in command line #pdfFilePath = null; - #pdfFilePath = null; //current PDF file to load and parse, null means loading/parsing not started #data = null; - #pdfFileMTime = null; // last time the current pdf was modified, used to recognize changes and ignore cache #PDFJS = null; - #data = null; //if file read success, data is PDF content; if failed, data is "err" object #processFieldInfoXML = false; - #PDFJS = null; //will be initialized in constructor - #processFieldInfoXML = false; //disable additional _fieldInfo.xml parsing and merging (do NOT set to true) - - /** - * PDFParser constructor. - * @param {object} context - The context object (only used in Web Service project); null in command line - * @param {boolean} needRawText - Whether raw text is needed or not - * @param {string} password - The password for PDF file +class PDFParser extends EventEmitter { + /** + * Static method to retrieve color dictionary. + * @returns {object} Color dictionary + */ + static get colorDict() { + return kColors; + } + + /** + * Static method to retrieve font face dictionary. + * @returns {object} Font face dictionary + */ + static get fontFaceDict() { + return kFontFaces; + } + + /** + * Static method to retrieve font style dictionary. + * @returns {object} Font style dictionary + */ + static get fontStyleDict() { + return kFontStyles; + } + + static #maxBinBufferCount = 10; + static #binBuffer = {}; + + #password = ""; + #context = null; // service context object, only used in Web Service project; null in command line #pdfFilePath = null; + #pdfFilePath = null; //current PDF file to load and parse, null means loading/parsing not started #data = null; + #pdfFileMTime = null; // last time the current pdf was modified, used to recognize changes and ignore cache #PDFJS = null; + #data = null; //if file read success, data is PDF content; if failed, data is "err" object #processFieldInfoXML = false; + #PDFJS = null; //will be initialized in constructor + #processFieldInfoXML = false; //disable additional _fieldInfo.xml parsing and merging (do NOT set to true) + + /** + * PDFParser constructor. + * @param {object} context - The context object (only used in Web Service project); null in command line + * @param {boolean} needRawText - Whether raw text is needed or not + * @param {string} password - The password for PDF file * @info Private methods accessible using the [funcName].call(this, ...) syntax - */ - constructor(context, needRawText, password) { - super(); - this.#context = context; - this.#pdfFilePath = null; //current PDF file to load and parse, null means loading/parsing not started this.#pdfFileMTime = null; - this.#pdfFileMTime = null; // last time the current pdf was modified, used to recognize changes and ignore cache this.#data = null; - this.#data = null; //if file read success, data is PDF content; if failed, data is "err" object this.#processFieldInfoXML = false; - this.#processFieldInfoXML = false;//disable additional _fieldInfo.xml parsing and merging (do NOT set to true) - - this.#PDFJS = new PDFJS(needRawText); - this.#password = password; - } - - /** - * @private - * @param {object} data - The parsed data - */ - #onPDFJSParseDataReady(data) { - if (!data) { - nodeUtil.p2jinfo("PDF parsing completed."); - this.emit("pdfParser_dataReady", this.#data); - } - else { - this.#data = { ...this.#data, ...data }; - } - } - - /** - * @private - * @param {Error} err - The error object - */ - #onPDFJSParserDataError(err) { - this.#data = null; - this.emit("pdfParser_dataError", { "parserError": err }); - } - - /** - * @private - * @param {Buffer} buffer - The PDF buffer - */ - #startParsingPDF(buffer) { - this.#data = {}; - this.#PDFJS.on("pdfjs_parseDataReady", data => this.#onPDFJSParseDataReady(data)); - this.#PDFJS.on("pdfjs_parseDataError", err => this.#onPDFJSParserDataError(err)); - - //v1.3.0 the following Readable Stream-like events are replacement for the top two custom events - this.#PDFJS.on("readable", meta => this.emit("readable", meta)); - this.#PDFJS.on("data", data => this.emit("data", data)); - this.#PDFJS.on("error", err => this.#onPDFJSParserDataError(err)); - - this.#PDFJS.parsePDFData(buffer || PDFParser.#binBuffer[this.binBufferKey], this.#password); - } - - /** - * @private - * @returns {boolean} - */ - #processBinaryCache() { + */ + constructor(context, needRawText, password) { + super(); + this.#context = context; + this.#pdfFilePath = null; //current PDF file to load and parse, null means loading/parsing not started this.#pdfFileMTime = null; + this.#pdfFileMTime = null; // last time the current pdf was modified, used to recognize changes and ignore cache this.#data = null; + this.#data = null; //if file read success, data is PDF content; if failed, data is "err" object this.#processFieldInfoXML = false; + this.#processFieldInfoXML = false; //disable additional _fieldInfo.xml parsing and merging (do NOT set to true) + + this.#PDFJS = new PDFJS(needRawText); + this.#password = password; + } + + /** + * @private + * @param {object} data - The parsed data + */ + #onPDFJSParseDataReady(data) { + if (!data) { + nodeUtil.p2jinfo("PDF parsing completed."); + this.emit("pdfParser_dataReady", this.#data); + } else { + this.#data = { ...this.#data, ...data }; + } + } + + /** + * @private + * @param {Error} err - The error object + */ + #onPDFJSParserDataError(err) { + this.#data = null; + this.emit("pdfParser_dataError", { parserError: err }); + } + + /** + * @private + * @param {Buffer} buffer - The PDF buffer + */ + #startParsingPDF(buffer) { + this.#data = {}; + this.#PDFJS.on("pdfjs_parseDataReady", (data) => + this.#onPDFJSParseDataReady(data) + ); + this.#PDFJS.on("pdfjs_parseDataError", (err) => + this.#onPDFJSParserDataError(err) + ); + + //v1.3.0 the following Readable Stream-like events are replacement for the top two custom events + this.#PDFJS.on("readable", (meta) => this.emit("readable", meta)); + this.#PDFJS.on("data", (data) => this.emit("data", data)); + this.#PDFJS.on("error", (err) => this.#onPDFJSParserDataError(err)); + + this.#PDFJS.parsePDFData( + buffer || PDFParser.#binBuffer[this.binBufferKey], + this.#password + ); + } + + /** + * @private + * @returns {boolean} + */ + #processBinaryCache() { if (this.binBufferKey in PDFParser.#binBuffer) { this.#startParsingPDF(); return true; } - - const allKeys = Object.keys(PDFParser.#binBuffer); - if (allKeys.length > PDFParser.#maxBinBufferCount) { - const idx = this.id % PDFParser.#maxBinBufferCount; - const key = allKeys[idx]; - PDFParser.#binBuffer[key] = null; - delete PDFParser.#binBuffer[key]; - nodeUtil.p2jinfo("re-cycled cache for " + key); - } + const allKeys = Object.keys(PDFParser.#binBuffer); + if (allKeys.length > PDFParser.#maxBinBufferCount) { + const idx = this.id % PDFParser.#maxBinBufferCount; + const key = allKeys[idx]; + PDFParser.#binBuffer[key] = null; + delete PDFParser.#binBuffer[key]; + + nodeUtil.p2jinfo("re-cycled cache for " + key); + } return false; } - /** - * Getter for #data - * @returns {object|null} Data - */ - get data() { return this.#data; } + /** + * Getter for #data + * @returns {object|null} Data + */ + get data() { + return this.#data; + } - /** - * Getter for binBufferKey - * @returns {string} The binBufferKey - */ - get binBufferKey() { return this.#pdfFilePath + this.#pdfFileMTime; } + /** + * Getter for binBufferKey + * @returns {string} The binBufferKey + */ + get binBufferKey() { + return this.#pdfFilePath + this.#pdfFileMTime; + } - /** - * Creates a parser stream - * @returns {ParserStream} A new parser stream - */ - createParserStream() { - return new ParserStream(this, { objectMode: true, bufferSize: 64 * 1024 }); - } + /** + * Creates a parser stream + * @returns {ParserStream} A new parser stream + */ + createParserStream() { + return new ParserStream(this, { objectMode: true, bufferSize: 64 * 1024 }); + } /** - * Asynchronously load a PDF from a file path. - * @param {string} pdfFilePath - Path of the PDF file - * @param {number} verbosity - Verbosity level - */ + * Asynchronously load a PDF from a file path. + * @param {string} pdfFilePath - Path of the PDF file + * @param {number} verbosity - Verbosity level + */ async loadPDF(pdfFilePath, verbosity) { nodeUtil.verbosity(verbosity || 0); nodeUtil.p2jinfo("about to load PDF file " + pdfFilePath); @@ -155,75 +172,86 @@ export default class PDFParser extends EventEmitter { this.#pdfFilePath = pdfFilePath; try { - this.#pdfFileMTime = fs.statSync(pdfFilePath).mtimeMs; - if (this.#processFieldInfoXML) { - this.#PDFJS.tryLoadFieldInfoXML(pdfFilePath); - } - - if (this.#processBinaryCache()) - return; - - PDFParser.#binBuffer[this.binBufferKey] = await readFile(pdfFilePath); - nodeUtil.p2jinfo(`Load OK: ${pdfFilePath}`); - this.#startParsingPDF(); - } - catch(err) { - nodeUtil.p2jerror(`Load Failed: ${pdfFilePath} - ${err}`); - this.emit("pdfParser_dataError", err); - } - } - - /** - * Parse PDF buffer. Introduce a way to directly process buffers without the need to write it to a temporary file - * @param {Buffer} pdfBuffer - PDF buffer - * @param {number} verbosity - Verbosity level - */ + this.#pdfFileMTime = fs.statSync(pdfFilePath).mtimeMs; + if (this.#processFieldInfoXML) { + this.#PDFJS.tryLoadFieldInfoXML(pdfFilePath); + } + + if (this.#processBinaryCache()) return; + + PDFParser.#binBuffer[this.binBufferKey] = await readFile(pdfFilePath); + nodeUtil.p2jinfo(`Load OK: ${pdfFilePath}`); + this.#startParsingPDF(); + } catch (err) { + nodeUtil.p2jerror(`Load Failed: ${pdfFilePath} - ${err}`); + this.emit("pdfParser_dataError", err); + } + } + + /** + * Parse PDF buffer. Introduce a way to directly process buffers without the need to write it to a temporary file + * @param {Buffer} pdfBuffer - PDF buffer + * @param {number} verbosity - Verbosity level + */ parseBuffer(pdfBuffer, verbosity) { nodeUtil.verbosity(verbosity || 0); this.#startParsingPDF(pdfBuffer); } - /** - * Retrieve raw text content from PDF. - * @returns {string} Raw text content - */ - getRawTextContent() { return this.#PDFJS.getRawTextContent(); } - - /** - * Retrieve raw text content stream. - * @returns {Stream} Raw text content stream - */ - getRawTextContentStream() { return ParserStream.createContentStream(this.getRawTextContent()); } - - /** - * Retrieve all field types. - * @returns {object[]} All field types - */ - getAllFieldsTypes() { return this.#PDFJS.getAllFieldsTypes(); } - - /** - * Retrieve all field types stream. - * @returns {Stream} All field types stream - */ - getAllFieldsTypesStream() { return ParserStream.createContentStream(this.getAllFieldsTypes()); } - - /** - * Retrieve merged text blocks if needed. - * @returns {object} Merged text blocks - */ - getMergedTextBlocksIfNeeded() { return this.#PDFJS.getMergedTextBlocksIfNeeded(); } - - /** - * Retrieve merged text blocks stream. - * @returns {Stream} Merged text blocks stream - */ - getMergedTextBlocksStream() { return ParserStream.createContentStream(this.getMergedTextBlocksIfNeeded()) } - - /** - * Destroy the PDFParser instance. - */ - destroy() { // invoked with stream transform process - super.removeAllListeners(); + /** + * Retrieve raw text content from PDF. + * @returns {string} Raw text content + */ + getRawTextContent() { + return this.#PDFJS.getRawTextContent(); + } + + /** + * Retrieve raw text content stream. + * @returns {Stream} Raw text content stream + */ + getRawTextContentStream() { + return ParserStream.createContentStream(this.getRawTextContent()); + } + + /** + * Retrieve all field types. + * @returns {object[]} All field types + */ + getAllFieldsTypes() { + return this.#PDFJS.getAllFieldsTypes(); + } + + /** + * Retrieve all field types stream. + * @returns {Stream} All field types stream + */ + getAllFieldsTypesStream() { + return ParserStream.createContentStream(this.getAllFieldsTypes()); + } + + /** + * Retrieve merged text blocks if needed. + * @returns {object} Merged text blocks + */ + getMergedTextBlocksIfNeeded() { + return this.#PDFJS.getMergedTextBlocksIfNeeded(); + } + + /** + * Retrieve merged text blocks stream. + * @returns {Stream} Merged text blocks stream + */ + getMergedTextBlocksStream() { + return ParserStream.createContentStream(this.getMergedTextBlocksIfNeeded()); + } + + /** + * Destroy the PDFParser instance. + */ + destroy() { + // invoked with stream transform process + super.removeAllListeners(); //context object will be set in Web Service project, but not in command line utility if (this.#context) { @@ -234,9 +262,20 @@ export default class PDFParser extends EventEmitter { this.#pdfFilePath = null; this.#pdfFileMTime = null; this.#data = null; - this.#processFieldInfoXML = false;//disable additional _fieldInfo.xml parsing and merging (do NOT set to true) + this.#processFieldInfoXML = false; //disable additional _fieldInfo.xml parsing and merging (do NOT set to true) - this.#PDFJS.destroy(); - this.#PDFJS = null; + this.#PDFJS.destroy(); + this.#PDFJS = null; } } + +export { + PDFParser, + ParserStream, + StringifyStream, + kColors, + kFontFaces, + kFontStyles, + pkInfo, + _PARSER_SIG, +}; diff --git a/readme.md b/readme.md index aa065523..14d7ecde 100644 --- a/readme.md +++ b/readme.md @@ -1,21 +1,29 @@ # pdf2json -pdf2json is a [node.js](http://nodejs.org/) module that parses and converts PDF from binary to json format, it's built with [pdf.js](https://github.com/mozilla/pdf.js/) and extends with interactive form elements and text content parsing outside browser. +pdf2json is a [node.js](http://nodejs.org/) module converts binary PDF to JSON and text. Built with [pdf.js](https://github.com/mozilla/pdf.js/), it extracts text content and interactive form elements for server-side processing and command-line use. -The goal is to enable server side PDF parsing with interactive form elements when wrapped in web service, and also enable parsing local PDF to json file when using as a command line utility. +## Features + +- PDF text extraction: extracts textual content of PDF documents into structured JSON. +- Form element handling: parses interactive form fields within PDFs for flexible data capture. +- Server-side and command-line versatility: Integrate with web services for remote PDF processing or use as a standalone command-line tool for local file conversion. +- Community driven: decade+ long community driven development ensures continuous improvement. ## Install ->npm install pdf2json +> npm i pdf2json Or, install it globally: ->sudo npm install pdf2json -g + +> npm i pdf2json -g To update with latest version: ->sudo npm update pdf2json -g -To Run in RESTful Web Service or as Commandline Utility -* More details can be found at the bottom of this document. +> npm update pdf2json -g + +To Run in RESTful Web Service or as command line Utility + +- More details can be found at the bottom of this document. ## Test @@ -23,7 +31,9 @@ After install, run command line: > npm run test -It'll scan and parse *260* PDF AcroForm files under *_./test/pdf_*, runs with *_-s -t -c -m_* command line options, generates primary output JSON, additional text content JSON, form fields JSON and merged text JSON file for each PDF. It usually takes ~20s in my MacBook Pro to complete, check *_./test/target/_* for outputs. +It'll scan and parse _260_ PDF AcroForm files under _*./test/pdf*_, runs with _*-s -t -c -m*_ command line options, generates primary output JSON, additional text content JSON, form fields JSON and merged text file for each PDF. It usually takes ~20s in my MacBook Pro to complete, check _*./test/target/*_ for outputs. + +_update on 4/27/2024_: parsing 260 PDFs by `npm run test` on M2 Mac takes 7~8s ### Test Exception Handlings @@ -31,23 +41,25 @@ After install, run command line: > npm run test-misc -It'll scan and parse all PDF files under *_./test/pdf/misc_*, also runs with *_-s -t -c -m_* command line options, generates primary output JSON, additional text content JSON, form fields JSON and merged text JSON file for 5 PDF fields, while catches exceptions with stack trace for: - * _bad XRef entry_ for `pdf/misc/i200_test.pdf` - * _unsupported encryption algorithm_ for `pdf/misc/i43_encrypted.pdf` - * _Invalid XRef stream header_ for `pdf/misc/i243_problem_file_anon.pdf` +It'll scan and parse all PDF files under _*./test/pdf/misc*_, also runs with _*-s -t -c -m*_ command line options, generates primary output JSON, additional text content JSON, form fields JSON and merged text JSON file for 5 PDF fields, while catches exceptions with stack trace for: + +- _bad XRef entry_ for `pdf/misc/i200_test.pdf` +- _unsupported encryption algorithm_ for `pdf/misc/i43_encrypted.pdf` +- _Invalid XRef stream header_ for `pdf/misc/i243_problem_file_anon.pdf` ### Test Streams + After install, run command line: > npm run parse-r -It scans 165 PDF files under *../test/pdf/fd/form_*, parses with [Stream API](https://nodejs.org/dist/latest-v14.x/docs/api/stream.html), then generates output to *_./test/target/fd/form_*. +It scans 165 PDF files under \_../test/pdf/fd/form\__, parses with [Stream API](https://nodejs.org/dist/latest-v14.x/docs/api/stream.html), then generates output to \_\_./test/target/fd/form_\_. -More test scripts with different commandline options can be found at *_package.json_*. +More test scripts with different command line options can be found at _*package.json*_. ### Disabling Test logs -During CI/CD, you probably would like to disable unnecessary logs for unit testing. +For CI/CD, you probably would like to disable unnecessary logs for unit testing. The code has two types of logs: @@ -58,114 +70,151 @@ To disable the first type, you could mock the console.log and console.warn APIs, ## Code Example -* Parse a PDF file then write to a JSON file: +- Parse a PDF file then write to a JSON file: -````javascript - import fs from "fs"; - import PDFParser from "pdf2json"; +```javascript +import fs from "fs"; +import { PDFParser } from "pdf2json"; // starting v3.1.0, PDFParser is no longer the default export - const pdfParser = new PDFParser(); +const pdfParser = new PDFParser(); - pdfParser.on("pdfParser_dataError", errData => console.error(errData.parserError) ); - pdfParser.on("pdfParser_dataReady", pdfData => { - fs.writeFile("./pdf2json/test/F1040EZ.json", JSON.stringify(pdfData),(data) => { console.log(data)}); - }); +pdfParser.on("pdfParser_dataError", (errData) => + console.error(errData.parserError) +); +pdfParser.on("pdfParser_dataReady", (pdfData) => { + fs.writeFile( + "./pdf2json/test/F1040EZ.json", + JSON.stringify(pdfData), + (data) => console.log(data) + ); +}); - pdfParser.loadPDF("./pdf2json/test/pdf/fd/form/F1040EZ.pdf"); -```` +pdfParser.loadPDF("./pdf2json/test/pdf/fd/form/F1040EZ.pdf"); +``` Or, call directly with buffer: -````javascript - fs.readFile(pdfFilePath, (err, pdfBuffer) => { - if (!err) { - pdfParser.parseBuffer(pdfBuffer); - } - }) -```` +```javascript +fs.readFile(pdfFilePath, (err, pdfBuffer) => { + if (!err) { + pdfParser.parseBuffer(pdfBuffer); + } +}); +``` Or, use more granular page level parsing events (v2.0.0) -````javascript - pdfParser.on("readable", meta => console.log("PDF Metadata", meta) ); - pdfParser.on("data", page => console.log(page ? "One page paged" : "All pages parsed", page)); - pdfParser.on("error", err => console.erro("Parser Error", err); -```` +```javascript +pdfParser.on("readable", (meta) => console.log("PDF Metadata", meta)); +pdfParser.on("data", (page) => + console.log(page ? "One page paged" : "All pages parsed", page) +); +pdfParser.on("error", (err) => console.error("Parser Error", err)); +``` -* Parse a PDF then write a .txt file (which only contains textual content of the PDF) +- Parse a PDF then write a .txt file (which only contains textual content of the PDF) -````javascript - import fs from "fs"; - import PDFParser from "pdf2json"; +```javascript +import fs from "fs"; +import { PDFParser } from "pdf2json"; // starting v3.1.0, PDFParser is no longer the default export - const pdfParser = new PDFParser(this,1); +const pdfParser = new PDFParser(this, 1); - pdfParser.on("pdfParser_dataError", errData => console.error(errData.parserError) ); - pdfParser.on("pdfParser_dataReady", pdfData => { - fs.writeFile("./pdf2json/test/F1040EZ.content.txt", pdfParser.getRawTextContent(), ()=>{console.log("Done.");}); - }); +pdfParser.on("pdfParser_dataError", (errData) => + console.error(errData.parserError) +); +pdfParser.on("pdfParser_dataReady", (pdfData) => { + fs.writeFile( + "./pdf2json/test/F1040EZ.content.txt", + pdfParser.getRawTextContent(), + () => { + console.log("Done."); + } + ); +}); - pdfParser.loadPDF("./pdf2json/test/pdf/fd/form/F1040EZ.pdf"); -```` +pdfParser.loadPDF("./pdf2json/test/pdf/fd/form/F1040EZ.pdf"); +``` -* Parse a PDF then write a fields.json file that only contains interactive forms' fields information: +- Parse a PDF then write a fields.json file that only contains interactive forms' fields information: -````javascript - import fs from "fs"; - import PDFParser from "pdf2json"; +```javascript +import fs from "fs"; +import { PDFParser } from "pdf2json"; // starting v3.1.0, PDFParser is no longer the default export - const pdfParser = new PDFParser(); +const pdfParser = new PDFParser(); - pdfParser.on("pdfParser_dataError", errData => console.error(errData.parserError) ); - pdfParser.on("pdfParser_dataReady", pdfData => { - fs.writeFile("./pdf2json/test/F1040EZ.fields.json", JSON.stringify(pdfParser.getAllFieldsTypes()), ()=>{console.log("Done.");}); - }); +pdfParser.on("pdfParser_dataError", (errData) => + console.error(errData.parserError) +); +pdfParser.on("pdfParser_dataReady", (pdfData) => { + fs.writeFile( + "./pdf2json/test/F1040EZ.fields.json", + JSON.stringify(pdfParser.getAllFieldsTypes()), + () => { + console.log("Done."); + } + ); +}); - pdfParser.loadPDF("./pdf2json/test/pdf/fd/form/F1040EZ.pdf"); -```` +pdfParser.loadPDF("./pdf2json/test/pdf/fd/form/F1040EZ.pdf"); +``` Alternatively, you can pipe input and output streams: (requires v1.1.4) -````javascript - import fs from "fs"; - import PDFParser from "pdf2json"; - - const inputStream = fs.createReadStream("./pdf2json/test/pdf/fd/form/F1040EZ.pdf", {bufferSize: 64 * 1024}); - const outputStream = fs.createWriteStream("./pdf2json/test/target/fd/form/F1040EZ.json"); - - inputStream.pipe(new PDFParser()).pipe(new StringifyStream()).pipe(outputStream); -```` +```javascript +import fs from "fs"; +import { PDFParser } from "pdf2json"; // starting v3.1.0, no default export of PDFParser + +const inputStream = fs.createReadStream( + "./pdf2json/test/pdf/fd/form/F1040EZ.pdf", + { bufferSize: 64 * 1024 } +); +const outputStream = fs.createWriteStream( + "./pdf2json/test/target/fd/form/F1040EZ.json" +); + +inputStream + .pipe(new PDFParser()) + .pipe(new StringifyStream()) + .pipe(outputStream); +``` With v2.0.0, last line above changes to -````javascript - inputStream.pipe(this.pdfParser.createParserStream()).pipe(new StringifyStream()).pipe(outputStream); -```` + +```javascript +inputStream + .pipe(this.pdfParser.createParserStream()) + .pipe(new StringifyStream()) + .pipe(outputStream); +``` For additional output streams support: -````javascript - //private methods - #generateMergedTextBlocksStream() { - return new Promise( (resolve, reject) => { - const outputStream = ParserStream.createOutputStream(this.outputPath.replace(".json", ".merged.json"), resolve, reject); - this.pdfParser.getMergedTextBlocksStream().pipe(new StringifyStream()).pipe(outputStream); - }); - } + +```javascript + //private methods + #generateMergedTextBlocksStream() { + return new Promise( (resolve, reject) => { + const outputStream = ParserStream.createOutputStream(this.outputPath.replace(".json", ".merged.json"), resolve, reject); + this.pdfParser.getMergedTextBlocksStream().pipe(new StringifyStream()).pipe(outputStream); + }); + } #generateRawTextContentStream() { - return new Promise( (resolve, reject) => { - const outputStream = ParserStream.createOutputStream(this.outputPath.replace(".json", ".content.txt"), resolve, reject); - this.pdfParser.getRawTextContentStream().pipe(outputStream); - }); + return new Promise( (resolve, reject) => { + const outputStream = ParserStream.createOutputStream(this.outputPath.replace(".json", ".content.txt"), resolve, reject); + this.pdfParser.getRawTextContentStream().pipe(outputStream); + }); } #generateFieldsTypesStream() { - return new Promise( (resolve, reject) => { - const outputStream = ParserStream.createOutputStream(this.outputPath.replace(".json", ".fields.json"), resolve, reject); - this.pdfParser.getAllFieldsTypesStream().pipe(new StringifyStream()).pipe(outputStream); - }); - } + return new Promise( (resolve, reject) => { + const outputStream = ParserStream.createOutputStream(this.outputPath.replace(".json", ".fields.json"), resolve, reject); + this.pdfParser.getAllFieldsTypesStream().pipe(new StringifyStream()).pipe(outputStream); + }); + } - #processAdditionalStreams() { + #processAdditionalStreams() { const outputTasks = []; if (PROCESS_FIELDS_CONTENT) {//needs to generate fields.json file outputTasks.push(this.#generateFieldsTypesStream()); @@ -176,59 +225,69 @@ For additional output streams support: if (PROCESS_MERGE_BROKEN_TEXT_BLOCKS) {//needs to generate json file with merged broken text blocks outputTasks.push(this.#generateMergedTextBlocksStream()); } - return Promise.allSettled(outputTasks); - } -```` + return Promise.allSettled(outputTasks); + } +``` + Note, if primary JSON parsing has exceptions, none of additional stream will be processed. See [p2jcmd.js](https://github.com/modesty/pdf2json/blob/master/lib/p2jcmd.js) for more details. - ## API Reference -* events: - * pdfParser_dataError: will be raised when parsing failed - * pdfParser_dataReady: when parsing succeeded +- events: + + - pdfParser_dataError: will be raised when parsing failed + - pdfParser_dataReady: when parsing succeeded + +- alternative events: (v2.0.0) + + - readable: first event dispatched after PDF file metadata is parsed and before processing any page + - data: one parsed page succeeded, null means last page has been processed, signle end of data stream + - error: exception or error occured -* alternative events: (v2.0.0) - * readable: first event dispatched after PDF file metadata is parsed and before processing any page - * data: one parsed page succeeded, null means last page has been processed, signle end of data stream - * error: exception or error occured +- start to parse PDF file from specified file path asynchronously: -* start to parse PDF file from specified file path asynchronously: -````javascript +```javascript function loadPDF(pdfFilePath); -```` +``` + If failed, event "pdfParser_dataError" will be raised with error object: {"parserError": errObj}; -If success, event "pdfParser_dataReady" will be raised with output data object: {"formImage": parseOutput}, which can be saved as json file (in command line) or serialized to json when running in web service. __note__: "formImage" is removed from v2.0.0, see breaking changes for details. +If success, event "pdfParser_dataReady" will be raised with output data object: {"formImage": parseOutput}, which can be saved as json file (in command line) or serialized to json when running in web service. **note**: "formImage" is removed from v2.0.0, see breaking changes for details. + +- Get all textual content from "pdfParser_dataReady" event handler: -* Get all textual content from "pdfParser_dataReady" event handler: -````javascript +```javascript function getRawTextContent(); -```` +``` + returns text in string. -* Get all input fields information from "pdfParser_dataReady" event handler: -````javascript +- Get all input fields information from "pdfParser_dataReady" event handler: + +```javascript function getAllFieldsTypes(); -```` -returns an array of field objects. +``` + +returns an array of field objects. ## Output format Reference Current parsed data has four main sub objects to describe the PDF document. -* 'Transcoder': pdf2json version number -* 'Agency': the main text identifier for the PDF document. If Id.AgencyId present, it'll be same, otherwise it'll be set as document title; (_deprecated since v2.0.0, see notes below_) -* 'Id': the XML meta data that embedded in PDF document (_deprecated since v2.0.0, see notes below_) - * all forms attributes metadata are defined in "Custom" tab of "Document Properties" dialog in Acrobat Pro; - * v0.1.22 added support for the following custom properties: - * AgencyId: default "unknown"; - * Name: default "unknown"; - * MC: default false; - * Max: default -1; - * Parent: parent name, default "unknown"; - * *_v2.0.0_*: 'Agency' and 'Id' are replaced with full metadata, example: for `./test/pdf/fd/form/F1040.pdf`, full metadata is: - ````json +- 'Transcoder': pdf2json version number +- 'Agency': the main text identifier for the PDF document. If Id.AgencyId present, it'll be same, otherwise it'll be set as document title; (_deprecated since v2.0.0, see notes below_) +- 'Id': the XML meta data that embedded in PDF document (_deprecated since v2.0.0, see notes below_) + + - all forms attributes metadata are defined in "Custom" tab of "Document Properties" dialog in Acrobat Pro; + - v0.1.22 added support for the following custom properties: + - AgencyId: default "unknown"; + - Name: default "unknown"; + - MC: default false; + - Max: default -1; + - Parent: parent name, default "unknown"; + - _*v2.0.0*_: 'Agency' and 'Id' are replaced with full metadata, example: for `./test/pdf/fd/form/F1040.pdf`, full metadata is: + + ```json Meta: { PDFFormatVersion: '1.7', IsAcroFormPresent: true, @@ -254,33 +313,33 @@ Current parsed data has four main sub objects to describe the PDF document. 'adhocwf:version': '1.1' } } - ```` -* 'Pages': array of 'Page' object that describes each page in the PDF, including sizes, lines, fills and texts within the page. More info about 'Page' object can be found at 'Page Object Reference' section -* 'Width': the PDF page width in page unit + ``` +- 'Pages': array of 'Page' object that describes each page in the PDF, including sizes, lines, fills and texts within the page. More info about 'Page' object can be found at 'Page Object Reference' section +- 'Width': the PDF page width in page unit ### Page object Reference Each page object within 'Pages' array describes page elements and attributes with 5 main fields: -* 'Height': height of the page in page unit -* 'Width': width of the page in page unit, moved from root to page object in v2.0.0 -* 'HLines': horizontal line array, each line has 'x', 'y' in relative coordinates for positioning, and 'w' for width, plus 'l' for length. Both width and length are in page unit -* 'Vline': vertical line array, each line has 'x', 'y' in relative coordinates for positioning, and 'w' for width, plus 'l' for length. Both width and length are in page unit; - * v0.4.3 added Line color support. Default is 'black', other wise set in 'clr' if found in color dictionary, or 'oc' field if not found in dictionary; - * v0.4.4 added dashed line support. Default is 'solid', if line style is dashed line, {dsh:1} is added to line object; -* 'Fills': an array of rectangular area with solid color fills, same as lines, each 'fill' object has 'x', 'y' in relative coordinates for positioning, 'w' and 'h' for width and height in page unit, plus 'clr' to reference a color with index in color dictionary. More info about 'color dictionary' can be found at 'Dictionary Reference' section. -* 'Texts': an array of text blocks with position, actual text and styling information: - * 'x' and 'y': relative coordinates for positioning - * 'clr': a color index in color dictionary, same 'clr' field as in 'Fill' object. If a color can be found in color dictionary, 'oc' field will be added to the field as 'original color" value. - * 'A': text alignment, including: - * left - * center - * right - * 'R': an array of text run, each text run object has two main fields: - * 'T': actual text - * 'S': style index from style dictionary. More info about 'Style Dictionary' can be found at 'Dictionary Reference' section - * 'TS': [fontFaceId, fontSize, 1/0 for bold, 1/0 for italic] +- 'Height': height of the page in page unit +- 'Width': width of the page in page unit, moved from root to page object in v2.0.0 +- 'HLines': horizontal line array, each line has 'x', 'y' in relative coordinates for positioning, and 'w' for width, plus 'l' for length. Both width and length are in page unit +- 'Vline': vertical line array, each line has 'x', 'y' in relative coordinates for positioning, and 'w' for width, plus 'l' for length. Both width and length are in page unit; + - v0.4.3 added Line color support. Default is 'black', other wise set in 'clr' if found in color dictionary, or 'oc' field if not found in dictionary; + - v0.4.4 added dashed line support. Default is 'solid', if line style is dashed line, {dsh:1} is added to line object; +- 'Fills': an array of rectangular area with solid color fills, same as lines, each 'fill' object has 'x', 'y' in relative coordinates for positioning, 'w' and 'h' for width and height in page unit, plus 'clr' to reference a color with index in color dictionary. More info about 'color dictionary' can be found at 'Dictionary Reference' section. +- 'Texts': an array of text blocks with position, actual text and styling information: + - 'x' and 'y': relative coordinates for positioning + - 'clr': a color index in color dictionary, same 'clr' field as in 'Fill' object. If a color can be found in color dictionary, 'oc' field will be added to the field as 'original color" value. + - 'A': text alignment, including: + - left + - center + - right + - 'R': an array of text run, each text run object has two main fields: + - 'T': actual text + - 'S': style index from style dictionary. More info about 'Style Dictionary' can be found at 'Dictionary Reference' section + - 'TS': [fontFaceId, fontSize, 1/0 for bold, 1/0 for italic] v0.4.5 added support when fields attributes information is defined in external xml file. pdf2json will always try load field attributes xml file based on file name convention (pdfFileName.pdf's field XML file must be named pdfFileName_fieldInfo.xml in the same directory). If found, fields info will be injected. @@ -290,137 +349,144 @@ Same reason to having "HLines" and "VLines" array in 'Page' object, color and st This dictionary data contract design will allow the output just reference a dictionary key , rather than the actual full definition of color or font style. It does require the client of the payload to have the same dictionary definition to make sense out of it when render the parser output on to screen. -* Color Dictionary -````javascript - const kColors = [ - '#000000', // 0 - '#ffffff', // 1 - '#4c4c4c', // 2 - '#808080', // 3 - '#999999', // 4 - '#c0c0c0', // 5 - '#cccccc', // 6 - '#e5e5e5', // 7 - '#f2f2f2', // 8 - '#008000', // 9 - '#00ff00', // 10 - '#bfffa0', // 11 - '#ffd629', // 12 - '#ff99cc', // 13 - '#004080', // 14 - '#9fc0e1', // 15 - '#5580ff', // 16 - '#a9c9fa', // 17 - '#ff0080', // 18 - '#800080', // 19 - '#ffbfff', // 20 - '#e45b21', // 21 - '#ffbfaa', // 22 - '#008080', // 23 - '#ff0000', // 24 - '#fdc59f', // 25 - '#808000', // 26 - '#bfbf00', // 27 - '#824100', // 28 - '#007256', // 29 - '#008000', // 30 - '#000080', // Last + 1 - '#008080', // Last + 2 - '#800080', // Last + 3 - '#ff0000', // Last + 4 - '#0000ff', // Last + 5 - '#008000', // Last + 6 - '#000000' // Last + 7 - ]; -```` - -* Style Dictionary: -````javascript - const kFontFaces = [ - "QuickType,Arial,Helvetica,sans-serif", // 00 - QuickType - sans-serif variable font - "QuickType Condensed,Arial Narrow,Arial,Helvetica,sans-serif", // 01 - QuickType Condensed - thin sans-serif variable font - "QuickTypePi", // 02 - QuickType Pi - "QuickType Mono,Courier New,Courier,monospace", // 03 - QuickType Mono - san-serif fixed font - "OCR-A,Courier New,Courier,monospace", // 04 - OCR-A - OCR readable san-serif fixed font - "OCR B MT,Courier New,Courier,monospace" // 05 - OCR-B MT - OCR readable san-serif fixed font - ]; - - const kFontStyles = [ - // Face Size Bold Italic StyleID(Comment) - // ----- ---- ---- ----- ----------------- - [0, 6, 0, 0], //00 - [0, 8, 0, 0], //01 - [0, 10, 0, 0], //02 - [0, 12, 0, 0], //03 - [0, 14, 0, 0], //04 - [0, 18, 0, 0], //05 - [0, 6, 1, 0], //06 - [0, 8, 1, 0], //07 - [0, 10, 1, 0], //08 - [0, 12, 1, 0], //09 - [0, 14, 1, 0], //10 - [0, 18, 1, 0], //11 - [0, 6, 0, 1], //12 - [0, 8, 0, 1], //13 - [0, 10, 0, 1], //14 - [0, 12, 0, 1], //15 - [0, 14, 0, 1], //16 - [0, 18, 0, 1], //17 - [0, 6, 1, 1], //18 - [0, 8, 1, 1], //19 - [0, 10, 1, 1], //20 - [0, 12, 1, 1], //21 - [0, 14, 1, 1], //22 - [0, 18, 1, 1], //23 - [1, 6, 0, 0], //24 - [1, 8, 0, 0], //25 - [1, 10, 0, 0], //26 - [1, 12, 0, 0], //27 - [1, 14, 0, 0], //28 - [1, 18, 0, 0], //29 - [1, 6, 1, 0], //30 - [1, 8, 1, 0], //31 - [1, 10, 1, 0], //32 - [1, 12, 1, 0], //33 - [1, 14, 1, 0], //34 - [1, 18, 1, 0], //35 - [1, 6, 0, 1], //36 - [1, 8, 0, 1], //37 - [1, 10, 0, 1], //38 - [1, 12, 0, 1], //39 - [1, 14, 0, 1], //40 - [1, 18, 0, 1], //41 - [2, 8, 0, 0], //42 - [2, 10, 0, 0], //43 - [2, 12, 0, 0], //44 - [2, 14, 0, 0], //45 - [2, 12, 0, 0], //46 - [3, 8, 0, 0], //47 - [3, 10, 0, 0], //48 - [3, 12, 0, 0], //49 - [4, 12, 0, 0], //50 - [0, 9, 0, 0], //51 - [0, 9, 1, 0], //52 - [0, 9, 0, 1], //53 - [0, 9, 1, 1], //54 - [1, 9, 0, 0], //55 - [1, 9, 1, 0], //56 - [1, 9, 1, 1], //57 - [4, 10, 0, 0], //58 - [5, 10, 0, 0], //59 - [5, 12, 0, 0] //60 - ]; -```` +- Color Dictionary + +```javascript +const kColors = [ + "#000000", // 0 + "#ffffff", // 1 + "#4c4c4c", // 2 + "#808080", // 3 + "#999999", // 4 + "#c0c0c0", // 5 + "#cccccc", // 6 + "#e5e5e5", // 7 + "#f2f2f2", // 8 + "#008000", // 9 + "#00ff00", // 10 + "#bfffa0", // 11 + "#ffd629", // 12 + "#ff99cc", // 13 + "#004080", // 14 + "#9fc0e1", // 15 + "#5580ff", // 16 + "#a9c9fa", // 17 + "#ff0080", // 18 + "#800080", // 19 + "#ffbfff", // 20 + "#e45b21", // 21 + "#ffbfaa", // 22 + "#008080", // 23 + "#ff0000", // 24 + "#fdc59f", // 25 + "#808000", // 26 + "#bfbf00", // 27 + "#824100", // 28 + "#007256", // 29 + "#008000", // 30 + "#000080", // Last + 1 + "#008080", // Last + 2 + "#800080", // Last + 3 + "#ff0000", // Last + 4 + "#0000ff", // Last + 5 + "#008000", // Last + 6 + "#000000", // Last + 7 +]; +``` + +- Style Dictionary: + +```javascript +const kFontFaces = [ + "QuickType,Arial,Helvetica,sans-serif", // 00 - QuickType - sans-serif variable font + "QuickType Condensed,Arial Narrow,Arial,Helvetica,sans-serif", // 01 - QuickType Condensed - thin sans-serif variable font + "QuickTypePi", // 02 - QuickType Pi + "QuickType Mono,Courier New,Courier,monospace", // 03 - QuickType Mono - san-serif fixed font + "OCR-A,Courier New,Courier,monospace", // 04 - OCR-A - OCR readable san-serif fixed font + "OCR B MT,Courier New,Courier,monospace", // 05 - OCR-B MT - OCR readable san-serif fixed font +]; + +const kFontStyles = [ + // Face Size Bold Italic StyleID(Comment) + // ----- ---- ---- ----- ----------------- + [0, 6, 0, 0], //00 + [0, 8, 0, 0], //01 + [0, 10, 0, 0], //02 + [0, 12, 0, 0], //03 + [0, 14, 0, 0], //04 + [0, 18, 0, 0], //05 + [0, 6, 1, 0], //06 + [0, 8, 1, 0], //07 + [0, 10, 1, 0], //08 + [0, 12, 1, 0], //09 + [0, 14, 1, 0], //10 + [0, 18, 1, 0], //11 + [0, 6, 0, 1], //12 + [0, 8, 0, 1], //13 + [0, 10, 0, 1], //14 + [0, 12, 0, 1], //15 + [0, 14, 0, 1], //16 + [0, 18, 0, 1], //17 + [0, 6, 1, 1], //18 + [0, 8, 1, 1], //19 + [0, 10, 1, 1], //20 + [0, 12, 1, 1], //21 + [0, 14, 1, 1], //22 + [0, 18, 1, 1], //23 + [1, 6, 0, 0], //24 + [1, 8, 0, 0], //25 + [1, 10, 0, 0], //26 + [1, 12, 0, 0], //27 + [1, 14, 0, 0], //28 + [1, 18, 0, 0], //29 + [1, 6, 1, 0], //30 + [1, 8, 1, 0], //31 + [1, 10, 1, 0], //32 + [1, 12, 1, 0], //33 + [1, 14, 1, 0], //34 + [1, 18, 1, 0], //35 + [1, 6, 0, 1], //36 + [1, 8, 0, 1], //37 + [1, 10, 0, 1], //38 + [1, 12, 0, 1], //39 + [1, 14, 0, 1], //40 + [1, 18, 0, 1], //41 + [2, 8, 0, 0], //42 + [2, 10, 0, 0], //43 + [2, 12, 0, 0], //44 + [2, 14, 0, 0], //45 + [2, 12, 0, 0], //46 + [3, 8, 0, 0], //47 + [3, 10, 0, 0], //48 + [3, 12, 0, 0], //49 + [4, 12, 0, 0], //50 + [0, 9, 0, 0], //51 + [0, 9, 1, 0], //52 + [0, 9, 0, 1], //53 + [0, 9, 1, 1], //54 + [1, 9, 0, 0], //55 + [1, 9, 1, 0], //56 + [1, 9, 1, 1], //57 + [4, 10, 0, 0], //58 + [5, 10, 0, 0], //59 + [5, 12, 0, 0], //60 +]; +``` + v2.0.0: to access these dictionary programactically, do either -````javascript - import {kColors, kFontFaces, kFontStyles} from "./lib/pdfconst.js"; -```` + +```javascript +import { kColors, kFontFaces, kFontStyles } from "./lib/pdfconst.js"; // <-- pre 3.1.0 +import { kColors, kFontFaces, kFontStyles } from "pdf2json"; // <-- since 3.1.0 +``` + or via public static getters of PDFParser: -````javascript - console.dir(PDFParser.colorDict); - console.dir(PDFParser.fontFaceDict); - console.dir(PDFParser.fontStyleDict); -```` + +```javascript +console.dir(PDFParser.colorDict); +console.dir(PDFParser.fontFaceDict); +console.dir(PDFParser.fontStyleDict); +``` ## Interactive Forms Elements @@ -432,148 +498,155 @@ All interactive form elements parsing output will be part of corresponding 'Page Each object with in 'Boxset' can be either checkbox or radio button, the only difference is that radio button object will have more than one element in 'boxes' array, it indicates it's a radio button group. The following sample output illustrate one checkbox ( Id: F8888 ) and one radio button group ( Id: ACC ) in the 'Boxsets' array: - Boxsets: [ - {//first element, check box - boxes: [ //only one box object object in this array - { - x: 47, - y: 40, - w: 3, - h: 1, - style: 48, - TI: 39, - AM: 4, - id: { - Id: "F8888", - }, - T: { - Name: "box" - } - } - ], - id: { - Id: "A446", - } - },//end of first element - {//second element, radio button group - boxes: [// has two box elements in boxes array - { - x: 54, - y: 41, - w: 3, - h: 1, - style: 48, - TI: 43, - AM: 132, - id: { - Id: "ACCC", - }, - T: { - Name: "box" - } - }, - { - x: 67, - y: 41, - w: 3, - h: 1, - style: 48, - TI: 44, - AM: 132, - id: { - Id: "ACCS", - EN: 0 - }, - T: { - Name: "box" - } - } - ], - id: { - Id: "ACC", - EN: 0 - } - }//end of second element - ] //end of Boxsets array +```Javascript +Boxsets: [ +{//first element, check box + boxes: [ //only one box object object in this array + { + x: 47, + y: 40, + w: 3, + h: 1, + style: 48, + TI: 39, + AM: 4, + id: { + Id: "F8888", + }, + T: { + Name: "box" + } + } + ], + id: { + Id: "A446", + } +},//end of first element +{//second element, radio button group + boxes: [// has two box elements in boxes array + { + x: 54, + y: 41, + w: 3, + h: 1, + style: 48, + TI: 43, + AM: 132, + id: { + Id: "ACCC", + }, + T: { + Name: "box" + } + }, + { + x: 67, + y: 41, + w: 3, + h: 1, + style: 48, + TI: 44, + AM: 132, + id: { + Id: "ACCS", + EN: 0 + }, + T: { + Name: "box" + } + } + ], + id: { + Id: "ACC", + EN: 0 + } +}//end of second element +] //end of Boxsets array +``` 'Fields' array contains parsed object for text input (Name: 'alpha'), drop down list (Name: 'apha', but has 'PL' object which contains label array in 'PL.D' and value array in 'PL.V'), link button (Name: 'link', linked URL is in 'FL.form.Id' field). Some examples: Text input box example: - { - style: 48, - T: { - Name: "alpha", - TypeInfo: { } - }, - id: { - Id: "p1_t40", - EN: 0 - }, - TU: "alternative text", //for accessibility, added only when available from PDF stream. (v0.3.6). - TI: 0, - x: 6.19, - y: 5.15, - w: 30.94, - h: 0.85, - V: "field value" //only available when the text input box has value - }, +```javascript +{ + style: 48, + T: { + Name: "alpha", + TypeInfo: { } + }, + id: { + Id: "p1_t40", + EN: 0 + }, + TU: "alternative text", //for accessibility, added only when available from PDF stream. (v0.3.6). + TI: 0, + x: 6.19, + y: 5.15, + w: 30.94, + h: 0.85, + V: "field value" //only available when the text input box has value +}, +``` Note: v0.7.0 extends TU (Alternative Text) to all interactive fields to better support accessibility. Drop down list box example: - { - x: 60, - y: 11, - w: 4, - h: 1, - style: 48, - TI: 13, - AM: 388, - mxL: 2, - id: { - Id: "ST", - EN: 0 - }, - T: { - Name: "alpha", - TypeInfo: { - } - }, - PL: { - V: [ - "", - "AL", - "AK" - ], - D: [ - "%28no%20entry%29", - "Alabama", - "Alaska" - ] - } - } - +```javascript +{ + x: 60, + y: 11, + w: 4, + h: 1, + style: 48, + TI: 13, + AM: 388, + mxL: 2, + id: { + Id: "ST", + EN: 0 + }, + T: { + Name: "alpha", + TypeInfo: { + } + }, + PL: { + V: [ + "", + "AL", + "AK" + ], + D: [ + "%28no%20entry%29", + "Alabama", + "Alaska" + ] + } +} +``` Link button example: - { - style: 48, - T: { - Name: "link" - }, - FL: {form: {Id:"http://www.github.com"}, - id: { - Id: "quad8", - EN: 0 - }, - TI: 0, - x: 52.35, - y: 28.35, - w: 8.88, - h: 0.85 - } +```javascript +{ + style: 48, + T: { + Name: "link" + }, + FL: {form: {Id:"http://www.github.com"}, + id: { + Id: "quad8", + EN: 0 + }, + TI: 0, + x: 52.35, + y: 28.35, + w: 8.88, + h: 0.85 +} +``` v0.2.2 added support for "field attribute mask", it'd be common for all fields, form author can set it in Acrobat Pro's Form Editing mode: if a field is ReadOnly, it's AM field will be set as 0x00000400, otherwise AM will be set as 0. @@ -581,25 +654,28 @@ Another supported field attributes is "required": when form author mark a field "Read-Only" filed attribute mask example: - { - style: 48, - T: { - Name: "alpha", - TypeInfo: { } - }, - id: { - Id: "p1_t40", - EN: 0 - }, - TI: 0, - AM: 1024, //If (AM & 0x00000400) set, it indicates this is a read-only filed - x: 6.19, - y: 5.15, - w: 30.94, - h: 0.85 - } +```favascript +{ + style: 48, + T: { + Name: "alpha", + TypeInfo: { } + }, + id: { + Id: "p1_t40", + EN: 0 + }, + TI: 0, + AM: 1024, //If (AM & 0x00000400) set, it indicates this is a read-only filed + x: 6.19, + y: 5.15, + w: 30.94, + h: 0.85 +} +``` v2.X.X added support for the signature form element (Name: 'signature'). If the field has been signed, the 'Sig' property will be present, and will contain any of the following signature details if available: + - 'Name' - Signer's name - 'M' - Time of signing in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format - 'Location' - Location of signing @@ -608,97 +684,105 @@ v2.X.X added support for the signature form element (Name: 'signature'). If the Signature example: - { - style: 48, - T: { - Name: "signature", - TypeInfo: {} - }, - id: { - Id: "SignatureFormField_1", - EN: 0 - }, - TI: 0, - AM: 16, - x: 5.506, - y: 31.394, - w: 14.367, - h: 4.241, - Sig: { - Name: "Signer Name", - M: "2022-03-15T19:17:34-04:00" - } - } +```javascript +{ + style: 48, + T: { + Name: "signature", + TypeInfo: {} + }, + id: { + Id: "SignatureFormField_1", + EN: 0 + }, + TI: 0, + AM: 16, + x: 5.506, + y: 31.394, + w: 14.367, + h: 4.241, + Sig: { + Name: "Signer Name", + M: "2022-03-15T19:17:34-04:00" + } +} +``` ## Text Input Field Formatter Types v0.1.8 added text input field formatter types detection for -* number -* ssn -* date (tested date formatter: mm/dd/yyyy, mm/dd, mm/yyyy and Custom yyyy) -* zip -* phone -* percent (added v0.5.6) +- number +- ssn +- date (tested date formatter: mm/dd/yyyy, mm/dd, mm/yyyy and Custom yyyy) +- zip +- phone +- percent (added v0.5.6) v0.3.9 added "arbitrary mask" (in "special" format category) support, the input field format type is "mask" and the mask string is added as "MV", its value can be found at Format => Special => Arbitrary Mask in Acrobat; Some examples of "mask" format including: -* 9999: 4 digit PIN field -* 99999: 5 digit PIN field -* 99-9999999: formatted 9 digit EIN number -* 999999999: 9 digit routing number -* aaa: 3 letters input + +- 9999: 4 digit PIN field +- 99999: 5 digit PIN field +- 99-9999999: formatted 9 digit EIN number +- 999999999: 9 digit routing number +- aaa: 3 letters input Additionally, the "arbitrary mask" length is extended from 1 characters to 64 characters. And when the mask has only one character, it has the following meanings: -* a: alphabet only input, no numeric input allowed -* n: numeric only input, no locale based number formatting, no alphabet or special characters allowed -* d: numeric only input, with locale based number formatting, one decimal point allowed, no rounding expected and no alphabet or special characters allowed -* -: negative number only, with locale based number formatting, no alphabet or special characters allowed -* +: positive number only, with locale based number formatting, no alphabet or special characters allowed + +- a: alphabet only input, no numeric input allowed +- n: numeric only input, no locale based number formatting, no alphabet or special characters allowed +- d: numeric only input, with locale based number formatting, one decimal point allowed, no rounding expected and no alphabet or special characters allowed +- -: negative number only, with locale based number formatting, no alphabet or special characters allowed +- +: positive number only, with locale based number formatting, no alphabet or special characters allowed v0.4.1 added more date format detection, these formats are set in Acrobat's field's Properties => Format => Date => Custom: -* yyyy: 4 digit year + +- yyyy: 4 digit year Types above are detected only when the widget field type is "Tx" and the additional-actions dictionary 'AA' is set. Like what you see, not all pre-defined formatters and special formatters are supported, if you need more support, you can extend the 'processFieldAttribute' function in core.js file. For the supported types, the result data is set to the field item's T object. Example of a 'number' field in final json output: - { - style: 48, - T: { - Name: "number", - TypeInfo: { } - }, - id: { - Id: "FAGI", - EN: 0 - }, - TI: 0, - x: 68.35, - y: 22.43, - w: 21.77, - h: 1.08 - }, +```javascript +{ + style: 48, + T: { + Name: "number", + TypeInfo: { } + }, + id: { + Id: "FAGI", + EN: 0 + }, + TI: 0, + x: 68.35, + y: 22.43, + w: 21.77, + h: 1.08 +}, +``` Another example of 'date' field: - { - style: 48, - T: { - Name: "date", - TypeInfo: { } - }, - id: { - Id: "Your Birth Date", - EN: 0 - }, - TI: 0, - x: 33.43, - y: 20.78, - w: 5.99, - h: 0.89 - }, - +```javascript +{ + style: 48, + T: { + Name: "date", + TypeInfo: { } + }, + id: { + Id: "Your Birth Date", + EN: 0 + }, + TI: 0, + x: 33.43, + y: 20.78, + w: 5.99, + h: 0.89 +}, +``` ## Text Style data without Style Dictionary @@ -708,27 +792,29 @@ When the actual text style doesn't match any pre-defined style dictionary entry, The new TS filed is an Array with format as: -* First element in TS Array is Font Face ID (integer) -* Second element is Font Size (px) -* Third is 1 when font weight is bold, otherwise 0 -* Forth is 1 when font style is italic, otherwise 0 +- First element in TS Array is Font Face ID (integer) +- Second element is Font Size (px) +- Third is 1 when font weight is bold, otherwise 0 +- Forth is 1 when font style is italic, otherwise 0 For example, the following is a text block data in the parsing result: - { - x: 7.11, - y: 2.47, - w: 1.6, - clr: 0, - A: "left", - R: [ - { - T: "Modesty%20PDF%20Parser%20NodeJS", - S: -1, - TS: [0, 15, 1, 0] - } - ] - }, +```javascript +{ + x: 7.11, + y: 2.47, + w: 1.6, + clr: 0, + A: "left", + R: [ + { + T: "Modesty%20PDF%20Parser%20NodeJS", + S: -1, + TS: [0, 15, 1, 0] + } + ] +}, +``` The text is "Modesty PDF Parser NodeJS", text style dictionary entry ID is -1 (S field, meaning no match), and its Font Face ID is 0 (TS[0], "QuickType,Arial,Helvetica,sans-serif"), Font Size is 15px (TS[1]), Font weight is bold (TS[2]) and font style is normal (TS[3]). @@ -738,47 +824,48 @@ Note: (v0.3.7) When a color is not in style dictionary, "clr" value will be set V0.1.13 added text rotation value (degree) in the R array's object, if and only if the text rotation angle is not 0. For example, if text is not rotated, the parsed output would be the same as above. When the rotation angle is 90 degree, the R array object would be extended with "RA" field: - { - x: 7.11, - y: 2.47, - w: 1.6, - clr: 0, - A: "left", - R: [ - { - T: "Modesty%20PDF%20Parser%20NodeJS", - S: -1, - TS: [0, 15, 1, 0], - RA: 90 - } - ] - }, - +```javascript +{ + x: 7.11, + y: 2.47, + w: 1.6, + clr: 0, + A: "left", + R: [ + { + T: "Modesty%20PDF%20Parser%20NodeJS", + S: -1, + TS: [0, 15, 1, 0], + RA: 90 + } + ] +}, +``` ## Notes pdf.js is designed and implemented to run within browsers that have HTML5 support, it has some dependencies that's only available from browser's JavaScript runtime, including: -* XHR Level 2 (for Ajax) -* DOMParser (for parsing embedded XML from PDF) -* Web Worker (to enable parsing work run in a separated thread) -* Canvas (to draw lines, fills, colors, shapes in browser) -* Others (like web fonts, canvas image, DOM manipulations, etc.) +- XHR Level 2 (for Ajax) +- DOMParser (for parsing embedded XML from PDF) +- Web Worker (to enable parsing work run in a separated thread) +- Canvas (to draw lines, fills, colors, shapes in browser) +- Others (like web fonts, canvas image, DOM manipulations, etc.) In order to run pdf.js in Node.js, we have to address those dependencies and also extend/modify the fork of pdf.js. Here below are some works implemented in this pdf2json module to enable pdf.js running with Node.js: -* Global Variables - * pdf.js' global objects (like PDFJS and globalScope) need to be wrapped in a node module's scope -* API Dependencies - * XHR Level 2: I don't need XMLHttpRequest to load PDF asynchronously in node.js, so replaced it with node's fs (File System) to load PDF file based on request parameters; - * DOMParser: pdf.js instantiates DOMParser to parse XML based PDF meta data, I used xmldom node module to replace this browser JS library dependency. xmldom can be found at https://github.com/xmldom/xmldom; - * Web Worker: pdf.js has "fake worker" code built in, not much works need to be done, only need to stay aware the parsing would occur in the same thread, not in background worker thread; - * Canvas: in order to keep pdf.js code intact as much as possible, I decided to create a HTML5 Canvas API implementation in a node module. It's named as 'PDFCanvas' and has the same API as HTML5 Canvas does, so no change in pdf.js' canvas.js file, we just need to replace the browser's Canvas API with PDFCanvas. This way, when 2D context API invoked, PDFCanvas just write it to a JS object based on the json format above, rather than drawing graphics on html5 canvas; -* Extend/Modify pdf.js - * Fonts: no need to call ensureFonts to make sure fonts downloaded, only need to parse out font info in CSS font format to be used in json's texts array. - * DOM: all DOM manipulation code in pdf.js are commented out, including creating canvas and div for screen rendering and font downloading purpose. - * Interactive Forms elements: (in process to support them) - * Leave out the support to embedded images +- Global Variables + - pdf.js' global objects (like PDFJS and globalScope) need to be wrapped in a node module's scope +- API Dependencies + - XHR Level 2: I don't need XMLHttpRequest to load PDF asynchronously in node.js, so replaced it with node's fs (File System) to load PDF file based on request parameters; + - DOMParser: pdf.js instantiates DOMParser to parse XML based PDF meta data, I used xmldom node module to replace this browser JS library dependency. xmldom can be found at ; + - Web Worker: pdf.js has "fake worker" code built in, not much works need to be done, only need to stay aware the parsing would occur in the same thread, not in background worker thread; + - Canvas: in order to keep pdf.js code intact as much as possible, I decided to create a HTML5 Canvas API implementation in a node module. It's named as 'PDFCanvas' and has the same API as HTML5 Canvas does, so no change in pdf.js' canvas.js file, we just need to replace the browser's Canvas API with PDFCanvas. This way, when 2D context API invoked, PDFCanvas just write it to a JS object based on the json format above, rather than drawing graphics on html5 canvas; +- Extend/Modify pdf.js + - Fonts: no need to call ensureFonts to make sure fonts downloaded, only need to parse out font info in CSS font format to be used in json's texts array. + - DOM: all DOM manipulation code in pdf.js are commented out, including creating canvas and div for screen rendering and font downloading purpose. + - Interactive Forms elements: (in process to support them) + - Leave out the support to embedded images After the changes and extensions listed above, this pdf2json node.js module will work either in a server environment ( I have a RESTful web service built with resitify and pdf2json, it's been running on an Amazon EC2 instance) or as a standalone command line tool (something similar to the Vows unit tests). @@ -788,20 +875,19 @@ More porting notes can be found at [Porting and Extending PDFJS to NodeJS](http: This pdf2json module's output does not 100% maps from PDF definitions, some of them is because of time limitation I currently have, some others result from the 'dictionary' concept for the output. Given these known issues or unsupported features in current implementation, it allows me to contribute back to the open source community with the most important features implemented while leaving some improvement space for the future. All un-supported features listed below can be resolved technically some way or other, if your use case really requires them: -* Embedded content: - * All embedded content are igored, current implementation focuses on static contents and interactive forms. Un-supported PDF embedded contents includes 'Images', 'Fonts' and other dynamic contents; -* Text and Form Styles: - * text and form elements styles has partial support. This means when you have client side renderer (say in HTML5 canvas or SVG renderer), the PDF content may not look exactly the same as how Acrobat renders. The reason is that we've used "style dictionary" in order to reduce the payload size over the wire, while "style dictionary" doesn't have all styles defined. This sort of partial support can be resolved by extending those 'style dictionaries'. Primary text style issues include: - * Font face: only limit to the font families defined in style dictionary - * Font size: only limit to 6, 8, 10, 12, 14, 18 that are defined in style dictionary, all other sized font are mapped to the closest size. For example: when a PDF defines a 7px sized font, the size will be mapped to 8px in the output; - * Color: either font color or fill colors, are limited to the entries in color dictionary - * Style combinations: when style combination is not supported, say in different size, face, bold and italic, the closest entry will be selected in the output; - * Note: v0.1.11 started to add support for actual font style (size, bold, italic), but still no full support on font family; -* Text positioning and spacing: - * Since embedded font and font styles are only honored if they defined in style dictionary, when they are not defined in there, the final output may have word positioning and spacing issues that's noticeable. I also found that even with specific font style support (added in v0.1.11), because of sometimes PDF text object data stream is breaking up into multiple blocks in the middle of a word, and text position is calculated based on the font settings, we still see some word breaking and extra spaces when rendering the parsed json data in browser (HTML5 canvas and IE's SVG). -* User input data in form element: - * As for interactive forms elements, their type, positions, sizes, limited styles and control data are all parsed and served in output, but user interactive data are not parsed, including radio button selection, checkbox status, text input box value, etc., these values should be handled in client renderer as part of user data, so that we can treat parsed PDF data as form template. - +- Embedded content: + - All embedded content are igored, current implementation focuses on static contents and interactive forms. Un-supported PDF embedded contents includes 'Images', 'Fonts' and other dynamic contents; +- Text and Form Styles: + - text and form elements styles has partial support. This means when you have client side renderer (say in HTML5 canvas or SVG renderer), the PDF content may not look exactly the same as how Acrobat renders. The reason is that we've used "style dictionary" in order to reduce the payload size over the wire, while "style dictionary" doesn't have all styles defined. This sort of partial support can be resolved by extending those 'style dictionaries'. Primary text style issues include: + - Font face: only limit to the font families defined in style dictionary + - Font size: only limit to 6, 8, 10, 12, 14, 18 that are defined in style dictionary, all other sized font are mapped to the closest size. For example: when a PDF defines a 7px sized font, the size will be mapped to 8px in the output; + - Color: either font color or fill colors, are limited to the entries in color dictionary + - Style combinations: when style combination is not supported, say in different size, face, bold and italic, the closest entry will be selected in the output; + - Note: v0.1.11 started to add support for actual font style (size, bold, italic), but still no full support on font family; +- Text positioning and spacing: + - Since embedded font and font styles are only honored if they defined in style dictionary, when they are not defined in there, the final output may have word positioning and spacing issues that's noticeable. I also found that even with specific font style support (added in v0.1.11), because of sometimes PDF text object data stream is breaking up into multiple blocks in the middle of a word, and text position is calculated based on the font settings, we still see some word breaking and extra spaces when rendering the parsed json data in browser (HTML5 canvas and IE's SVG). +- User input data in form element: + - As for interactive forms elements, their type, positions, sizes, limited styles and control data are all parsed and served in output, but user interactive data are not parsed, including radio button selection, checkbox status, text input box value, etc., these values should be handled in client renderer as part of user data, so that we can treat parsed PDF data as form template. ## Run As a Commandline Utility @@ -811,176 +897,203 @@ This command line utility is added as an extension, it doesn't break previous fu To use the command line utility to transcode a folder or a file: - node pdf2json.js -f [input directory or pdf file] +```javascript +node pdf2json.js -f [input directory or pdf file] +``` When -f is a PDF file, it'll be converted to json file with the same name and saved in the same directory. If -f is a directory, it'll scan all ".pdf" files within the specified directory to transcode them one by one. Optionally, you can specify the output directory: -o: - node pdf2json.js -f [input directory or pdf file] -o [output directory] +```javascript +node pdf2json.js -f [input directory or pdf file] -o [output directory] +``` The output directory must exist, otherwise, it'll exit with an error. Additionally, you can also use -v or --version to show version number or to display more help info with -h. -### Note: +### Note v0.2.1 added the ability to run pdf2json directly from the command line without specifying "node" and the path of pdf2json. To run this self-executable in command line, first install pdf2json globally: - npm install pdf2json -g +```javascript +npm install pdf2json -g +``` Then run it in command line: - pdf2json -f [input directory or pdf file] - or - pdf2json -f [input directory or pdf file] -o [output directory] +```javascript +pdf2json -f [input directory or pdf file] +``` -v0.5.4 added "-s" or "--silent" command line argument to suppress informative logging output. When using pdf2json as a commandline tool, the default verbosity is 5 (INFOS). While when running as a web service, default verbosity is 9 (ERRORS). -Examples to suppress logging info from commandline: +or - pdf2json -f [input directory or pdf file] -o [output directory] -s - or - pdf2json -f [input directory or pdf file] -o [output directory] --silent +```javascript +pdf2json -f [input directory or pdf file] -o [output directory] +``` + +v0.5.4 added "-s" or "--silent" command line argument to suppress informative logging output. When using pdf2json as a command line tool, the default verbosity is 5 (INFOS). While when running as a web service, default verbosity is 9 (ERRORS). +Examples to suppress logging info from command line: + +```javascript +pdf2json -f [input directory or pdf file] -o [output directory] -s +``` + +or + +```javascript +pdf2json -f [input directory or pdf file] -o [output directory] --silent +``` Examples to turn on logging info in web service: - var pdfParser = new PFParser(); - ... - pdfParser.loadPDF(pdfFilePath, 5); +```javascript +var pdfParser = new PFParser(); +... +pdfParser.loadPDF(pdfFilePath, 5); +``` -v0.5.7 added the capability to skip input PDF files if filename begins with any one of "!@#$%^&*()+=[]\\\';,/{}|\":<>?~`.-_ ", usually these files are created by PDF authoring tools as backup files. +v0.5.7 added the capability to skip input PDF files if filename begins with any one of "!@#$%^&\*()+=[]\\\';,/{}|\":<>?~`.-\_ ", usually these files are created by PDF authoring tools as backup files. v0.6.2 added "-t" command line argument to generate fields json file in addition to parsed json. The fields json file will contain one Array which contains fieldInfo object for each field, and each fieldInfo object will have 4 fields: -* id: field ID -* type: string name of field type, like radio, alpha, etc -* calc: true if read only, otherwise false -* value: initial value of the field + +- id: field ID +- type: string name of field type, like radio, alpha, etc +- calc: true if read only, otherwise false +- value: initial value of the field Example of fields.json content: - [ - {"id":"ADDRCH","type":"alpha","calc":false,"value":"user input data"}, - {"id":"FSRB","type":"radio","calc":false,"value":"Single"}, - {"id":"APPROVED","type":"alpha","calc":true,"value":"Approved Form"} - ... - ] +```javascript +[ + {"id":"ADDRCH","type":"alpha","calc":false,"value":"user input data"}, + {"id":"FSRB","type":"radio","calc":false,"value":"Single"}, + {"id":"APPROVED","type":"alpha","calc":true,"value":"Approved Form"} +... +] +``` The fields.json output can be used to validate fields IDs with other data source, and/or to extract data value from user submitted PDFs. v0.6.8 added "-c" or "--content" command line argument to extract raw text content from PDF. It'll be a separated output file named as (pdf_file_name).content.txt. -If all you need is the textual content of the PDF, "-c" essentially converts PDF to text, of cause, all formatting and styling will be lost. +If all you need is the textual content of the PDF, "-c" essentially converts PDF to text, of cause, all formatting and styling will be lost. ## Run Unit Test (commandline) -It takes less than 1 minutes for pdf2json to parse 261 PDFs under `test/pdf` directory. Usually, it takes about 40 seconds or so to parses all of them. Besides the parimary JSON for each PDF, it also generates text content JSON and form fields JSON file (by `-c` and `-t` parameters) for further testing. +It takes less than 1 minutes for pdf2json to parse 261 PDFs under `test/pdf` directory. Usually, it takes about 40 seconds or so to parses all of them. Besides the primary JSON for each PDF, it also generates text content JSON and form fields JSON file (by `-c` and `-t` parameters) for further testing. The 265 PDFs are all fill-able tax forms from government agencies for tax year 2013, including 165 federal forms, 23 efile instructions and 9 other state tax forms. Shell script is current driver for unit test. To parse one agency's PDFs, run the command line: -```` - cd test - sh p2f.one.sh [2_character_agency_name] -```` +```terminal + cd test + sh p2f.one.sh [2_character_agency_name] +``` For example, to parse and generate all 165 federal forms together with text content and forms fields: -```` - cd test - sh p2f.one.sh fd -```` +```terminal + cd test + sh p2f.one.sh fd +``` To parse and generate all VA forms together with text content and forms fields: -```` - cd test - sh p2f.one.sh va -```` +```terminal + cd test + sh p2f.one.sh va +``` Additionally, to parse all 261 PDFs from commandline: -```` - cd test - sh p2f.forms.sh -```` +```terminal + cd test + sh p2f.forms.sh +``` Or, from `npm scripts`: -```` - npm test -```` +```terminal + npm test +``` Some testing PDFs are provided by bug reporters, like the "unsupported encryption" ([#43](https://github.com/modesty/pdf2json/issues/43)), "read property num from undefined" ([#26](https://github.com/modesty/pdf2json/issues/26)), and "excessive line breaks in text content" ([#28](https://github.com/modesty/pdf2json/issues/28)), their PDFs are all stored in `test/pdf/misc` directory. To run tests against these community contributed PDFs, run commandline: -```` - npm run-script test-misc -```` - +```terminal + npm run-script test-misc +``` ## Upgrade to ~v1.x.x -If you have an early version of pdf2json, please remove your local `node_modules` directory and re-run `npm install` to upgrade to pdf2json@1.0.x. +If you have an early version of pdf2json, please remove your local `node_modules` directory and re-run `npm install` to upgrade to . v1.x.x upgraded dependency packages, removed some unnecessary dependencies, started to assumes ES6 / ES2015 with node ~v4.x. More PDFs are added for unit testing. **Note:** pdf2json has been in production for over 3 years, it's pretty reliable and solid when parsing hundreds (sometimes tens of thousands) of PDF forms every day, thanks to everybody's help. -Starting v1.0.3, I'm trying to address a long over due annoying problem on [broken text blocks](https://github.com/modesty/pdf2json/issues/18). It's the biggest problem that hinders the efficiency of PDF content creation in our projects. Although the root cause lies in the original PDF streams, since the client doesn't render JSON character by character, it's a problem often appears in final rendered web content. We had to work around it by manually merge those text blocks. With the solution in v1.0.x, the need for manual text block merging is greately reduced. +Starting v1.0.3, I'm trying to address a long over due annoying problem on [broken text blocks](https://github.com/modesty/pdf2json/issues/18). It's the biggest problem that hinders the efficiency of PDF content creation in our projects. Although the root cause lies in the original PDF streams, since the client doesn't render JSON character by character, it's a problem often appears in final rendered web content. We had to work around it by manually merge those text blocks. With the solution in v1.0.x, the need for manual text block merging is greatly reduced. The solution is to put to a post-parsing process stage to identify and auto-merge those adjacent blocks. It's not ideal, but works in most of my tests with those 261 PDFs underneath test directory. -The auto merge solution still needs some fine tuning, I keep it as an experimental feature for now, it's off by default, can be turned on by "-m" switch in commandline. +The auto merge solution still needs some fine tuning, I keep it as an experimental feature for now, it's off by default, can be turned on by "-m" switch in command line. In order to support this auto merging capability, text block objects have an additional "sw" (space width of the font) property together with x, y, clr and R. If you have a more effective usage of this new property for merging text blocks, please drop me a line. **Breaking Changes:** -* v1.1.4 unified event data structure: **only when you handle these top level events, no change if you use commandline** - * event "pdfParser_dataError": {"parserError": errObj} - * event "pdfParser_dataReady": {"formImage": parseOutput} __note__: "formImage" is removed from v2.0.0, see breaking changes for details. +- v1.1.4 unified event data structure: **only when you handle these top level events, no change if you use commandline** -* v1.0.8 fixed [issue 27](https://github.com/modesty/pdf2json/issues/27), it converts x coordinate with the same ratio as y, which is 24 (96/4), rather than 8.7 (96/11), please adjust client renderer accordingly when position all elements' x coordinate. + - event "pdfParser_dataError": {"parserError": errObj} + - event "pdfParser_dataReady": {"formImage": parseOutput} **note**: "formImage" is removed from v2.0.0, see breaking changes for details. -* v2.0.0 output data field, `Agency` and `Id` are replaced with `Meta`, JSON of the PDF's full metadata. (See above for details). Each page object also added `Width` property besides `Height`. +- v1.0.8 fixed [issue 27](https://github.com/modesty/pdf2json/issues/27), it converts x coordinate with the same ratio as y, which is 24 (96/4), rather than 8.7 (96/11), please adjust client renderer accordingly when position all elements' x coordinate. -* v3.0.0 converted commonJS to ES Modules, plus dependency update and other minor bug fixes. Please update your project configuration file to enable ES Module before upgrade, ex., in `tsconfig.json`, set `"compilerOptions":{"module":"ESNext"}` +- v2.0.0 output data field, `Agency` and `Id` are replaced with `Meta`, JSON of the PDF's full metadata. (See above for details). Each page object also added `Width` property besides `Height`. -**Major Refactoring** -* v2.0.0 has the major refactoring since 2015. Primary updates including: - * Full PDF metadata support (see page format and breaking changes for details) - * Simplify root properties, besides the addition of `Meta` as root property, unnecessary "formImage" is removed from v2.0.0, also `Width` is move from root to each page object under `Pages`. - * Improved Stream support with test _`npm run parse-r`_, plus new events are added to PDF.js, including _`readable`_, _`data`_, _`end`_, _`error`_. These new Readable Stream like events can be optional replacement for customed events (_`pdfjs_parseDataReady`_, and _`pdfjs_parseDataError`_). It offers more granular data chunk flow control, like _`readable`_ with Meta, _`data`_ sequence for each PDF page result, instead of _`pdfjs_parseDataReady`_ combines all pages in one shot. See `./lib/parserstream.js` for more details - * Object with {clr:-1} (like HLines, VLines, Fills, etc.) is replaced with {oc: "#xxxxxx"}. If `clr` index value is valid, then `oc` (original color) field is removed. - * Greater performance, near ~20% improvements with PDFs under _test_ directory - * Better exception handling, fixes a few uncaught exception errors - * More test coverage, 4 more test scripts added, see _package.json_ for details - * Easier access to dictionaries, including color, font face and font style, see Dictionary reference section for details - * Refactor to ES6 class for major entry modules - * Dependencies removed: lodash, async and yargs - * Upgrade to Node v14.18.0 LTSs -* v3.0.0 converted commonJS to ES Modules +- v3.0.0 converted commonJS to ES Modules, plus dependency update and other minor bug fixes. Please update your project configuration file to enable ES Module before upgrade, ex., in `tsconfig.json`, set `"compilerOptions":{"module":"ESNext"}` +## Major Refactoring + +- v2.0.0 has the major refactoring since 2015. Primary updates including: + - Full PDF metadata support (see page format and breaking changes for details) + - Simplify root properties, besides the addition of `Meta` as root property, unnecessary "formImage" is removed from v2.0.0, also `Width` is move from root to each page object under `Pages`. + - Improved Stream support with test _`npm run parse-r`_, plus new events are added to PDF.js, including _`readable`_, _`data`_, _`end`_, _`error`_. These new Readable Stream like events can be optional replacement for customed events (_`pdfjs_parseDataReady`_, and _`pdfjs_parseDataError`_). It offers more granular data chunk flow control, like _`readable`_ with Meta, _`data`_ sequence for each PDF page result, instead of _`pdfjs_parseDataReady`_ combines all pages in one shot. See `./lib/parserstream.js` for more details + - Object with {clr:-1} (like HLines, VLines, Fills, etc.) is replaced with {oc: "#xxxxxx"}. If `clr` index value is valid, then `oc` (original color) field is removed. + - Greater performance, near ~20% improvements with PDFs under _test_ directory + - Better exception handling, fixes a few uncaught exception errors + - More test coverage, 4 more test scripts added, see _package.json_ for details + - Easier access to dictionaries, including color, font face and font style, see Dictionary reference section for details + - Refactor to ES6 class for major entry modules + - Dependencies removed: lodash, async and yargs + - Upgrade to Node v14.18.0 LTSs +- v3.0.0 converted commonJS to ES Modules + - v3.1.0 added build step to output both ES Module and CommonJS bundles + - `PDFParser` is no longer the default export, it's a named export that requires changes to import statement. + - test is written in Jest + - PR will require GitHub work flow check, currently is `npm ci` and `npm test` ### Install on Ubuntu -* Make sure nodejs is installed. Detailed installation steps can be found at http://stackoverflow.com/a/16303380/433814. +- Make sure nodejs is installed. Detailed installation steps can be found at . -```` +```terminal $ nodejs --version v0.10.22 -```` +``` -* Create a symbolic link from node to nodejs +- Create a symbolic link from node to nodejs -``` -$ sudo rm -f /usr/sbin/node -$ sudo ln -s /usr/bin/nodejs /usr/sbin/node +```terminal +sudo rm -f /usr/sbin/node +sudo ln -s /usr/bin/nodejs /usr/sbin/node ``` -* Verify the version of node and installation +- Verify the version of node and installation -``` +```terminal $ which node /usr/sbin/node @@ -988,16 +1101,16 @@ $ node --version v4.5.0 ``` -* Proceed with the install of pdf2json as described above +- Proceed with the install of pdf2json as described above -``` -$ sudo npm install -g pdf2json +```terminal +$ npm install -g pdf2json npm http GET https://registry.npmjs.org/pdf2json npm http 304 https://registry.npmjs.org/pdf2json /usr/bin/pdf2json -> /usr/lib/node_modules/pdf2json/bin/pdf2json pdf2json@0.6.1 /usr/lib/node_modules/pdf2json -$ which pdf2json +$ which pdf2json /usr/bin/pdf2json $ pdf2json --version diff --git a/rollup.config.js b/rollup.config.js index 7e0a3b39..ea3a419a 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,33 +1,56 @@ -import nodeResolve from '@rollup/plugin-node-resolve'; -import replace from '@rollup/plugin-replace'; -import builtins from 'rollup-plugin-node-builtins'; +import nodeResolve from "@rollup/plugin-node-resolve"; +import builtins from "rollup-plugin-node-builtins"; +import path from "path"; +import inject from "rollup-plugin-inject"; +import sourcemaps from "rollup-plugin-sourcemaps"; export default [ - { - input: './pdfparser.js', - external: [ - 'fs', - 'util', - 'fs/promises', - 'events', - 'path', - 'url', - 'buffer', - '@xmldom/xmldom', - 'stream', - ], - output: { - file: 'pdfparser.cjs', - format: 'cjs', - // exports: 'named', - }, - treeshake: false, - plugins: [ - replace({ - '../base': '/base/', - delimiters: ['/', '/'], - }), - builtins(), - ], - }, + { + input: "./pdfparser.js", + external: [ + "fs", + "util", + "fs/promises", + "events", + "path", + "url", + "buffer", + "stream", + "@xmldom/xmldom", + ], + output: [ + { + file: "dist/pdfparser.cjs", + format: "cjs", + sourcemap: true, + }, + { + file: "dist/pdfparser.js", + format: "es", + sourcemap: true, + }, + ], + treeshake: false, + plugins: [ + nodeResolve({ + preferBuiltins: true, + }), + builtins(), + inject({ + createScratchCanvas: [ + path.resolve("lib/pdfcanvas.js"), + "createScratchCanvas", + ], + PDFAnno: [ + path.resolve("lib/pdfanno.js"), + "PDFAnno", + ], + Image: [ + path.resolve("lib/pdfimage.js"), + "Image", + ], + }), + sourcemaps(), + ], + }, ]; diff --git a/rollup/addDestructedImports.js b/rollup/addDestructedImports.js deleted file mode 100644 index 79121441..00000000 --- a/rollup/addDestructedImports.js +++ /dev/null @@ -1,46 +0,0 @@ -import fs from 'fs'; -import path from 'path'; -import { fileURLToPath } from 'url'; -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); -// Specify the directory where you want to search for pdfparser.cjs -const directoryToSearch = path.join(__dirname, '..'); - -// Function to search for pdfparser.cjs and update the file -function updatePdfParser(filePath) { - // Read the content of the file - const content = fs.readFileSync(filePath, 'utf8'); - - // Check if the file contains the line we want to modify - if (content.includes("var xmldom = require('@xmldom/xmldom');")) { - // Add the line underneath - const updatedContent = content.replace( - "var xmldom = require('@xmldom/xmldom');", - "var xmldom = require('@xmldom/xmldom');\nvar DOMParser = xmldom.DOMParser;" - ).replace( - "require('buffer');", - "var { Blob } = require('buffer');" - ).replace("const PDFJS = {};", "const PDFJS = {};\nvar Image = PDFImage") - - // Write the updated content back to the file - fs.writeFileSync(filePath, updatedContent, 'utf8'); - console.log(`Updated ${filePath} successfully.`); - } else { - console.log(`File ${filePath} does not contain the required line.`); - } -} - -// Function to recursively search for pdfparser.cjs -function searchForPdfParser(directory) { - fs.readdirSync(directory).forEach((file) => { - const filePath = path.join(directory, file); - if (fs.statSync(filePath).isDirectory()) { - searchForPdfParser(filePath); - } else if (file === 'pdfparser.cjs') { - updatePdfParser(filePath); - } - }); -} - -// Start searching from the specified directory -searchForPdfParser(directoryToSearch); diff --git a/rollup/bundle-pdfjs-base.js b/rollup/bundle-pdfjs-base.js new file mode 100644 index 00000000..b3f04491 --- /dev/null +++ b/rollup/bundle-pdfjs-base.js @@ -0,0 +1,77 @@ +import fs from "fs"; +import path from "path"; +import { fileURLToPath } from "url"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +// const _pdfjsFiles = []; +const baseDir = `${__dirname}/../base/`; + +// function scanPdfJsBaseFiles(directory) { +// fs.readdirSync(directory).forEach((file) => { +// const filePath = path.join(directory, file); +// if (fs.statSync(filePath).isDirectory()) { +// scanPdfJsBaseFiles(filePath); +// } else if (filePath.endsWith(".js")) { +// _pdfjsFiles.push(filePath); +// } +// }); +// } + +// scanPdfJsBaseFiles(baseDir); // order matters, will revisit this later + +const _pdfjsFiles = [ + "shared/util.js", + "shared/colorspace.js", + "shared/pattern.js", + "shared/function.js", + "shared/annotation.js", + + "core/core.js", + "core/obj.js", + "core/charsets.js", + "core/crypto.js", + "core/evaluator.js", + "core/fonts.js", + "core/font_renderer.js", + "core/glyphlist.js", + "core/image.js", + "core/metrics.js", + "core/parser.js", + "core/stream.js", + "core/worker.js", + "core/jpx.js", + "core/jbig2.js", + "core/bidi.js", + "core/jpg.js", + "core/chunked_stream.js", + "core/pdf_manager.js", + "core/cmap.js", + "core/cidmaps.js", + + "display/canvas.js", + "display/font_loader.js", + "display/metadata.js", + "display/api.js", +]; + +const _baseCode = _pdfjsFiles.reduce( + (preContent, fileName) => + (preContent += fs.readFileSync(path.join(baseDir, fileName), "utf8")), + "" +); + +fs.writeFileSync( + __dirname + "/../lib/pdfjs-code.js", + ` + ${"import nodeUtil from 'util';import { Blob } from 'buffer';import { DOMParser } from '@xmldom/xmldom';import PDFAnno from './pdfanno.js';import Image from './pdfimage.js';import { createScratchCanvas } from './pdfcanvas.js';"} + ${"export const PDFJS = {};"} + ${"const globalScope = { console };"} + ${_baseCode} + `, + { + encoding: "utf8", + mode: 0o666, + } +); diff --git a/test/_test_.cjs b/test/_test_.cjs new file mode 100644 index 00000000..84af4b56 --- /dev/null +++ b/test/_test_.cjs @@ -0,0 +1,218 @@ +const assert = require("assert"); +const fs = require("fs"); + +const { PDFParser } = require("../dist/pdfparser.cjs"); + +function pdfParserRunner(fileName, fromBuffer) { + const pdfParser = new PDFParser(); + + var pdfFilePath = __dirname + "/pdf/fd/form/" + fileName + ".pdf"; + if (fromBuffer) { + pdf = fs.readFileSync(pdfFilePath); + pdfParser.parseBuffer(pdf); + } else { + pdfParser.loadPDF(pdfFilePath); + } + + return pdfParser; +} + +function checkResult_parseStatus(err, stat, fileName) { + assert( + err === null || typeof err === "undefined", + fileName + " has errors : " + err || "" + ); + assert( + typeof stat === "object" && stat !== null, + fileName + " parsing result should be JS object : " + stat || "" + ); +} + +function checkResult_mainFields(parsedData, fileName) { + assert( + parsedData.hasOwnProperty("Transcoder"), + fileName + " parsing error: doesn't have Transcoder object" + ); + assert( + parsedData.hasOwnProperty("Meta"), + fileName + " parsing error: doesn't have Meta object" + ); + assert( + parsedData.Meta.hasOwnProperty("Metadata"), + fileName + " parsing error: doesn't have Meta.Metadata object" + ); + assert( + parsedData.hasOwnProperty("Pages"), + fileName + " parsing error: doesn't have Pages object" + ); +} + +function checkResult_pageCount(Pages, count, fileName) { + assert( + Array.isArray(Pages), + fileName + " parsing error: doesn't have Pages array" + ); + assert( + Pages.length === count, + fileName + + " parsing error: Pages count " + + Pages.length + + " not equal to " + + count + ); +} + +function checkResult_pageContent(Pages, fileName) { + Pages.forEach((page, index, list) => { + assert( + page.hasOwnProperty("Height"), + fileName + " page " + index + " : doesn't have Height field" + ); + assert( + page.hasOwnProperty("HLines"), + fileName + " page " + index + " : doesn't have HLines object" + ); + assert( + page.hasOwnProperty("VLines"), + fileName + " page " + index + " : doesn't have VLines object" + ); + assert( + page.hasOwnProperty("Fills"), + fileName + " page " + index + " : doesn't have Fills object" + ); + assert( + page.hasOwnProperty("Texts"), + fileName + " page " + index + " : doesn't have Texts object" + ); + assert( + page.hasOwnProperty("Width"), + fileName + " page " + index + " : doesn't have With object" + ); + assert( + page.hasOwnProperty("Height"), + fileName + " page " + index + " : doesn't have Height object" + ); + }); +} + +async function parseAndVerifyOnePDF(fileName, fromBuffer, pageCount) { + const pdfParser = pdfParserRunner(fileName, fromBuffer); + const pdfParserDataReady = new Promise((resolve, reject) => { + pdfParser.on("pdfParser_dataReady", (evtData) => { + resolve(evtData); + }); + + pdfParser.on("pdfParser_dataError", (evtData) => { + reject(evtData); + }); + }); + + try { + const evtData = await pdfParserDataReady; + expect(evtData).toBeDefined(); + checkResult_parseStatus(null, evtData, fileName); + checkResult_mainFields(evtData, fileName); + checkResult_pageCount(evtData.Pages, pageCount, fileName); + checkResult_pageContent(evtData.Pages, fileName); + } catch (error) { + console.error("Error: ", error); + expect(error).toBeNull(); + } +} + +describe("Federal main forms", () => { + test("1040ez file and buffer", async () => { + await parseAndVerifyOnePDF("F1040EZ", false, 2); + await parseAndVerifyOnePDF("F1040EZ", true, 2); + }); + test("1040a file and buffer", async () => { + await parseAndVerifyOnePDF("F1040A", false, 2); + await parseAndVerifyOnePDF("F1040A", true, 2); + }); + test("1040 file and buffer", async () => { + await parseAndVerifyOnePDF("F1040", false, 2); + await parseAndVerifyOnePDF("F1040", true, 2); + }); + test("1040st file and buffer", async () => { + await parseAndVerifyOnePDF("F1040ST", false, 2); + await parseAndVerifyOnePDF("F1040ST", true, 2); + }); + test("1040V file and buffer", async () => { + await parseAndVerifyOnePDF("F1040V", false, 1); + await parseAndVerifyOnePDF("F1040V", true, 1); + }); +}); + +describe("Federal schedules", () => { + test("Fed Schedule A", async () => { + await parseAndVerifyOnePDF("FSCHA", false, 1); + }); + test("Fed Schedule B, B2, B3", async () => { + await parseAndVerifyOnePDF("FSCHB", true, 1); + await parseAndVerifyOnePDF("FSCHB2", false, 1); + await parseAndVerifyOnePDF("FSCHB3", true, 1); + }); + test("Fed schedule C, CEZS, CEZT", async () => { + await parseAndVerifyOnePDF("FSCHC", true, 2); + await parseAndVerifyOnePDF("FSCHCEZS", true, 1); + await parseAndVerifyOnePDF("FSCHCEZT", true, 1); + }); + test("Fed schedule D", async () => { + await parseAndVerifyOnePDF("FSCHD", true, 2); + }); + test("Fed schedule E1, E2, EIC", async () => { + await parseAndVerifyOnePDF("FSCHE1", true, 1); + await parseAndVerifyOnePDF("FSCHE2", true, 1); + await parseAndVerifyOnePDF("FSCHEIC", true, 1); + }); + test("Fed schedule F", async () => { + await parseAndVerifyOnePDF("FSCHF", true, 2); + }); + test("Fed schedule H HS, HT", async () => { + await parseAndVerifyOnePDF("FSCHHS", true, 2); + await parseAndVerifyOnePDF("FSCHHT", true, 2); + }); + test("Fed schedule J", async () => { + await parseAndVerifyOnePDF("FSCHJ", true, 2); + }); + test("Fed schedule R", async () => { + await parseAndVerifyOnePDF("FSCHR", true, 2); + }); +}); + +describe("Federal other forms", () => { + test("F982", async () => { + await parseAndVerifyOnePDF("F982", false, 1); + }); + test("F1116", async () => { + await parseAndVerifyOnePDF("F1116", false, 2); + }); + test("F1310", async () => { + await parseAndVerifyOnePDF("F1310", false, 1); + }); + test("F2106, EZ, EZS, S", async () => { + await parseAndVerifyOnePDF("F2106", false, 2); + await parseAndVerifyOnePDF("F2106EZ", false, 1); + await parseAndVerifyOnePDF("F2106EZS", false, 1); + await parseAndVerifyOnePDF("F2106S", false, 2); + }); + test("F2120", async () => { + await parseAndVerifyOnePDF("F2120", false, 1); + }); + test("F2210, AI, F", async () => { + await parseAndVerifyOnePDF("F2210", false, 3); + await parseAndVerifyOnePDF("F2210AI", false, 1); + await parseAndVerifyOnePDF("F2210F", false, 1); + }); + test("F2439", async () => { + await parseAndVerifyOnePDF("F2439", false, 1); + }); + test("F2441, DEP", async () => { + await parseAndVerifyOnePDF("F2441", false, 2); + await parseAndVerifyOnePDF("F2441DEP", false, 1); + }); + test("F2555EZ, EZS", async () => { + await parseAndVerifyOnePDF("F2555EZ", false, 2); + await parseAndVerifyOnePDF("F2555EZS", false, 2); + }); +}); diff --git a/test/index.js b/test/index.js deleted file mode 100644 index 8df253c2..00000000 --- a/test/index.js +++ /dev/null @@ -1,217 +0,0 @@ -//Vows test suite to load and parse 3 PDF in parallel -//12 test cases should be honored - -var vows = require('vows'), - assert = require('assert'), - fs = require('fs'); -(nodeEvents = require('events')), - (_ = require('lodash')), - (PdfParser_js = require('../pdfparser.js')); -PdfParser_cjs = require('../pdfparser.cjs'); -PdfParser_mjs = require('../pdfparser.mjs'); - -testSuite(PdfParser_js); -testSuite(PdfParser_cjs); -testSuite(PdfParser_mjs); - -function testSuite(PFParser) { - var suite = vows.describe('PDF Node Parser'); - - function pdfParserPromise(fileName, fromBuffer) { - var promise = new nodeEvents.EventEmitter(); - - var pdfParser = new PFParser(); - - pdfParser.on('pdfParser_dataReady', function (evtData) { - if (!!evtData && !!evtData.data) { - promise.emit('success', evtData.data); - } else { - promise.emit('error', new Error()); - } - }); - - pdfParser.on('pdfParser_dataError', function (evtData) { - promise.emit('error', evtData.data); - }); - - var pdfFilePath = __dirname + '/data/' + fileName + '.pdf'; - if (fromBuffer) { - pdf = fs.readFileSync(pdfFilePath); - pdfParser.parseBuffer(pdf); - } else { - pdfParser.loadPDF(pdfFilePath); - } - - return promise; - } - - function checkResult_parseStatus(err, stat, fileName) { - assert(_.isNull(err) || _.isUndefined(err), fileName + ' has errors!'); // We have no error - assert( - _.isObject(stat), - fileName + ' parsing result should be JS object' - ); // We have a stat object - } - - function checkResult_mainFields(parsedData, fileName) { - assert( - _.has(parsedData, 'Agency'), - fileName + " parsing error: doesn't have Agency object" - ); - assert( - _.has(parsedData, 'Id'), - fileName + " parsing error: doesn't have Id object" - ); - assert( - _.has(parsedData, 'Pages'), - fileName + " parsing error: doesn't have Pages object" - ); - assert( - _.has(parsedData, 'Width'), - fileName + " parsing error: doesn't have Width object" - ); - } - - function checkResult_pageCount(Pages, count, fileName) { - assert( - _.isArray(Pages), - fileName + " parsing error: doesn't have Pages array" - ); - assert( - Pages.length === count, - fileName + ' parsing error: Pages array is empty' - ); - } - - function checkResult_pageContent(Pages, fileName) { - _.each(Pages, function (page, index, list) { - assert( - _.has(page, 'Height'), - fileName + ' page ' + index + " : doesn't have Height field" - ); - assert( - _.has(page, 'HLines'), - fileName + ' page ' + index + " : doesn't have HLines object" - ); - assert( - _.has(page, 'VLines'), - fileName + ' page ' + index + " : doesn't have VLines object" - ); - assert( - _.has(page, 'Fills'), - fileName + ' page ' + index + " : doesn't have Fills object" - ); - assert( - _.has(page, 'Texts'), - fileName + ' page ' + index + " : doesn't have Texts object" - ); - }); - } - - suite.addBatch({ - 'Parse 1040ez first (from file)': { - topic: function () { - return pdfParserPromise('f1040ez', false); - }, - 'has parsing data': function (err, stat) { - checkResult_parseStatus(err, stat, 'f1040ez'); - }, - 'has four main objects': function (err, stat) { - checkResult_mainFields(stat, 'f1040ez'); - }, - 'has pages': function (err, stat) { - checkResult_pageCount(stat.Pages, 1, 'f1040ez'); - }, - 'has page elements': function (err, stat) { - checkResult_pageContent(stat.Pages, 'f1040ez'); - }, - }, - 'Parse 1040ez first (from buffer)': { - topic: function () { - return pdfParserPromise('f1040ez', true); - }, - 'has parsing data': function (err, stat) { - checkResult_parseStatus(err, stat, 'f1040ez'); - }, - 'has four main objects': function (err, stat) { - checkResult_mainFields(stat, 'f1040ez'); - }, - 'has pages': function (err, stat) { - checkResult_pageCount(stat.Pages, 1, 'f1040ez'); - }, - 'has page elements': function (err, stat) { - checkResult_pageContent(stat.Pages, 'f1040ez'); - }, - }, - 'Parse 1040a (from file)': { - topic: function () { - return pdfParserPromise('f1040a', false); - }, - 'has parsing data': function (err, stat) { - checkResult_parseStatus(err, stat, 'f1040a'); - }, - 'has four main objects': function (err, stat) { - checkResult_mainFields(stat, 'f1040a'); - }, - 'has pages': function (err, stat) { - checkResult_pageCount(stat.Pages, 2, 'f1040a'); - }, - 'has page elements': function (err, stat) { - checkResult_pageContent(stat.Pages, 'f1040a'); - }, - }, - 'Parse 1040a (from buffer)': { - topic: function () { - return pdfParserPromise('f1040a', true); - }, - 'has parsing data': function (err, stat) { - checkResult_parseStatus(err, stat, 'f1040a'); - }, - 'has four main objects': function (err, stat) { - checkResult_mainFields(stat, 'f1040a'); - }, - 'has pages': function (err, stat) { - checkResult_pageCount(stat.Pages, 2, 'f1040a'); - }, - 'has page elements': function (err, stat) { - checkResult_pageContent(stat.Pages, 'f1040a'); - }, - }, - 'Parse 1040 (from file)': { - topic: function () { - return pdfParserPromise('f1040', false); - }, - 'has parsing data': function (err, stat) { - checkResult_parseStatus(err, stat, 'f1040'); - }, - 'has four main objects': function (err, stat) { - checkResult_mainFields(stat, 'f1040'); - }, - 'has pages': function (err, stat) { - checkResult_pageCount(stat.Pages, 2, 'f1040'); - }, - 'has page elements': function (err, stat) { - checkResult_pageContent(stat.Pages, 'f1040'); - }, - }, - 'Parse 1040 (from buffer)': { - topic: function () { - return pdfParserPromise('f1040', true); - }, - 'has parsing data': function (err, stat) { - checkResult_parseStatus(err, stat, 'f1040'); - }, - 'has four main objects': function (err, stat) { - checkResult_mainFields(stat, 'f1040'); - }, - 'has pages': function (err, stat) { - checkResult_pageCount(stat.Pages, 2, 'f1040'); - }, - 'has page elements': function (err, stat) { - checkResult_pageContent(stat.Pages, 'f1040'); - }, - }, - }); - - suite.run(); -}