Skip to content

Commit ce379d7

Browse files
authored
Added mozjpeg defaults for jpeg compression (#484)
refs TryGhost/Product#4140 - added `mozjpeg: true` for all jpeg formats to reduce file sizes - added some basic integration tests as the existing unit tests don't actually verify any file creation/compression
1 parent e746792 commit ce379d7

File tree

12 files changed

+155
-4
lines changed

12 files changed

+155
-4
lines changed

packages/image-transform/lib/transform.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,16 @@ const unsafeResizeFromBuffer = async (originalBuffer, options = {}) => {
107107
// CASE: Automatically remove metadata and rotate based on the orientation.
108108
.rotate();
109109

110+
const metadata = await s.metadata();
111+
110112
if (options.format) {
111-
s = s.toFormat(options.format);
113+
if (options.format === 'jpeg') {
114+
s.jpeg({mozjpeg: true}); // .jpeg sets format
115+
} else {
116+
s = s.toFormat(options.format);
117+
}
118+
} else if (metadata.format === 'jpeg') {
119+
s.jpeg({mozjpeg: true}); // .jpeg sets format
112120
}
113121

114122
const resizedBuffer = await s.toBuffer();
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
const path = require('path');
2+
3+
// Helpers
4+
const getPath = function (filename) {
5+
return path.join(__dirname, filename);
6+
};
7+
8+
module.exports = {
9+
inputJpeg: getPath('saw.jpg'),
10+
inputPng: getPath('saw.png'),
11+
inputWebp: getPath('tree.webp')
12+
};
96 KB
Loading
1.85 MB
Loading
536 KB
Loading
Binary file not shown.
810 KB
Loading
6.83 MB
Loading
Binary file not shown.
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
'use strict';
2+
3+
const sharp = require('sharp');
4+
// eslint-disable-next-line ghost/ghost-custom/node-assert-strict
5+
const assert = require('assert');
6+
const imageTransform = require('../../');
7+
const path = require('path');
8+
const fixtures = require('./fixtures');
9+
10+
const makeOutpath = (inPath, mod, ext) => {
11+
if (!ext) {
12+
ext = path.extname(inPath);
13+
}
14+
const fileName = path.basename(inPath, ext);
15+
return path.join(__dirname, `fixtures/output/${fileName}_${mod}${ext}`);
16+
};
17+
18+
describe('Image compression', function () {
19+
describe('JPEG', function () {
20+
let fixtureBuffer;
21+
22+
before(async function () {
23+
fixtureBuffer = await sharp(fixtures.inputJpeg).toBuffer();
24+
});
25+
26+
it('should always compress JPEG images', async function () {
27+
const inPath = fixtures.inputJpeg;
28+
const outPath = makeOutpath(inPath, 'base', '.jpg');
29+
30+
await imageTransform.resizeFromPath({in: inPath, out: outPath});
31+
32+
await sharp(outPath)
33+
.toBuffer(function (err, result) {
34+
if (err) {
35+
throw err;
36+
}
37+
assert(result instanceof Buffer);
38+
assert(result.length < fixtureBuffer.length);
39+
});
40+
});
41+
42+
it('should compress JPEG images with width attribute', async function () {
43+
const inPath = fixtures.inputJpeg;
44+
const outPath = makeOutpath(inPath, '1000w', '.jpg');
45+
46+
await imageTransform.resizeFromPath({in: inPath, out: outPath, width: 1000});
47+
48+
await sharp(outPath)
49+
.toBuffer(function (err, result, info) {
50+
if (err) {
51+
throw err;
52+
}
53+
assert(result instanceof Buffer);
54+
assert(result.length < fixtureBuffer.length);
55+
assert(info.width === 1000);
56+
});
57+
});
58+
59+
it('can create a JPEG from another format by passing the format option', async function () {
60+
const inputPngBuffer = await sharp(fixtures.inputPng).toBuffer();
61+
62+
const outputBuffer = await imageTransform.resizeFromBuffer(inputPngBuffer, {format: 'jpeg'});
63+
64+
sharp(outputBuffer).metadata().then(function (metadata) {
65+
assert.equal(metadata.format, 'jpeg');
66+
});
67+
});
68+
});
69+
70+
describe('PNG', function () {
71+
let fixtureBuffer;
72+
73+
before(async function () {
74+
fixtureBuffer = await sharp(fixtures.inputPng).toBuffer();
75+
});
76+
77+
it('should compress PNG images with width attribute', async function () {
78+
const inPath = fixtures.inputPng;
79+
const outPath = makeOutpath(inPath, '1000w', '.png');
80+
81+
await imageTransform.resizeFromPath({in: inPath, out: outPath, width: 1000});
82+
83+
await sharp(outPath)
84+
.toBuffer(function (err, result, info) {
85+
if (err) {
86+
throw err;
87+
}
88+
assert(result instanceof Buffer);
89+
assert(result.length < fixtureBuffer.length);
90+
assert(info.width === 1000);
91+
});
92+
});
93+
94+
it('can create PNG from another format by passing the format option', async function () {
95+
const inputJpegBuffer = await sharp(fixtures.inputJpeg).toBuffer();
96+
97+
const outputBuffer = await imageTransform.resizeFromBuffer(inputJpegBuffer, {format: 'png'});
98+
99+
sharp(outputBuffer).metadata().then(function (metadata) {
100+
assert.equal(metadata.format, 'png');
101+
});
102+
});
103+
});
104+
105+
describe('WEBP', function () {
106+
let fixtureBuffer;
107+
108+
before(async function () {
109+
fixtureBuffer = await sharp(fixtures.inputWebp).toBuffer();
110+
});
111+
112+
it('should compress WEBP images with width attribute', async function () {
113+
const inPath = fixtures.inputWebp;
114+
const outPath = makeOutpath(inPath, '1000w', '.webp');
115+
116+
await imageTransform.resizeFromPath({in: inPath, out: outPath, width: 1000});
117+
118+
await sharp(outPath)
119+
.toBuffer(function (err, result, info) {
120+
if (err) {
121+
throw err;
122+
}
123+
assert(result instanceof Buffer);
124+
assert(result.length < fixtureBuffer.length);
125+
assert(info.width === 1000);
126+
});
127+
});
128+
});
129+
});

0 commit comments

Comments
 (0)