1 | 'use strict'
|
2 |
|
3 | const fs = require('fs')
|
4 | const async = require('async')
|
5 | const piexif = require('piexifjs')
|
6 | const CustomError = require('./error.js')
|
7 | const transform = require('./transform.js')
|
8 |
|
9 | const m = {}
|
10 |
|
11 | m.errors = {}
|
12 | m.errors.read_file = 'read_file'
|
13 | m.errors.read_exif = 'read_exif'
|
14 | m.errors.no_orientation = 'no_orientation'
|
15 | m.errors.unknown_orientation = 'unknown_orientation'
|
16 | m.errors.correct_orientation = 'correct_orientation'
|
17 | m.errors.rotate_file = 'rotate_file'
|
18 |
|
19 | m.rotate = function(path_or_buffer, options, module_callback) {
|
20 | let quality = typeof options === 'object' && typeof options.quality !== 'undefined' ? parseInt(options.quality) : 100
|
21 | quality = !isNaN(quality) && quality >= 0 && quality <= 100 ? quality : 100
|
22 | module_callback = typeof module_callback === 'function' ? module_callback : function() {}
|
23 |
|
24 | let jpeg_buffer = null
|
25 | let jpeg_exif_data = null
|
26 | let jpeg_orientation = null
|
27 |
|
28 | if (typeof path_or_buffer === 'string') {
|
29 | fs.readFile(path_or_buffer, _onReadFile)
|
30 | } else if (typeof path_or_buffer === 'object' && Buffer.isBuffer(path_or_buffer)) {
|
31 | _onReadFile(null, path_or_buffer)
|
32 | } else {
|
33 | _onReadFile(new Error('Not a file path or buffer'), null)
|
34 | }
|
35 |
|
36 | |
37 |
|
38 |
|
39 |
|
40 |
|
41 | function _onReadFile(error, buffer) {
|
42 | if (error) {
|
43 | module_callback(
|
44 | new CustomError(m.errors.read_file, 'Could not read file (' + error.message + ')'),
|
45 | null,
|
46 | null,
|
47 | null
|
48 | )
|
49 | return
|
50 | }
|
51 | try {
|
52 | jpeg_buffer = buffer
|
53 | jpeg_exif_data = piexif.load(jpeg_buffer.toString('binary'))
|
54 | } catch (error) {
|
55 | module_callback(
|
56 | new CustomError(m.errors.read_exif, 'Could not read EXIF data (' + error.message + ')'),
|
57 | null,
|
58 | null,
|
59 | null
|
60 | )
|
61 | return
|
62 | }
|
63 | if (
|
64 | typeof jpeg_exif_data['0th'] === 'undefined' ||
|
65 | typeof jpeg_exif_data['0th'][piexif.ImageIFD.Orientation] === 'undefined'
|
66 | ) {
|
67 | module_callback(new CustomError(m.errors.no_orientation, 'No orientation tag found in EXIF'), buffer, null, null)
|
68 | return
|
69 | }
|
70 | jpeg_orientation = parseInt(jpeg_exif_data['0th'][piexif.ImageIFD.Orientation])
|
71 | if (isNaN(jpeg_orientation) || jpeg_orientation < 1 || jpeg_orientation > 8) {
|
72 | module_callback(
|
73 | new CustomError(m.errors.unknown_orientation, 'Unknown orientation (' + jpeg_orientation + ')'),
|
74 | buffer,
|
75 | null,
|
76 | null
|
77 | )
|
78 | return
|
79 | }
|
80 | if (jpeg_orientation === 1) {
|
81 | module_callback(new CustomError(m.errors.correct_orientation, 'Orientation already correct'), buffer, null, null)
|
82 | return
|
83 | }
|
84 | async.parallel({image: _rotateImage, thumbnail: _rotateThumbnail}, _onRotatedImages)
|
85 | }
|
86 |
|
87 | |
88 |
|
89 |
|
90 |
|
91 | function _rotateImage(callback) {
|
92 | transform.do(jpeg_buffer, jpeg_orientation, quality, function(error, buffer, width, height) {
|
93 | callback(error, {buffer: !error ? buffer : null, width: width, height: height})
|
94 | })
|
95 | }
|
96 |
|
97 | |
98 |
|
99 |
|
100 |
|
101 | function _rotateThumbnail(callback) {
|
102 | if (typeof jpeg_exif_data['thumbnail'] === 'undefined' || jpeg_exif_data['thumbnail'] === null) {
|
103 | callback(null, {buffer: null, width: 0, height: 0})
|
104 | return
|
105 | }
|
106 | transform.do(Buffer.from(jpeg_exif_data['thumbnail'], 'binary'), jpeg_orientation, quality, function(
|
107 | error,
|
108 | buffer,
|
109 | width,
|
110 | height
|
111 | ) {
|
112 | callback(null, {buffer: !error ? buffer : null, width: width, height: height})
|
113 | })
|
114 | }
|
115 |
|
116 | |
117 |
|
118 |
|
119 |
|
120 |
|
121 | function _onRotatedImages(error, images) {
|
122 | if (error) {
|
123 | module_callback(
|
124 | new CustomError(m.errors.rotate_file, 'Could not rotate image (' + error.message + ')'),
|
125 | null,
|
126 | null,
|
127 | null
|
128 | )
|
129 | return
|
130 | }
|
131 | jpeg_exif_data['0th'][piexif.ImageIFD.Orientation] = 1
|
132 | if (typeof jpeg_exif_data['Exif'][piexif.ExifIFD.PixelXDimension] !== 'undefined') {
|
133 | jpeg_exif_data['Exif'][piexif.ExifIFD.PixelXDimension] = images.image.width
|
134 | }
|
135 | if (typeof jpeg_exif_data['Exif'][piexif.ExifIFD.PixelYDimension] !== 'undefined') {
|
136 | jpeg_exif_data['Exif'][piexif.ExifIFD.PixelYDimension] = images.image.height
|
137 | }
|
138 | if (images.thumbnail.buffer !== null) {
|
139 | jpeg_exif_data['thumbnail'] = images.thumbnail.buffer.toString('binary')
|
140 | }
|
141 | const exif_bytes = piexif.dump(jpeg_exif_data)
|
142 | const updated_jpeg_buffer = Buffer.from(piexif.insert(exif_bytes, images.image.buffer.toString('binary')), 'binary')
|
143 | const updated_jpeg_dimensions = {
|
144 | height: images.image.height,
|
145 | width: images.image.width,
|
146 | }
|
147 | module_callback(null, updated_jpeg_buffer, jpeg_orientation, updated_jpeg_dimensions)
|
148 | }
|
149 | }
|
150 |
|
151 | module.exports = m
|