diff options
Diffstat (limited to 'server')
-rw-r--r-- | server/helpers/image-utils.ts | 11 | ||||
-rw-r--r-- | server/tests/fixtures/exif.jpg | bin | 0 -> 10877 bytes | |||
-rw-r--r-- | server/tests/fixtures/exif.png | bin | 0 -> 21059 bytes | |||
-rw-r--r-- | server/tests/helpers/image.ts | 58 |
4 files changed, 59 insertions, 10 deletions
diff --git a/server/helpers/image-utils.ts b/server/helpers/image-utils.ts index b174ae436..28d8fff4c 100644 --- a/server/helpers/image-utils.ts +++ b/server/helpers/image-utils.ts | |||
@@ -80,6 +80,8 @@ async function autoResize (options: { | |||
80 | const sourceIsPortrait = sourceImage.getWidth() < sourceImage.getHeight() | 80 | const sourceIsPortrait = sourceImage.getWidth() < sourceImage.getHeight() |
81 | const destIsPortraitOrSquare = newSize.width <= newSize.height | 81 | const destIsPortraitOrSquare = newSize.width <= newSize.height |
82 | 82 | ||
83 | removeExif(sourceImage) | ||
84 | |||
83 | if (sourceIsPortrait && !destIsPortraitOrSquare) { | 85 | if (sourceIsPortrait && !destIsPortraitOrSquare) { |
84 | const baseImage = sourceImage.cloneQuiet().cover(newSize.width, newSize.height) | 86 | const baseImage = sourceImage.cloneQuiet().cover(newSize.width, newSize.height) |
85 | .color([ { apply: 'shade', params: [ 50 ] } ]) | 87 | .color([ { apply: 'shade', params: [ 50 ] } ]) |
@@ -106,6 +108,7 @@ function skipProcessing (options: { | |||
106 | const { sourceImage, newSize, imageBytes, inputExt, outputExt } = options | 108 | const { sourceImage, newSize, imageBytes, inputExt, outputExt } = options |
107 | const { width, height } = newSize | 109 | const { width, height } = newSize |
108 | 110 | ||
111 | if (hasExif(sourceImage)) return false | ||
109 | if (sourceImage.getWidth() > width || sourceImage.getHeight() > height) return false | 112 | if (sourceImage.getWidth() > width || sourceImage.getHeight() > height) return false |
110 | if (inputExt !== outputExt) return false | 113 | if (inputExt !== outputExt) return false |
111 | 114 | ||
@@ -116,3 +119,11 @@ function skipProcessing (options: { | |||
116 | 119 | ||
117 | return imageBytes <= 15 * kB | 120 | return imageBytes <= 15 * kB |
118 | } | 121 | } |
122 | |||
123 | function hasExif (image: Jimp) { | ||
124 | return !!(image.bitmap as any).exifBuffer | ||
125 | } | ||
126 | |||
127 | function removeExif (image: Jimp) { | ||
128 | (image.bitmap as any).exifBuffer = null | ||
129 | } | ||
diff --git a/server/tests/fixtures/exif.jpg b/server/tests/fixtures/exif.jpg new file mode 100644 index 000000000..2997b38e9 --- /dev/null +++ b/server/tests/fixtures/exif.jpg | |||
Binary files differ | |||
diff --git a/server/tests/fixtures/exif.png b/server/tests/fixtures/exif.png new file mode 100644 index 000000000..a1a0113f8 --- /dev/null +++ b/server/tests/fixtures/exif.png | |||
Binary files differ | |||
diff --git a/server/tests/helpers/image.ts b/server/tests/helpers/image.ts index 64bd373cc..475ca8fb2 100644 --- a/server/tests/helpers/image.ts +++ b/server/tests/helpers/image.ts | |||
@@ -4,6 +4,7 @@ import 'mocha' | |||
4 | import { expect } from 'chai' | 4 | import { expect } from 'chai' |
5 | import { readFile, remove } from 'fs-extra' | 5 | import { readFile, remove } from 'fs-extra' |
6 | import { join } from 'path' | 6 | import { join } from 'path' |
7 | import { execPromise } from '@server/helpers/core-utils' | ||
7 | import { buildAbsoluteFixturePath, root } from '@shared/core-utils' | 8 | import { buildAbsoluteFixturePath, root } from '@shared/core-utils' |
8 | import { processImage } from '../../../server/helpers/image-utils' | 9 | import { processImage } from '../../../server/helpers/image-utils' |
9 | 10 | ||
@@ -20,40 +21,77 @@ async function checkBuffers (path1: string, path2: string, equals: boolean) { | |||
20 | } | 21 | } |
21 | } | 22 | } |
22 | 23 | ||
24 | async function hasTitleExif (path: string) { | ||
25 | const result = JSON.parse(await execPromise(`exiftool -json ${path}`)) | ||
26 | |||
27 | return result[0]?.Title === 'should be removed' | ||
28 | } | ||
29 | |||
23 | describe('Image helpers', function () { | 30 | describe('Image helpers', function () { |
24 | const imageDestDir = join(root(), 'test-images') | 31 | const imageDestDir = join(root(), 'test-images') |
25 | const imageDest = join(imageDestDir, 'test.jpg') | 32 | |
33 | const imageDestJPG = join(imageDestDir, 'test.jpg') | ||
34 | const imageDestPNG = join(imageDestDir, 'test.png') | ||
35 | |||
26 | const thumbnailSize = { width: 223, height: 122 } | 36 | const thumbnailSize = { width: 223, height: 122 } |
27 | 37 | ||
28 | it('Should skip processing if the source image is okay', async function () { | 38 | it('Should skip processing if the source image is okay', async function () { |
29 | const input = buildAbsoluteFixturePath('thumbnail.jpg') | 39 | const input = buildAbsoluteFixturePath('thumbnail.jpg') |
30 | await processImage(input, imageDest, thumbnailSize, true) | 40 | await processImage(input, imageDestJPG, thumbnailSize, true) |
31 | 41 | ||
32 | await checkBuffers(input, imageDest, true) | 42 | await checkBuffers(input, imageDestJPG, true) |
33 | }) | 43 | }) |
34 | 44 | ||
35 | it('Should not skip processing if the source image does not have the appropriate extension', async function () { | 45 | it('Should not skip processing if the source image does not have the appropriate extension', async function () { |
36 | const input = buildAbsoluteFixturePath('thumbnail.png') | 46 | const input = buildAbsoluteFixturePath('thumbnail.png') |
37 | await processImage(input, imageDest, thumbnailSize, true) | 47 | await processImage(input, imageDestJPG, thumbnailSize, true) |
38 | 48 | ||
39 | await checkBuffers(input, imageDest, false) | 49 | await checkBuffers(input, imageDestJPG, false) |
40 | }) | 50 | }) |
41 | 51 | ||
42 | it('Should not skip processing if the source image does not have the appropriate size', async function () { | 52 | it('Should not skip processing if the source image does not have the appropriate size', async function () { |
43 | const input = buildAbsoluteFixturePath('preview.jpg') | 53 | const input = buildAbsoluteFixturePath('preview.jpg') |
44 | await processImage(input, imageDest, thumbnailSize, true) | 54 | await processImage(input, imageDestJPG, thumbnailSize, true) |
45 | 55 | ||
46 | await checkBuffers(input, imageDest, false) | 56 | await checkBuffers(input, imageDestJPG, false) |
47 | }) | 57 | }) |
48 | 58 | ||
49 | it('Should not skip processing if the source image does not have the appropriate size', async function () { | 59 | it('Should not skip processing if the source image does not have the appropriate size', async function () { |
50 | const input = buildAbsoluteFixturePath('thumbnail-big.jpg') | 60 | const input = buildAbsoluteFixturePath('thumbnail-big.jpg') |
51 | await processImage(input, imageDest, thumbnailSize, true) | 61 | await processImage(input, imageDestJPG, thumbnailSize, true) |
62 | |||
63 | await checkBuffers(input, imageDestJPG, false) | ||
64 | }) | ||
65 | |||
66 | it('Should strip exif for a jpg file that can not be copied', async function () { | ||
67 | const input = buildAbsoluteFixturePath('exif.jpg') | ||
68 | expect(await hasTitleExif(input)).to.be.true | ||
69 | |||
70 | await processImage(input, imageDestJPG, { width: 100, height: 100 }, true) | ||
71 | await checkBuffers(input, imageDestJPG, false) | ||
72 | |||
73 | expect(await hasTitleExif(imageDestJPG)).to.be.false | ||
74 | }) | ||
75 | |||
76 | it('Should strip exif for a jpg file that could be copied', async function () { | ||
77 | const input = buildAbsoluteFixturePath('exif.jpg') | ||
78 | expect(await hasTitleExif(input)).to.be.true | ||
79 | |||
80 | await processImage(input, imageDestJPG, thumbnailSize, true) | ||
81 | await checkBuffers(input, imageDestJPG, false) | ||
82 | |||
83 | expect(await hasTitleExif(imageDestJPG)).to.be.false | ||
84 | }) | ||
85 | |||
86 | it('Should strip exif for png', async function () { | ||
87 | const input = buildAbsoluteFixturePath('exif.png') | ||
88 | expect(await hasTitleExif(input)).to.be.true | ||
52 | 89 | ||
53 | await checkBuffers(input, imageDest, false) | 90 | await processImage(input, imageDestPNG, thumbnailSize, true) |
91 | expect(await hasTitleExif(imageDestPNG)).to.be.false | ||
54 | }) | 92 | }) |
55 | 93 | ||
56 | after(async function () { | 94 | after(async function () { |
57 | await remove(imageDest) | 95 | await remove(imageDestDir) |
58 | }) | 96 | }) |
59 | }) | 97 | }) |