@webex/helper-image 3.0.0-beta.9 → 3.0.0-bnr.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/detect-filetype.js +3 -12
- package/dist/detect-filetype.js.map +1 -1
- package/dist/index.js +52 -90
- package/dist/index.js.map +1 -1
- package/dist/orient.js +61 -0
- package/dist/orient.js.map +1 -0
- package/dist/process-image.browser.js +13 -34
- package/dist/process-image.browser.js.map +1 -1
- package/dist/process-image.js +7 -24
- package/dist/process-image.js.map +1 -1
- package/dist/types/detect-filetype.d.ts +7 -0
- package/dist/types/index.d.ts +26 -0
- package/dist/types/process-image.browser.d.ts +19 -0
- package/dist/types/process-image.d.ts +17 -0
- package/package.json +6 -6
- package/src/detect-filetype.js +8 -9
- package/src/index.js +31 -37
- package/src/process-image.browser.js +77 -69
- package/src/process-image.js +38 -31
- package/test/unit/spec/index.js +58 -59
package/dist/process-image.js
CHANGED
|
@@ -1,21 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
var _Object$defineProperty = require("@babel/runtime-corejs2/core-js/object/define-property");
|
|
4
|
-
|
|
5
4
|
var _interopRequireDefault = require("@babel/runtime-corejs2/helpers/interopRequireDefault");
|
|
6
|
-
|
|
7
5
|
_Object$defineProperty(exports, "__esModule", {
|
|
8
6
|
value: true
|
|
9
7
|
});
|
|
10
|
-
|
|
11
8
|
exports.default = processImage;
|
|
12
|
-
|
|
13
9
|
var _promise = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/promise"));
|
|
14
|
-
|
|
15
10
|
var _pick2 = _interopRequireDefault(require("lodash/pick"));
|
|
16
|
-
|
|
17
11
|
var _gm = _interopRequireDefault(require("gm"));
|
|
18
|
-
|
|
19
12
|
/*!
|
|
20
13
|
* Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
|
|
21
14
|
*/
|
|
@@ -32,29 +25,26 @@ var _gm = _interopRequireDefault(require("gm"));
|
|
|
32
25
|
*/
|
|
33
26
|
function processImage(_ref) {
|
|
34
27
|
var file = _ref.file,
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
28
|
+
type = _ref.type,
|
|
29
|
+
thumbnailMaxWidth = _ref.thumbnailMaxWidth,
|
|
30
|
+
thumbnailMaxHeight = _ref.thumbnailMaxHeight,
|
|
31
|
+
enableThumbnails = _ref.enableThumbnails,
|
|
32
|
+
logger = _ref.logger;
|
|
40
33
|
var fileType = type || file.type;
|
|
41
|
-
|
|
42
34
|
if (!fileType || !fileType.startsWith('image')) {
|
|
43
35
|
return _promise.default.resolve();
|
|
44
36
|
}
|
|
45
|
-
|
|
46
37
|
var fileDimensions = new _promise.default(function (resolve, reject) {
|
|
47
38
|
(0, _gm.default)(file).size(function (err, size) {
|
|
48
39
|
if (err) {
|
|
49
40
|
reject(err);
|
|
50
41
|
return;
|
|
51
42
|
}
|
|
52
|
-
|
|
53
43
|
resolve((0, _pick2.default)(size, 'width', 'height'));
|
|
54
44
|
});
|
|
55
45
|
});
|
|
56
|
-
var thumbnail
|
|
57
|
-
|
|
46
|
+
var thumbnail;
|
|
47
|
+
var thumbnailDimensions;
|
|
58
48
|
if (enableThumbnails) {
|
|
59
49
|
thumbnail = new _promise.default(function (resolve, reject) {
|
|
60
50
|
(0, _gm.default)(file).resize(thumbnailMaxWidth, thumbnailMaxHeight).autoOrient().toBuffer('PNG', function (err, buffer) {
|
|
@@ -62,7 +52,6 @@ function processImage(_ref) {
|
|
|
62
52
|
reject(err);
|
|
63
53
|
return;
|
|
64
54
|
}
|
|
65
|
-
|
|
66
55
|
resolve(buffer);
|
|
67
56
|
});
|
|
68
57
|
});
|
|
@@ -73,31 +62,25 @@ function processImage(_ref) {
|
|
|
73
62
|
reject(err);
|
|
74
63
|
return;
|
|
75
64
|
}
|
|
76
|
-
|
|
77
65
|
resolve((0, _pick2.default)(size, 'width', 'height'));
|
|
78
66
|
});
|
|
79
67
|
});
|
|
80
68
|
});
|
|
81
69
|
}
|
|
82
|
-
|
|
83
70
|
return _promise.default.all([thumbnail, fileDimensions, thumbnailDimensions]).catch(function (err) {
|
|
84
71
|
var errorString = err.toString();
|
|
85
|
-
|
|
86
72
|
if (errorString.includes('EPIPE')) {
|
|
87
73
|
logger.warn(err, 'Is GraphicsMagick installed?');
|
|
88
74
|
return _promise.default.resolve();
|
|
89
75
|
}
|
|
90
|
-
|
|
91
76
|
if (errorString.includes('No decode delegate for this image format')) {
|
|
92
77
|
logger.debug(err, 'File does not appear to be an image');
|
|
93
78
|
return _promise.default.resolve();
|
|
94
79
|
}
|
|
95
|
-
|
|
96
80
|
if (errorString.includes('Stream yields empty buffer')) {
|
|
97
81
|
logger.debug(err, 'File does not appear to be an image');
|
|
98
82
|
return _promise.default.resolve();
|
|
99
83
|
}
|
|
100
|
-
|
|
101
84
|
return _promise.default.reject(err);
|
|
102
85
|
});
|
|
103
86
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["processImage","file","type","thumbnailMaxWidth","thumbnailMaxHeight","enableThumbnails","logger","fileType","startsWith","resolve","fileDimensions","reject","gm","size","err","thumbnail","thumbnailDimensions","resize","autoOrient","toBuffer","buffer","then","all","catch","errorString","toString","includes","warn","debug"],"sources":["process-image.js"],"sourcesContent":["/*!\n * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.\n */\n\nimport gm from 'gm';\nimport {pick} from 'lodash';\n\n/**\n * Measures an image file and produces a thumbnail for it\n * @param {Object} options\n * @param {Blob|ArrayBuffer} options.file\n * @param {Number} options.thumbnailMaxWidth\n * @param {Number} options.thumbnailMaxHeight\n * @param {Boolean} options.enableThumbnails\n * @param {Object} options.logger\n * @returns {Promise<Array>} Buffer, Dimensions, thumbnailDimensions\n */\nexport default function processImage({\n file
|
|
1
|
+
{"version":3,"names":["processImage","file","type","thumbnailMaxWidth","thumbnailMaxHeight","enableThumbnails","logger","fileType","startsWith","resolve","fileDimensions","reject","gm","size","err","thumbnail","thumbnailDimensions","resize","autoOrient","toBuffer","buffer","then","all","catch","errorString","toString","includes","warn","debug"],"sources":["process-image.js"],"sourcesContent":["/*!\n * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.\n */\n\nimport gm from 'gm';\nimport {pick} from 'lodash';\n\n/**\n * Measures an image file and produces a thumbnail for it\n * @param {Object} options\n * @param {Blob|ArrayBuffer} options.file\n * @param {Number} options.thumbnailMaxWidth\n * @param {Number} options.thumbnailMaxHeight\n * @param {Boolean} options.enableThumbnails\n * @param {Object} options.logger\n * @returns {Promise<Array>} Buffer, Dimensions, thumbnailDimensions\n */\nexport default function processImage({\n file,\n type,\n thumbnailMaxWidth,\n thumbnailMaxHeight,\n enableThumbnails,\n logger,\n}) {\n const fileType = type || file.type;\n\n if (!fileType || !fileType.startsWith('image')) {\n return Promise.resolve();\n }\n\n const fileDimensions = new Promise((resolve, reject) => {\n gm(file).size((err, size) => {\n if (err) {\n reject(err);\n\n return;\n }\n\n resolve(pick(size, 'width', 'height'));\n });\n });\n\n let thumbnail;\n let thumbnailDimensions;\n\n if (enableThumbnails) {\n thumbnail = new Promise((resolve, reject) => {\n gm(file)\n .resize(thumbnailMaxWidth, thumbnailMaxHeight)\n .autoOrient()\n .toBuffer('PNG', (err, buffer) => {\n if (err) {\n reject(err);\n\n return;\n }\n\n resolve(buffer);\n });\n });\n\n thumbnailDimensions = thumbnail.then(\n (buffer) =>\n new Promise((resolve, reject) => {\n gm(buffer).size((err, size) => {\n if (err) {\n reject(err);\n\n return;\n }\n\n resolve(pick(size, 'width', 'height'));\n });\n })\n );\n }\n\n return Promise.all([thumbnail, fileDimensions, thumbnailDimensions]).catch((err) => {\n const errorString = err.toString();\n\n if (errorString.includes('EPIPE')) {\n logger.warn(err, 'Is GraphicsMagick installed?');\n\n return Promise.resolve();\n }\n\n if (errorString.includes('No decode delegate for this image format')) {\n logger.debug(err, 'File does not appear to be an image');\n\n return Promise.resolve();\n }\n\n if (errorString.includes('Stream yields empty buffer')) {\n logger.debug(err, 'File does not appear to be an image');\n\n return Promise.resolve();\n }\n\n return Promise.reject(err);\n });\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Updates the image file with exif information, required to correctly rotate the image activity
|
|
3
|
+
* @param {Object} file
|
|
4
|
+
* @param {Object} options
|
|
5
|
+
* @param {boolean} options.shouldNotAddExifData
|
|
6
|
+
* @returns {Promise<Object>}
|
|
7
|
+
*/
|
|
8
|
+
export function updateImageOrientation(file: any, options?: {
|
|
9
|
+
shouldNotAddExifData: boolean;
|
|
10
|
+
}): Promise<any>;
|
|
11
|
+
/**
|
|
12
|
+
* Adds exif orientation information on the image file
|
|
13
|
+
* @param {Object} file
|
|
14
|
+
* @param {Object} buf
|
|
15
|
+
* @returns {Promise<ExifImage>}
|
|
16
|
+
*/
|
|
17
|
+
export function readExifData(file: any, buf: any): Promise<ExifImage>;
|
|
18
|
+
/**
|
|
19
|
+
* Rotates/flips the image on the canvas as per exif information
|
|
20
|
+
* @param {Object} options(orientation: image exif orientation range from 1-8, img: Image object, x: start x-axis, y: start y-axis, width: width of the thumbnail, height: height of the thumbnail, ctx: canvas context)
|
|
21
|
+
* @param {Object} file
|
|
22
|
+
* @returns {Object}
|
|
23
|
+
*/
|
|
24
|
+
export function orient(options: any, file: any): any;
|
|
25
|
+
export { default as processImage } from "./process-image";
|
|
26
|
+
export { default as detectFileType } from "./detect-filetype";
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Measures an image file and produces a thumbnail for it
|
|
3
|
+
* @param {Object} options
|
|
4
|
+
* @param {Blob|ArrayBuffer} options.file
|
|
5
|
+
* @param {Number} options.thumbnailMaxWidth
|
|
6
|
+
* @param {Number} options.thumbnailMaxHeight
|
|
7
|
+
* @param {Boolean} options.enableThumbnails
|
|
8
|
+
* @param {Object} options.logger
|
|
9
|
+
* @param {Boolean} options.isAvatar
|
|
10
|
+
* @returns {Promise<Array>} Buffer, Dimensions, thumbnailDimensions
|
|
11
|
+
*/
|
|
12
|
+
export default function processImage({ file, type, thumbnailMaxWidth, thumbnailMaxHeight, enableThumbnails, logger, isAvatar, }: {
|
|
13
|
+
file: Blob | ArrayBuffer;
|
|
14
|
+
thumbnailMaxWidth: number;
|
|
15
|
+
thumbnailMaxHeight: number;
|
|
16
|
+
enableThumbnails: boolean;
|
|
17
|
+
logger: any;
|
|
18
|
+
isAvatar: boolean;
|
|
19
|
+
}): Promise<any[]>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Measures an image file and produces a thumbnail for it
|
|
3
|
+
* @param {Object} options
|
|
4
|
+
* @param {Blob|ArrayBuffer} options.file
|
|
5
|
+
* @param {Number} options.thumbnailMaxWidth
|
|
6
|
+
* @param {Number} options.thumbnailMaxHeight
|
|
7
|
+
* @param {Boolean} options.enableThumbnails
|
|
8
|
+
* @param {Object} options.logger
|
|
9
|
+
* @returns {Promise<Array>} Buffer, Dimensions, thumbnailDimensions
|
|
10
|
+
*/
|
|
11
|
+
export default function processImage({ file, type, thumbnailMaxWidth, thumbnailMaxHeight, enableThumbnails, logger, }: {
|
|
12
|
+
file: Blob | ArrayBuffer;
|
|
13
|
+
thumbnailMaxWidth: number;
|
|
14
|
+
thumbnailMaxHeight: number;
|
|
15
|
+
enableThumbnails: boolean;
|
|
16
|
+
logger: any;
|
|
17
|
+
}): Promise<any[]>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webex/helper-image",
|
|
3
|
-
"version": "3.0.0-
|
|
3
|
+
"version": "3.0.0-bnr.2",
|
|
4
4
|
"description": "",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Saurabh Jain <saurjai3@cisco.com>",
|
|
@@ -28,11 +28,11 @@
|
|
|
28
28
|
"sinon": "^9.2.4"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@webex/helper-image": "3.0.0-
|
|
32
|
-
"@webex/http-core": "3.0.0-
|
|
33
|
-
"@webex/test-helper-chai": "3.0.0-
|
|
34
|
-
"@webex/test-helper-file": "3.0.0-
|
|
35
|
-
"@webex/test-helper-mocha": "3.0.0-
|
|
31
|
+
"@webex/helper-image": "3.0.0-bnr.2",
|
|
32
|
+
"@webex/http-core": "3.0.0-bnr.2",
|
|
33
|
+
"@webex/test-helper-chai": "3.0.0-bnr.2",
|
|
34
|
+
"@webex/test-helper-file": "3.0.0-bnr.2",
|
|
35
|
+
"@webex/test-helper-mocha": "3.0.0-bnr.2",
|
|
36
36
|
"exifr": "^5.0.3",
|
|
37
37
|
"gm": "^1.23.1",
|
|
38
38
|
"lodash": "^4.17.21",
|
package/src/detect-filetype.js
CHANGED
|
@@ -26,16 +26,15 @@ export default function detectFileType(file, logger) {
|
|
|
26
26
|
|
|
27
27
|
// This kinda belongs in http core, but since we have no guarantee that
|
|
28
28
|
// buffers are expected to have names there, it'll stay here for now.
|
|
29
|
-
return detect(file)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
logger.info(`detected filetype to be ${type}. Falling back to mime.lookup`);
|
|
29
|
+
return detect(file).then((type) => {
|
|
30
|
+
if (type === 'application/x-msi' || type === 'application/octet-stream') {
|
|
31
|
+
logger.info(`detected filetype to be ${type}. Falling back to mime.lookup`);
|
|
33
32
|
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
return getType(file.name);
|
|
34
|
+
}
|
|
36
35
|
|
|
37
|
-
|
|
36
|
+
logger.info(`detected filetype to be ${type}. returning it`);
|
|
38
37
|
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
return type;
|
|
39
|
+
});
|
|
41
40
|
}
|
package/src/index.js
CHANGED
|
@@ -3,18 +3,18 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
/* eslint no-unused-vars: ["error", { "vars": "local" }] */
|
|
6
|
-
|
|
6
|
+
// eslint-disable-next-line no-redeclare
|
|
7
7
|
|
|
8
8
|
const {Buffer} = require('safe-buffer');
|
|
9
9
|
const {parse} = require('exifr/dist/lite.umd');
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
* Updates the image file with exif information, required to correctly rotate the image activity
|
|
13
|
-
* @param {Object} file
|
|
14
|
-
* @param {Object} options
|
|
15
|
-
* @param {boolean} options.shouldNotAddExifData
|
|
16
|
-
* @returns {Promise<Object>}
|
|
17
|
-
*/
|
|
12
|
+
* Updates the image file with exif information, required to correctly rotate the image activity
|
|
13
|
+
* @param {Object} file
|
|
14
|
+
* @param {Object} options
|
|
15
|
+
* @param {boolean} options.shouldNotAddExifData
|
|
16
|
+
* @returns {Promise<Object>}
|
|
17
|
+
*/
|
|
18
18
|
export function updateImageOrientation(file, options = {}) {
|
|
19
19
|
return new Promise((resolve) => {
|
|
20
20
|
const reader = new FileReader();
|
|
@@ -26,28 +26,24 @@ export function updateImageOrientation(file, options = {}) {
|
|
|
26
26
|
|
|
27
27
|
resolve(buf);
|
|
28
28
|
};
|
|
29
|
-
})
|
|
30
|
-
.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
29
|
+
}).then((buf) => {
|
|
30
|
+
if (options.shouldNotAddExifData) {
|
|
31
|
+
return buf;
|
|
32
|
+
}
|
|
34
33
|
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
return readExifData(file, buf);
|
|
35
|
+
});
|
|
37
36
|
}
|
|
38
37
|
|
|
39
38
|
/**
|
|
40
|
-
* Adds exif orientation information on the image file
|
|
41
|
-
* @param {Object} file
|
|
42
|
-
* @param {Object} buf
|
|
43
|
-
* @returns {Promise<ExifImage>}
|
|
44
|
-
*/
|
|
39
|
+
* Adds exif orientation information on the image file
|
|
40
|
+
* @param {Object} file
|
|
41
|
+
* @param {Object} buf
|
|
42
|
+
* @returns {Promise<ExifImage>}
|
|
43
|
+
*/
|
|
45
44
|
export async function readExifData(file, buf) {
|
|
46
45
|
// For avatar images the file.type is set as image/jpeg, however for images shared in an activity file.mimeType is set as image/jpeg. Handling both conditions.
|
|
47
|
-
if (
|
|
48
|
-
file &&
|
|
49
|
-
(file.type === 'image/jpeg' || file.mimeType === 'image/jpeg')
|
|
50
|
-
) {
|
|
46
|
+
if (file && (file.type === 'image/jpeg' || file.mimeType === 'image/jpeg')) {
|
|
51
47
|
const exifData = await parse(buf, {translateValues: false});
|
|
52
48
|
|
|
53
49
|
if (exifData) {
|
|
@@ -68,15 +64,13 @@ export async function readExifData(file, buf) {
|
|
|
68
64
|
|
|
69
65
|
/* eslint-disable complexity */
|
|
70
66
|
/**
|
|
71
|
-
* Rotates/flips the image on the canvas as per exif information
|
|
72
|
-
* @param {Object} options(orientation: image exif orientation range from 1-8, img: Image object, x: start x-axis, y: start y-axis, width: width of the thumbnail, height: height of the thumbnail, ctx: canvas context)
|
|
73
|
-
* @param {Object} file
|
|
74
|
-
* @returns {Object}
|
|
75
|
-
*/
|
|
67
|
+
* Rotates/flips the image on the canvas as per exif information
|
|
68
|
+
* @param {Object} options(orientation: image exif orientation range from 1-8, img: Image object, x: start x-axis, y: start y-axis, width: width of the thumbnail, height: height of the thumbnail, ctx: canvas context)
|
|
69
|
+
* @param {Object} file
|
|
70
|
+
* @returns {Object}
|
|
71
|
+
*/
|
|
76
72
|
export function orient(options, file) {
|
|
77
|
-
const {
|
|
78
|
-
width, height, ctx, img, orientation, x, y
|
|
79
|
-
} = options;
|
|
73
|
+
const {width, height, ctx, img, orientation, x, y} = options;
|
|
80
74
|
|
|
81
75
|
if (file && file.orientation && file.orientation !== 1) {
|
|
82
76
|
// explanation of orientation:
|
|
@@ -87,27 +81,27 @@ export function orient(options, file) {
|
|
|
87
81
|
ctx.transform(-1, 0, 0, 1, width, 0);
|
|
88
82
|
break;
|
|
89
83
|
case 3:
|
|
90
|
-
|
|
84
|
+
// rotateImage180
|
|
91
85
|
ctx.transform(-1, 0, 0, -1, width, height);
|
|
92
86
|
break;
|
|
93
87
|
case 4:
|
|
94
|
-
|
|
88
|
+
// rotate180AndFlipImage
|
|
95
89
|
ctx.transform(1, 0, 0, -1, 0, height);
|
|
96
90
|
break;
|
|
97
91
|
case 5:
|
|
98
|
-
|
|
92
|
+
// rotate90AndFlipImage
|
|
99
93
|
ctx.transform(0, 1, 1, 0, 0, 0);
|
|
100
94
|
break;
|
|
101
95
|
case 6:
|
|
102
|
-
|
|
96
|
+
// rotateImage90
|
|
103
97
|
ctx.transform(0, 1, -1, 0, height, 0);
|
|
104
98
|
break;
|
|
105
99
|
case 7:
|
|
106
|
-
|
|
100
|
+
// rotateNeg90AndFlipImage
|
|
107
101
|
ctx.transform(0, -1, -1, 0, height, width);
|
|
108
102
|
break;
|
|
109
103
|
case 8:
|
|
110
|
-
|
|
104
|
+
// rotateNeg90
|
|
111
105
|
ctx.transform(0, -1, 1, 0, 0, width);
|
|
112
106
|
break;
|
|
113
107
|
default:
|
|
@@ -19,23 +19,22 @@ import {orient} from './index';
|
|
|
19
19
|
function computeDimensions({width, height}, maxWidth, maxHeight) {
|
|
20
20
|
if (height > width) {
|
|
21
21
|
if (height > maxHeight) {
|
|
22
|
-
width = width * maxHeight / height;
|
|
22
|
+
width = (width * maxHeight) / height;
|
|
23
23
|
height = maxHeight;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
if (width > maxWidth) {
|
|
27
|
-
height = height * maxWidth / width;
|
|
27
|
+
height = (height * maxWidth) / width;
|
|
28
28
|
width = maxWidth;
|
|
29
29
|
}
|
|
30
|
-
}
|
|
31
|
-
else {
|
|
30
|
+
} else {
|
|
32
31
|
if (width > maxWidth) {
|
|
33
|
-
height = height * maxWidth / width;
|
|
32
|
+
height = (height * maxWidth) / width;
|
|
34
33
|
width = maxWidth;
|
|
35
34
|
}
|
|
36
35
|
|
|
37
36
|
if (height > maxHeight) {
|
|
38
|
-
width = width * maxHeight / height;
|
|
37
|
+
width = (width * maxHeight) / height;
|
|
39
38
|
height = maxHeight;
|
|
40
39
|
}
|
|
41
40
|
}
|
|
@@ -55,7 +54,13 @@ function computeDimensions({width, height}, maxWidth, maxHeight) {
|
|
|
55
54
|
* @returns {Promise<Array>} Buffer, Dimensions, thumbnailDimensions
|
|
56
55
|
*/
|
|
57
56
|
export default function processImage({
|
|
58
|
-
file,
|
|
57
|
+
file,
|
|
58
|
+
type,
|
|
59
|
+
thumbnailMaxWidth,
|
|
60
|
+
thumbnailMaxHeight,
|
|
61
|
+
enableThumbnails,
|
|
62
|
+
logger,
|
|
63
|
+
isAvatar,
|
|
59
64
|
}) {
|
|
60
65
|
if (!type || !type.startsWith('image')) {
|
|
61
66
|
return Promise.resolve();
|
|
@@ -71,66 +76,69 @@ export default function processImage({
|
|
|
71
76
|
};
|
|
72
77
|
img.onerror = reject;
|
|
73
78
|
img.src = URL.createObjectURL(file);
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}
|
|
79
|
+
}).then((img) => {
|
|
80
|
+
const fileDimensions = pick(img, 'height', 'width');
|
|
81
|
+
|
|
82
|
+
if (isAvatar) {
|
|
83
|
+
// only if image is a profile avatar
|
|
84
|
+
logger.info('dimensions will be set for avatar image');
|
|
85
|
+
const size =
|
|
86
|
+
fileDimensions.height > fileDimensions.width ? fileDimensions.height : fileDimensions.width;
|
|
87
|
+
|
|
88
|
+
fileDimensions.height = size;
|
|
89
|
+
fileDimensions.width = size;
|
|
90
|
+
}
|
|
91
|
+
if (!enableThumbnails) {
|
|
92
|
+
logger.info('thumbnails not enabled');
|
|
93
|
+
|
|
94
|
+
return [null, fileDimensions, null];
|
|
95
|
+
}
|
|
96
|
+
const thumbnailDimensions = computeDimensions(
|
|
97
|
+
fileDimensions,
|
|
98
|
+
thumbnailMaxWidth,
|
|
99
|
+
thumbnailMaxHeight
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
const canvas = document.createElement('canvas');
|
|
103
|
+
const ctx = canvas.getContext('2d');
|
|
104
|
+
const {width, height} = thumbnailDimensions;
|
|
105
|
+
|
|
106
|
+
// explanation of orientation:
|
|
107
|
+
// https://stackoverflow.com/questions/20600800/js-client-side-exif-orientation-rotate-and-mirror-jpeg-images
|
|
108
|
+
if (file.orientation && file.orientation > 4) {
|
|
109
|
+
canvas.width = height;
|
|
110
|
+
canvas.height = width;
|
|
111
|
+
thumbnailDimensions.width = height;
|
|
112
|
+
thumbnailDimensions.height = width;
|
|
113
|
+
} else {
|
|
114
|
+
canvas.width = thumbnailDimensions.width;
|
|
115
|
+
canvas.height = thumbnailDimensions.height;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
orient(
|
|
119
|
+
{
|
|
120
|
+
orientation: file && file.orientation ? file.orientation : '',
|
|
121
|
+
img,
|
|
122
|
+
x: 0,
|
|
123
|
+
y: 0,
|
|
124
|
+
width,
|
|
125
|
+
height,
|
|
126
|
+
ctx,
|
|
127
|
+
},
|
|
128
|
+
file
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
const parts = canvas.toDataURL('image/png').split(',');
|
|
132
|
+
// Thumbnail uploads were failing with common/base64 decoding
|
|
133
|
+
const byteString = atob(parts[1]);
|
|
134
|
+
|
|
135
|
+
const buffer = new ArrayBuffer(byteString.length);
|
|
136
|
+
const view = new DataView(buffer);
|
|
137
|
+
|
|
138
|
+
for (let i = 0; i < byteString.length; i += 1) {
|
|
139
|
+
view.setUint8(i, byteString.charCodeAt(i));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return [buffer, fileDimensions, thumbnailDimensions];
|
|
143
|
+
});
|
|
136
144
|
}
|
package/src/process-image.js
CHANGED
|
@@ -16,7 +16,12 @@ import {pick} from 'lodash';
|
|
|
16
16
|
* @returns {Promise<Array>} Buffer, Dimensions, thumbnailDimensions
|
|
17
17
|
*/
|
|
18
18
|
export default function processImage({
|
|
19
|
-
file,
|
|
19
|
+
file,
|
|
20
|
+
type,
|
|
21
|
+
thumbnailMaxWidth,
|
|
22
|
+
thumbnailMaxHeight,
|
|
23
|
+
enableThumbnails,
|
|
24
|
+
logger,
|
|
20
25
|
}) {
|
|
21
26
|
const fileType = type || file.type;
|
|
22
27
|
|
|
@@ -36,11 +41,13 @@ export default function processImage({
|
|
|
36
41
|
});
|
|
37
42
|
});
|
|
38
43
|
|
|
39
|
-
let thumbnail
|
|
44
|
+
let thumbnail;
|
|
45
|
+
let thumbnailDimensions;
|
|
40
46
|
|
|
41
47
|
if (enableThumbnails) {
|
|
42
48
|
thumbnail = new Promise((resolve, reject) => {
|
|
43
|
-
gm(file)
|
|
49
|
+
gm(file)
|
|
50
|
+
.resize(thumbnailMaxWidth, thumbnailMaxHeight)
|
|
44
51
|
.autoOrient()
|
|
45
52
|
.toBuffer('PNG', (err, buffer) => {
|
|
46
53
|
if (err) {
|
|
@@ -53,43 +60,43 @@ export default function processImage({
|
|
|
53
60
|
});
|
|
54
61
|
});
|
|
55
62
|
|
|
56
|
-
thumbnailDimensions = thumbnail.then(
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
63
|
+
thumbnailDimensions = thumbnail.then(
|
|
64
|
+
(buffer) =>
|
|
65
|
+
new Promise((resolve, reject) => {
|
|
66
|
+
gm(buffer).size((err, size) => {
|
|
67
|
+
if (err) {
|
|
68
|
+
reject(err);
|
|
61
69
|
|
|
62
|
-
|
|
63
|
-
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
64
72
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
73
|
+
resolve(pick(size, 'width', 'height'));
|
|
74
|
+
});
|
|
75
|
+
})
|
|
76
|
+
);
|
|
68
77
|
}
|
|
69
78
|
|
|
79
|
+
return Promise.all([thumbnail, fileDimensions, thumbnailDimensions]).catch((err) => {
|
|
80
|
+
const errorString = err.toString();
|
|
70
81
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const errorString = err.toString();
|
|
82
|
+
if (errorString.includes('EPIPE')) {
|
|
83
|
+
logger.warn(err, 'Is GraphicsMagick installed?');
|
|
74
84
|
|
|
75
|
-
|
|
76
|
-
|
|
85
|
+
return Promise.resolve();
|
|
86
|
+
}
|
|
77
87
|
|
|
78
|
-
|
|
79
|
-
|
|
88
|
+
if (errorString.includes('No decode delegate for this image format')) {
|
|
89
|
+
logger.debug(err, 'File does not appear to be an image');
|
|
80
90
|
|
|
81
|
-
|
|
82
|
-
|
|
91
|
+
return Promise.resolve();
|
|
92
|
+
}
|
|
83
93
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if (errorString.includes('Stream yields empty buffer')) {
|
|
88
|
-
logger.debug(err, 'File does not appear to be an image');
|
|
94
|
+
if (errorString.includes('Stream yields empty buffer')) {
|
|
95
|
+
logger.debug(err, 'File does not appear to be an image');
|
|
89
96
|
|
|
90
|
-
|
|
91
|
-
|
|
97
|
+
return Promise.resolve();
|
|
98
|
+
}
|
|
92
99
|
|
|
93
|
-
|
|
94
|
-
|
|
100
|
+
return Promise.reject(err);
|
|
101
|
+
});
|
|
95
102
|
}
|