gsx-pdf-optimize
Version:
Optimize PDFs with Ghostscript command
198 lines (174 loc) • 5.08 kB
JavaScript
const exec = require("child_process").exec;
const minimist = require("minimist");
const path = require("path");
const fs = require("fs");
const dateformat = require("dateformat");
const { promisify } = require("util");
const mkdirp = promisify(require("mkdirp"));
module.exports = optimize;
module.exports.build = build;
module.exports.spawn = spawn;
function spawn(opt = {}) {
const [command, args] = build(opt);
return exec(command, args, opt);
}
async function optimize(opt = {}) {
const { quiet = true, stdout = false, stdin = false } = opt;
let input = opt.input || "";
let output = opt.output || "";
if (!stdin && !input) throw new Error(`No input specified`);
if (input) input = path.resolve(input);
if (!output && !stdout) {
if (input) {
const ext = path.extname(input);
const name = path.basename(input, ext);
const dir = path.dirname(input);
const newFile = `${name}-optimized-${getTimeStamp()}${ext}`;
output = path.resolve(dir, newFile);
} else {
output = `optimized-${getTimeStamp()}.pdf`;
}
}
if (output) output = path.resolve(output);
if (input && input === output) {
throw new Error(`Input and output should be different`);
}
if (!stdin && input && !fs.existsSync(input)) {
throw new Error("Input file does not exist");
}
if (output && !stdout) {
const dir = path.dirname(output);
await mkdirp(dir);
}
const [command, args] = build({
...opt,
input,
output,
stdout,
quiet,
});
if (!quiet) {
console.log(`${command} ${args.join(" ")}`);
}
const str = [command, ...args].join(" ");
return new Promise((resolve, reject) => {
exec(
str,
{
stdio: "inherit",
},
(err, stdout, stderr) => {
if (err) reject(err);
else resolve({ stdout, stderr });
}
);
});
}
function build(opt = {}) {
const command = process.env.GSX_OPTIMIZE_COMMAND || opt.command || "gsx";
const {
input = "-",
output = "-",
compatibilityLevel = 1.5,
compressFonts = true,
embedAllFonts = true,
subsetFonts = true,
dpi = 300,
quiet = true,
preset = "screen",
colorConversionStrategy = "RGB",
} = opt;
const stdin = input === "-" || Boolean(opt.stdin);
const stdout = output === "-" || Boolean(opt.stdout);
const outputFile = stdout ? "-" : JSON.stringify(output);
const args = [
"-sDEVICE=pdfwrite",
`-dPDFSETTINGS=/${preset}`,
"-dNOPAUSE",
quiet ? "-dQUIET" : "",
"-dBATCH",
`-dCompatibilityLevel=${String(compatibilityLevel)}`,
// font settings
`-dSubsetFonts=${subsetFonts}`,
`-dCompressFonts=${compressFonts}`,
`-dEmbedAllFonts=${embedAllFonts}`,
// color format
"-sProcessColorModel=DeviceRGB",
`-sColorConversionStrategy=${colorConversionStrategy}`,
`-sColorConversionStrategyForImages=${colorConversionStrategy}`,
"-dConvertCMYKImagesToRGB=true",
// image resampling
"-dDetectDuplicateImages=true",
"-dColorImageDownsampleType=/Bicubic",
`-dColorImageResolution=${dpi}`,
"-dGrayImageDownsampleType=/Bicubic",
`-dGrayImageResolution=${dpi}`,
"-dMonoImageDownsampleType=/Bicubic",
`-dMonoImageResolution=${dpi}`,
"-dDownsampleColorImages=true",
// other overrides
"-dDoThumbnails=false",
"-dCreateJobTicket=false",
"-dPreserveEPSInfo=false",
"-dPreserveOPIComments=false",
"-dPreserveOverprintSettings=false",
"-dUCRandBGInfo=/Remove",
`-sOutputFile=${outputFile}`,
stdin ? "-" : JSON.stringify(input),
].filter(Boolean);
return [command, args];
}
function getTimeStamp() {
const dateFormatStr = `yyyy.mm.dd-HH.MM.ss`;
return dateformat(new Date(), dateFormatStr);
}
module.exports.parseArgs = parseArgs;
function parseArgs(args = []) {
const argv = minimist(args, {
string: ["command", "preset", "colorConversionStrategy"],
boolean: ["quiet", "help", "stdout"],
default: {
quiet: true,
},
alias: {
dpi: "D",
preset: "P",
subsetFonts: "subset-fonts",
compressFonts: "compress-fonts",
compatibilityLevel: "compatibility-level",
embedAllFonts: "embed-all-fonts",
colorConversionStrategy: "color-conversion-strategy",
},
});
const input = argv._[0];
const output = argv._[1];
delete argv._;
return {
...argv,
input,
output,
};
}
if (!module.parent) {
(async () => {
const opt = parseArgs(process.argv.slice(2));
if (opt.help) {
console.log(`
gsx-pdf-optimize input.pdf [output.pdf] [opts]
Options:
--preset, -P one of: screen (default), printer, prepress, ebook
--dpi, -D image resampling resolution in DPI, default 300
--quiet enable or disable logging (default quiet=true)
--command the Ghostscript command to use, default gsx
--stdout pipe to stdout instead of writing to a file
`);
return;
}
try {
await optimize(opt);
} catch (err) {
console.error(err);
}
})();
}