Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow custom qpdf binary #39

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 65 additions & 40 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
[![npm](https://img.shields.io/npm/dm/node-qpdf)](https://www.npmjs.com/package/node-qpdf2)
[![qpdf 11+](https://img.shields.io/badge/dependencies-qpdf-green)](https://github.com/qpdf/qpdf)

A very simple wrapper for [qpdf](https://github.com/qpdf/qpdf) which is performs content-preserving transformations on PDF files. It includes encrypting and decrypting PDF files with AES 256, AES 128, RC4 (128 & 40) encryption algorithms. This is a fork of [nrhirani/node-qpdf](https://github.com/nrhirani/node-qpdf), adding Promises and Types, and is kept mostly up to date with `qpdf`.
A very simple wrapper for [qpdf](https://github.com/qpdf/qpdf) which performs content-preserving transformations on PDF files. It includes encrypting and decrypting PDF files with AES 256, AES 128, RC4 (128 & 40) encryption algorithms. This is a fork of [nrhirani/node-qpdf](https://github.com/nrhirani/node-qpdf), adding `Promises` and `Types`, and is kept mostly up to date with `qpdf`.

## Dependencies

Expand All @@ -16,94 +16,119 @@ A very simple wrapper for [qpdf](https://github.com/qpdf/qpdf) which is performs

## Installation

1. Install qpdf:
- Download [qpdf](https://github.com/qpdf/qpdf/releases)
2. Install node-qpdf:
```
npm install node-qpdf2
```
1. Install [qpdf](https://github.com/qpdf/qpdf/releases):
2. Install `node-qpdf2`:

## Serverless?
```bash
npm install node-qpdf2
# or
yarn add node-qpdf2
```

This package can be uses on serverless platforms by using your vendor's layers functionality. Use the zip release from `qpdf` as the layer. For example, `qpdf-11.1.0-bin-linux-x86_64.zip` can be directly uploaded as a layer, and has been tested using Amazon Lambda.
## Serverless?

## Encryption
This package can be used on serverless platforms and has been tested on `Amazon Lambda` and `Google Cloud Functions`.

You can encrypt your PDF by following:
### Amazon Lambda
Download latest `qpdf-XX.X.X-bin-linux-x86_64.zip` release from `qpdf` then directly upload it as a [layer](https://docs.aws.amazon.com/lambda/latest/dg/chapter-layers.html).

```
import { encrypt } from "node-qpdf2";
### Google Cloud Functions
Download latest `qpdf-XX.X.X-bin-linux-x86_64.zip` release then extract it's content where functions entry point (generally `index.js`) is:

const pdf = {
input: "./test/example.pdf",
output: "/tmp/encrypted.pdf",
password: "1234",
```bash
# package.json
{
...
# entry point
"main": "build/index.js",
...
}

await encrypt(pdf);
# directory structure
/root_dir
/build
# entry point
/index.js
/other files...
# you can rename qpdf folder to whatever you want
/qpdf_dir
# zip content
/bin
/lib
/src
/index.ts
/other files...
```

### Options for Encryption
Then provide a `qpdfPath` pointing to `bin` folder inside `qpdf` directory:
```bash
import path from "path";

Please see [src/encrypt.ts](https://github.com/Sparticuz/node-qpdf2/blob/master/src/encrypt.ts#L9) for the latest options, as well as [QPDF's documentation](https://qpdf.readthedocs.io/en/stable/cli.html#encryption) for information on what each restriction does.
const pdf = {
qpdfPath: path.join( process.cwd(), 'qpdf_dir/bin' ),
... other props
}
```

### Examples
## Encryption

#### Render and Save:
You can encrypt your PDF by following:

```
```bash
import { encrypt } from "node-qpdf2";

const options = {
input: "./test/example.pdf",
keyLength: 128,
output: "/tmp/encrypted.pdf",
password: 'YOUR_PASSWORD_TO_ENCRYPT',
restrictions: {
print: 'low',
useAes: 'y'
}
input: "./test/example.pdf",
output: "/tmp/encrypted.pdf",
password: 'ENCRYPTION_PASSWORD',
restrictions: {
print: 'low',
useAes: 'y'
}
}

await encrypt(options);
```

### Encryption options

Please see [src/encrypt.ts](https://github.com/Sparticuz/node-qpdf2/blob/master/src/encrypt.ts#L9) for the all available options, as well as [qpdf's documentation](https://qpdf.readthedocs.io/en/stable/cli.html#encryption) for informations about each restriction prop.

## Decryption

You can decrypt your PDF by following:

```
```bash
import { decrypt } from "node-qpdf2";

const options = {
input: "/tmp/encrypted.pdf",
output: "/tmp/decrypted.pdf",
password: "YOUR_PASSWORD_TO_DECRYPT_PDF",
password: "ENCRYPTION_PASSWORD",
}

await decrypt(options);
```

## PDF Encryption info
## Encryption info

You can retrieve encryption information using the info function. This function will return a Promise<string>. See [here](https://qpdf.readthedocs.io/en/stable/cli.html#option-show-encryption) for more information.
You can retrieve encryption information using the info function. This function will return a `Promise<string>`. See [here](https://qpdf.readthedocs.io/en/stable/cli.html#option-show-encryption) for more information.

```
```bash
import { info } from "node-qpdf2";

const options = {
input: "/tmp/encrypted.pdf",
password: "FILE_PASSWORD",
password: "ENCRYPTION_PASSWORD",
}

console.log(await info(options));
```

If the file is not encrypted, the result will be "File is not encrypted".
If file is not encrypted, the result will be `File is not encrypted`.

## Coverage

| File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
| File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Lines |
| ---------- | ------- | -------- | ------- | ------- | ----------------- |
| All files | 100 | 100 | 100 | 100 |
| decrypt.ts | 100 | 100 | 100 | 100 |
Expand Down
4 changes: 3 additions & 1 deletion src/decrypt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import execute from "./spawn.js";
import { fileExists } from "./utils.js";

export interface DecryptSettings {
/** Optional - Path to custom qpdf binary */
qpdfPath?: string
/** The path for the encrypted pdf */
input: string;
/** The path for the decrypted pdf */
Expand Down Expand Up @@ -36,5 +38,5 @@ export const decrypt = async (payload: DecryptSettings): Promise<Buffer> => {
callArguments.push("-");
}

return execute(callArguments);
return execute(callArguments, payload.qpdfPath);
};
8 changes: 5 additions & 3 deletions src/encrypt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ const EncryptDefaults = {
};

export interface EncryptOptions {
/** Optional - Path to custom qpdf binary */
qpdfPath?: string
/** The location of the unencrypted pdf file */
input: string;
/**
Expand Down Expand Up @@ -76,9 +78,9 @@ export const encrypt = async (userPayload: EncryptOptions): Promise<Buffer> => {

const callArguments = [];

// If the keyLength is 40, `--allow-weak-crypto` needs to be specified before `--encrypt`.
// If the keyLength is not 256, `--allow-weak-crypto` needs to be specified before `--encrypt`.
// This is required for qpdf 11+.
if (payload.keyLength === 40) callArguments.push("--allow-weak-crypto");
if (payload.keyLength !== 256) callArguments.push("--allow-weak-crypto");

callArguments.push("--encrypt");

Expand Down Expand Up @@ -136,5 +138,5 @@ export const encrypt = async (userPayload: EncryptOptions): Promise<Buffer> => {
callArguments.push("-");
}
// Execute command and return stdout for pipe
return execute(callArguments);
return execute(callArguments, payload.qpdfPath);
};
4 changes: 3 additions & 1 deletion src/info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import execute from "./spawn.js";
import { fileExists } from "./utils.js";

export interface InfoSettings {
/** Optional - Path to custom qpdf binary */
qpdfPath?: string
/** The path for the encrypted pdf */
input: string;
/** The password for the encrypted pdf */
Expand All @@ -27,7 +29,7 @@ export const info = async (payload: InfoSettings): Promise<string> => {
// Input file path
callArguments.push(payload.input);

const result = await execute(callArguments);
const result = await execute(callArguments, payload.qpdfPath);

return result.toLocaleString().trim();
};
6 changes: 4 additions & 2 deletions src/spawn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@

import { spawn } from "node:child_process";

export default (callArguments: string[]): Promise<Buffer> =>
export default (callArguments: string[], qpdfPath?:string): Promise<Buffer> =>
new Promise((resolve, reject) => {
const process = spawn("qpdf", callArguments);
const process = spawn(qpdfPath? "./qpdf" :"qpdf", callArguments, {
Sparticuz marked this conversation as resolved.
Show resolved Hide resolved
cwd: qpdfPath
});
const stdout: string[] = [];
const stderr: string[] = [];
process.stdout.on("data", (data) => {
Expand Down