peasy-pdf 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +260 -0
- package/dist/index.d.ts +160 -0
- package/dist/index.js +224 -0
- package/package.json +45 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Peasy Tools
|
|
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.
|
package/README.md
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
# peasy-pdf
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/peasy-pdf)
|
|
4
|
+
[](https://www.typescriptlang.org/)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
7
|
+
PDF manipulation library for Node.js -- merge, split, rotate, reorder, extract pages, and manage metadata. TypeScript-first with full type safety, powered by [pdf-lib](https://pdf-lib.js.org/) for pure-JavaScript PDF processing with zero native dependencies.
|
|
8
|
+
|
|
9
|
+
Built from [Peasy PDF](https://peasypdf.com), the developer tools platform for PDF processing, conversion, and optimization.
|
|
10
|
+
|
|
11
|
+
> **Try the interactive tools at [peasypdf.com](https://peasypdf.com)** -- [Merge PDF](https://peasypdf.com/tools/merge-pdf/), [Split PDF](https://peasypdf.com/tools/split-pdf/), [Rotate PDF](https://peasypdf.com/tools/rotate-pdf/)
|
|
12
|
+
|
|
13
|
+
## Table of Contents
|
|
14
|
+
|
|
15
|
+
- [Install](#install)
|
|
16
|
+
- [Quick Start](#quick-start)
|
|
17
|
+
- [What You Can Do](#what-you-can-do)
|
|
18
|
+
- [Merge and Split](#merge-and-split)
|
|
19
|
+
- [Rotate and Reorder](#rotate-and-reorder)
|
|
20
|
+
- [Page Management](#page-management)
|
|
21
|
+
- [Metadata](#metadata)
|
|
22
|
+
- [TypeScript Types](#typescript-types)
|
|
23
|
+
- [API Reference](#api-reference)
|
|
24
|
+
- [Also Available for Python](#also-available-for-python)
|
|
25
|
+
- [Peasy Developer Tools](#peasy-developer-tools)
|
|
26
|
+
- [License](#license)
|
|
27
|
+
|
|
28
|
+
## Install
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install peasy-pdf
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Quick Start
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
import { merge, split, rotate, info, setMetadata } from "peasy-pdf";
|
|
38
|
+
import { readFileSync, writeFileSync } from "fs";
|
|
39
|
+
|
|
40
|
+
// Merge two PDFs into one
|
|
41
|
+
const pdf1 = readFileSync("report-q1.pdf");
|
|
42
|
+
const pdf2 = readFileSync("report-q2.pdf");
|
|
43
|
+
const merged = await merge(pdf1, pdf2);
|
|
44
|
+
writeFileSync("annual-report.pdf", merged);
|
|
45
|
+
|
|
46
|
+
// Split a PDF by page ranges
|
|
47
|
+
const parts = await split(readFileSync("document.pdf"), "1-3,4-6");
|
|
48
|
+
parts.forEach((part, i) => writeFileSync(`part-${i + 1}.pdf`, part));
|
|
49
|
+
|
|
50
|
+
// Rotate all pages 90 degrees clockwise
|
|
51
|
+
const rotated = await rotate(readFileSync("landscape.pdf"), 90);
|
|
52
|
+
|
|
53
|
+
// Get PDF information
|
|
54
|
+
const pdfInfo = await info(readFileSync("document.pdf"));
|
|
55
|
+
console.log(`Pages: ${pdfInfo.pages}, Title: ${pdfInfo.title}`);
|
|
56
|
+
|
|
57
|
+
// Set document metadata
|
|
58
|
+
const tagged = await setMetadata(readFileSync("draft.pdf"), {
|
|
59
|
+
title: "Annual Report 2026",
|
|
60
|
+
author: "Finance Team",
|
|
61
|
+
});
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## What You Can Do
|
|
65
|
+
|
|
66
|
+
### Merge and Split
|
|
67
|
+
|
|
68
|
+
Combine multiple PDF documents into a single file, or split a large PDF into smaller parts by page ranges. The merge operation preserves all page content, annotations, and formatting from the source documents.
|
|
69
|
+
|
|
70
|
+
| Operation | Function | Description |
|
|
71
|
+
|-----------|----------|-------------|
|
|
72
|
+
| Merge | `merge(...sources)` | Combine multiple PDFs into one document |
|
|
73
|
+
| Split | `split(source, ranges)` | Split a PDF by page ranges (e.g. "1-3,4-6") |
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
import { merge, split } from "peasy-pdf";
|
|
77
|
+
|
|
78
|
+
// Merge three reports into one combined document
|
|
79
|
+
const combined = await merge(q1Report, q2Report, q3Report);
|
|
80
|
+
|
|
81
|
+
// Split a 10-page document into three parts
|
|
82
|
+
const parts = await split(document, "1-3,4-7,8-10");
|
|
83
|
+
console.log(parts.length); // 3 separate PDF byte arrays
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Learn more: [Merge PDF Tool](https://peasypdf.com/tools/merge-pdf/) -- [Split PDF Tool](https://peasypdf.com/tools/split-pdf/)
|
|
87
|
+
|
|
88
|
+
### Rotate and Reorder
|
|
89
|
+
|
|
90
|
+
Rotate pages by 90, 180, or 270 degrees, reverse page order, or extract odd/even pages. Rotation can target all pages or a specific subset using page specifications.
|
|
91
|
+
|
|
92
|
+
| Operation | Function | Description |
|
|
93
|
+
|-----------|----------|-------------|
|
|
94
|
+
| Rotate | `rotate(source, angle, pages?)` | Rotate pages by 90/180/270 degrees |
|
|
95
|
+
| Reverse | `reverse(source)` | Reverse the page order |
|
|
96
|
+
| Odd/Even | `oddEven(source, mode)` | Extract odd or even pages |
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
import { rotate, reverse, oddEven } from "peasy-pdf";
|
|
100
|
+
|
|
101
|
+
// Rotate all pages 90 degrees clockwise
|
|
102
|
+
const rotated = await rotate(pdf, 90);
|
|
103
|
+
|
|
104
|
+
// Rotate only pages 1 and 3
|
|
105
|
+
const partial = await rotate(pdf, 180, "1,3");
|
|
106
|
+
|
|
107
|
+
// Reverse page order (last page becomes first)
|
|
108
|
+
const reversed = await reverse(pdf);
|
|
109
|
+
|
|
110
|
+
// Extract only odd pages (1, 3, 5...) for duplex printing
|
|
111
|
+
const oddPages = await oddEven(pdf, "odd");
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Learn more: [Rotate PDF Tool](https://peasypdf.com/tools/rotate-pdf/)
|
|
115
|
+
|
|
116
|
+
### Page Management
|
|
117
|
+
|
|
118
|
+
Delete, extract, insert, or duplicate individual pages. All page specifications use 1-based page numbers and support ranges (e.g. "1-3,5,7-9") or the keyword "all".
|
|
119
|
+
|
|
120
|
+
| Operation | Function | Description |
|
|
121
|
+
|-----------|----------|-------------|
|
|
122
|
+
| Delete | `deletePages(source, pages)` | Remove specific pages |
|
|
123
|
+
| Extract | `extractPages(source, pages)` | Extract pages into a new PDF |
|
|
124
|
+
| Insert blank | `insertBlank(source, after?, options?)` | Insert blank pages |
|
|
125
|
+
| Duplicate | `duplicatePages(source, pages?, copies?)` | Duplicate pages in place |
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
import { deletePages, extractPages, insertBlank, duplicatePages } from "peasy-pdf";
|
|
129
|
+
|
|
130
|
+
// Remove the cover page and table of contents
|
|
131
|
+
const trimmed = await deletePages(pdf, "1-2");
|
|
132
|
+
|
|
133
|
+
// Extract pages 5 through 10 into a new document
|
|
134
|
+
const excerpt = await extractPages(pdf, "5-10");
|
|
135
|
+
|
|
136
|
+
// Insert a blank separator page after page 3
|
|
137
|
+
const withSeparator = await insertBlank(pdf, "3");
|
|
138
|
+
|
|
139
|
+
// Insert 2 blank A4 pages at the end
|
|
140
|
+
const padded = await insertBlank(pdf, undefined, {
|
|
141
|
+
count: 2,
|
|
142
|
+
width: 595, // A4 width in points
|
|
143
|
+
height: 842, // A4 height in points
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// Duplicate every page (useful for printing 2-up)
|
|
147
|
+
const doubled = await duplicatePages(pdf);
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Learn more: [PDF Page Tools](https://peasypdf.com/tools/)
|
|
151
|
+
|
|
152
|
+
### Metadata
|
|
153
|
+
|
|
154
|
+
Read, write, or strip PDF document metadata including title, author, subject, keywords, creator, and producer fields. The `info()` function provides a quick overview including page count and file size.
|
|
155
|
+
|
|
156
|
+
| Operation | Function | Description |
|
|
157
|
+
|-----------|----------|-------------|
|
|
158
|
+
| Info | `info(source)` | Page count, metadata, file size |
|
|
159
|
+
| Get | `getMetadata(source)` | Read all metadata fields |
|
|
160
|
+
| Set | `setMetadata(source, metadata)` | Update metadata fields |
|
|
161
|
+
| Strip | `stripMetadata(source)` | Remove all metadata |
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
import { info, getMetadata, setMetadata, stripMetadata } from "peasy-pdf";
|
|
165
|
+
|
|
166
|
+
// Quick document overview
|
|
167
|
+
const pdfInfo = await info(pdf);
|
|
168
|
+
console.log(`${pdfInfo.pages} pages, ${pdfInfo.sizeBytes} bytes`);
|
|
169
|
+
console.log(`Title: ${pdfInfo.title}, Author: ${pdfInfo.author}`);
|
|
170
|
+
|
|
171
|
+
// Set metadata before publishing
|
|
172
|
+
const published = await setMetadata(pdf, {
|
|
173
|
+
title: "Q1 Financial Report",
|
|
174
|
+
author: "Finance Department",
|
|
175
|
+
subject: "Quarterly financials",
|
|
176
|
+
keywords: "finance, quarterly, 2026",
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// Strip all metadata for privacy
|
|
180
|
+
const clean = await stripMetadata(pdf);
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Learn more: [PDF Metadata Tool](https://peasypdf.com/tools/pdf-metadata/)
|
|
184
|
+
|
|
185
|
+
## TypeScript Types
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
import type { PdfInfo, PdfMetadata, PageSize, OddEvenMode } from "peasy-pdf";
|
|
189
|
+
|
|
190
|
+
// PdfInfo -- document overview from info()
|
|
191
|
+
const pdfInfo: PdfInfo = {
|
|
192
|
+
pages: 10,
|
|
193
|
+
title: "Annual Report",
|
|
194
|
+
author: "Finance Team",
|
|
195
|
+
subject: "2026 Financials",
|
|
196
|
+
creator: "peasy-pdf",
|
|
197
|
+
producer: "pdf-lib",
|
|
198
|
+
sizeBytes: 245_760,
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
// PdfMetadata -- fields for get/set metadata
|
|
202
|
+
const metadata: PdfMetadata = {
|
|
203
|
+
title: "My Document",
|
|
204
|
+
author: "John Doe",
|
|
205
|
+
subject: "Testing",
|
|
206
|
+
keywords: "test, pdf",
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
// OddEvenMode -- "odd" | "even" for page extraction
|
|
210
|
+
const mode: OddEvenMode = "odd";
|
|
211
|
+
|
|
212
|
+
// PageSize -- standard page sizes (planned for future use)
|
|
213
|
+
const size: PageSize = "a4";
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## API Reference
|
|
217
|
+
|
|
218
|
+
| Function | Description |
|
|
219
|
+
|----------|-------------|
|
|
220
|
+
| `merge(...sources)` | Merge multiple PDFs into one |
|
|
221
|
+
| `split(source, ranges)` | Split PDF by comma-separated page ranges |
|
|
222
|
+
| `rotate(source, angle, pages?)` | Rotate pages by 90/180/270 degrees |
|
|
223
|
+
| `reverse(source)` | Reverse page order |
|
|
224
|
+
| `deletePages(source, pages)` | Remove specified pages |
|
|
225
|
+
| `extractPages(source, pages)` | Extract pages into new PDF |
|
|
226
|
+
| `oddEven(source, mode)` | Extract odd or even pages |
|
|
227
|
+
| `insertBlank(source, after?, options?)` | Insert blank pages |
|
|
228
|
+
| `duplicatePages(source, pages?, copies?)` | Duplicate pages in place |
|
|
229
|
+
| `info(source)` | Get page count, metadata, file size |
|
|
230
|
+
| `getMetadata(source)` | Read document metadata |
|
|
231
|
+
| `setMetadata(source, metadata)` | Set document metadata |
|
|
232
|
+
| `stripMetadata(source)` | Remove all metadata |
|
|
233
|
+
| `parsePages(spec, total)` | Parse page specification to 0-based indices |
|
|
234
|
+
|
|
235
|
+
All functions accept `Uint8Array` (PDF bytes) and return `Promise<Uint8Array>` or structured data. No filesystem access -- works in Node.js and browsers.
|
|
236
|
+
|
|
237
|
+
## Also Available for Python
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
pip install peasy-pdf
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
The Python package provides PDF merge, split, rotate, compress, extract text, encrypt/decrypt, and 14 more operations with CLI, MCP server, and REST API client. See [peasy-pdf on PyPI](https://pypi.org/project/peasy-pdf/).
|
|
244
|
+
|
|
245
|
+
## Peasy Developer Tools
|
|
246
|
+
|
|
247
|
+
| Package | PyPI | npm | Description |
|
|
248
|
+
|---------|------|-----|-------------|
|
|
249
|
+
| peasytext | [PyPI](https://pypi.org/project/peasytext/) | [npm](https://www.npmjs.com/package/peasytext) | Text analysis -- readability, sentiment, keywords |
|
|
250
|
+
| **peasy-pdf** | [PyPI](https://pypi.org/project/peasy-pdf/) | **[npm](https://www.npmjs.com/package/peasy-pdf)** | **PDF processing -- merge, split, rotate, metadata** |
|
|
251
|
+
| peasy-image | [PyPI](https://pypi.org/project/peasy-image/) | -- | Image ops -- resize, crop, filter, watermark |
|
|
252
|
+
| peasy-css | [PyPI](https://pypi.org/project/peasy-css/) | [npm](https://www.npmjs.com/package/peasy-css) | CSS generation -- gradients, shadows, flexbox, grid |
|
|
253
|
+
| peasy-compress | [PyPI](https://pypi.org/project/peasy-compress/) | [npm](https://www.npmjs.com/package/peasy-compress) | Archive & compression -- ZIP, gzip, brotli, deflate |
|
|
254
|
+
| peasy-document | [PyPI](https://pypi.org/project/peasy-document/) | [npm](https://www.npmjs.com/package/peasy-document) | Document conversion -- DOCX, HTML, Markdown |
|
|
255
|
+
|
|
256
|
+
Part of the [Peasy](https://peasytools.com) developer tools ecosystem.
|
|
257
|
+
|
|
258
|
+
## License
|
|
259
|
+
|
|
260
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/** Information about a PDF document. */
|
|
2
|
+
interface PdfInfo {
|
|
3
|
+
/** Total number of pages. */
|
|
4
|
+
pages: number;
|
|
5
|
+
/** Document title from metadata. */
|
|
6
|
+
title: string;
|
|
7
|
+
/** Document author from metadata. */
|
|
8
|
+
author: string;
|
|
9
|
+
/** Document subject from metadata. */
|
|
10
|
+
subject: string;
|
|
11
|
+
/** Creator application from metadata. */
|
|
12
|
+
creator: string;
|
|
13
|
+
/** PDF producer from metadata. */
|
|
14
|
+
producer: string;
|
|
15
|
+
/** File size in bytes. */
|
|
16
|
+
sizeBytes: number;
|
|
17
|
+
}
|
|
18
|
+
/** Metadata fields that can be set on a PDF document. */
|
|
19
|
+
interface PdfMetadata {
|
|
20
|
+
title?: string;
|
|
21
|
+
author?: string;
|
|
22
|
+
subject?: string;
|
|
23
|
+
keywords?: string;
|
|
24
|
+
creator?: string;
|
|
25
|
+
producer?: string;
|
|
26
|
+
}
|
|
27
|
+
/** Standard page sizes. */
|
|
28
|
+
type PageSize = "a3" | "a4" | "a5" | "letter" | "legal" | "tabloid";
|
|
29
|
+
/** Mode for odd/even page extraction. */
|
|
30
|
+
type OddEvenMode = "odd" | "even";
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* peasy-pdf — PDF manipulation engine powered by pdf-lib.
|
|
34
|
+
*
|
|
35
|
+
* All functions accept Uint8Array (PDF bytes) and return Uint8Array or
|
|
36
|
+
* structured data. No filesystem access — works in Node.js and browsers.
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Parse a page specification string into zero-based page indices.
|
|
41
|
+
*
|
|
42
|
+
* Supported formats:
|
|
43
|
+
* - "all" — every page
|
|
44
|
+
* - "1,3,5" — specific pages (1-based)
|
|
45
|
+
* - "1-3,7-9" — ranges (inclusive, 1-based)
|
|
46
|
+
* - "2-4,6" — mixed ranges and singles
|
|
47
|
+
*
|
|
48
|
+
* Out-of-range values are clamped to [1, total].
|
|
49
|
+
*/
|
|
50
|
+
declare function parsePages(spec: string, total: number): number[];
|
|
51
|
+
/**
|
|
52
|
+
* Merge multiple PDF documents into a single PDF.
|
|
53
|
+
*
|
|
54
|
+
* @param sources - Two or more PDF byte arrays to merge in order.
|
|
55
|
+
* @returns The merged PDF as a Uint8Array.
|
|
56
|
+
*/
|
|
57
|
+
declare function merge(...sources: Uint8Array[]): Promise<Uint8Array>;
|
|
58
|
+
/**
|
|
59
|
+
* Split a PDF into multiple PDFs by page ranges.
|
|
60
|
+
*
|
|
61
|
+
* @param source - The source PDF bytes.
|
|
62
|
+
* @param ranges - Comma-separated page ranges, e.g. "1-3,4-6,7".
|
|
63
|
+
* Each range becomes a separate output PDF.
|
|
64
|
+
* @returns An array of PDF byte arrays, one per range group.
|
|
65
|
+
*/
|
|
66
|
+
declare function split(source: Uint8Array, ranges: string): Promise<Uint8Array[]>;
|
|
67
|
+
/**
|
|
68
|
+
* Rotate pages by the given angle (90, 180, or 270 degrees clockwise).
|
|
69
|
+
*
|
|
70
|
+
* @param source - The source PDF bytes.
|
|
71
|
+
* @param angle - Rotation angle in degrees (90, 180, 270).
|
|
72
|
+
* @param pages - Optional page spec (default: "all").
|
|
73
|
+
* @returns The PDF with rotated pages.
|
|
74
|
+
*/
|
|
75
|
+
declare function rotate(source: Uint8Array, angle: number, pages?: string): Promise<Uint8Array>;
|
|
76
|
+
/**
|
|
77
|
+
* Reverse the page order of a PDF.
|
|
78
|
+
*
|
|
79
|
+
* @param source - The source PDF bytes.
|
|
80
|
+
* @returns The PDF with pages in reverse order.
|
|
81
|
+
*/
|
|
82
|
+
declare function reverse(source: Uint8Array): Promise<Uint8Array>;
|
|
83
|
+
/**
|
|
84
|
+
* Delete specified pages from a PDF.
|
|
85
|
+
*
|
|
86
|
+
* @param source - The source PDF bytes.
|
|
87
|
+
* @param pages - Page spec of pages to remove (e.g. "1,3,5-7").
|
|
88
|
+
* @returns The PDF with specified pages removed.
|
|
89
|
+
*/
|
|
90
|
+
declare function deletePages(source: Uint8Array, pages: string): Promise<Uint8Array>;
|
|
91
|
+
/**
|
|
92
|
+
* Extract specified pages from a PDF into a new PDF.
|
|
93
|
+
*
|
|
94
|
+
* @param source - The source PDF bytes.
|
|
95
|
+
* @param pages - Page spec of pages to extract (e.g. "1-3,5").
|
|
96
|
+
* @returns A new PDF containing only the specified pages.
|
|
97
|
+
*/
|
|
98
|
+
declare function extractPages(source: Uint8Array, pages: string): Promise<Uint8Array>;
|
|
99
|
+
/**
|
|
100
|
+
* Extract odd or even pages from a PDF.
|
|
101
|
+
*
|
|
102
|
+
* @param source - The source PDF bytes.
|
|
103
|
+
* @param mode - "odd" for pages 1, 3, 5... or "even" for pages 2, 4, 6...
|
|
104
|
+
* @returns A new PDF containing only the selected pages.
|
|
105
|
+
*/
|
|
106
|
+
declare function oddEven(source: Uint8Array, mode: OddEvenMode): Promise<Uint8Array>;
|
|
107
|
+
/**
|
|
108
|
+
* Insert blank pages into a PDF.
|
|
109
|
+
*
|
|
110
|
+
* @param source - The source PDF bytes.
|
|
111
|
+
* @param after - Page spec for insertion point (e.g. "2" inserts after page 2).
|
|
112
|
+
* If omitted, blank pages are appended at the end.
|
|
113
|
+
* @param options - Optional: count (default 1), width/height in points.
|
|
114
|
+
* @returns The PDF with blank pages inserted.
|
|
115
|
+
*/
|
|
116
|
+
declare function insertBlank(source: Uint8Array, after?: string, options?: {
|
|
117
|
+
count?: number;
|
|
118
|
+
width?: number;
|
|
119
|
+
height?: number;
|
|
120
|
+
}): Promise<Uint8Array>;
|
|
121
|
+
/**
|
|
122
|
+
* Duplicate pages within a PDF.
|
|
123
|
+
*
|
|
124
|
+
* @param source - The source PDF bytes.
|
|
125
|
+
* @param pages - Page spec of pages to duplicate (default: "all").
|
|
126
|
+
* @param copies - Number of copies of each page (default: 1, meaning 2 total).
|
|
127
|
+
* @returns The PDF with duplicated pages.
|
|
128
|
+
*/
|
|
129
|
+
declare function duplicatePages(source: Uint8Array, pages?: string, copies?: number): Promise<Uint8Array>;
|
|
130
|
+
/**
|
|
131
|
+
* Get information about a PDF document.
|
|
132
|
+
*
|
|
133
|
+
* @param source - The PDF bytes.
|
|
134
|
+
* @returns PDF info including page count, metadata, and file size.
|
|
135
|
+
*/
|
|
136
|
+
declare function info(source: Uint8Array): Promise<PdfInfo>;
|
|
137
|
+
/**
|
|
138
|
+
* Get document metadata from a PDF.
|
|
139
|
+
*
|
|
140
|
+
* @param source - The PDF bytes.
|
|
141
|
+
* @returns The document metadata fields.
|
|
142
|
+
*/
|
|
143
|
+
declare function getMetadata(source: Uint8Array): Promise<PdfMetadata>;
|
|
144
|
+
/**
|
|
145
|
+
* Set document metadata on a PDF.
|
|
146
|
+
*
|
|
147
|
+
* @param source - The PDF bytes.
|
|
148
|
+
* @param metadata - Metadata fields to set. Only provided fields are updated.
|
|
149
|
+
* @returns The PDF with updated metadata.
|
|
150
|
+
*/
|
|
151
|
+
declare function setMetadata(source: Uint8Array, metadata: PdfMetadata): Promise<Uint8Array>;
|
|
152
|
+
/**
|
|
153
|
+
* Remove all metadata from a PDF.
|
|
154
|
+
*
|
|
155
|
+
* @param source - The PDF bytes.
|
|
156
|
+
* @returns The PDF with all metadata fields cleared.
|
|
157
|
+
*/
|
|
158
|
+
declare function stripMetadata(source: Uint8Array): Promise<Uint8Array>;
|
|
159
|
+
|
|
160
|
+
export { type OddEvenMode, type PageSize, type PdfInfo, type PdfMetadata, deletePages, duplicatePages, extractPages, getMetadata, info, insertBlank, merge, oddEven, parsePages, reverse, rotate, setMetadata, split, stripMetadata };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
// src/engine.ts
|
|
2
|
+
import { PDFDocument, degrees } from "pdf-lib";
|
|
3
|
+
function parsePages(spec, total) {
|
|
4
|
+
if (spec.trim().toLowerCase() === "all") {
|
|
5
|
+
return Array.from({ length: total }, (_, i) => i);
|
|
6
|
+
}
|
|
7
|
+
const indices = [];
|
|
8
|
+
for (const part of spec.split(",")) {
|
|
9
|
+
const trimmed = part.trim();
|
|
10
|
+
if (trimmed === "") continue;
|
|
11
|
+
if (trimmed.includes("-")) {
|
|
12
|
+
const [startStr, endStr] = trimmed.split("-", 2);
|
|
13
|
+
const start = Math.max(1, parseInt(startStr, 10));
|
|
14
|
+
const end = Math.min(total, parseInt(endStr, 10));
|
|
15
|
+
for (let i = start - 1; i < end; i++) {
|
|
16
|
+
indices.push(i);
|
|
17
|
+
}
|
|
18
|
+
} else {
|
|
19
|
+
const page = parseInt(trimmed, 10);
|
|
20
|
+
if (page >= 1 && page <= total) {
|
|
21
|
+
indices.push(page - 1);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return indices;
|
|
26
|
+
}
|
|
27
|
+
async function merge(...sources) {
|
|
28
|
+
if (sources.length === 0) {
|
|
29
|
+
throw new Error("merge requires at least one PDF");
|
|
30
|
+
}
|
|
31
|
+
const merged = await PDFDocument.create();
|
|
32
|
+
for (const src of sources) {
|
|
33
|
+
const doc = await PDFDocument.load(src);
|
|
34
|
+
const pages = await merged.copyPages(doc, doc.getPageIndices());
|
|
35
|
+
pages.forEach((p) => merged.addPage(p));
|
|
36
|
+
}
|
|
37
|
+
return merged.save();
|
|
38
|
+
}
|
|
39
|
+
async function split(source, ranges) {
|
|
40
|
+
const srcDoc = await PDFDocument.load(source);
|
|
41
|
+
const total = srcDoc.getPageCount();
|
|
42
|
+
const rangeGroups = ranges.split(",").reduce(
|
|
43
|
+
(groups, part) => {
|
|
44
|
+
const trimmed = part.trim();
|
|
45
|
+
if (trimmed === "") return groups;
|
|
46
|
+
groups.push([trimmed]);
|
|
47
|
+
return groups;
|
|
48
|
+
},
|
|
49
|
+
[]
|
|
50
|
+
);
|
|
51
|
+
const results = [];
|
|
52
|
+
for (const group of rangeGroups) {
|
|
53
|
+
const spec = group.join(",");
|
|
54
|
+
const indices = parsePages(spec, total);
|
|
55
|
+
if (indices.length === 0) continue;
|
|
56
|
+
const newDoc = await PDFDocument.create();
|
|
57
|
+
const copiedPages = await newDoc.copyPages(srcDoc, indices);
|
|
58
|
+
copiedPages.forEach((p) => newDoc.addPage(p));
|
|
59
|
+
results.push(await newDoc.save());
|
|
60
|
+
}
|
|
61
|
+
return results;
|
|
62
|
+
}
|
|
63
|
+
async function rotate(source, angle, pages) {
|
|
64
|
+
const doc = await PDFDocument.load(source);
|
|
65
|
+
const indices = parsePages(pages || "all", doc.getPageCount());
|
|
66
|
+
for (const idx of indices) {
|
|
67
|
+
const page = doc.getPage(idx);
|
|
68
|
+
const current = page.getRotation().angle;
|
|
69
|
+
page.setRotation(degrees(current + angle));
|
|
70
|
+
}
|
|
71
|
+
return doc.save();
|
|
72
|
+
}
|
|
73
|
+
async function reverse(source) {
|
|
74
|
+
const srcDoc = await PDFDocument.load(source);
|
|
75
|
+
const total = srcDoc.getPageCount();
|
|
76
|
+
const reversed = await PDFDocument.create();
|
|
77
|
+
const indices = Array.from({ length: total }, (_, i) => total - 1 - i);
|
|
78
|
+
const pages = await reversed.copyPages(srcDoc, indices);
|
|
79
|
+
pages.forEach((p) => reversed.addPage(p));
|
|
80
|
+
return reversed.save();
|
|
81
|
+
}
|
|
82
|
+
async function deletePages(source, pages) {
|
|
83
|
+
const srcDoc = await PDFDocument.load(source);
|
|
84
|
+
const total = srcDoc.getPageCount();
|
|
85
|
+
const toDelete = new Set(parsePages(pages, total));
|
|
86
|
+
const keepIndices = Array.from({ length: total }, (_, i) => i).filter(
|
|
87
|
+
(i) => !toDelete.has(i)
|
|
88
|
+
);
|
|
89
|
+
if (keepIndices.length === 0) {
|
|
90
|
+
throw new Error("Cannot delete all pages from a PDF");
|
|
91
|
+
}
|
|
92
|
+
const newDoc = await PDFDocument.create();
|
|
93
|
+
const copiedPages = await newDoc.copyPages(srcDoc, keepIndices);
|
|
94
|
+
copiedPages.forEach((p) => newDoc.addPage(p));
|
|
95
|
+
return newDoc.save();
|
|
96
|
+
}
|
|
97
|
+
async function extractPages(source, pages) {
|
|
98
|
+
const srcDoc = await PDFDocument.load(source);
|
|
99
|
+
const indices = parsePages(pages, srcDoc.getPageCount());
|
|
100
|
+
if (indices.length === 0) {
|
|
101
|
+
throw new Error("No pages to extract");
|
|
102
|
+
}
|
|
103
|
+
const newDoc = await PDFDocument.create();
|
|
104
|
+
const copiedPages = await newDoc.copyPages(srcDoc, indices);
|
|
105
|
+
copiedPages.forEach((p) => newDoc.addPage(p));
|
|
106
|
+
return newDoc.save();
|
|
107
|
+
}
|
|
108
|
+
async function oddEven(source, mode) {
|
|
109
|
+
const srcDoc = await PDFDocument.load(source);
|
|
110
|
+
const total = srcDoc.getPageCount();
|
|
111
|
+
const indices = [];
|
|
112
|
+
for (let i = 0; i < total; i++) {
|
|
113
|
+
const pageNum = i + 1;
|
|
114
|
+
if (mode === "odd" && pageNum % 2 === 1) indices.push(i);
|
|
115
|
+
if (mode === "even" && pageNum % 2 === 0) indices.push(i);
|
|
116
|
+
}
|
|
117
|
+
if (indices.length === 0) {
|
|
118
|
+
throw new Error(`No ${mode} pages found`);
|
|
119
|
+
}
|
|
120
|
+
const newDoc = await PDFDocument.create();
|
|
121
|
+
const copiedPages = await newDoc.copyPages(srcDoc, indices);
|
|
122
|
+
copiedPages.forEach((p) => newDoc.addPage(p));
|
|
123
|
+
return newDoc.save();
|
|
124
|
+
}
|
|
125
|
+
async function insertBlank(source, after, options) {
|
|
126
|
+
const doc = await PDFDocument.load(source);
|
|
127
|
+
const total = doc.getPageCount();
|
|
128
|
+
const count = options?.count ?? 1;
|
|
129
|
+
const width = options?.width ?? 612;
|
|
130
|
+
const height = options?.height ?? 792;
|
|
131
|
+
if (after === void 0 || after === null) {
|
|
132
|
+
for (let i = 0; i < count; i++) {
|
|
133
|
+
doc.addPage([width, height]);
|
|
134
|
+
}
|
|
135
|
+
} else {
|
|
136
|
+
const indices = parsePages(after, total);
|
|
137
|
+
if (indices.length === 0) {
|
|
138
|
+
throw new Error("Invalid page specification for insertion point");
|
|
139
|
+
}
|
|
140
|
+
const insertAfter = Math.max(...indices);
|
|
141
|
+
for (let i = 0; i < count; i++) {
|
|
142
|
+
doc.insertPage(insertAfter + 1 + i, [width, height]);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return doc.save();
|
|
146
|
+
}
|
|
147
|
+
async function duplicatePages(source, pages, copies) {
|
|
148
|
+
const srcDoc = await PDFDocument.load(source);
|
|
149
|
+
const total = srcDoc.getPageCount();
|
|
150
|
+
const indices = parsePages(pages || "all", total);
|
|
151
|
+
const numCopies = copies ?? 1;
|
|
152
|
+
const newDoc = await PDFDocument.create();
|
|
153
|
+
for (let i = 0; i < total; i++) {
|
|
154
|
+
const [page] = await newDoc.copyPages(srcDoc, [i]);
|
|
155
|
+
newDoc.addPage(page);
|
|
156
|
+
if (indices.includes(i)) {
|
|
157
|
+
for (let c = 0; c < numCopies; c++) {
|
|
158
|
+
const [copy] = await newDoc.copyPages(srcDoc, [i]);
|
|
159
|
+
newDoc.addPage(copy);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return newDoc.save();
|
|
164
|
+
}
|
|
165
|
+
async function info(source) {
|
|
166
|
+
const doc = await PDFDocument.load(source);
|
|
167
|
+
return {
|
|
168
|
+
pages: doc.getPageCount(),
|
|
169
|
+
title: doc.getTitle() ?? "",
|
|
170
|
+
author: doc.getAuthor() ?? "",
|
|
171
|
+
subject: doc.getSubject() ?? "",
|
|
172
|
+
creator: doc.getCreator() ?? "",
|
|
173
|
+
producer: doc.getProducer() ?? "",
|
|
174
|
+
sizeBytes: source.byteLength
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
async function getMetadata(source) {
|
|
178
|
+
const doc = await PDFDocument.load(source);
|
|
179
|
+
return {
|
|
180
|
+
title: doc.getTitle() ?? void 0,
|
|
181
|
+
author: doc.getAuthor() ?? void 0,
|
|
182
|
+
subject: doc.getSubject() ?? void 0,
|
|
183
|
+
keywords: doc.getKeywords() ?? void 0,
|
|
184
|
+
creator: doc.getCreator() ?? void 0,
|
|
185
|
+
producer: doc.getProducer() ?? void 0
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
async function setMetadata(source, metadata) {
|
|
189
|
+
const doc = await PDFDocument.load(source);
|
|
190
|
+
if (metadata.title !== void 0) doc.setTitle(metadata.title);
|
|
191
|
+
if (metadata.author !== void 0) doc.setAuthor(metadata.author);
|
|
192
|
+
if (metadata.subject !== void 0) doc.setSubject(metadata.subject);
|
|
193
|
+
if (metadata.keywords !== void 0)
|
|
194
|
+
doc.setKeywords([metadata.keywords]);
|
|
195
|
+
if (metadata.creator !== void 0) doc.setCreator(metadata.creator);
|
|
196
|
+
if (metadata.producer !== void 0) doc.setProducer(metadata.producer);
|
|
197
|
+
return doc.save();
|
|
198
|
+
}
|
|
199
|
+
async function stripMetadata(source) {
|
|
200
|
+
const doc = await PDFDocument.load(source);
|
|
201
|
+
doc.setTitle("");
|
|
202
|
+
doc.setAuthor("");
|
|
203
|
+
doc.setSubject("");
|
|
204
|
+
doc.setKeywords([]);
|
|
205
|
+
doc.setCreator("");
|
|
206
|
+
doc.setProducer("");
|
|
207
|
+
return doc.save();
|
|
208
|
+
}
|
|
209
|
+
export {
|
|
210
|
+
deletePages,
|
|
211
|
+
duplicatePages,
|
|
212
|
+
extractPages,
|
|
213
|
+
getMetadata,
|
|
214
|
+
info,
|
|
215
|
+
insertBlank,
|
|
216
|
+
merge,
|
|
217
|
+
oddEven,
|
|
218
|
+
parsePages,
|
|
219
|
+
reverse,
|
|
220
|
+
rotate,
|
|
221
|
+
setMetadata,
|
|
222
|
+
split,
|
|
223
|
+
stripMetadata
|
|
224
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "peasy-pdf",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "PDF manipulation library for Node.js — merge, split, rotate, extract text, metadata. TypeScript-first.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsup src/index.ts --format esm --dts",
|
|
19
|
+
"test": "vitest run",
|
|
20
|
+
"typecheck": "tsc --noEmit"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"pdf",
|
|
24
|
+
"merge",
|
|
25
|
+
"split",
|
|
26
|
+
"rotate",
|
|
27
|
+
"extract",
|
|
28
|
+
"metadata",
|
|
29
|
+
"peasy"
|
|
30
|
+
],
|
|
31
|
+
"author": "Peasy Tools",
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"repository": {
|
|
34
|
+
"url": "https://github.com/peasytools/peasy-pdf-js.git"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://peasypdf.com",
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"pdf-lib": "^1.17"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"tsup": "^8.0",
|
|
42
|
+
"typescript": "^5.7",
|
|
43
|
+
"vitest": "^3.0"
|
|
44
|
+
}
|
|
45
|
+
}
|