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/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
- int quality, int maxDpi, bool stripMeta,
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), quality_(quality),
58
- maxDpi_(maxDpi), stripMeta_(stripMeta), useFile_(false),
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, int quality,
63
- int maxDpi, bool stripMeta, std::string outputPath)
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), quality_(quality),
66
- maxDpi_(maxDpi), stripMeta_(stripMeta), useFile_(true),
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.quality = quality_;
101
+ opts.skipThreshold = 65;
102
+ opts.targetQuality = 75;
99
103
  optimizeImages(qpdf, opts);
104
+ downscaleImages(qpdf, 72, 75);
100
105
  }
101
- if (maxDpi_ > 0)
102
- downscaleImages(qpdf, maxDpi_);
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("mode")) {
187
- auto mode = options.Get("mode").As<Napi::String>().Utf8Value();
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, quality,
221
- maxDpi, stripMeta, std::move(outputPath));
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, quality,
229
- maxDpi, stripMeta, std::move(outputPath));
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
  }