qpdf-compress 0.2.0 → 0.4.1
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 +124 -0
- package/README.md +87 -57
- package/binding.gyp +3 -1
- package/dist/index.d.ts +8 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -14
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +3 -9
- package/dist/types.d.ts.map +1 -1
- package/lib/index.ts +12 -21
- package/lib/types.ts +3 -9
- package/package.json +5 -2
- package/src/font_subset.cc +480 -0
- package/src/font_subset.h +17 -0
- package/src/images.cc +473 -126
- package/src/images.h +6 -6
- package/src/optimize.cc +1048 -0
- package/src/optimize.h +15 -0
- package/src/qpdf_addon.cc +34 -44
package/src/optimize.h
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <qpdf/QPDF.hh>
|
|
4
|
+
|
|
5
|
+
void stripMetadata(QPDF &qpdf);
|
|
6
|
+
void removeUnusedFonts(QPDF &qpdf);
|
|
7
|
+
void coalesceContentStreams(QPDF &qpdf);
|
|
8
|
+
void deduplicateStreams(QPDF &qpdf);
|
|
9
|
+
void subsetFonts(QPDF &qpdf);
|
|
10
|
+
void stripIccProfiles(QPDF &qpdf);
|
|
11
|
+
void stripEmbeddedFiles(QPDF &qpdf);
|
|
12
|
+
void stripJavaScript(QPDF &qpdf);
|
|
13
|
+
void flattenForms(QPDF &qpdf);
|
|
14
|
+
void flattenPageTree(QPDF &qpdf);
|
|
15
|
+
void minifyContentStreams(QPDF &qpdf);
|
package/src/qpdf_addon.cc
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
#include <qpdf/QPDFWriter.hh>
|
|
15
15
|
|
|
16
16
|
#include "images.h"
|
|
17
|
+
#include "optimize.h"
|
|
17
18
|
|
|
18
19
|
// ---------------------------------------------------------------------------
|
|
19
20
|
// File output helper — returns empty string on success, error message on
|
|
@@ -51,20 +52,17 @@ class CompressWorker : public Napi::AsyncWorker {
|
|
|
51
52
|
public:
|
|
52
53
|
// buffer variant
|
|
53
54
|
CompressWorker(Napi::Env env, std::vector<uint8_t> data, bool lossy,
|
|
54
|
-
|
|
55
|
-
std::string outputPath)
|
|
55
|
+
bool stripMeta, std::string outputPath)
|
|
56
56
|
: Napi::AsyncWorker(env), deferred_(Napi::Promise::Deferred::New(env)),
|
|
57
|
-
bufferData_(std::move(data)), lossy_(lossy),
|
|
58
|
-
|
|
59
|
-
outputPath_(std::move(outputPath)) {}
|
|
57
|
+
bufferData_(std::move(data)), lossy_(lossy), stripMeta_(stripMeta),
|
|
58
|
+
useFile_(false), outputPath_(std::move(outputPath)) {}
|
|
60
59
|
|
|
61
60
|
// file path variant
|
|
62
|
-
CompressWorker(Napi::Env env, std::string path, bool lossy,
|
|
63
|
-
|
|
61
|
+
CompressWorker(Napi::Env env, std::string path, bool lossy, bool stripMeta,
|
|
62
|
+
std::string outputPath)
|
|
64
63
|
: Napi::AsyncWorker(env), deferred_(Napi::Promise::Deferred::New(env)),
|
|
65
|
-
filePath_(std::move(path)), lossy_(lossy),
|
|
66
|
-
|
|
67
|
-
outputPath_(std::move(outputPath)) {}
|
|
64
|
+
filePath_(std::move(path)), lossy_(lossy), stripMeta_(stripMeta),
|
|
65
|
+
useFile_(true), outputPath_(std::move(outputPath)) {}
|
|
68
66
|
|
|
69
67
|
Napi::Promise Promise() { return deferred_.Promise(); }
|
|
70
68
|
|
|
@@ -93,15 +91,31 @@ protected:
|
|
|
93
91
|
}
|
|
94
92
|
|
|
95
93
|
deduplicateImages(qpdf);
|
|
94
|
+
convertGrayscaleImages(qpdf);
|
|
95
|
+
convertBitonalImages(qpdf);
|
|
96
|
+
flattenPageTree(qpdf);
|
|
97
|
+
|
|
96
98
|
if (lossy_) {
|
|
99
|
+
// lossy: re-encode high-quality JPEGs at lower quality
|
|
97
100
|
CompressOptions opts;
|
|
98
|
-
opts.
|
|
101
|
+
opts.skipThreshold = 65;
|
|
102
|
+
opts.targetQuality = 75;
|
|
99
103
|
optimizeImages(qpdf, opts);
|
|
104
|
+
downscaleImages(qpdf, 72, 75);
|
|
100
105
|
}
|
|
101
|
-
|
|
102
|
-
|
|
106
|
+
|
|
107
|
+
// lossless Huffman optimization for all existing JPEGs
|
|
103
108
|
optimizeExistingJpegs(qpdf);
|
|
109
|
+
optimizeSoftMasks(qpdf);
|
|
104
110
|
removeUnusedFonts(qpdf);
|
|
111
|
+
subsetFonts(qpdf);
|
|
112
|
+
stripIccProfiles(qpdf);
|
|
113
|
+
flattenForms(qpdf);
|
|
114
|
+
coalesceContentStreams(qpdf);
|
|
115
|
+
minifyContentStreams(qpdf);
|
|
116
|
+
deduplicateStreams(qpdf);
|
|
117
|
+
stripEmbeddedFiles(qpdf);
|
|
118
|
+
stripJavaScript(qpdf);
|
|
105
119
|
if (stripMeta_)
|
|
106
120
|
stripMetadata(qpdf);
|
|
107
121
|
|
|
@@ -109,6 +123,7 @@ protected:
|
|
|
109
123
|
|
|
110
124
|
QPDFWriter writer(qpdf);
|
|
111
125
|
writer.setOutputMemory();
|
|
126
|
+
writer.setLinearization(false);
|
|
112
127
|
writer.setStreamDataMode(qpdf_s_compress);
|
|
113
128
|
writer.setRecompressFlate(true);
|
|
114
129
|
writer.setObjectStreamMode(qpdf_o_generate);
|
|
@@ -153,8 +168,6 @@ private:
|
|
|
153
168
|
std::vector<uint8_t> bufferData_;
|
|
154
169
|
std::string filePath_;
|
|
155
170
|
bool lossy_;
|
|
156
|
-
int quality_;
|
|
157
|
-
int maxDpi_;
|
|
158
171
|
bool stripMeta_;
|
|
159
172
|
bool useFile_;
|
|
160
173
|
std::string outputPath_;
|
|
@@ -175,37 +188,14 @@ static Napi::Value Compress(const Napi::CallbackInfo &info) {
|
|
|
175
188
|
}
|
|
176
189
|
|
|
177
190
|
bool lossy = false;
|
|
178
|
-
int quality = 0;
|
|
179
|
-
int maxDpi = 0;
|
|
180
191
|
bool stripMeta = false;
|
|
181
192
|
std::string outputPath;
|
|
182
193
|
|
|
183
194
|
if (info.Length() >= 2 && info[1].IsObject()) {
|
|
184
195
|
auto options = info[1].As<Napi::Object>();
|
|
185
196
|
|
|
186
|
-
if (options.Has("
|
|
187
|
-
|
|
188
|
-
if (mode != "lossy" && mode != "lossless") {
|
|
189
|
-
Napi::TypeError::New(env, "Mode must be 'lossy' or 'lossless'")
|
|
190
|
-
.ThrowAsJavaScriptException();
|
|
191
|
-
return env.Undefined();
|
|
192
|
-
}
|
|
193
|
-
lossy = (mode == "lossy");
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
if (options.Has("quality")) {
|
|
197
|
-
quality = options.Get("quality").As<Napi::Number>().Int32Value();
|
|
198
|
-
if (quality < 0)
|
|
199
|
-
quality = 0;
|
|
200
|
-
if (quality > 100)
|
|
201
|
-
quality = 100;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
if (options.Has("maxDpi")) {
|
|
205
|
-
maxDpi = options.Get("maxDpi").As<Napi::Number>().Int32Value();
|
|
206
|
-
if (maxDpi < 0)
|
|
207
|
-
maxDpi = 0;
|
|
208
|
-
}
|
|
197
|
+
if (options.Has("lossy"))
|
|
198
|
+
lossy = options.Get("lossy").As<Napi::Boolean>().Value();
|
|
209
199
|
|
|
210
200
|
if (options.Has("stripMetadata"))
|
|
211
201
|
stripMeta = options.Get("stripMetadata").As<Napi::Boolean>().Value();
|
|
@@ -217,16 +207,16 @@ static Napi::Value Compress(const Napi::CallbackInfo &info) {
|
|
|
217
207
|
if (info[0].IsBuffer()) {
|
|
218
208
|
auto buf = info[0].As<Napi::Buffer<uint8_t>>();
|
|
219
209
|
std::vector<uint8_t> data(buf.Data(), buf.Data() + buf.Length());
|
|
220
|
-
auto *worker = new CompressWorker(env, std::move(data), lossy,
|
|
221
|
-
|
|
210
|
+
auto *worker = new CompressWorker(env, std::move(data), lossy, stripMeta,
|
|
211
|
+
std::move(outputPath));
|
|
222
212
|
worker->Queue();
|
|
223
213
|
return worker->Promise();
|
|
224
214
|
}
|
|
225
215
|
|
|
226
216
|
if (info[0].IsString()) {
|
|
227
217
|
auto path = info[0].As<Napi::String>().Utf8Value();
|
|
228
|
-
auto *worker = new CompressWorker(env, std::move(path), lossy,
|
|
229
|
-
|
|
218
|
+
auto *worker = new CompressWorker(env, std::move(path), lossy, stripMeta,
|
|
219
|
+
std::move(outputPath));
|
|
230
220
|
worker->Queue();
|
|
231
221
|
return worker->Promise();
|
|
232
222
|
}
|