├── .clang-format ├── .github └── workflows │ └── tests.yaml ├── .gitignore ├── .npmignore ├── .prettierrc.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── dist ├── index.d.ts └── index.js ├── jest.config.js ├── package-lock.json ├── package.json ├── src └── index.ts ├── test ├── files │ ├── index.ts │ ├── out2.txt │ ├── pdf1.pdf │ ├── pdf2.pdf │ ├── pdf3.pdf │ ├── renderPDFPagesToPNG-rotated-output.png │ └── renderPDFPagesToPNG-rotated-output2.png └── index.test.ts └── tsconfig.json /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: google 2 | ColumnLimit: 120 3 | -------------------------------------------------------------------------------- /.github/workflows/tests.yaml: -------------------------------------------------------------------------------- 1 | name: Unit tests 2 | on: 3 | push: 4 | branches: [ main ] 5 | pull_request: 6 | branches: [ main ] 7 | jobs: 8 | tests: 9 | runs-on: ubuntu-latest 10 | steps: 11 | # Checkout the Repo 12 | - uses: actions/checkout@v2 13 | 14 | # Install Node 16 15 | - name: Setup Node 16 | uses: actions/setup-node@v1 17 | with: 18 | node-version: 16 19 | 20 | # Install APT dependencies 21 | - run: sudo apt update && sudo apt install -y python2.7 python3 build-essential ghostscript libjpeg-dev libpng-dev libcurl4-openssl-dev mupdf-tools libfreetype6-dev qpdf 22 | 23 | # Install NPM dependencies 24 | - run: npm install 25 | 26 | # Run tests 27 | - run: npm test 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | 84 | # Gatsby files 85 | .cache/ 86 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 87 | # https://nextjs.org/blog/next-9-1#public-directory-support 88 | # public 89 | 90 | # vuepress build output 91 | .vuepress/dist 92 | 93 | # Serverless directories 94 | .serverless/ 95 | 96 | # FuseBox cache 97 | .fusebox/ 98 | 99 | # DynamoDB Local files 100 | .dynamodb/ 101 | 102 | # TernJS port file 103 | .tern-port 104 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | test/* 2 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "trailingComma": "all", 4 | "singleQuote": true, 5 | "ignorePath": ".gitignore", 6 | "arrowParens": "avoid" 7 | } 8 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 1.5.0 (2023-09-06) 2 | 3 | - add `compressPDF` (gabolera) [#4](https://github.com/sigalor/ghostscript-node/pull/4) 4 | - add `convertToPDFA` (MedAzizKhayati) [#6](https://github.com/sigalor/ghostscript-node/pull/6) 5 | 6 | # 1.4.1 (2023-01-26) 7 | 8 | - updated NPM dependencies 9 | 10 | # 1.4.0 (2022-06-07) 11 | 12 | - transformed entire library to use TypeScript 13 | - fix `countPDFPages` to ignore Ghostscript warnings (e.g. due to a damaged XREF table) 14 | 15 | # 1.3.2 (2022-02-16) 16 | 17 | - escape input in countPDFPages for Windows, fix redundant await (Loupi) [#2](https://github.com/sigalor/ghostscript-node/pull/2) 18 | 19 | # 1.3.1 (2022-01-24) 20 | 21 | - fix handling of rotated PDFs 22 | - improve internal structure of unit tests 23 | 24 | # 1.3.0 (2022-01-06) 25 | 26 | - added `isValidPDF` function 27 | 28 | # 1.2.0 (2021-07-27) 29 | 30 | - added `renderPDFPagesToPNG` function 31 | 32 | # 1.1.2 (2021-01-28) 33 | 34 | - use qpdf instead of PDFtk for rotating PDFs 35 | 36 | # 1.1.1 (2021-01-28) 37 | 38 | - fix TypeScript definition file 39 | 40 | # 1.1.0 (2021-01-28) 41 | 42 | - added `rotatePDF` function, based on PDFtk 43 | 44 | # 1.0.0 (2021-01-25) 45 | 46 | - initial release 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 sigalor 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ghostscript-node 2 | 3 | [![GitHub license](https://img.shields.io/github/license/sigalor/ghostscript-node)](https://github.com/sigalor/ghostscript-node/blob/master/LICENSE) [![npm](https://img.shields.io/npm/v/ghostscript-node)](https://www.npmjs.com/package/ghostscript-node) [![Unit tests workflow status](https://github.com/sigalor/ghostscript-node/actions/workflows/tests.yaml/badge.svg)](https://github.com/sigalor/ghostscript-node/actions/workflows/tests.yaml) 4 | 5 | ## Introduction 6 | 7 | A fully promise-based Node.js library which can work with PDFs, based on Ghostscript and qpdf. 8 | 9 | All PDF files that are handled by this library in the form of `Buffer` objects, i.e. to a user of this library, it looks like everything works only in memory. In the background though, file system access (via [tempy](https://www.npmjs.com/package/tempy)) is needed. 10 | 11 | Additionally, this library requires the `gs` command (Ghostscript) as well as `qpdf` to be available. The required apt dependencies on Ubuntu 20.04 can be installed via the following command: 12 | 13 | ``` 14 | sudo apt install -y python python3 build-essential ghostscript libjpeg-dev libpng-dev libcurl4-openssl-dev mupdf-tools libfreetype6-dev qpdf 15 | ``` 16 | 17 | ## Getting started 18 | 19 | ``` 20 | npm install ghostscript-node 21 | ``` 22 | 23 | ## Usage 24 | 25 | ```javascript 26 | const gs = require("ghostscript-node"); 27 | 28 | (async () => { 29 | const pdf1 = /* load first PDF as Buffer, e.g. from database */; 30 | const pdf2 = /* load second PDF as Buffer, e.g. from database */; 31 | 32 | // get Buffer object with bytes of combined PDF 33 | const combinedPDF = await gs.combinePDFs([pdf1, pdf2]); 34 | 35 | // get number of pages of pdf1 36 | const numPagesPDF1 = await gs.countPDFPages(pdf1); 37 | 38 | // get Buffer object with bytes of specified set of pages 39 | // page numbers begin with 1, last page is included 40 | const partOfPDF1 = await gs.extractPDFPages(pdf1, 3, 5); 41 | 42 | // rotate all pages of pdf1 by 90 degrees clockwise 43 | const rotatedPDF = await gs.rotatePDF(pdf1, "90"); 44 | 45 | // returns an array of buffers containing PNG images of the desired pages 46 | const renderedPages = await gs.renderPDFPagesToPNG(pdf1); 47 | 48 | // checks if pdf1 is a valid PDF file 49 | const isPDF1Valid = await gs.isValidPDF(pdf1); 50 | })(); 51 | ``` 52 | 53 | ## License 54 | 55 | MIT 56 | -------------------------------------------------------------------------------- /dist/index.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | export declare function combinePDFs(pdfBuffers: Buffer[]): Promise; 3 | export declare function countPDFPages(pdfBuffer: Buffer): Promise; 4 | export declare function extractPDFPages(pdfBuffer: Buffer, firstPage: number, lastPage: number): Promise; 5 | export declare function rotatePDF(pdfBuffer: Buffer, direction: '90' | '180' | '270'): Promise; 6 | /** 7 | * Converts a PDF to PDF/A. 8 | * 9 | * @param pdfBuffer - Buffer of the PDF to convert 10 | * @param options - Options for the conversion 11 | * @param options.version - PDF/A version to convert to. Defaults to 1. 12 | * @returns 13 | */ 14 | export declare function convertToPDFA(pdfBuffer: Buffer, options?: { 15 | version: number; 16 | }): Promise; 17 | /** 18 | * If `firstPage` is not given, 1 is used. 19 | * If `lastPage` is not given, the document's last page is used. 20 | * If `firstPage` is negative (e.g. -n), this refers to the last n pages and `lastPage` must be undefined. 21 | * All page numbers start at 1. 22 | */ 23 | export declare function renderPDFPagesToPNG(pdfBuffer: Buffer, firstPage?: number, lastPage?: number, resolution?: number): Promise; 24 | export declare function isValidPDF(pdfBuffer: Buffer): Promise; 25 | /** 26 | * This function try, reduce size of your PDF not destroying quality 27 | * @param pdfBuffer Buffer 28 | * @returns Buffer 29 | */ 30 | export declare function compressPDF(pdfBuffer: Buffer | string, encoding?: BufferEncoding): Promise; 31 | -------------------------------------------------------------------------------- /dist/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | exports.compressPDF = exports.isValidPDF = exports.renderPDFPagesToPNG = exports.convertToPDFA = exports.rotatePDF = exports.extractPDFPages = exports.countPDFPages = exports.combinePDFs = void 0; 7 | const child_process_1 = __importDefault(require("child_process")); 8 | const fs_extra_1 = __importDefault(require("fs-extra")); 9 | const tempy_1 = __importDefault(require("tempy")); 10 | const util_1 = __importDefault(require("util")); 11 | const exec = util_1.default.promisify(child_process_1.default.exec); 12 | async function useTempFiles(filenameSets, fn) { 13 | const filenames = {}; 14 | // create all desired temporary files (either empty files or write data buffers to them) 15 | for (const [k, config] of Object.entries(filenameSets)) { 16 | const { numFiles, writeBuffers, tempyConfig } = config; 17 | if (numFiles !== undefined) { 18 | filenames[k] = Array(numFiles) 19 | .fill(0) 20 | .map(() => tempy_1.default.file(tempyConfig)); 21 | } 22 | else if (writeBuffers !== undefined) { 23 | filenames[k] = await Promise.all(writeBuffers.map(b => tempy_1.default.write(b, tempyConfig))); 24 | } 25 | } 26 | // execute the worker function which uses these files 27 | const ret = await fn(filenames); 28 | // remove all the temporary files again 29 | await Promise.all([].concat(...Object.values(filenames)).map(f => fs_extra_1.default.unlink(f))); 30 | // return the result of the worker function 31 | return ret; 32 | } 33 | async function useTempFilesPDF(filenameSets, fn) { 34 | Object.values(filenameSets).forEach(v => (v.tempyConfig = { extension: '.pdf' })); 35 | return useTempFiles(filenameSets, fn); 36 | } 37 | // writes inputBuffer to one temporary file, creates an empty output file, calls the worker function and returns the output file contents as Buffer 38 | async function useTempFilesPDFInOut(inputBuffer, fn) { 39 | return useTempFilesPDF({ input: { writeBuffers: [inputBuffer] }, output: { numFiles: 1 } }, async ({ input, output }) => { 40 | await fn(input[0], output[0]); 41 | return fs_extra_1.default.readFile(output[0]); 42 | }); 43 | } 44 | async function useTempFilesPDFIn(inputBuffer, fn) { 45 | return useTempFilesPDF({ input: { writeBuffers: [inputBuffer] } }, async ({ input }) => fn(input[0])); 46 | } 47 | async function combinePDFs(pdfBuffers) { 48 | if (pdfBuffers.length === 0) 49 | return Buffer.alloc(0); 50 | if (pdfBuffers.length === 1) 51 | return pdfBuffers[0]; 52 | try { 53 | return await useTempFilesPDF({ inputs: { writeBuffers: pdfBuffers }, output: { numFiles: 1 } }, async ({ inputs, output }) => { 54 | await exec(`gs -q -dNOPAUSE -sDEVICE=pdfwrite -sOUTPUTFILE=${output[0]} -dBATCH -dAutoRotatePages=/None ${inputs.join(' ')} -c "[ /Creator () /Producer () /DOCINFO pdfmark"`); 55 | return fs_extra_1.default.readFile(output[0]); 56 | }); 57 | } 58 | catch (e) { 59 | throw new Error('Failed to combine PDFs: ' + e.message); 60 | } 61 | } 62 | exports.combinePDFs = combinePDFs; 63 | async function countPDFPages(pdfBuffer) { 64 | try { 65 | return await useTempFilesPDFIn(pdfBuffer, async (input) => { 66 | const escapedInput = input.replace(/\\/g, '\\\\'); 67 | let { stdout } = await exec(`gs -q -dNOPAUSE -dBATCH -dNOSAFER -dNODISPLAY -c "(${escapedInput}) (r) file runpdfbegin pdfpagecount = quit"`); 68 | /** 69 | * Necessary for when Ghostscript detects a damaged but repairable PDF file, because then it outputs 70 | * the following before the number of pages (three spaces before each line starting with "****"): 71 | * 72 | * **** Error: An error occurred while reading an XREF table. 73 | * **** The file has been damaged. This may have been caused 74 | * **** by a problem while converting or transfering the file. 75 | * **** Ghostscript will attempt to recover the data. 76 | * **** However, the output may be incorrect. 77 | */ 78 | stdout = stdout 79 | .split('\n') 80 | .filter(l => !l.startsWith(' **** ') && l.length > 0) 81 | .join('\n') 82 | .trim(); 83 | const ret = parseInt(stdout); 84 | if (isNaN(ret)) 85 | throw new Error('parsing failed: ' + stdout); 86 | return ret; 87 | }); 88 | } 89 | catch (e) { 90 | throw new Error('Failed to determine number of pages in PDF: ' + e.message); 91 | } 92 | } 93 | exports.countPDFPages = countPDFPages; 94 | async function extractPDFPages(pdfBuffer, firstPage, lastPage) { 95 | try { 96 | return await useTempFilesPDFInOut(pdfBuffer, async (input, output) => { 97 | await exec(`gs -q -dNOPAUSE -sDEVICE=pdfwrite -dBATCH -dNOSAFER -dFirstPage=${firstPage} -dLastPage=${lastPage} -dAutoRotatePages=/None -sOutputFile=${output} ${input}`); 98 | }); 99 | } 100 | catch (e) { 101 | throw new Error('Failed to extract PDF pages: ' + e.message); 102 | } 103 | } 104 | exports.extractPDFPages = extractPDFPages; 105 | async function rotatePDF(pdfBuffer, direction) { 106 | if (!['90', '180', '270'].includes(direction)) 107 | throw new Error('Invalid rotation direction: ' + direction); 108 | try { 109 | return await useTempFilesPDFInOut(pdfBuffer, async (input, output) => { 110 | await exec(`qpdf ${input} ${output} --rotate=${direction}`); 111 | }); 112 | } 113 | catch (e) { 114 | throw new Error('Failed to rotate PDF: ' + e.message); 115 | } 116 | } 117 | exports.rotatePDF = rotatePDF; 118 | /** 119 | * Converts a PDF to PDF/A. 120 | * 121 | * @param pdfBuffer - Buffer of the PDF to convert 122 | * @param options - Options for the conversion 123 | * @param options.version - PDF/A version to convert to. Defaults to 1. 124 | * @returns 125 | */ 126 | async function convertToPDFA(pdfBuffer, options = { 127 | version: 1, 128 | }) { 129 | try { 130 | return await useTempFilesPDFInOut(pdfBuffer, async (input, output) => { 131 | await exec(`gs -dPDFA -dBATCH -dNOPAUSE -sColorConversionStrategy=UseDeviceIndependentColor -sDEVICE=pdfwrite -dPDFACompatibilityPolicy=${options.version} -sOutputFile=${output} ${input}`); 132 | }); 133 | } 134 | catch (e) { 135 | throw new Error('Failed to convert PDF to PDF/A: ' + e.message); 136 | } 137 | } 138 | exports.convertToPDFA = convertToPDFA; 139 | /** 140 | * If `firstPage` is not given, 1 is used. 141 | * If `lastPage` is not given, the document's last page is used. 142 | * If `firstPage` is negative (e.g. -n), this refers to the last n pages and `lastPage` must be undefined. 143 | * All page numbers start at 1. 144 | */ 145 | async function renderPDFPagesToPNG(pdfBuffer, firstPage, lastPage, resolution = 300) { 146 | const numPages = await countPDFPages(pdfBuffer); 147 | if (firstPage === undefined) 148 | firstPage = 1; 149 | else if (firstPage === 0 || (firstPage < 0 && firstPage < -numPages)) 150 | throw new Error('First page number out of range: ' + firstPage); 151 | if (firstPage < 0) { 152 | if (lastPage !== undefined) 153 | throw new Error('Last page must be undefined when first page is negative'); 154 | firstPage = numPages + firstPage + 1; 155 | lastPage = numPages; 156 | } 157 | if (lastPage === undefined) 158 | lastPage = numPages; 159 | else if (lastPage > numPages) 160 | throw new Error('Last page number out of range: ' + lastPage); 161 | if (firstPage > lastPage) 162 | throw new Error('Invalid page range: ' + firstPage + '-' + lastPage); 163 | try { 164 | return await useTempFilesPDFIn(pdfBuffer, async (input) => { 165 | const outDir = tempy_1.default.directory(); 166 | await exec(`gs -q -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 -sDEVICE=png16m -dTextAlphaBits=4 -dGraphicsAlphaBits=4 -r${resolution} -sOutputFile=${outDir}/%d.png -dFirstPage=${firstPage} -dLastPage=${lastPage} ${input}`); 167 | const outFiles = []; 168 | for (let i = 1; i <= lastPage - firstPage + 1; i++) { 169 | outFiles.push(await fs_extra_1.default.readFile(outDir + '/' + i + '.png')); 170 | } 171 | await fs_extra_1.default.rmdir(outDir, { recursive: true }); 172 | return outFiles; 173 | }); 174 | } 175 | catch (e) { 176 | throw new Error('Failed to render PDF pages to PNG: ' + e.message); 177 | } 178 | } 179 | exports.renderPDFPagesToPNG = renderPDFPagesToPNG; 180 | async function isValidPDF(pdfBuffer) { 181 | try { 182 | await countPDFPages(pdfBuffer); 183 | return true; 184 | } 185 | catch (e) { 186 | return false; 187 | } 188 | } 189 | exports.isValidPDF = isValidPDF; 190 | /** 191 | * This function try, reduce size of your PDF not destroying quality 192 | * @param pdfBuffer Buffer 193 | * @returns Buffer 194 | */ 195 | async function compressPDF(pdfBuffer, encoding) { 196 | try { 197 | if (typeof pdfBuffer === 'string') { 198 | pdfBuffer = Buffer.from(pdfBuffer, encoding !== null && encoding !== void 0 ? encoding : 'base64'); 199 | } 200 | const compressedPdf = await useTempFilesPDFInOut(pdfBuffer, async (input, output) => { 201 | await exec(`gs -q -dNOPAUSE -dBATCH -dSAFER -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/screen -dEmbedAllFonts=true -dSubsetFonts=true -dColorImageDownsampleType=/Bicubic -dColorImageResolution=144 -dGrayImageDownsampleType=/Bicubic -dGrayImageResolution=144 -dMonoImageDownsampleType=/Bicubic -dMonoImageResolution=144 -sOutputFile=${output} ${input}`); 202 | }); 203 | if (pdfBuffer.length < compressedPdf.length) { 204 | return pdfBuffer; 205 | } 206 | return compressedPdf; 207 | } 208 | catch (e) { 209 | throw new Error('Failed optimize PDF: ' + e.message); 210 | } 211 | } 212 | exports.compressPDF = compressPDF; 213 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ 2 | module.exports = { 3 | preset: 'ts-jest', 4 | testEnvironment: 'node', 5 | }; 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ghostscript-node", 3 | "version": "1.5.0", 4 | "description": "A Node.js library which can work with PDFs, based on Ghostscript.", 5 | "main": "dist/index.js", 6 | "scripts": { 7 | "build": "rimraf dist; tsc", 8 | "test": "jest" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/sigalor/ghostscript-node.git" 13 | }, 14 | "keywords": [ 15 | "pdf", 16 | "ghostscript" 17 | ], 18 | "author": "sigalor", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/sigalor/ghostscript-node/issues" 22 | }, 23 | "homepage": "https://github.com/sigalor/ghostscript-node#readme", 24 | "dependencies": { 25 | "fs-extra": "^11.1.0", 26 | "tempy": "^1.0.1" 27 | }, 28 | "devDependencies": { 29 | "@types/fs-extra": "^11.0.1", 30 | "@types/jest": "^29.4.0", 31 | "jest": "^29.4.0", 32 | "rimraf": "^4.1.2", 33 | "ts-jest": "^29.0.5", 34 | "typescript": "^4.9.4" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import childProcess from 'child_process'; 2 | import fs from 'fs-extra'; 3 | import tempy, { FileOptions } from 'tempy'; 4 | import util from 'util'; 5 | 6 | const exec = util.promisify(childProcess.exec); 7 | 8 | interface TempFilenameSetDefs { 9 | [setName: string]: { 10 | writeBuffers?: Buffer[]; 11 | numFiles?: number; 12 | tempyConfig?: FileOptions; 13 | }; 14 | } 15 | 16 | type TempFilenameSets = { 17 | [setName: string]: string[]; 18 | }; 19 | 20 | type TempFileFnSingle = (input: string, output?: string) => Promise; 21 | type TempFileFnMany = (filenames: TempFilenameSets) => Promise; 22 | 23 | async function useTempFiles(filenameSets: TempFilenameSetDefs, fn: TempFileFnMany) { 24 | const filenames: TempFilenameSets = {}; 25 | 26 | // create all desired temporary files (either empty files or write data buffers to them) 27 | for (const [k, config] of Object.entries(filenameSets)) { 28 | const { numFiles, writeBuffers, tempyConfig } = config; 29 | 30 | if (numFiles !== undefined) { 31 | filenames[k] = Array(numFiles) 32 | .fill(0) 33 | .map(() => tempy.file(tempyConfig)); 34 | } else if (writeBuffers !== undefined) { 35 | filenames[k] = await Promise.all(writeBuffers.map(b => tempy.write(b, tempyConfig))); 36 | } 37 | } 38 | 39 | // execute the worker function which uses these files 40 | const ret = await fn(filenames); 41 | 42 | // remove all the temporary files again 43 | await Promise.all([].concat(...(Object.values(filenames))).map(f => fs.unlink(f))); 44 | 45 | // return the result of the worker function 46 | return ret; 47 | } 48 | 49 | async function useTempFilesPDF(filenameSets: TempFilenameSetDefs, fn: TempFileFnMany) { 50 | Object.values(filenameSets).forEach(v => (v.tempyConfig = { extension: '.pdf' })); 51 | return useTempFiles(filenameSets, fn); 52 | } 53 | 54 | // writes inputBuffer to one temporary file, creates an empty output file, calls the worker function and returns the output file contents as Buffer 55 | async function useTempFilesPDFInOut(inputBuffer: Buffer, fn: TempFileFnSingle): Promise { 56 | return useTempFilesPDF( 57 | { input: { writeBuffers: [inputBuffer] }, output: { numFiles: 1 } }, 58 | async ({ input, output }) => { 59 | await fn(input[0], output[0]); 60 | return fs.readFile(output[0]); 61 | }, 62 | ); 63 | } 64 | 65 | async function useTempFilesPDFIn(inputBuffer: Buffer, fn: TempFileFnSingle): Promise { 66 | return useTempFilesPDF({ input: { writeBuffers: [inputBuffer] } }, async ({ input }) => fn(input[0])); 67 | } 68 | 69 | export async function combinePDFs(pdfBuffers: Buffer[]): Promise { 70 | if (pdfBuffers.length === 0) return Buffer.alloc(0); 71 | if (pdfBuffers.length === 1) return pdfBuffers[0]; 72 | 73 | try { 74 | return await useTempFilesPDF( 75 | { inputs: { writeBuffers: pdfBuffers }, output: { numFiles: 1 } }, 76 | async ({ inputs, output }) => { 77 | await exec( 78 | `gs -q -dNOPAUSE -sDEVICE=pdfwrite -sOUTPUTFILE=${output[0]} -dBATCH -dAutoRotatePages=/None ${inputs.join( 79 | ' ', 80 | )} -c "[ /Creator () /Producer () /DOCINFO pdfmark"`, 81 | ); 82 | return fs.readFile(output[0]); 83 | }, 84 | ); 85 | } catch (e: any) { 86 | throw new Error('Failed to combine PDFs: ' + e.message); 87 | } 88 | } 89 | 90 | export async function countPDFPages(pdfBuffer: Buffer): Promise { 91 | try { 92 | return await useTempFilesPDFIn(pdfBuffer, async input => { 93 | const escapedInput = input.replace(/\\/g, '\\\\'); 94 | let { stdout } = await exec( 95 | `gs -q -dNOPAUSE -dBATCH -dNOSAFER -dNODISPLAY -c "(${escapedInput}) (r) file runpdfbegin pdfpagecount = quit"`, 96 | ); 97 | 98 | /** 99 | * Necessary for when Ghostscript detects a damaged but repairable PDF file, because then it outputs 100 | * the following before the number of pages (three spaces before each line starting with "****"): 101 | * 102 | * **** Error: An error occurred while reading an XREF table. 103 | * **** The file has been damaged. This may have been caused 104 | * **** by a problem while converting or transfering the file. 105 | * **** Ghostscript will attempt to recover the data. 106 | * **** However, the output may be incorrect. 107 | */ 108 | stdout = stdout 109 | .split('\n') 110 | .filter(l => !l.startsWith(' **** ') && l.length > 0) 111 | .join('\n') 112 | .trim(); 113 | 114 | const ret = parseInt(stdout); 115 | if (isNaN(ret)) throw new Error('parsing failed: ' + stdout); 116 | return ret; 117 | }); 118 | } catch (e: any) { 119 | throw new Error('Failed to determine number of pages in PDF: ' + e.message); 120 | } 121 | } 122 | 123 | export async function extractPDFPages(pdfBuffer: Buffer, firstPage: number, lastPage: number): Promise { 124 | try { 125 | return await useTempFilesPDFInOut(pdfBuffer, async (input, output) => { 126 | await exec( 127 | `gs -q -dNOPAUSE -sDEVICE=pdfwrite -dBATCH -dNOSAFER -dFirstPage=${firstPage} -dLastPage=${lastPage} -dAutoRotatePages=/None -sOutputFile=${output} ${input}`, 128 | ); 129 | }); 130 | } catch (e: any) { 131 | throw new Error('Failed to extract PDF pages: ' + e.message); 132 | } 133 | } 134 | 135 | export async function rotatePDF(pdfBuffer: Buffer, direction: '90' | '180' | '270'): Promise { 136 | if (!['90', '180', '270'].includes(direction)) throw new Error('Invalid rotation direction: ' + direction); 137 | 138 | try { 139 | return await useTempFilesPDFInOut(pdfBuffer, async (input, output) => { 140 | await exec(`qpdf ${input} ${output} --rotate=${direction}`); 141 | }); 142 | } catch (e: any) { 143 | throw new Error('Failed to rotate PDF: ' + e.message); 144 | } 145 | } 146 | 147 | /** 148 | * Converts a PDF to PDF/A. 149 | * 150 | * @param pdfBuffer - Buffer of the PDF to convert 151 | * @param options - Options for the conversion 152 | * @param options.version - PDF/A version to convert to. Defaults to 1. 153 | * @returns 154 | */ 155 | export async function convertToPDFA( 156 | pdfBuffer: Buffer, 157 | options = { 158 | version: 1, 159 | }, 160 | ): Promise { 161 | try { 162 | return await useTempFilesPDFInOut(pdfBuffer, async (input, output) => { 163 | await exec( 164 | `gs -dPDFA -dBATCH -dNOPAUSE -sColorConversionStrategy=UseDeviceIndependentColor -sDEVICE=pdfwrite -dPDFACompatibilityPolicy=${options.version} -sOutputFile=${output} ${input}`, 165 | ); 166 | }); 167 | } catch (e: any) { 168 | throw new Error('Failed to convert PDF to PDF/A: ' + e.message); 169 | } 170 | } 171 | 172 | /** 173 | * If `firstPage` is not given, 1 is used. 174 | * If `lastPage` is not given, the document's last page is used. 175 | * If `firstPage` is negative (e.g. -n), this refers to the last n pages and `lastPage` must be undefined. 176 | * All page numbers start at 1. 177 | */ 178 | export async function renderPDFPagesToPNG( 179 | pdfBuffer: Buffer, 180 | firstPage?: number, 181 | lastPage?: number, 182 | resolution = 300, 183 | ): Promise { 184 | const numPages = await countPDFPages(pdfBuffer); 185 | 186 | if (firstPage === undefined) firstPage = 1; 187 | else if (firstPage === 0 || (firstPage < 0 && firstPage < -numPages)) 188 | throw new Error('First page number out of range: ' + firstPage); 189 | 190 | if (firstPage < 0) { 191 | if (lastPage !== undefined) throw new Error('Last page must be undefined when first page is negative'); 192 | firstPage = numPages + firstPage + 1; 193 | lastPage = numPages; 194 | } 195 | 196 | if (lastPage === undefined) lastPage = numPages; 197 | else if (lastPage > numPages) throw new Error('Last page number out of range: ' + lastPage); 198 | 199 | if (firstPage! > lastPage!) throw new Error('Invalid page range: ' + firstPage + '-' + lastPage); 200 | 201 | try { 202 | return await useTempFilesPDFIn(pdfBuffer, async input => { 203 | const outDir = tempy.directory(); 204 | await exec( 205 | `gs -q -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 -sDEVICE=png16m -dTextAlphaBits=4 -dGraphicsAlphaBits=4 -r${resolution} -sOutputFile=${outDir}/%d.png -dFirstPage=${firstPage} -dLastPage=${lastPage} ${input}`, 206 | ); 207 | 208 | const outFiles = []; 209 | for (let i = 1; i <= lastPage! - firstPage! + 1; i++) { 210 | outFiles.push(await fs.readFile(outDir + '/' + i + '.png')); 211 | } 212 | 213 | await fs.rmdir(outDir, { recursive: true }); 214 | return outFiles; 215 | }); 216 | } catch (e: any) { 217 | throw new Error('Failed to render PDF pages to PNG: ' + e.message); 218 | } 219 | } 220 | 221 | export async function isValidPDF(pdfBuffer: Buffer): Promise { 222 | try { 223 | await countPDFPages(pdfBuffer); 224 | return true; 225 | } catch (e) { 226 | return false; 227 | } 228 | } 229 | 230 | /** 231 | * This function try, reduce size of your PDF not destroying quality 232 | * @param pdfBuffer Buffer 233 | * @returns Buffer 234 | */ 235 | export async function compressPDF(pdfBuffer: Buffer | string, encoding?: BufferEncoding): Promise { 236 | try { 237 | if(typeof pdfBuffer === 'string'){ 238 | pdfBuffer = Buffer.from(pdfBuffer, encoding ?? 'base64') 239 | } 240 | const compressedPdf = await useTempFilesPDFInOut(pdfBuffer, async (input, output) => { 241 | await exec( 242 | `gs -q -dNOPAUSE -dBATCH -dSAFER -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/screen -dEmbedAllFonts=true -dSubsetFonts=true -dColorImageDownsampleType=/Bicubic -dColorImageResolution=144 -dGrayImageDownsampleType=/Bicubic -dGrayImageResolution=144 -dMonoImageDownsampleType=/Bicubic -dMonoImageResolution=144 -sOutputFile=${output} ${input}`, 243 | ); 244 | }); 245 | if (pdfBuffer.length < compressedPdf.length) { 246 | return pdfBuffer; 247 | } 248 | return compressedPdf; 249 | } catch (e: any) { 250 | throw new Error('Failed optimize PDF: ' + e.message); 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /test/files/index.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs-extra'; 2 | import path from 'path'; 3 | 4 | export type FilesMap = { [filename: string]: Buffer }; 5 | 6 | export async function getFiles(): Promise { 7 | const fileNames: string[] = (await fs.readdir(__dirname)).filter(f => f !== 'index.js'); 8 | const files: FilesMap = {}; 9 | 10 | for (let fileName of fileNames) { 11 | files[fileName] = await fs.readFile(path.join(__dirname, fileName)); 12 | } 13 | 14 | return files; 15 | } 16 | -------------------------------------------------------------------------------- /test/files/out2.txt: -------------------------------------------------------------------------------- 1 | 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AAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACiiCMBAAAAAAAAAACAKOJIAAAAAAAAAAAAIIo4EgAAAAAAAAAAAIgijgQAAAAAAAAAAACi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AAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRABLgAosAACAASURBVAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAQOzagQwAAADAIH/re3zFEQAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAADErh3IAAAAAAzyt77HVxytyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJXtRs2wAAIABJREFUAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAsWsHMgAAAACD/K3v8RVHAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAABW9CtcAAAeP0lEQVQAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJQOzagQwAAADAIH/re3zFEQAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAADUrh3IAAAAAAzyt77HVxwBAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsyJEAAAAAAAAAAADAihwJAAAAAAAAAAAArMiRAAAAAAAAAAAAwIocCQAAAAAAAAAAAKzIkQAAAAAAAAAAAMCKHAkAAAAAAAAAAACsBONB8FzUzh8wAAAAAElFTkSuQmCC 2 | -------------------------------------------------------------------------------- /test/files/pdf1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sigalor/ghostscript-node/1a7cb8a98de0d6a8b081d224cdb637ee5a8fe633/test/files/pdf1.pdf -------------------------------------------------------------------------------- /test/files/pdf2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sigalor/ghostscript-node/1a7cb8a98de0d6a8b081d224cdb637ee5a8fe633/test/files/pdf2.pdf -------------------------------------------------------------------------------- /test/files/pdf3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sigalor/ghostscript-node/1a7cb8a98de0d6a8b081d224cdb637ee5a8fe633/test/files/pdf3.pdf -------------------------------------------------------------------------------- /test/files/renderPDFPagesToPNG-rotated-output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sigalor/ghostscript-node/1a7cb8a98de0d6a8b081d224cdb637ee5a8fe633/test/files/renderPDFPagesToPNG-rotated-output.png -------------------------------------------------------------------------------- /test/files/renderPDFPagesToPNG-rotated-output2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sigalor/ghostscript-node/1a7cb8a98de0d6a8b081d224cdb637ee5a8fe633/test/files/renderPDFPagesToPNG-rotated-output2.png -------------------------------------------------------------------------------- /test/index.test.ts: -------------------------------------------------------------------------------- 1 | import * as gs from '../src/index'; 2 | import { FilesMap, getFiles } from './files'; 3 | 4 | let files: FilesMap; 5 | beforeAll(async () => { 6 | files = await getFiles(); 7 | }); 8 | 9 | describe('combinePDFs', () => { 10 | test('works', async () => { 11 | const res = await gs.combinePDFs([files['pdf1.pdf'], files['pdf2.pdf']]); 12 | await expect(gs.countPDFPages(res)).resolves.toBe(4); 13 | }); 14 | }); 15 | 16 | describe('countPDFPages', () => { 17 | test('works', async () => { 18 | await expect(gs.countPDFPages(files['pdf1.pdf'])).resolves.toBe(1); 19 | await expect(gs.countPDFPages(files['pdf2.pdf'])).resolves.toBe(3); 20 | }); 21 | 22 | test('fails for invalid PDF', async () => { 23 | await expect(gs.countPDFPages(Buffer.from([1, 2, 3]))).rejects.toThrow( 24 | /^Failed to determine number of pages in PDF: Command failed/, 25 | ); 26 | }); 27 | }); 28 | 29 | describe('extractPDFPages', () => { 30 | test('works', async () => { 31 | const res = await gs.extractPDFPages(files['pdf2.pdf'], 2, 3); 32 | await expect(gs.countPDFPages(res)).resolves.toBe(2); 33 | }); 34 | }); 35 | 36 | describe('rotatePDF', () => { 37 | test('works', async () => { 38 | const res = await gs.rotatePDF(files['pdf2.pdf'], '90'); 39 | await expect(gs.countPDFPages(res)).resolves.toBe(3); 40 | }); 41 | }); 42 | 43 | describe('convertToPDFA', () => { 44 | test('works', async () => { 45 | const res = await gs.convertToPDFA(files['pdf2.pdf']); 46 | await expect(gs.countPDFPages(res)).resolves.toBe(3); 47 | }); 48 | }); 49 | 50 | describe('renderPDFPagesToPNG', () => { 51 | test('works for single page PDF', async () => { 52 | const res = await gs.renderPDFPagesToPNG(files['pdf1.pdf']); 53 | expect(res.length).toBe(1); 54 | }); 55 | 56 | test('works for three page PDF', async () => { 57 | const res = await gs.renderPDFPagesToPNG(files['pdf2.pdf']); 58 | expect(res.length).toBe(3); 59 | }); 60 | 61 | test('works for second page of a PDF', async () => { 62 | const res = await gs.renderPDFPagesToPNG(files['pdf2.pdf'], 2, 2); 63 | expect(res.length).toBe(1); 64 | }); 65 | 66 | test('works for the last two pages of a PDF', async () => { 67 | const res = await gs.renderPDFPagesToPNG(files['pdf2.pdf'], -2); 68 | expect(res.length).toBe(2); 69 | }); 70 | 71 | test('fails when first page is 0', async () => { 72 | await expect(gs.renderPDFPagesToPNG(files['pdf1.pdf'], 0)).rejects.toThrow('First page number out of range: 0'); 73 | }); 74 | 75 | test('fails when last page is out of range', async () => { 76 | await expect(gs.renderPDFPagesToPNG(files['pdf1.pdf'], undefined, 3)).rejects.toThrow( 77 | 'Last page number out of range: 3', 78 | ); 79 | }); 80 | 81 | test('fails when last page number is given when first page number is negative', async () => { 82 | await expect(gs.renderPDFPagesToPNG(files['pdf1.pdf'], -1, 2)).rejects.toThrow( 83 | 'Last page must be undefined when first page is negative', 84 | ); 85 | }); 86 | 87 | test('fails for invalid PDF', async () => { 88 | await expect(gs.renderPDFPagesToPNG(Buffer.from([1, 2, 3]))).rejects.toThrow( 89 | /^Failed to determine number of pages in PDF: Command failed/, 90 | ); 91 | }); 92 | 93 | test('correctly renders rotated PDF', async () => { 94 | const rotatedPdf = await gs.rotatePDF(files['pdf1.pdf'], '90'); 95 | const png = (await gs.renderPDFPagesToPNG(rotatedPdf))[0]; 96 | expect([ 97 | files['renderPDFPagesToPNG-rotated-output.png'].toString('base64'), 98 | files['renderPDFPagesToPNG-rotated-output2.png'].toString('base64'), 99 | ]).toContain(png.toString('base64')); 100 | }); 101 | }); 102 | 103 | describe('isValidPDF', () => { 104 | test('returns true for valid PDF', async () => { 105 | expect(await gs.isValidPDF(files['pdf1.pdf'])).toBe(true); 106 | }); 107 | 108 | test('returns true for another valid PDF', async () => { 109 | expect(await gs.isValidPDF(files['pdf3.pdf'])).toBe(true); 110 | }); 111 | 112 | test('returns true for PDF that has been extracted and combined again', async () => { 113 | const page = await gs.extractPDFPages(files['pdf3.pdf'], 1, 1); 114 | const pdf = await gs.combinePDFs([page]); 115 | expect(await gs.isValidPDF(pdf)).toBe(true); 116 | }); 117 | 118 | test('returns false for invalid PDF', async () => { 119 | expect(await gs.isValidPDF(Buffer.from([1, 2, 3]))).toBe(false); 120 | }); 121 | }); 122 | 123 | 124 | describe('compressPDF', () => { 125 | test('returns PDF reduce size send buffer file', async () => { 126 | const optimizedPDF = await gs.compressPDF(files['pdf3.pdf']) 127 | expect(optimizedPDF.length).toBeLessThanOrEqual(files['pdf3.pdf'].length); 128 | }); 129 | test('returns PDF reduce size send string encoded file', async () => { 130 | const optimizedPDF = await gs.compressPDF(files['pdf3.pdf'].toString('base64')) 131 | expect(optimizedPDF.length).toBeLessThanOrEqual(files['pdf3.pdf'].length); 132 | }); 133 | }) -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "incremental": true, 4 | "target": "ES2018", 5 | "module": "commonjs", 6 | "declaration": true, 7 | "outDir": "./dist", 8 | "esModuleInterop": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "strict": true, 11 | "skipLibCheck": true 12 | }, 13 | "exclude": [ 14 | "test/*" 15 | ] 16 | } 17 | --------------------------------------------------------------------------------