Skip to content
Open
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
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,7 @@ typings/
.env

.idea/
test/*.jpg

#test results
test/results/*.jpg
.DS_Store
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

## Getting started

This module works with gm, so you have to install imagemagick and ghostscript on your pc.
This module works with cross-spawn to delegate all the work to imagemagick and ghostscript, so you have to install these programs on your pc.

On Mac OS X:

Expand Down
36 changes: 26 additions & 10 deletions lib/generatePreview.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const spawn = require("cross-spawn")
const promiseWaterfall = require("promise.waterfall")
const gm = require("gm").subClass({ imageMagick: true })
const compress = require("./operations/compress")
const crop = require("./operations/crop")
const resize = require("./operations/resize")
Expand All @@ -13,15 +13,31 @@ const allTasks = {
if (!Object.entries)
Object.entries = obj => Object.keys(obj).map(prop => [prop, obj[prop]])

module.exports = (body, options = {}) => {
const flattened = gm(body, "test.pdf[0]").flatten()

function toBuffer(body) {
if (Buffer.isBuffer(body)) return Promise.resolve(body)
return new Promise((resolve, reject) => {
const chunks = []
body.on("data", chunk => chunks.push(chunk))
body.on("end", () => resolve(Buffer.concat(chunks)))
body.on("error", reject)
})
}

const tasks = Object.entries(options).reduce((acc, [operation, params]) => {
acc.push(data => allTasks[operation](data, params))
return acc
}, [() => Promise.resolve(flattened)])
module.exports = (body, options = {}) => {
return toBuffer(body).then(buffer => {
const tasks = Object.entries(options).reduce((acc, [operation, params]) => {
acc.push(state => allTasks[operation](state, params))
return acc
}, [() => Promise.resolve({ args: [], buffer })])

return promiseWaterfall(tasks)
.then(result => result.stream("jpg"))
return promiseWaterfall(tasks).then(({ args, buffer }) => {
return new Promise((resolve, reject) => {
const proc = spawn("convert", ["pdf:-[0]", "-flatten", ...args, "jpg:-"])
proc.on("error", reject)
proc.stdin.on("error", reject)
proc.stdin.end(buffer)
resolve(proc.stdout)
})
})
})
}
9 changes: 3 additions & 6 deletions lib/operations/compress.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
module.exports = (input, params) =>
new Promise (resolve => {
module.exports = ({ args, buffer }, params) =>
new Promise(resolve => {
const { type, quality } = Object.assign({ type: "JPEG", quality: 70 }, params)
resolve(input
.compress(type)
.quality(quality)
)
resolve({ args: [...args, "-compress", type, "-quality", String(quality)], buffer })
})
39 changes: 30 additions & 9 deletions lib/operations/crop.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,30 @@
module.exports = (input, { width: maxWidth, height: maxHeight, x, y, ratio }) =>
const spawn = require("cross-spawn")

function getImageSize(buffer) {
return new Promise((resolve, reject) => {
const proc = spawn("convert", ["pdf:-[0]", "-flatten", "-format", "%wx%h", "info:"])
let output = ""
proc.stdout.on("data", data => { output += data.toString() })
proc.stderr.on("data", () => {})
proc.on("close", code => {
if (code !== 0) return reject(new Error(`convert exited with code ${code}`))
const match = output.match(/(\d+)x(\d+)/)
if (!match) return reject(new Error("Could not parse image size"))
resolve({ width: parseInt(match[1]), height: parseInt(match[2]) })
})
proc.on("error", reject)
proc.stdin.end(buffer)
})
}

module.exports = ({ args, buffer }, { width: maxWidth, height: maxHeight, x, y, ratio }) =>
new Promise((resolve, reject) => {
if (ratio) {
input
.size({ bufferStream: true }, (err, size) => {
if (err) return reject(err)
const { width, height } = size
const ratio = height / width
getImageSize(buffer)
.then(({ width, height }) => {
const imgRatio = height / width
const finalRatio = maxHeight / maxWidth
const [finalWidth, finalHeight] = ratio > finalRatio ?
const [finalWidth, finalHeight] = imgRatio > finalRatio ?
[width, width * finalRatio] :
[height / finalRatio, height]
const [cropX, cropY] = [
Expand Down Expand Up @@ -47,9 +64,13 @@ module.exports = (input, { width: maxWidth, height: maxHeight, x, y, ratio }) =>
return (originalDimension - finalDimension) / 2
})

resolve(input.crop(finalWidth, finalHeight, cropX, cropY))
resolve({
args: [...args, "-crop", `${Math.round(finalWidth)}x${Math.round(finalHeight)}+${Math.round(cropX)}+${Math.round(cropY)}`],
buffer
})
})
.catch(reject)
}
else
resolve(input.crop(maxWidth, maxHeight, x, y))
resolve({ args: [...args, "-crop", `${maxWidth}x${maxHeight}+${x || 0}+${y || 0}`], buffer })
})
4 changes: 2 additions & 2 deletions lib/operations/resize.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module.exports = (input, params) =>
module.exports = ({ args, buffer }, params) =>
new Promise(resolve => {
const { width, height } = Object.assign({ width: 200, height: 200 }, params)
resolve(input.resize(width, height))
resolve({ args: [...args, "-resize", `${width}x${height}`], buffer })
})
Loading