qpdf-compress 0.5.0 → 0.6.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 CHANGED
@@ -4,6 +4,18 @@ 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.6.1] - 2026-04-06
8
+
9
+ ### Fixed
10
+
11
+ - Statically link libstdc++ on Linux to fix `GLIBCXX_3.4.31 not found` errors on systems with older GCC
12
+
13
+ ## [0.6.0] - 2026-04-04
14
+
15
+ ### Changed
16
+
17
+ - **mozjpeg**: replaced libjpeg-turbo with vendored mozjpeg 4.1.1 for 5–15% smaller JPEGs via trellis quantization, overshoot deringing, and optimized progressive scan ordering — applies to both lossy recompression and lossless Huffman optimization
18
+
7
19
  ## [0.5.0] - 2026-04-04
8
20
 
9
21
  ### Added
package/README.md CHANGED
@@ -58,7 +58,7 @@ const smaller = await compress(pdfBuffer, { lossy: true });
58
58
  | Integration | Native Node.js addon | Shell exec | Shell exec |
59
59
  | Async I/O | ✅ Non-blocking | ❌ Blocks on exec | ❌ Blocks on exec |
60
60
  | Image deduplication | ✅ | ❌ | ❌ |
61
- | JPEG Huffman optimization | ✅ Lossless (libjpeg) | ❌ | ❌ |
61
+ | JPEG Huffman optimization | ✅ Lossless (mozjpeg) | ❌ | ❌ |
62
62
  | Lossy image compression | ✅ Auto quality | ❌ | ✅ |
63
63
  | CMYK → RGB conversion | ✅ Automatic | ❌ | ✅ |
64
64
  | DPI downscaling | ✅ Lossy mode | ❌ | ✅ |
