Skip to content

Commit

Permalink
feat(#72): provide command line interface
Browse files Browse the repository at this point in the history
  • Loading branch information
mojoaxel committed Dec 28, 2023
1 parent 5623a60 commit 8d7465b
Show file tree
Hide file tree
Showing 9 changed files with 224 additions and 1 deletion.
2 changes: 1 addition & 1 deletion PDFMergerBase.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { PDFDocument } from 'pdf-lib'

import { parsePagesString } from './parsePagesString'
import { parsePagesString } from './parsePagesString.js'

/**
* @typedef {Object} Metadata
Expand Down
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,33 @@ This library is inspired by the [PHP library PDFMerger](https://github.com/myoky

`npm install --save pdf-merger-js`

of global installation if you want to use the cli tool:

`npm install -g pdf-merger-js`

## Usage

### CLI

```
Options:
-V, --version output the version number
-o, --output <outputFile> Merged PDF output file path
-v, --verbose Print verbose output
-s, --silent do not print any output to stdout. Overwrites --verbose
-h, --help display help for command
```

#### Example calls

Merge pages 1-2 from the first input with pages 1,2 and 5-7 from the second pdf document:

`pdf-merge --output ./merged.pdf ./input1.pdf#1-2 ./input2.pdf#1,2,5-7`

Get two pdf files from the an url and merge the first one with pages 2-3 from the second one:

`pdf-merge --verbose --output ./sample.pdf Testfile.pdf https://pdfobject.com/pdf/sample.pdf https://upload.wikimedia.org/wikipedia/commons/1/13/Example.pdf#2-3`

### node.js

The node.js version has the following export functions:
Expand Down
68 changes: 68 additions & 0 deletions cli.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#!/usr/bin/env node

import fs from 'fs-extra'
import { program } from 'commander'

import PDFMerger from './index.js'
import { parsePagesString } from './parsePagesString.js'

function main (packageJson) {
program
.version(packageJson.version)
.description(packageJson.description)
.option('-o, --output <outputFile>', 'Merged PDF output file path')
.option('-v, --verbose', 'Print verbose output')
.option('-s, --silent', 'do not print any output to stdout. Overwrites --verbose')
.arguments('<inputFiles...>')
.action(async (inputFiles, cmd) => {
const outputFile = cmd.output
const verbose = cmd.verbose && !cmd.silent
const silent = cmd.silent

if (!outputFile) {
console.error('Please provide an output file using the --output flag')
return
}

if (!inputFiles || !inputFiles.length) {
console.error('Please provide at least one input file')
return
}

try {
const merger = new PDFMerger()

for (const inputFile of inputFiles) {
const [filePath, pagesString] = inputFile.split('#')
const pages = pagesString ? parsePagesString(pagesString) : null
if (verbose) {
if (pages && pages.length) {
console.log(`adding page${pages.length > 1 ? 's' : ''} ${pages.join(',')} from ${filePath} to output...`)
} else {
console.log(`adding all pages from ${filePath} to output...`)
}
}
await merger.add(filePath, pages)
}

if (verbose) {
console.log(`Saving merged output to ${outputFile}...`)
}

await merger.save(outputFile)

if (!silent) {
console.log(`Merged pages successfully into ${outputFile}`)
}
} catch (error) {
console.error('An error occurred while merging the PDFs:', error)
}
})

program.parse(process.argv)
}

(() => {
const packageJson = fs.readJsonSync(new URL('./package.json', import.meta.url))
main(packageJson)
})()
17 changes: 17 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
"main": "./index.js",
"types": "./index.d.ts",
"browser": "./browser.js",
"bin": {
"pdf-merge": "cli.js"
},
"scripts": {
"standard": "standard",
"standard:fix": "standard --fix",
Expand All @@ -29,6 +32,7 @@
"author": "nbesli",
"license": "MIT",
"dependencies": {
"commander": "^11.1.0",
"pdf-lib": "^1.17.1"
},
"devDependencies": {
Expand Down
109 changes: 109 additions & 0 deletions test/cli.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import path from 'path'
import fs from 'fs-extra'
import util from 'util'
import { exec } from 'child_process'
import { jest } from '@jest/globals'
import pdfDiff from 'pdf-diff'

const asyncExec = util.promisify(exec)

const __dirname = path.dirname(new URL(import.meta.url).pathname)
const FIXTURES_DIR = path.join(__dirname, 'fixtures')
const TMP_DIR = path.join(__dirname, 'tmp')

jest.setTimeout(10000)

async function mergePDFsCli (outputFile, inputFiles) {
const { stdout, stderr } = await asyncExec(`node ./cli.js --output ${outputFile} ${inputFiles.join(' ')}`)
return { stdout, stderr }
}

describe('issues', () => {
beforeAll(async () => {
await fs.ensureDir(TMP_DIR)
})

test('should merge two pdfs', async () => {
await mergePDFsCli(path.join(TMP_DIR, 'Testfile_AB.pdf'), [
path.join(FIXTURES_DIR, 'Testfile_A.pdf'),
path.join(FIXTURES_DIR, 'Testfile_B.pdf')
])
const diff = await pdfDiff(
path.join(FIXTURES_DIR, 'Testfile_AB.pdf'),
path.join(TMP_DIR, 'Testfile_AB.pdf')
)
expect(diff).toBeFalsy()
})

test('should merge two pdfs from URL', async () => {
await mergePDFsCli(path.join(TMP_DIR, 'Testfile_AB.pdf'), [
'https://github.com/nbesli/pdf-merger-js/raw/master/test/fixtures/Testfile_A.pdf',
'https://github.com/nbesli/pdf-merger-js/raw/master/test/fixtures/Testfile_B.pdf'
])
const diff = await pdfDiff(
path.join(FIXTURES_DIR, 'Testfile_AB.pdf'),
path.join(TMP_DIR, 'Testfile_AB.pdf')
)
expect(diff).toBeFalsy()
})

test('combine single pages from multiple pdfs', async () => {
await mergePDFsCli(path.join(TMP_DIR, '2468.pdf'), [
path.join(FIXTURES_DIR, '123456789.pdf#2'),
path.join(FIXTURES_DIR, '123456789.pdf#4'),
path.join(FIXTURES_DIR, '123456789.pdf#6'),
path.join(FIXTURES_DIR, '123456789.pdf#8')
])
const diff = await pdfDiff(
path.join(FIXTURES_DIR, '2468.pdf'),
path.join(TMP_DIR, '2468.pdf')
)
expect(diff).toBeFalsy()
})

test('combine pages from multiple pdfs (list)', async () => {
await mergePDFsCli(path.join(TMP_DIR, '2468.pdf'), [
path.join(FIXTURES_DIR, '123456789.pdf#2,4'),
path.join(FIXTURES_DIR, '123456789.pdf#6,8')
])
const diff = await pdfDiff(
path.join(FIXTURES_DIR, '2468.pdf'),
path.join(TMP_DIR, '2468.pdf')
)
expect(diff).toBeFalsy()
})

test('combine pages from multipel pdfs (rang)', async () => {
await mergePDFsCli(path.join(TMP_DIR, '123456789.pdf'), [
path.join(FIXTURES_DIR, '123456789.pdf#1-5'),
path.join(FIXTURES_DIR, '123456789.pdf#6-9')
])
const diff = await pdfDiff(
path.join(FIXTURES_DIR, '123456789.pdf'),
path.join(TMP_DIR, '123456789.pdf')
)
expect(diff).toBeFalsy()
})

test('combine pages from multipel pdfs (combined list and range)', async () => {
await mergePDFsCli(path.join(TMP_DIR, '123456789.pdf'), [
path.join(FIXTURES_DIR, '123456789.pdf#1,2,3-4'),
path.join(FIXTURES_DIR, '123456789.pdf#5-7,8to9')
])
const diff = await pdfDiff(
path.join(FIXTURES_DIR, '123456789.pdf'),
path.join(TMP_DIR, '123456789.pdf')
)
expect(diff).toBeFalsy()
})

afterEach(async () => {
for (const file of await fs.readdir(TMP_DIR)) {
await fs.unlink(path.join(TMP_DIR, file))
}
})

afterAll(async () => {
await fs.remove(TMP_DIR)
})
})
Binary file added test/fixtures/123.pdf
Binary file not shown.
Binary file added test/fixtures/123456789.pdf
Binary file not shown.
Binary file added test/fixtures/2468.pdf
Binary file not shown.

0 comments on commit 8d7465b

Please sign in to comment.