UNPKG

4.95 kBJavaScriptView Raw
1'use strict'
2
3const fs = require('fs')
4const async = require('async')
5const piexif = require('piexifjs')
6const CustomError = require('./error.js')
7const transform = require('./transform.js')
8
9const m = {}
10
11m.errors = {}
12m.errors.read_file = 'read_file'
13m.errors.read_exif = 'read_exif'
14m.errors.no_orientation = 'no_orientation'
15m.errors.unknown_orientation = 'unknown_orientation'
16m.errors.correct_orientation = 'correct_orientation'
17m.errors.rotate_file = 'rotate_file'
18
19m.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 * Tries to read EXIF data when the image has been loaded
38 * @param error
39 * @param buffer
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 * Tries to rotate the main image
89 * @param callback
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 * Tries to rotate the thumbnail, if it exists
99 * @param callback
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 * Merges EXIF data in the rotated buffer and returns
118 * @param error
119 * @param buffers
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
151module.exports = m