UNPKG

6.84 kBJavaScriptView Raw
1'use strict'
2
3const expect = require('chai').expect
4const fs = require('fs')
5const exec = require('child_process').exec
6const piexif = require('piexifjs')
7const jpegjs = require('jpeg-js')
8const jo = require('../src/main.js')
9const describe = require('mocha').describe
10const it = require('mocha').it
11
12require('chai').should()
13
14describe('jpeg-autorotate', function() {
15 itShouldTransform(__dirname + '/samples/image_2.jpg', 'image_2.jpg')
16 itShouldTransform(__dirname + '/samples/image_3.jpg', 'image_3.jpg')
17 itShouldTransform(__dirname + '/samples/image_4.jpg', 'image_4.jpg')
18 itShouldTransform(__dirname + '/samples/image_5.jpg', 'image_5.jpg')
19 itShouldTransform(__dirname + '/samples/image_6.jpg', 'image_6.jpg')
20 itShouldTransform(__dirname + '/samples/image_7.jpg', 'image_7.jpg')
21 itShouldTransform(__dirname + '/samples/image_8.jpg', 'image_8.jpg')
22 itShouldTransform(__dirname + '/samples/image_exif.jpg', 'image_exif.jpg')
23 itShouldTransform(fs.readFileSync(__dirname + '/samples/image_8.jpg'), 'From a buffer')
24
25 it('Should return an error if the orientation is 1', function(done) {
26 jo.rotate(__dirname + '/samples/image_1.jpg', {}, function(error, buffer) {
27 error.should.have.property('code').equal(jo.errors.correct_orientation)
28 Buffer.isBuffer(buffer).should.be.ok
29 done()
30 })
31 })
32
33 it('Should return an error if the image does not exist', function(done) {
34 jo.rotate('foo.jpg', {}, function(error, buffer, orientation) {
35 error.should.have.property('code').equal(jo.errors.read_file)
36 expect(buffer).to.equal(null)
37 expect(orientation).to.equal(null)
38 done()
39 })
40 })
41
42 it('Should return an error if the file is not an image', function(done) {
43 jo.rotate(__dirname + '/samples/textfile.md', {}, function(error, buffer, orientation) {
44 error.should.have.property('code').equal(jo.errors.read_exif)
45 expect(buffer).to.equal(null)
46 expect(orientation).to.equal(null)
47 done()
48 })
49 })
50
51 it('Should return an error if the path is not a string/buffer', function(done) {
52 jo.rotate(['foo'], {}, function(error, buffer, orientation) {
53 error.should.have.property('code').equal(jo.errors.read_file)
54 expect(buffer).to.equal(null)
55 expect(orientation).to.equal(null)
56 done()
57 })
58 })
59
60 it('Should work if `options` is not an object', function(done) {
61 jo.rotate(__dirname + '/samples/image_2.jpg', 'options', function(error, buffer, orientation) {
62 expect(error).to.equal(null)
63 Buffer.isBuffer(buffer).should.be.ok
64 expect(orientation).to.equal(2)
65 done()
66 })
67 })
68
69 it('Should return an error if the image has no orientation tag', function(done) {
70 jo.rotate(__dirname + '/samples/image_no_orientation.jpg', {}, function(error, buffer, orientation) {
71 error.should.have.property('code').equal(jo.errors.no_orientation)
72 Buffer.isBuffer(buffer).should.be.ok
73 expect(orientation).to.equal(null)
74 done()
75 })
76 })
77
78 it('Should return an error if the image has an unknown orientation tag', function(done) {
79 jo.rotate(__dirname + '/samples/image_unknown_orientation.jpg', {}, function(error, buffer, orientation) {
80 error.should.have.property('code').equal(jo.errors.unknown_orientation)
81 Buffer.isBuffer(buffer).should.be.ok
82 expect(orientation).to.equal(null)
83 done()
84 })
85 })
86
87 it('Should run on CLI (with glob support)', function(done) {
88 const command = `
89 cp test/samples/image_2.jpg test/samples/image_2.cli.jpg &&
90 cp test/samples/image_3.jpg test/samples/image_3.cli.jpg &&
91 cp test/samples/image_4.jpg test/samples/image_4.cli.jpg &&
92 ./src/cli.js test/samples/image_2.cli.jpg "test/samples/image_{3,4}.cli.jpg" &&
93 rm test/samples/image_2.cli.jpg &&
94 rm test/samples/image_3.cli.jpg &&
95 rm test/samples/image_4.cli.jpg
96 `
97 exec(command, function(error, stdout, stderr) {
98 stdout.should.be.a('string').and.contain('Processed (Orientation was 2)')
99 stdout.should.be.a('string').and.contain('Processed (Orientation was 3)')
100 stdout.should.be.a('string').and.contain('Processed (Orientation was 4)')
101 stderr.should.equal('')
102 done()
103 })
104 })
105
106 // @todo test jo.errors.read_exif (corrupted EXIF data ?)
107 // @todo test jo.errors.rotate_file (corrupted JPEG ?)
108})
109
110/**
111 * Tries to transform the given path/buffer, and checks data integrity (EXIF, dimensions)
112 * @param path_or_buffer
113 * @param label
114 */
115function itShouldTransform(path_or_buffer, label) {
116 it('Should rotate image (' + label + ')', function(done) {
117 this.timeout(20000)
118 const orig_buffer = typeof path_or_buffer === 'string' ? fs.readFileSync(path_or_buffer) : path_or_buffer
119 const orig_exif = piexif.load(orig_buffer.toString('binary'))
120 const orig_jpeg = jpegjs.decode(orig_buffer)
121 jo.rotate(path_or_buffer, {}, function(error, buffer, orientation, dimensions) {
122 if (error) {
123 throw error
124 }
125 const dest_exif = piexif.load(buffer.toString('binary'))
126 if (orientation < 5 && (orig_jpeg.width !== dimensions.width || orig_jpeg.height !== dimensions.height)) {
127 throw new Error('Dimensions do not match')
128 }
129 if (orientation >= 5 && (orig_jpeg.width !== dimensions.height || orig_jpeg.height !== dimensions.width)) {
130 throw new Error('Dimensions do not match')
131 }
132 if (!compareEXIF(orig_exif, dest_exif)) {
133 throw new Error('EXIF do not match')
134 }
135 if (typeof path_or_buffer === 'string') {
136 fs.writeFileSync(path_or_buffer.replace('samples/', 'samples/transformed/'), buffer)
137 }
138 done()
139 })
140 })
141}
142
143/**
144 * Compares EXIF arrays
145 * The properties allowed to differ between the two versions are set to 0
146 * @param orig
147 * @param dest
148 */
149function compareEXIF(orig, dest) {
150 orig['thumbnail'] = 0 // The thumbnail
151 dest['thumbnail'] = 0
152 orig['0th'][piexif.ImageIFD.Orientation] = 0 // Orientation
153 dest['0th'][piexif.ImageIFD.Orientation] = 0
154 orig['0th'][piexif.ImageIFD.ExifTag] = 0 // Pointer to the Exif IFD
155 dest['0th'][piexif.ImageIFD.ExifTag] = 0
156 orig['Exif'][piexif.ExifIFD.PixelXDimension] = 0 // Image width
157 dest['Exif'][piexif.ExifIFD.PixelXDimension] = 0
158 orig['Exif'][piexif.ExifIFD.PixelYDimension] = 0 // Image height
159 dest['Exif'][piexif.ExifIFD.PixelYDimension] = 0
160 orig['1st'][piexif.ImageIFD.JPEGInterchangeFormat] = 0 // Offset to the start byte of the thumbnail
161 dest['1st'][piexif.ImageIFD.JPEGInterchangeFormat] = 0
162 orig['1st'][piexif.ImageIFD.JPEGInterchangeFormatLength] = 0 // Number of bytes of the thumbnail
163 dest['1st'][piexif.ImageIFD.JPEGInterchangeFormatLength] = 0
164
165 const orig_json = JSON.stringify(orig)
166 const dest_json = JSON.stringify(dest)
167
168 return orig_json === dest_json
169}