Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add flatten background color option #86

Merged
merged 2 commits into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ try {
- __fit__ [string] - method by which the image should fit the width/height. Default = contain ([details](https://sharp.pixelplumbing.com/api-resize))
- __failOnError__ [boolean] - Set to false to avoid read problems for images from some phones (i.e Samsung) in the sharp lib. Default = true ([details](https://github.com/lovell/sharp/issues/1578))
- __withMetaData__ [boolean] - Keep metadata in the thumbnail (will increase file size)
- __flattenBackgroundColor__ [background colour] - parsed by the color module, defaults to #ffffff.

### Examples
```js
Expand Down
133 changes: 100 additions & 33 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ const { fail } = require('assert');
const PERCENTAGE = 10;
const RESPONSE_TYPE = 'buffer';

const fromBase64 = async (source, percentage, width, height, responseType, jpegOptions, fit, failOnError, withMetaData) => {
const fromBase64 = async (source, percentage, width, height, responseType, jpegOptions, fit, failOnError, withMetaData, flattenBackgroundColor) => {
const imageBuffer = Buffer.from(source, 'base64');
const dimensions = getDimensions(imageBuffer, percentage, { width, height });
const thumbnailBuffer = await sharpResize(imageBuffer, dimensions, jpegOptions, fit, failOnError, withMetaData);
const thumbnailBuffer = await sharpResize(imageBuffer, dimensions, jpegOptions, fit, failOnError, withMetaData, flattenBackgroundColor);

if (responseType === 'base64') {
return thumbnailBuffer.toString('base64');
Expand All @@ -24,13 +24,12 @@ const fromBase64 = async (source, percentage, width, height, responseType, jpegO
return thumbnailBuffer;
};

const fromUri = async (source, percentage, width, height, responseType, jpegOptions, fit, failOnError, withMetaData) => {
const fromUri = async (source, percentage, width, height, responseType, jpegOptions, fit, failOnError, withMetaData, flattenBackgroundColor) => {
const response = await axios.get(source.uri, { responseType: 'arraybuffer' });
const imageBuffer = Buffer.from(response.data, 'binary');

const dimensions = getDimensions(imageBuffer, percentage, { width, height });
const thumbnailBuffer = await sharpResize(imageBuffer, dimensions, jpegOptions, fit, failOnError, withMetaData);

const thumbnailBuffer = await sharpResize(imageBuffer, dimensions, jpegOptions, fit, failOnError, withMetaData, flattenBackgroundColor);

if (responseType === 'base64') {
return thumbnailBuffer.toString('base64');
Expand All @@ -39,11 +38,11 @@ const fromUri = async (source, percentage, width, height, responseType, jpegOpti
return thumbnailBuffer;
};

const fromPath = async (source, percentage, width, height, responseType, jpegOptions, fit, failOnError, withMetaData) => {
const fromPath = async (source, percentage, width, height, responseType, jpegOptions, fit, failOnError, withMetaData, flattenBackgroundColor) => {
const imageBuffer = fs.readFileSync(source);

const dimensions = getDimensions(imageBuffer, percentage, { width, height });
const thumbnailBuffer = await sharpResize(imageBuffer, dimensions, jpegOptions, fit, failOnError, withMetaData);
const thumbnailBuffer = await sharpResize(imageBuffer, dimensions, jpegOptions, fit, failOnError, withMetaData, flattenBackgroundColor);

if (responseType === 'base64') {
return thumbnailBuffer.toString('base64');
Expand All @@ -52,10 +51,21 @@ const fromPath = async (source, percentage, width, height, responseType, jpegOpt
return thumbnailBuffer;
};

const fromReadStream = async (source, percentage, width, height, responseType, jpegOptions, fit, failOnError, withMetaData) => {
const fromReadStream = async (
source,
percentage,
width,
height,
responseType,
jpegOptions,
fit,
failOnError,
withMetaData,
flattenBackgroundColor,
) => {
const imageBuffer = await util.streamToBuffer(source);
const dimensions = getDimensions(imageBuffer, percentage, { width, height });
const thumbnailBuffer = await sharpResize(imageBuffer, dimensions, jpegOptions, fit, failOnError, withMetaData);
const thumbnailBuffer = await sharpResize(imageBuffer, dimensions, jpegOptions, fit, failOnError, withMetaData, flattenBackgroundColor);

if (responseType === 'base64') {
return thumbnailBuffer.toString('base64');
Expand All @@ -64,11 +74,10 @@ const fromReadStream = async (source, percentage, width, height, responseType, j
return thumbnailBuffer;
};

const fromBuffer = async (source, percentage, width, height, responseType, jpegOptions, fit, failOnError, withMetaData) => {
const fromBuffer = async (source, percentage, width, height, responseType, jpegOptions, fit, failOnError, withMetaData, flattenBackgroundColor) => {
const imageBuffer = source;

const dimensions = getDimensions(imageBuffer, percentage, { width, height });
const thumbnailBuffer = await sharpResize(imageBuffer, dimensions, jpegOptions, fit, failOnError, withMetaData);
const thumbnailBuffer = await sharpResize(imageBuffer, dimensions, jpegOptions, fit, failOnError, withMetaData, flattenBackgroundColor);

if (responseType === 'base64') {
return thumbnailBuffer.toString('base64');
Expand All @@ -84,26 +93,82 @@ module.exports = async (source, options) => {
const responseType = options && options.responseType ? options.responseType : RESPONSE_TYPE;
const jpegOptions = options && options.jpegOptions ? options.jpegOptions : undefined;
const fit = options && options.fit ? options.fit : undefined;
const failOnError = options && typeof(options.failOnError) !== 'undefined' ? options.failOnError : true;
const withMetaData = options && typeof(options.withMetaData) !== 'undefined' ? options.withMetaData : false;
const failOnError = options && typeof options.failOnError !== 'undefined' ? options.failOnError : true;
const withMetaData = options && typeof options.withMetaData !== 'undefined' ? options.withMetaData : false;
const flattenBackgroundColor = options && typeof options.flattenBackgroundColor !== 'undefined' ? options.flattenBackgroundColor : '#ffffff';

try {
switch (typeof source) {
case 'object':
let response;
if (source instanceof fs.ReadStream || source instanceof stream.PassThrough) {
response = await fromReadStream(source, percentage, width, height, responseType, jpegOptions, fit, failOnError, withMetaData);
response = await fromReadStream(
source,
percentage,
width,
height,
responseType,
jpegOptions,
fit,
failOnError,
withMetaData,
flattenBackgroundColor,
);
} else if (source instanceof Buffer) {
response = await fromBuffer(source, percentage, width, height, responseType, jpegOptions, fit, failOnError, withMetaData);
response = await fromBuffer(
source,
percentage,
width,
height,
responseType,
jpegOptions,
fit,
failOnError,
withMetaData,
flattenBackgroundColor,
);
} else {
response = await fromUri(source, percentage, width, height, responseType, jpegOptions, fit, failOnError, withMetaData);
response = await fromUri(
source,
percentage,
width,
height,
responseType,
jpegOptions,
fit,
failOnError,
withMetaData,
flattenBackgroundColor,
);
}
return response;
case 'string':
if (validator.isBase64(source)) {
return await fromBase64(source, percentage, width, height, responseType, jpegOptions, fit, failOnError, withMetaData);
return await fromBase64(
source,
percentage,
width,
height,
responseType,
jpegOptions,
fit,
failOnError,
withMetaData,
flattenBackgroundColor,
);
} else {
return await fromPath(source, percentage, width, height, responseType, jpegOptions, fit, failOnError, withMetaData);
return await fromPath(
source,
percentage,
width,
height,
responseType,
jpegOptions,
fit,
failOnError,
withMetaData,
flattenBackgroundColor,
);
}
default:
throw new Error('unsupported source type');
Expand All @@ -124,26 +189,28 @@ const getDimensions = (imageBuffer, percentageOfImage, dimensions) => {
const height = parseInt((originalDimensions.height * (percentageOfImage / 100)).toFixed(0));

return { width, height };
}
};

const sharpResize = (imageBuffer, dimensions, jpegOptions, fit, failOnError, withMetadata) => {
const sharpResize = (imageBuffer, dimensions, jpegOptions, fit, failOnError, withMetadata, flattenBackgroundColor) => {
return new Promise((resolve, reject) => {
let result = sharp(imageBuffer, { failOnError })
.resize({
...dimensions, withoutEnlargement: true, fit: fit ? fit : 'contain',
...dimensions,
withoutEnlargement: true,
fit: fit ? fit : 'contain',
})
.flatten({ background: flattenBackgroundColor });

if(withMetadata){
result.withMetadata()
}
if (withMetadata) {
result.withMetadata();
}

result.jpeg(jpegOptions ? jpegOptions : { force: false })
.toBuffer((err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
result.jpeg(jpegOptions ? jpegOptions : { force: false }).toBuffer((err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
};