@@ -91,20 +91,20 @@ Prebuilt binaries are available for all [supported platforms](#-supported-platfo
91
91
  - CMake ≥ 3.16
92
92
  - C++20 compiler (GCC 10+, Clang 13+, MSVC 2019+)
93
93
  - zlib development headers
94
- - libjpeg-turbo (or libjpeg) development headers
94
+ - nasm (optional, for mozjpeg SIMD acceleration)
95
95
 
96
96
  ```bash
97
97
  # macOS
98
- brew install cmake jpeg-turbo
98
+ brew install cmake nasm
99
99
 
100
100
  # Ubuntu / Debian
101
- sudo apt install cmake g++ zlib1g-dev libjpeg-turbo8-dev
101
+ sudo apt install cmake g++ zlib1g-dev nasm
102
102
 
103
103
  # Amazon Linux / RHEL
104
- sudo yum install cmake3 gcc-c++ zlib-devel libjpeg-turbo-devel
104
+ sudo yum install cmake3 gcc-c++ zlib-devel nasm
105
105
 
106
106
  # Windows (using vcpkg)
107
- vcpkg install zlib libjpeg-turbo --triplet x64-windows-static
107
+ vcpkg install zlib --triplet x64-windows-static
108
108
  ```
109
109
 
110
110
  ## 🌍 Supported Platforms
@@ -209,7 +209,7 @@ concurrency(0); // reset to default
209
209
 
210
210
  ## ⚙️ How it works
211
211
 
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.
212
+ This package embeds [QPDF](https://github.com/qpdf/qpdf) (v12.3.2) and [mozjpeg](https://github.com/mozilla/mozjpeg) (v4.1.1) as statically linked C++ libraries, exposed to Node.js via N-API. Lossless JPEG optimization uses mozjpeg at the DCT coefficient level with progressive scan optimization. Image recompression in lossy mode uses mozjpeg's trellis quantization for 5–15% smaller JPEGs at the same perceptual quality. 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.
213
213
 
214
214
  All operations run in a background thread via `Napi::AsyncWorker`, so the event loop is never blocked.
215
215
 
package/binding.gyp CHANGED
@@ -12,6 +12,7 @@
12
12
  "include_dirs": [
13
13
  "<!@(node -p \"require('node-addon-api').include\")",
14
14
  "deps/qpdf/include",
15
+ "deps/mozjpeg/include",
15
16
  "src"
16
17
  ],
17
18
  "defines": [
@@ -39,7 +40,8 @@
39
40
  {
40
41
  "include_dirs": [
41
42
  "/opt/homebrew/include",
42
- "/usr/local/include"
43
+ "/usr/local/include",
44
+ "<(module_root_dir)/deps/mozjpeg/include"
43
45
  ],
44
46
  "xcode_settings": {
45
47
  "GCC_ENABLE_CPP_EXCEPTIONS": "YES",
@@ -58,7 +60,7 @@
58
60
  "-L/opt/homebrew/lib",
59
61
  "-L/usr/local/lib",
60
62
  "-lz",
61
- "-ljpeg",
63
+ "<(module_root_dir)/deps/mozjpeg/lib/libjpeg.a",
62
64
  "-flto",
63
65
  "-Wl,-dead_strip",
64
66
  "-Wl,-S"
@@ -72,7 +74,8 @@
72
74
  "libraries": [
73
75
  "<(module_root_dir)/deps/qpdf/lib/libqpdf.a",
74
76
  "-lz",
75
- "-ljpeg",
77
+ "<(module_root_dir)/deps/mozjpeg/lib/libjpeg.a",
78
+ "-static-libstdc++",
76
79
  "-flto",
77
80
  "-Wl,--gc-sections",
78
81
  "-Wl,-S",
@@ -104,7 +107,7 @@
104
107
  "libraries": [
105
108
  "<(module_root_dir)/deps/qpdf/lib/qpdf.lib",
106
109
  "<(module_root_dir)/deps/qpdf/lib/zlib.lib",
107
- "<(module_root_dir)/deps/qpdf/lib/jpeg.lib"
110
+ "<(module_root_dir)/deps/mozjpeg/lib/jpeg-static.lib"
108
111
  ]
109
112
  }
110
113
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qpdf-compress",
3
- "version": "0.5.0",
3
+ "version": "0.6.1",
4
4
  "description": "Native PDF compression for Node.js, powered by QPDF",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -69,7 +69,9 @@
69
69
  "build": "node-gyp rebuild && node scripts/bundle-lib.mjs",
70
70
  "build:ts": "tsc",
71
71
  "prepublishOnly": "tsc",
72
- "download": "node scripts/download-qpdf.mjs",
72
+ "download": "node scripts/download-mozjpeg.mjs && node scripts/download-qpdf.mjs",
73
+ "download:mozjpeg": "node scripts/download-mozjpeg.mjs",
74
+ "download:qpdf": "node scripts/download-qpdf.mjs",
73
75
  "test": "vitest run",
74
76
  "lint": "eslint --fix .",
75
77
  "lint:check": "eslint .",
@@ -0,0 +1,162 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ import { createWriteStream, mkdirSync, existsSync, rmSync, readdirSync } from 'node:fs';
3
+ import { pipeline } from 'node:stream/promises';
4
+ import { Readable } from 'node:stream';
5
+ import { join } from 'node:path';
6
+
7
+ const MOZJPEG_VERSION = '4.1.1';
8
+ const BASE_URL = 'https://github.com/mozilla/mozjpeg/archive/refs/tags';
9
+
10
+ const root = join(import.meta.dirname, '..');
11
+ const depsDir = join(root, 'deps', 'mozjpeg');
12
+
13
+ if (existsSync(join(depsDir, 'include', 'jpeglib.h'))) {
14
+ console.log('mozjpeg already built, skipping.');
15
+ process.exit(0);
16
+ }
17
+
18
+ // validate version to prevent SSRF
19
+ if (!/^\d+\.\d+\.\d+$/.test(MOZJPEG_VERSION)) {
20
+ console.error(`Invalid mozjpeg version: ${MOZJPEG_VERSION}`);
21
+ process.exit(1);
22
+ }
23
+
24
+ const url = `${BASE_URL}/v${MOZJPEG_VERSION}.tar.gz`;
25
+ const tarball = join(root, `mozjpeg-${MOZJPEG_VERSION}.tar.gz`);
26
+ const srcDir = join(root, `mozjpeg-${MOZJPEG_VERSION}`);
27
+ const buildDir = join(root, 'build-mozjpeg');
28
+
29
+ // step 1: download
30
+ console.log(`Downloading mozjpeg ${MOZJPEG_VERSION}...`);
31
+ console.log(`URL: ${url}`);
32
+
33
+ const response = await fetch(url, { redirect: 'follow' });
34
+ if (!response.ok) {
35
+ console.error(`Download failed: ${response.status} ${response.statusText}`);
36
+ process.exit(1);
37
+ }
38
+
39
+ mkdirSync(join(root, 'deps'), { recursive: true });
40
+ await pipeline(Readable.fromWeb(response.body), createWriteStream(tarball));
41
+
42
+ console.log('Extracting...');
43
+ execFileSync('tar', ['-xzf', tarball, '-C', root], { stdio: 'inherit' });
44
+ rmSync(tarball);
45
+
46
+ // GitHub archive tarballs extract to `mozjpeg-{tag}` — find the directory
47
+ if (!existsSync(srcDir)) {
48
+ const candidates = readdirSync(root).filter(
49
+ (d) => d.startsWith('mozjpeg-') && !d.endsWith('.tar.gz'),
50
+ );
51
+ const match = candidates.find((d) => d.includes(MOZJPEG_VERSION));
52
+ if (match) {
53
+ const { renameSync } = await import('node:fs');
54
+ renameSync(join(root, match), srcDir);
55
+ console.log(`Renamed ${match} → mozjpeg-${MOZJPEG_VERSION}`);
56
+ } else {
57
+ console.error(
58
+ `Could not find extracted mozjpeg source directory. Found: ${candidates.join(', ')}`,
59
+ );
60
+ process.exit(1);
61
+ }
62
+ }
63
+
64
+ // step 2: build with CMake
65
+ console.log('Building mozjpeg...');
66
+ mkdirSync(buildDir, { recursive: true });
67
+
68
+ const cmakeArgs = [
69
+ '-S',
70
+ srcDir,
71
+ '-B',
72
+ buildDir,
73
+ '-DCMAKE_BUILD_TYPE=Release',
74
+ '-DCMAKE_POSITION_INDEPENDENT_CODE=ON',
75
+ '-DCMAKE_POLICY_VERSION_MINIMUM=3.5',
76
+ '-DENABLE_STATIC=ON',
77
+ '-DENABLE_SHARED=OFF',
78
+ '-DPNG_SUPPORTED=OFF',
79
+ '-DWITH_TURBOJPEG=OFF',
80
+ `-DCMAKE_INSTALL_LIBDIR=${join(depsDir, 'lib')}`,
81
+ `-DCMAKE_INSTALL_PREFIX=${depsDir}`,
82
+ ];
83
+
84
+ // force -fPIC on Linux
85
+ if (process.platform === 'linux') {
86
+ cmakeArgs.push('-DCMAKE_C_FLAGS=-fPIC', '-DCMAKE_CXX_FLAGS=-fPIC');
87
+ }
88
+
89
+ // match node-gyp's deployment target on macOS to avoid linker warnings
90
+ if (process.platform === 'darwin') {
91
+ cmakeArgs.push('-DCMAKE_OSX_DEPLOYMENT_TARGET=11.0');
92
+ }
93
+
94
+ // Windows multi-config generator
95
+ if (process.platform === 'win32') {
96
+ cmakeArgs.push('-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded');
97
+ cmakeArgs.splice(cmakeArgs.indexOf('-DCMAKE_BUILD_TYPE=Release'), 1);
98
+
99
+ // cross-compile for ARM64 when VCPKG_TARGET_TRIPLET indicates it
100
+ const triplet = process.env.VCPKG_TARGET_TRIPLET || '';
101
+ if (triplet.startsWith('arm64')) {
102
+ // use lowercase 'arm64' — mozjpeg's CMakeLists.txt checks
103
+ // CMAKE_GENERATOR_PLATFORM with a case-sensitive regex ("arm64")
104
+ cmakeArgs.push('-A', 'arm64');
105
+ cmakeArgs.push('-DNEON_INTRINSICS=ON');
106
+ }
107
+ }
108
+
109
+ execFileSync('cmake', cmakeArgs, { stdio: 'inherit' });
110
+
111
+ const buildArgs = ['--build', buildDir, '--parallel'];
112
+ if (process.platform === 'win32') {
113
+ buildArgs.push('--config', 'Release');
114
+ }
115
+ execFileSync('cmake', buildArgs, { stdio: 'inherit' });
116
+
117
+ // step 3: install
118
+ console.log('Installing to deps/mozjpeg...');
119
+ const installArgs = ['--install', buildDir];
120
+ if (process.platform === 'win32') {
121
+ installArgs.push('--config', 'Release');
122
+ }
123
+ execFileSync('cmake', installArgs, { stdio: 'inherit' });
124
+
125
+ // verify installation
126
+ if (!existsSync(join(depsDir, 'include', 'jpeglib.h'))) {
127
+ console.error('mozjpeg install failed: jpeglib.h not found');
128
+ process.exit(1);
129
+ }
130
+
131
+ // find the static library (varies by platform and install layout)
132
+ const libDirs = ['lib', 'lib64'].map((d) => join(depsDir, d));
133
+ const libNames = ['libjpeg.a', 'jpeg-static.lib', 'jpeg.lib'];
134
+ let foundLib = false;
135
+ for (const dir of libDirs) {
136
+ if (!existsSync(dir)) continue;
137
+ for (const name of libNames) {
138
+ if (existsSync(join(dir, name))) {
139
+ foundLib = true;
140
+ console.log(`Found static library: ${join(dir, name)}`);
141
+ break;
142
+ }
143
+ }
144
+ if (foundLib) break;
145
+ }
146
+
147
+ if (!foundLib) {
148
+ // list what we have for debugging
149
+ for (const dir of libDirs) {
150
+ if (existsSync(dir)) {
151
+ console.log(`Contents of ${dir}:`, readdirSync(dir));
152
+ }
153
+ }
154
+ console.error('mozjpeg install failed: static library not found');
155
+ process.exit(1);
156
+ }
157
+
158
+ // step 4: clean up source and build dirs
159
+ rmSync(srcDir, { recursive: true, force: true });
160
+ rmSync(buildDir, { recursive: true, force: true });
161
+
162
+ console.log(`mozjpeg ${MOZJPEG_VERSION} installed to ${depsDir}`);
@@ -88,24 +88,35 @@ if (process.platform === 'linux') {
88
88
  cmakeArgs.push('-DCMAKE_C_FLAGS=-fPIC', '-DCMAKE_CXX_FLAGS=-fPIC');
89
89
  }
90
90
 
91
- // on macOS, help CMake find Homebrew libjpeg-turbo
92
- if (process.platform === 'darwin') {
93
- const brewPrefixes = ['/opt/homebrew', '/usr/local'];
94
- for (const prefix of brewPrefixes) {
95
- const jpegDir = join(prefix, 'opt', 'jpeg-turbo');
96
- if (existsSync(jpegDir)) {
97
- cmakeArgs.push(`-DCMAKE_PREFIX_PATH=${jpegDir}`);
98
- break;
99
- }
100
- const jpegDir2 = join(prefix, 'opt', 'jpeg');
101
- if (existsSync(jpegDir2)) {
102
- cmakeArgs.push(`-DCMAKE_PREFIX_PATH=${jpegDir2}`);
103
- break;
104
- }
105
- }
91
+ // point QPDF at vendored mozjpeg
92
+ const mozjpegDir = join(root, 'deps', 'mozjpeg');
93
+ if (existsSync(join(mozjpegDir, 'include', 'jpeglib.h'))) {
94
+ // try lib/ then lib64/ (some CMake installs use lib64 on Linux)
95
+ const libDir = existsSync(join(mozjpegDir, 'lib')) ? 'lib' : 'lib64';
96
+ const libExt = process.platform === 'win32' ? 'jpeg-static.lib' : 'libjpeg.a';
97
+
98
+ // QPDF uses pkg_check_modules first, then falls back to find_path/find_library
99
+ // with custom variable names LIBJPEG_H_PATH and LIBJPEG_LIB_PATH.
100
+
101
+ // 1) set PKG_CONFIG_PATH so pkg-config finds mozjpeg's libjpeg.pc (Linux/macOS)
102
+ const pkgConfigDir = join(mozjpegDir, libDir, 'pkgconfig');
103
+ const existing = process.env.PKG_CONFIG_PATH || '';
104
+ process.env.PKG_CONFIG_PATH = existing ? `${pkgConfigDir}:${existing}` : pkgConfigDir;
105
+
106
+ // 2) set QPDF's cmake cache vars for the find_path/find_library fallback (Windows)
107
+ cmakeArgs.push(
108
+ `-DLIBJPEG_H_PATH:PATH=${join(mozjpegDir, 'include')}`,
109
+ `-DLIBJPEG_LIB_PATH:FILEPATH=${join(mozjpegDir, libDir, libExt)}`,
110
+ );
111
+
112
+ // 3) add mozjpeg to CMAKE_PREFIX_PATH as a belt-and-suspenders fallback
113
+ cmakeArgs.push(`-DCMAKE_PREFIX_PATH=${mozjpegDir}`);
114
+ } else {
115
+ console.error('mozjpeg not found — run "node scripts/download-mozjpeg.mjs" first');
116
+ process.exit(1);
106
117
  }
107
118
 
108
- // on Windows, use vcpkg for zlib and libjpeg-turbo
119
+ // on Windows, use vcpkg for zlib (mozjpeg is vendored separately)
109
120
  if (process.platform === 'win32') {
110
121
  const vcpkgRoot = process.env.VCPKG_ROOT || join(process.env.GITHUB_WORKSPACE || '', 'vcpkg');
111
122
  if (existsSync(join(vcpkgRoot, 'scripts', 'buildsystems', 'vcpkg.cmake'))) {
@@ -117,7 +128,7 @@ if (process.platform === 'win32') {
117
128
 
118
129
  // cross-compile for ARM64 when triplet indicates it
119
130
  if (triplet.startsWith('arm64')) {
120
- cmakeArgs.push('-A', 'ARM64');
131
+ cmakeArgs.push('-A', 'arm64');
121
132
  }
122
133
  }
123
134
  // force static CRT (/MT) to match node-gyp
@@ -170,7 +181,7 @@ if (staticLibs.length === 0) {
170
181
  }
171
182
  }
172
183
 
173
- // step 4: on Windows, copy vcpkg zlib/jpeg static libs and headers for binding.gyp
184
+ // step 4: on Windows, copy vcpkg zlib static lib and headers for binding.gyp
174
185
  if (process.platform === 'win32') {
175
186
  const triplet = process.env.VCPKG_TARGET_TRIPLET || `${process.arch}-windows-static`;
176
187
  const vcpkgRoot = process.env.VCPKG_ROOT || '';
@@ -180,27 +191,15 @@ if (process.platform === 'win32') {
180
191
  join(buildDir, 'vcpkg_installed', triplet, 'lib'),
181
192
  join(vcpkgRoot, 'installed', triplet, 'lib'),
182
193
  ];
183
- const candidateIncludeDirs = [
184
- join(buildDir, 'vcpkg_installed', triplet, 'include'),
185
- join(vcpkgRoot, 'installed', triplet, 'include'),
186
- ];
187
194
 
188
195
  const vcpkgLibDir = candidateLibDirs.find((d) => existsSync(d));
189
196
  if (vcpkgLibDir) {
190
- for (const lib of ['zlib.lib', 'jpeg.lib', 'turbojpeg.lib']) {
191
- const src = join(vcpkgLibDir, lib);
192
- if (existsSync(src)) {
193
- cpSync(src, join(depsDir, 'lib', lib));
194
- console.log(`Copied vcpkg ${lib}`);
195
- }
197
+ const src = join(vcpkgLibDir, 'zlib.lib');
198
+ if (existsSync(src)) {
199
+ cpSync(src, join(depsDir, 'lib', 'zlib.lib'));
200
+ console.log('Copied vcpkg zlib.lib');
196
201
  }
197
202
  }
198
-
199
- const vcpkgIncludeDir = candidateIncludeDirs.find((d) => existsSync(d));
200
- if (vcpkgIncludeDir) {
201
- cpSync(vcpkgIncludeDir, join(depsDir, 'include'), { recursive: true, force: true });
202
- console.log(`Copied vcpkg headers from ${vcpkgIncludeDir}`);
203
- }
204
203
  }
205
204
 
206
205
  // step 5: clean up source and build dirs
@@ -96,6 +96,7 @@ async function tryDownload() {
96
96
 
97
97
  async function buildFromSource() {
98
98
  console.log('Building from source...');
99
+ execSync('node scripts/download-mozjpeg.mjs', { stdio: 'inherit', cwd: root });
99
100
  execSync('node scripts/download-qpdf.mjs', { stdio: 'inherit', cwd: root });
100
101
  execSync('npx node-gyp rebuild', { stdio: 'inherit', cwd: root });
101
102
  execSync('node scripts/bundle-lib.mjs', { stdio: 'inherit', cwd: root });
package/src/images.cc CHANGED
@@ -391,7 +391,7 @@ void optimizeImages(QPDF &qpdf, const CompressOptions &opts) {
391
391
  return;
392
392
  }
393
393
 
394
- // encode as JPEG via libjpeg-turbo
394
+ // encode as JPEG via mozjpeg
395
395
  std::vector<uint8_t> jpegData;
396
396
  if (!encodeJpeg(pixels, width, height, encodeComponents, opts.targetQuality,
397
397
  jpegData))
package/src/jpeg.cc CHANGED
@@ -55,6 +55,10 @@ static bool losslessJpegOptimizeImpl(const unsigned char *data, size_t size,
55
55
  jpeg_copy_critical_parameters(&srcinfo, &dstinfo);
56
56
  dstinfo.optimize_coding = TRUE;
57
57
 
58
+ // mozjpeg progressive scan optimization — lossless, reorders existing DCT
59
+ // coefficients into optimized progressive scans for better entropy coding
60
+ jpeg_simple_progression(&dstinfo);
61
+
58
62
  jpeg_write_coefficients(&dstinfo, coef_arrays);
59
63
  jpeg_finish_compress(&dstinfo);
60
64
  jpeg_finish_decompress(&srcinfo);
@@ -79,7 +83,7 @@ bool losslessJpegOptimize(const unsigned char *data, size_t size,
79
83
  }
80
84
 
81
85
  // ---------------------------------------------------------------------------
82
- // Lossy JPEG encoding via libjpeg-turbo
86
+ // Lossy JPEG encoding via mozjpeg
83
87
  // ---------------------------------------------------------------------------
84
88
 
85
89
  // isolated setjmp scope
@@ -115,6 +119,16 @@ static bool encodeJpegImpl(const unsigned char *pixels, int width, int height,
115
119
  jpeg_set_quality(&cinfo, quality, TRUE);
116
120
  cinfo.optimize_coding = TRUE;
117
121
 
122
+ // mozjpeg trellis quantization — 5–15% smaller at same perceptual quality
123
+ jpeg_c_set_bool_param(&cinfo, JBOOLEAN_TRELLIS_QUANT, TRUE);
124
+ jpeg_c_set_bool_param(&cinfo, JBOOLEAN_TRELLIS_QUANT_DC, TRUE);
125
+ jpeg_c_set_bool_param(&cinfo, JBOOLEAN_OVERSHOOT_DERINGING, TRUE);
126
+ jpeg_c_set_bool_param(&cinfo, JBOOLEAN_USE_SCANS_IN_TRELLIS, TRUE);
127
+ jpeg_c_set_bool_param(&cinfo, JBOOLEAN_USE_LAMBDA_WEIGHT_TBL, TRUE);
128
+
129
+ // mozjpeg progressive scan optimization
130
+ jpeg_simple_progression(&cinfo);
131
+
118
132
  jpeg_start_compress(&cinfo, TRUE);
119
133
 
120
134
  int row_stride = width * components;
package/src/jpeg.h CHANGED
@@ -21,7 +21,7 @@ void jpegErrorExit(j_common_ptr cinfo);
21
21
  bool losslessJpegOptimize(const unsigned char *data, size_t size,
22
22
  std::vector<uint8_t> &out);
23
23
 
24
- // encodes raw pixels as JPEG at the given quality (1–100) via libjpeg-turbo
24
+ // encodes raw pixels as JPEG at the given quality (1–100) via mozjpeg
25
25
  bool encodeJpeg(const unsigned char *pixels, int width, int height,
26
26
  int components, int quality, std::vector<uint8_t> &out);
27
27