qpdf-compress 0.4.1 → 0.5.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/CHANGELOG.md +7 -0
- package/README.md +18 -0
- package/dist/concurrency.d.ts +9 -0
- package/dist/concurrency.d.ts.map +1 -0
- package/dist/concurrency.js +22 -0
- package/dist/concurrency.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/lib/concurrency.ts +24 -0
- package/lib/index.ts +9 -5
- package/package.json +11 -3
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/), and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## [0.5.0] - 2026-04-04
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- `concurrency()` — get/set max concurrent compress operations dispatched to the libuv thread pool (default: CPU cores, powered by p-limit)
|
|
12
|
+
- Husky + lint-staged pre-commit hook (Prettier + ESLint on staged files)
|
|
13
|
+
|
|
7
14
|
## [0.4.0] - 2026-03-31
|
|
8
15
|
|
|
9
16
|
### Added
|
package/README.md
CHANGED
|
@@ -189,6 +189,24 @@ Compresses a PDF document. Automatically repairs damaged PDFs.
|
|
|
189
189
|
- Only replaces images where the result is actually smaller
|
|
190
190
|
- Skips tiny images (< 50×50 px)
|
|
191
191
|
|
|
192
|
+
### `concurrency(value?): number`
|
|
193
|
+
|
|
194
|
+
Gets or sets the maximum number of concurrent compress operations dispatched to the thread pool.
|
|
195
|
+
|
|
196
|
+
The default is the number of CPU cores (`os.availableParallelism()`). A value of `0` resets to the default.
|
|
197
|
+
|
|
198
|
+
Excess calls are queued in JavaScript, preventing libuv thread pool starvation.
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
import { compress, concurrency } from 'qpdf-compress';
|
|
202
|
+
|
|
203
|
+
concurrency(); // 8 (CPU cores)
|
|
204
|
+
concurrency(2); // limit to 2 concurrent operations
|
|
205
|
+
concurrency(0); // reset to default
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
192
210
|
## ⚙️ How it works
|
|
193
211
|
|
|
194
212
|
This package embeds [QPDF](https://github.com/qpdf/qpdf) (v12.3.2) as a statically linked C++ library, exposed to Node.js via N-API. Lossless JPEG optimization uses [libjpeg-turbo](https://libjpeg-turbo.org/) at the DCT coefficient level. Image recompression in lossy mode also uses libjpeg-turbo for JPEG encoding. TrueType font subsetting is handled by a custom binary parser that reads cmap tables, resolves composite glyph dependencies, and rebuilds glyf/loca/hmtx tables with only the used glyphs.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gets or sets the maximum number of concurrent operations.
|
|
3
|
+
*
|
|
4
|
+
* The default value is the number of CPU cores (`os.availableParallelism()`).
|
|
5
|
+
* A value of `0` resets to the default.
|
|
6
|
+
*/
|
|
7
|
+
export declare function concurrency(value?: number): number;
|
|
8
|
+
export declare function withConcurrency<T>(fn: () => Promise<T>): Promise<T>;
|
|
9
|
+
//# sourceMappingURL=concurrency.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"concurrency.d.ts","sourceRoot":"","sources":["../lib/concurrency.ts"],"names":[],"mappings":"AAKA;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAQlD;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAEnE"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { availableParallelism } from 'node:os';
|
|
2
|
+
import pLimit from 'p-limit';
|
|
3
|
+
let limit = pLimit(availableParallelism());
|
|
4
|
+
/**
|
|
5
|
+
* Gets or sets the maximum number of concurrent operations.
|
|
6
|
+
*
|
|
7
|
+
* The default value is the number of CPU cores (`os.availableParallelism()`).
|
|
8
|
+
* A value of `0` resets to the default.
|
|
9
|
+
*/
|
|
10
|
+
export function concurrency(value) {
|
|
11
|
+
if (value !== undefined) {
|
|
12
|
+
if (!Number.isInteger(value) || value < 0) {
|
|
13
|
+
throw new TypeError('Concurrency must be a non-negative integer');
|
|
14
|
+
}
|
|
15
|
+
limit = pLimit(value === 0 ? availableParallelism() : value);
|
|
16
|
+
}
|
|
17
|
+
return limit.concurrency;
|
|
18
|
+
}
|
|
19
|
+
export function withConcurrency(fn) {
|
|
20
|
+
return limit(fn);
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=concurrency.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"concurrency.js","sourceRoot":"","sources":["../lib/concurrency.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,MAA8B,MAAM,SAAS,CAAC;AAErD,IAAI,KAAK,GAAkB,MAAM,CAAC,oBAAoB,EAAE,CAAC,CAAC;AAE1D;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;YAC1C,MAAM,IAAI,SAAS,CAAC,4CAA4C,CAAC,CAAC;QACpE,CAAC;QACD,KAAK,GAAG,MAAM,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,oBAAoB,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,KAAK,CAAC,WAAW,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,eAAe,CAAI,EAAoB;IACrD,OAAO,KAAK,CAAC,EAAE,CAAC,CAAC;AACnB,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -16,5 +16,6 @@ export declare function compress(input: PdfInput, options: CompressOptions & {
|
|
|
16
16
|
output: string;
|
|
17
17
|
}): Promise<void>;
|
|
18
18
|
export declare function compress(input: PdfInput, options?: CompressOptions): Promise<Buffer>;
|
|
19
|
+
export { concurrency } from './concurrency.js';
|
|
19
20
|
export type { CompressOptions } from './types.js';
|
|
20
21
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAe,MAAM,YAAY,CAAC;AAY/D,KAAK,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC;AAEhC;;;;;;;;;;;GAWG;AACH,wBAAgB,QAAQ,CACtB,KAAK,EAAE,QAAQ,EACf,OAAO,EAAE,eAAe,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,GAC5C,OAAO,CAAC,IAAI,CAAC,CAAC;AACjB,wBAAgB,QAAQ,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;AAuBtF,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,YAAY,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
2
|
import { dirname, resolve } from 'node:path';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { withConcurrency } from './concurrency.js';
|
|
4
5
|
const require = createRequire(import.meta.url);
|
|
5
6
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
6
7
|
const addonDir = resolve(__dirname, '..', 'build', 'Release');
|
|
@@ -23,10 +24,11 @@ export async function compress(input, options) {
|
|
|
23
24
|
throw new TypeError('Input must be a Buffer or file path string');
|
|
24
25
|
}
|
|
25
26
|
const stripMetadata = options?.stripMetadata ?? true;
|
|
26
|
-
return addon.compress(input, {
|
|
27
|
+
return withConcurrency(() => addon.compress(input, {
|
|
27
28
|
...(options?.lossy ? { lossy: true } : {}),
|
|
28
29
|
...(stripMetadata ? { stripMetadata: true } : {}),
|
|
29
30
|
...(options?.output ? { output: options.output } : {}),
|
|
30
|
-
});
|
|
31
|
+
}));
|
|
31
32
|
}
|
|
33
|
+
export { concurrency } from './concurrency.js';
|
|
32
34
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAGnD,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/C,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAE1D,MAAM,QAAQ,GAAG,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;AAC9D,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;IACjC,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,QAAQ,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;AAC7D,CAAC;AAED,MAAM,KAAK,GAAgB,OAAO,CAAC,qCAAqC,CAAC,CAAC;AAqB1E,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,KAAe,EAAE,OAAyB;IACvE,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,MAAM,IAAI,SAAS,CAAC,8BAA8B,CAAC,CAAC;QACtD,CAAC;IACH,CAAC;SAAM,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACrC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,MAAM,IAAI,SAAS,CAAC,4BAA4B,CAAC,CAAC;QACpD,CAAC;IACH,CAAC;SAAM,CAAC;QACN,MAAM,IAAI,SAAS,CAAC,4CAA4C,CAAC,CAAC;IACpE,CAAC;IACD,MAAM,aAAa,GAAG,OAAO,EAAE,aAAa,IAAI,IAAI,CAAC;IACrD,OAAO,eAAe,CAAC,GAAG,EAAE,CAC1B,KAAK,CAAC,QAAQ,CAAC,KAAK,EAAE;QACpB,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC1C,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjD,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACvD,CAAC,CACuB,CAAC;AAC9B,CAAC;AAED,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { availableParallelism } from 'node:os';
|
|
2
|
+
import pLimit, { type LimitFunction } from 'p-limit';
|
|
3
|
+
|
|
4
|
+
let limit: LimitFunction = pLimit(availableParallelism());
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Gets or sets the maximum number of concurrent operations.
|
|
8
|
+
*
|
|
9
|
+
* The default value is the number of CPU cores (`os.availableParallelism()`).
|
|
10
|
+
* A value of `0` resets to the default.
|
|
11
|
+
*/
|
|
12
|
+
export function concurrency(value?: number): number {
|
|
13
|
+
if (value !== undefined) {
|
|
14
|
+
if (!Number.isInteger(value) || value < 0) {
|
|
15
|
+
throw new TypeError('Concurrency must be a non-negative integer');
|
|
16
|
+
}
|
|
17
|
+
limit = pLimit(value === 0 ? availableParallelism() : value);
|
|
18
|
+
}
|
|
19
|
+
return limit.concurrency;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function withConcurrency<T>(fn: () => Promise<T>): Promise<T> {
|
|
23
|
+
return limit(fn);
|
|
24
|
+
}
|
package/lib/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
2
|
import { dirname, resolve } from 'node:path';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { withConcurrency } from './concurrency.js';
|
|
4
5
|
import type { CompressOptions, NativeAddon } from './types.js';
|
|
5
6
|
|
|
6
7
|
const require = createRequire(import.meta.url);
|
|
@@ -45,11 +46,14 @@ export async function compress(input: PdfInput, options?: CompressOptions): Prom
|
|
|
45
46
|
throw new TypeError('Input must be a Buffer or file path string');
|
|
46
47
|
}
|
|
47
48
|
const stripMetadata = options?.stripMetadata ?? true;
|
|
48
|
-
return
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
return withConcurrency(() =>
|
|
50
|
+
addon.compress(input, {
|
|
51
|
+
...(options?.lossy ? { lossy: true } : {}),
|
|
52
|
+
...(stripMetadata ? { stripMetadata: true } : {}),
|
|
53
|
+
...(options?.output ? { output: options.output } : {}),
|
|
54
|
+
}),
|
|
55
|
+
) as Promise<Buffer | void>;
|
|
53
56
|
}
|
|
54
57
|
|
|
58
|
+
export { concurrency } from './concurrency.js';
|
|
55
59
|
export type { CompressOptions } from './types.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qpdf-compress",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Native PDF compression for Node.js, powered by QPDF",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -74,10 +74,16 @@
|
|
|
74
74
|
"lint": "eslint --fix .",
|
|
75
75
|
"lint:check": "eslint .",
|
|
76
76
|
"format": "prettier --write .",
|
|
77
|
-
"format:check": "prettier --check ."
|
|
77
|
+
"format:check": "prettier --check .",
|
|
78
|
+
"prepare": "husky"
|
|
79
|
+
},
|
|
80
|
+
"lint-staged": {
|
|
81
|
+
"*.{ts,js,json,md,yaml,yml}": "prettier --write",
|
|
82
|
+
"*.{ts,js}": "eslint --max-warnings 0"
|
|
78
83
|
},
|
|
79
84
|
"dependencies": {
|
|
80
|
-
"node-addon-api": "^8.0.0"
|
|
85
|
+
"node-addon-api": "^8.0.0",
|
|
86
|
+
"p-limit": "^7.3.0"
|
|
81
87
|
},
|
|
82
88
|
"devDependencies": {
|
|
83
89
|
"@eslint/js": "^10.0.1",
|
|
@@ -85,6 +91,8 @@
|
|
|
85
91
|
"eslint": "^10.1.0",
|
|
86
92
|
"eslint-config-prettier": "^10.1.8",
|
|
87
93
|
"eslint-plugin-prettier": "^5.5.5",
|
|
94
|
+
"husky": "^9.1.7",
|
|
95
|
+
"lint-staged": "^16.4.0",
|
|
88
96
|
"node-gyp": "^11.0.0",
|
|
89
97
|
"prettier": "^3.8.1",
|
|
90
98
|
"typescript": "^5.9.3",
|