├── .eslintrc.json ├── .gitignore ├── .travis.yml ├── CNAME ├── LICENSE ├── README.md ├── _config.yml ├── jest.config.json ├── package.json ├── src └── index.ts ├── test ├── files │ ├── logo.pdf │ └── sample.pdf ├── io.spec.ts └── options.spec.ts ├── tsconfig.json └── tsconfig.test.json /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "extends": [ 4 | "airbnb-typescript/base" 5 | ], 6 | "parserOptions": { 7 | "ecmaVersion": 2018, 8 | "sourceType": "module" 9 | }, 10 | "rules": { 11 | "no-dupe-class-members": "off", 12 | "import/prefer-default-export": "off" 13 | }, 14 | "env": { 15 | "jest": true 16 | } 17 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | node_modules 3 | lib 4 | test-data 5 | coverage 6 | build 7 | yarn.lock 8 | *.DS_Store -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | before_install: 2 | - sudo apt-get update 3 | - sudo apt-get install -y poppler-utils 4 | language: node_js 5 | node_js: 6 | - "12.0.0" 7 | script: 8 | - npm run lint 9 | - npm run test 10 | - npm run codecov -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | node-pdftocairo.daraw.cn -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 月迷津渡 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 | # node-pdftocairo 2 | [![Build Status](https://travis-ci.org/CodeDaraW/node-pdftocairo.svg?branch=master)](https://travis-ci.org/CodeDaraW/node-pdftocairo) 3 | [![Codecov](https://img.shields.io/codecov/c/github/CodeDaraW/node-pdftocairo)](https://codecov.io/gh/CodeDaraW/node-pdftocairo) 4 | [![npm](https://img.shields.io/npm/v/node-pdftocairo)](https://www.npmjs.com/package/node-pdftocairo) 5 | [![MIT](https://img.shields.io/npm/l/node-pdftocairo)](https://github.com/CodeDaraW/node-pdftocairo/blob/master/LICENSE) 6 | 7 | Node.js wrapper for pdftocairo - PDF to PNG/JPEG/TIFF/PDF/PS/EPS/SVG using cairo 8 | Inspired by [jjwilly16/node-pdftk](https://github.com/jjwilly16/node-pdftk/) 9 | 10 | ## Requirements 11 | Since `pdftocairo` is included in [Poppler](https://poppler.freedesktop.org/), you should install `Poppler` before using this library. 12 | 13 | ## Installation 14 | ``` shell 15 | yarn add node-pdftocairo 16 | ``` 17 | 18 | ``` typescript 19 | import { input } from 'node-pdftocairo'; 20 | ``` 21 | 22 | ## API 23 | ### Simple Usage 24 | The first argument of `input` can be a file path or buffer. 25 | If you pass a output file path to `output`, it will generate files and returns `Promise`; otherwise return buffers without generating files. 26 | 27 | **Generate file buffer(s) from a PDF file** 28 | ``` typescript 29 | const inputPath = path.join(__dirname, '../test/files/sample.pdf'); 30 | const options = { format: 'png' }; 31 | const outputBuffer = await input(inputPath, options).output(); 32 | ``` 33 | 34 | **Generate file buffer(s) from the buffer** 35 | ``` typescript 36 | const inputPath = path.join(__dirname, '../test/files/sample.pdf'); 37 | const buffer = fs.readFileSync(inputPath); 38 | const options = { format: 'png' }; 39 | const outputBuffer = await input(buffer, options).output(); 40 | ``` 41 | 42 | **Generate file(s) on the specified output path** 43 | ``` typescript 44 | const inputPath = path.join(__dirname, '../test/files/sample.pdf'); 45 | const outputPath = path.join(__dirname, '../test/files/sample-img'); 46 | const options = { format: 'png' }; 47 | await input(inputPath, options).output(outputPath); 48 | ``` 49 | 50 | ### Options 51 | Reference: [Ubuntu Manpage: pdftocairo](http://manpages.ubuntu.com/manpages/bionic/man1/pdftocairo.1.html) 52 | 53 | | Property | Description | Type | Default | 54 | |---|---|---|---| 55 | | `bin` | specify the path of `pdftocairo` | `string` | - | 56 | | `format` | output file format, should be one of `png` `jpeg` `tiff` `ps` `eps` `pdf` `svg` | `string` | - | 57 | | `antialias` | Set the cairo antialias option used for text and drawing in image files (or rasterized regions in vector output), should be one of `default` `none` `gray` `subpixel` `fast` `good` `best` | `string` | - | 58 | | `range` | Specifies the first/last page to convert. |`{ f?: number, l?: number }`| - | 59 | | `filter` | Generates only the `odd` or `even` numbered pages. | `string` | - | 60 | | `singlefile` | Writes only the first page and does not add digits. | `boolean` | `false` | 61 | | `resolution` | Specifies the X and Y resolution, in pixels per inch of image files (or rasterized regions in vector output). The default is 150 PPI. | `number` \| `{ x: number, y: number }` | - | 62 | | `scale` | Scales the long side of each page (width for landscape pages, height for portrait pages) to fit in scale-to pixels. The size of the short side will be determined by the aspect ratio of the page (PNG/JPEG/TIFF only). | `number` \| `{ x: number, y: number }` | - | 63 | | `crop` | Specifies the x-coordinate/y-coordinate of the crop area top left corner in pixels (image output) or points (vector output) and Specifies the width/height/size of crop area in pixels (image output) or points (vector output) (default is 0) | `{ x?: number, y?: number, W?: number, H?: number, sz?: number }` | - | 64 | | `cropbox` | Uses the crop box rather than media box when generating the files (PNG/JPEG/TIFF only) | `boolean` | `false` | 65 | | `mono` | Generate a monochrome file (PNG and TIFF only). | `boolean` | `false` | 66 | | `gray` | Generate a grayscale file (PNG, JPEG, and TIFF only). | `boolean` | `false` | 67 | | `transparent` | Use a transparent page color instead of white (PNG and TIFF only). | `boolean` | `false` | 68 | | `level2` | Generate Level 2 PostScript (PS only). | `boolean` | `false` | 69 | | `level3` | Generate Level 3 PostScript (PS only). This enables all Level 2 features plus shading patterns and masked images. This is the default setting. | `boolean` | `false` | 70 | | `originPageSizes` | This option is the same as "-paper match". | `boolean` | `false` | 71 | | `icc` | Use the specified ICC file as the output profile (PNG only). The profile will be embedded in the PNG file. | `string` | - | 72 | | `jpegopt` | When used with -jpeg, takes a list of options to control the jpeg compression. See JPEG OPTIONS for the available options. | `string` | - | 73 | | `paper` | Set the paper size to one of "letter", "legal", "A4", or "A3" (PS,PDF,SVG only). This can also be set to "match", which will set the paper size of each page to match the size specified in the PDF file. If none the -paper, -paperw, or -paperh options are specified the default is to match the paper size. | `string` \| `{ w: number, h: number }`| - | 74 | | `nocrop` | By default, printing output is cropped to the CropBox specified in the PDF file. This option disables cropping (PS,PDF,SVG only). | `boolean` | `false` | 75 | | `expand` | Expand PDF pages smaller than the paper to fill the paper (PS,PDF,SVG only). By default, these pages are not scaled.| `boolean` | `false` | 76 | |`noshrink`| Don't scale PDF pages which are larger than the paper (PS,PDF,SVG only). By default, pages larger than the paper are shrunk to fit. | `boolean` | `false` | 77 | | `nocenter` | By default, PDF pages smaller than the paper (after any scaling) are centered on the paper. This option causes them to be aligned to the lower-left corner of the paper instead (PS,PDF,SVG only). | `boolean` | `false` | 78 | | `duplex` | Adds the %%IncludeFeature: *Duplex DuplexNoTumble DSC comment to the PostScript file (PS only). This tells the print manager to enable duplexing. | `boolean` | `false` | 79 | | `ownerPassword` | Specify the owner password for the PDF file. Providing this will bypass all security restrictions. | `string` | - | 80 | | `userPassword` | Specify the user password for the PDF file. | `string` | - | 81 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /jest.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "globals": { 3 | "ts-jest": { 4 | "tsConfig": "./tsconfig.test.json" 5 | } 6 | }, 7 | "transform": { 8 | "^.+\\.jsx?$": "babel-jest", 9 | "^.+\\.tsx?$": "ts-jest" 10 | }, 11 | "testEnvironment": "node", 12 | "testRegex": "test\\/.+\\.spec\\.ts", 13 | "moduleFileExtensions": ["ts", "tsx", "js", "jsx", "json", "node"], 14 | "coverageReporters": [ 15 | "json", "json-summary", "lcov", "text", "clover" 16 | ], 17 | "collectCoverageFrom": ["src/**/*.ts"] 18 | } 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-pdftocairo", 3 | "version": "1.2.0", 4 | "description": "node.js wrapper for pdftocairo", 5 | "main": "lib/index.js", 6 | "files": [ 7 | "lib/*" 8 | ], 9 | "scripts": { 10 | "build": "rm -rf lib && tsc", 11 | "test": "jest --config ./jest.config.json --coverage --env=node", 12 | "lint": "eslint --ext .js,.ts ./src ./test", 13 | "codecov": "codecov -f coverage/*.json" 14 | }, 15 | "author": "CodeDaraW@gmail.com", 16 | "repository": "github:CodeDaraW/node-pdftocairo", 17 | "homepage": "https://github.com/CodeDaraW/node-pdftocairo", 18 | "bugs": { 19 | "url": "https://github.com/CodeDaraW/node-pdftocairo/issues" 20 | }, 21 | "os": [ 22 | "darwin", 23 | "linux" 24 | ], 25 | "license": "MIT", 26 | "devDependencies": { 27 | "@types/glob": "^7.1.1", 28 | "@types/jest": "^24.0.15", 29 | "@types/node": "^12.0.12", 30 | "@types/rimraf": "^4.0.5", 31 | "@typescript-eslint/eslint-plugin": "^2.7.0", 32 | "@typescript-eslint/parser": "^2.12.0", 33 | "codecov": "^3.5.0", 34 | "eslint": "^6.1.0", 35 | "eslint-config-airbnb-typescript": "^6.2.0", 36 | "eslint-plugin-import": "^2.18.2", 37 | "jest": "^24.8.0", 38 | "ts-jest": "^24.0.2", 39 | "ts-node": "^10.1.0", 40 | "typescript": "^3.5.2" 41 | }, 42 | "dependencies": { 43 | "glob": "^7.1.4", 44 | "rimraf": "^4.3.1" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import path from 'path'; 3 | import crypto from 'crypto'; 4 | import { spawn } from 'child_process'; 5 | import glob from 'glob'; 6 | import rimraf from 'rimraf'; 7 | 8 | export interface Options { 9 | bin?: string; 10 | format: 'png' | 'jpeg' | 'tiff' | 'ps' | 'eps' | 'pdf' | 'svg'; 11 | antialias?: 'default' | 'none' | 'gray' | 'subpixel' | 'fast' | 'good' | 'best'; 12 | range?: { f?: number, l?: number }; 13 | filter?: 'odd' | 'even'; 14 | singlefile?: boolean; 15 | resolution?: number | { x: number, y: number }; 16 | scale?: number | { x: number, y: number }; 17 | crop?: { x?: number, y?: number, W?: number, H?: number, sz?: number }; 18 | cropbox?: boolean; 19 | mono?: boolean; 20 | gray?: boolean; 21 | transparent?: boolean; 22 | level2?: boolean; 23 | level3?: boolean; 24 | originPageSizes?: boolean; 25 | icc?: string; 26 | jpegopt?: string; 27 | paper?: string | { w: number, h: number }; 28 | nocrop?: boolean; 29 | expand?: boolean; 30 | noshrink?: boolean; 31 | nocenter?: boolean; 32 | duplex?: boolean; 33 | ownerPassword?: string; 34 | userPassword?: string; 35 | } 36 | 37 | const DEFAULT_BIN = 'pdftocairo'; 38 | 39 | const getOptionArgs = (options: Options): string[] => { 40 | const args: string[] = []; 41 | args.push(`-${options.format}`); 42 | 43 | if (options.range) { 44 | if (typeof options.range.f === 'number') args.push(`-f ${options.range.f}`); 45 | if (typeof options.range.l === 'number') args.push(`-l ${options.range.l}`); 46 | } 47 | 48 | if (options.filter) { 49 | const arg = options.filter === 'odd' ? '-o' : '-e'; 50 | args.push(arg); 51 | } 52 | 53 | if (options.singlefile) args.push('-singlefile'); 54 | 55 | if (options.resolution) { 56 | if (typeof options.resolution === 'number') { 57 | args.push(`-r ${options.resolution}`); 58 | } else { 59 | if (typeof options.resolution.x === 'number') { 60 | args.push(`-rx ${options.resolution.x}`); 61 | } 62 | if (typeof options.resolution.y === 'number') { 63 | args.push(`-ry ${options.resolution.y}`); 64 | } 65 | } 66 | } 67 | 68 | if (options.scale) { 69 | if (typeof options.scale === 'number') { 70 | args.push(`-scale-to ${options.scale}`); 71 | } else { 72 | if (typeof options.scale.x === 'number') { 73 | args.push(`-scale-to-x ${options.scale.x}`); 74 | } 75 | if (typeof options.scale.y === 'number') { 76 | args.push(`-scale-to-y ${options.scale.y}`); 77 | } 78 | } 79 | } 80 | 81 | if (options.paper) { 82 | if (typeof options.paper === 'string') { 83 | args.push(`-paper ${options.paper}`); 84 | } else { 85 | if (typeof options.paper.w === 'number') { 86 | args.push(`-paperw ${options.paper.w}`); 87 | } 88 | if (typeof options.paper.h === 'number') { 89 | args.push(`-paperh ${options.paper.h}`); 90 | } 91 | } 92 | } 93 | 94 | if (options.crop) { 95 | if (options.crop.x) args.push(`-x ${options.crop.x}`); 96 | if (options.crop.y) args.push(`-y ${options.crop.y}`); 97 | if (options.crop.H) args.push(`-H ${options.crop.H}`); 98 | if (options.crop.W) args.push(`-W ${options.crop.W}`); 99 | if (options.crop.sz) args.push(`-sz ${options.crop.sz}`); 100 | } 101 | 102 | if (options.cropbox) args.push('-cropbox'); 103 | if (options.mono) args.push('-mono'); 104 | if (options.gray) args.push('-gray'); 105 | if (options.antialias) args.push(`-antialias ${options.antialias}`); 106 | if (options.level2) args.push('-level2'); 107 | if (options.level3) args.push('-level3'); 108 | if (options.transparent) args.push('-transp'); 109 | if (options.originPageSizes) args.push('-origpagesizes'); 110 | if (options.icc) args.push(`-icc ${options.icc}`); 111 | if (options.jpegopt) args.push(`-jpegopt ${options.jpegopt}`); 112 | if (options.nocrop) args.push('-nocrop'); 113 | if (options.expand) args.push('-expand'); 114 | if (options.noshrink) args.push('-noshrink'); 115 | if (options.nocenter) args.push('-nocenter'); 116 | if (options.duplex) args.push('-duplex'); 117 | if (options.ownerPassword) args.push(`-opw ${options.ownerPassword}`); 118 | if (options.userPassword) args.push(`-upw ${options.userPassword}`); 119 | 120 | args.push('-q'); 121 | 122 | return args; 123 | }; 124 | 125 | const findFiles = async (patten: string): Promise => new Promise((resolve, reject) => { 126 | glob(patten, (err, files) => (err ? reject(err) : resolve(files))); 127 | }); 128 | 129 | class PDFToCairo { 130 | private bin: string; 131 | 132 | private args: string[] = []; 133 | 134 | private input: string | Buffer; 135 | 136 | private tmps: string[] = []; 137 | 138 | public constructor(file: string | Buffer, options: Options) { 139 | this.input = file; 140 | this.bin = options.bin || process.env.PDFTOCAIRO_PATH || DEFAULT_BIN; 141 | this.args.push(...getOptionArgs(options)); 142 | } 143 | 144 | // overload signature 145 | public async output(): Promise; 146 | 147 | public async output(outputFile: string): Promise; 148 | 149 | public async output(outputFile?: string): Promise { 150 | // '-' means reading PDF file from stdin 151 | const inputPath = typeof this.input === 'string' ? this.input : '-'; 152 | const outputPath = outputFile || path.join(await this.makeTempDir(), 'result'); 153 | 154 | this.args.push(inputPath, outputPath); 155 | 156 | const child = spawn(this.bin, this.args, { shell: true }); 157 | 158 | if (typeof this.input !== 'string') { 159 | child.stdin.setDefaultEncoding('utf-8'); 160 | child.stdin.write(this.input); 161 | child.stdin.end(); 162 | } 163 | 164 | return new Promise((resolve, reject) => { 165 | let errMsg: string; 166 | child.stderr.on('data', (data) => { 167 | errMsg = `${data}`; 168 | }); 169 | 170 | child.on('close', async (code) => { 171 | if (code !== 0) { 172 | reject(new Error(`[code ${code}] ${errMsg || 'unknown error'}`)); 173 | return; 174 | } 175 | 176 | if (typeof outputFile === 'string') { 177 | resolve(null); 178 | } else { 179 | const files = await findFiles(`${outputPath}*`); 180 | const buffers = await Promise.all( 181 | files.sort().map((file) => fs.promises.readFile(file)), 182 | ); 183 | this.cleanUpTemp(); 184 | resolve(buffers); 185 | } 186 | }); 187 | }); 188 | } 189 | 190 | private async makeTempDir(): Promise { 191 | const uniqueId = crypto.randomBytes(16).toString('hex'); 192 | const outputPath = path.join('/tmp', uniqueId); 193 | await fs.promises.mkdir(outputPath); 194 | this.tmps.push(outputPath); 195 | return outputPath; 196 | } 197 | 198 | private cleanUpTemp(): void { 199 | try { 200 | this.tmps.forEach((f) => rimraf.sync(f)); 201 | } catch (error) { 202 | // ignore 203 | } 204 | } 205 | } 206 | 207 | export const input = (file: string | Buffer, options: Options) => new PDFToCairo(file, options); 208 | -------------------------------------------------------------------------------- /test/files/logo.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeDaraW/node-pdftocairo/a5194dbe24b95f41742ae4ab3d2af62b554caaf5/test/files/logo.pdf -------------------------------------------------------------------------------- /test/files/sample.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeDaraW/node-pdftocairo/a5194dbe24b95f41742ae4ab3d2af62b554caaf5/test/files/sample.pdf -------------------------------------------------------------------------------- /test/io.spec.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import path from 'path'; 3 | import glob from 'glob'; 4 | import rimraf from 'rimraf'; 5 | import { input, Options } from '../src'; 6 | 7 | const findFiles = async (patten: string): Promise => new Promise((resolve, reject) => { 8 | glob(patten, (err, files) => (err ? reject(err) : resolve(files))); 9 | }); 10 | 11 | const inputPath = path.join(__dirname, '../test/files/sample.pdf'); 12 | const outputPath = path.join(__dirname, '../test/files/sample-img'); 13 | const pageCount = 2; 14 | const options: Options = { format: 'png' }; 15 | 16 | describe('io', () => { 17 | // cleanup generated image files 18 | afterAll(async () => { 19 | const imgFiles = await findFiles(`${outputPath}*`); 20 | imgFiles.forEach((f) => rimraf.sync(f)); 21 | }); 22 | 23 | test('input as buffer', async () => { 24 | const buffer = fs.readFileSync(inputPath); 25 | const res = await input(buffer, options).output(); 26 | expect(res.length).toBe(pageCount); 27 | }); 28 | 29 | test('error input', async () => { 30 | const buffer = Buffer.from(''); 31 | try { 32 | await input(buffer, options).output(); 33 | } catch (error) { 34 | expect(error).toEqual(new Error('[code 1] Error opening PDF file.\n')); 35 | } 36 | }); 37 | 38 | test('output as file', async () => { 39 | const res = await input(inputPath, options).output(outputPath); 40 | const imgFiles = await findFiles(`${outputPath}*`); 41 | expect(res).toBeNull(); 42 | expect(imgFiles.length).toBe(pageCount); 43 | }); 44 | 45 | test('output as buffer', async () => { 46 | const res = await input(inputPath, options).output(); 47 | expect(res.length).toBe(pageCount); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /test/options.spec.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import { input, Options } from '../src'; 3 | 4 | const samplePath = path.join(__dirname, '../test/files/sample.pdf'); 5 | const logoPath = path.join(__dirname, '../test/files/logo.pdf'); 6 | 7 | describe('options', () => { 8 | test('range', async () => { 9 | const options: Options = { 10 | format: 'png', 11 | range: { f: 1, l: 1 }, 12 | }; 13 | 14 | const res = await input(samplePath, options).output(); 15 | expect(res.length).toBe(1); 16 | }); 17 | 18 | test('filter', async () => { 19 | const options: Options = { 20 | format: 'png', 21 | filter: 'even', 22 | }; 23 | 24 | const res = await input(samplePath, options).output(); 25 | expect(res.length).toBe(1); 26 | }); 27 | 28 | test('singlefile', async () => { 29 | const options: Options = { 30 | format: 'png', 31 | singlefile: true, 32 | }; 33 | 34 | const res = await input(samplePath, options).output(); 35 | expect(res.length).toBe(1); 36 | }); 37 | 38 | test('resolution', async () => { 39 | const options: Options = { 40 | format: 'png', 41 | resolution: 120, 42 | }; 43 | 44 | const res = await input(samplePath, options).output(); 45 | expect(res.length).toBe(2); 46 | }); 47 | 48 | test('resolution x & y', async () => { 49 | const options: Options = { 50 | format: 'png', 51 | resolution: { x: 120, y: 120 }, 52 | }; 53 | 54 | const res = await input(samplePath, options).output(); 55 | expect(res.length).toBe(2); 56 | }); 57 | 58 | test('scale', async () => { 59 | const options: Options = { 60 | format: 'png', 61 | scale: 2, 62 | }; 63 | 64 | const res = await input(samplePath, options).output(); 65 | expect(res.length).toBe(2); 66 | }); 67 | 68 | test('scale x & y', async () => { 69 | const options: Options = { 70 | format: 'png', 71 | scale: { x: 300, y: 300 }, 72 | }; 73 | 74 | const res = await input(samplePath, options).output(); 75 | expect(res.length).toBe(2); 76 | }); 77 | 78 | test('paper', async () => { 79 | const options: Options = { 80 | format: 'pdf', 81 | paper: 'A3', 82 | }; 83 | 84 | const res = await input(samplePath, options).output(); 85 | expect(res.length).toBe(1); 86 | }); 87 | 88 | test('paper w & h', async () => { 89 | const options: Options = { 90 | format: 'pdf', 91 | paper: { 92 | w: 100, 93 | h: 200, 94 | }, 95 | }; 96 | 97 | const res = await input(samplePath, options).output(); 98 | expect(res.length).toBe(1); 99 | }); 100 | 101 | test('crop', async () => { 102 | const options: Options = { 103 | format: 'png', 104 | crop: { 105 | x: 300, y: 300, H: 100, W: 100, sz: 50, 106 | }, 107 | }; 108 | 109 | const res = await input(samplePath, options).output(); 110 | expect(res.length).toBe(2); 111 | }); 112 | 113 | test('cropbox', async () => { 114 | const options: Options = { 115 | format: 'png', 116 | cropbox: true, 117 | }; 118 | 119 | const res = await input(samplePath, options).output(); 120 | expect(res.length).toBe(2); 121 | }); 122 | 123 | test('mono', async () => { 124 | const options: Options = { 125 | format: 'png', 126 | mono: true, 127 | }; 128 | 129 | const res = await input(logoPath, options).output(); 130 | expect(res.length).toBe(1); 131 | }); 132 | 133 | test('gray', async () => { 134 | const options: Options = { 135 | format: 'png', 136 | gray: true, 137 | }; 138 | 139 | const res = await input(logoPath, options).output(); 140 | expect(res.length).toBe(1); 141 | }); 142 | }); 143 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "rootDir": "src/", 5 | "outDir": "lib/", 6 | "module": "commonjs", 7 | "target": "es2015", 8 | "sourceMap": false, 9 | "allowJs": false, 10 | "moduleResolution": "node", 11 | "forceConsistentCasingInFileNames": true, 12 | "noImplicitReturns": true, 13 | "noImplicitThis": true, 14 | "noImplicitAny": false, 15 | "strictNullChecks": true, 16 | "suppressImplicitAnyIndexErrors": true, 17 | "noUnusedLocals": true, 18 | "importHelpers": true, 19 | "esModuleInterop": true, 20 | "declaration": true, 21 | "downlevelIteration": true, 22 | "resolveJsonModule": true, 23 | "types": [ 24 | "jest", 25 | "node" 26 | ] 27 | }, 28 | "exclude": [ 29 | "node_modules", 30 | "public", 31 | "build", 32 | "scripts", 33 | "acceptance-tests", 34 | "webpack", 35 | "jest", 36 | "src/setupTests.ts", 37 | "test/", 38 | "test-data/" 39 | ] 40 | } -------------------------------------------------------------------------------- /tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "target": "es2017", 5 | "module": "commonjs", 6 | "types": ["node", "jest"] 7 | }, 8 | "include": ["./src"] 9 | } 10 | --------------------------------------------------------------------------------