@junjie_npm/prince
Version:
Node API for executing XML/HTML to PDF renderer PrinceXML via prince(1) CLI
365 lines (335 loc) • 12.8 kB
JavaScript
/*
** node-prince -- Node API for executing PrinceXML via prince(1) CLI
** Copyright (c) 2014-2021 Dr. Ralf S. Engelschall <rse@engelschall.com>
**
** Permission is hereby granted, free of charge, to any person obtaining
** a copy of this software and associated documentation files (the
** "Software"), to deal in the Software without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Software, and to
** permit persons to whom the Software is furnished to do so, subject to
** the following conditions:
**
** The above copyright notice and this permission notice shall be included
** in all copies or substantial portions of the Software.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* global __dirname: false */
/* global process: false */
/* global module: false */
/* global require: false */
/*
* prince-api.js: Node run-time API
*/
/* core requirements */
var child_process = require("child_process");
var fs = require("fs");
var path = require("path");
var util = require("util");
/* extra requirements */
var promise = require("promise");
var _ = require("lodash");
/* the officially support options of prince(1) */
var princeOptions = {
"help": false,
"version": false,
"credits": false,
"verbose": false,
"debug": false,
"log": true,
"no-warn-css": false,
"no-warn-css-unknown": false,
"no-warn-css-unsupported": false,
"structured-log": true,
"input": true,
"input-list": true,
"baseurl": true,
"remap": true,
"fileroot": true,
"xinclude": false,
"xml-external-entities": false,
"no-local-files": false,
"no-network": false,
"auth-user": true,
"auth-password": true,
"auth-server": true,
"auth-scheme": true,
"auth-method": true,
"auth": true,
"no-auth-preemptive": false,
"http-proxy": true,
"http-timeout": true,
"cookie": true,
"cookiejar": true,
"ssl-cacert": true,
"ssl-capath": true,
"ssl-cert": true,
"ssl-cert-type": true,
"ssl-key": true,
"ssl-key-type": true,
"ssl-key-password": true,
"ssl-version": true,
"insecure": false,
"no-parallel-downloads": false,
"javascript": false,
"script": true,
"style": true,
"media": true,
"page-size": true,
"page-margin": true,
"no-author-style": false,
"no-default-style": false,
"output": true,
"pdf-profile": true,
"pdf-xmp": true,
"pdf-output-intent": true,
"pdf-lang": true,
"attach": true,
"tagged-pdf": false,
"no-artificial-fonts": false,
"no-embed-fonts": false,
"no-subset-fonts": false,
"force-identity-encoding": false,
"no-compress": false,
"no-object-streams": false,
"convert-colors": false,
"fallback-cmyk-profile": true,
"pdf-title": true,
"pdf-subject": true,
"pdf-author": true,
"pdf-keywords": true,
"pdf-creator": true,
"encrypt": false,
"key-bits": true,
"user-password": true,
"owner-password": true,
"disallow-print": false,
"disallow-copy": false,
"disallow-annotate": false,
"disallow-modify": false,
"raster-output": true,
"raster-format": true,
"raster-pages": true,
"raster-dpi": true,
"raster-background": true,
"raster-threads": true,
"scanfonts": false,
"control": false
};
/* API constructor */
function Prince (options) {
/* optionally on-the-fly generate an instance */
if (!(this instanceof Prince))
return new Prince(options);
/* create default configuration */
this.config = {
binary: "prince",
prefix: "",
license: "",
timeout: 10 * 1000,
maxbuffer: 10 * 1024 * 1024,
cwd: ".",
option: {},
inputs: [],
cookies: [],
output: ""
};
/* override defaults with more reasonable information about environment */
var install = [
{ basedir: "prince/lib/prince", binary: "bin/prince" },
{ basedir: "prince\\program files\\Prince\\Engine", binary: "bin\\prince.exe" }
];
var basedir;
var binary;
for (var i = 0; i < install.length; i++) {
basedir = path.resolve(path.join(__dirname, install[i].basedir));
binary = path.join(basedir, install[i].binary);
if (fs.existsSync(binary)) {
this.binary(binary);
this.prefix(basedir);
break;
}
}
/* allow caller to override defaults */
if (typeof options === "object") {
if (typeof options.binary !== "undefined")
this.binary(options.binary);
if (typeof options.prefix !== "undefined")
this.prefix(options.prefix);
if (typeof options.inputs !== "undefined")
this.inputs(options.inputs);
if (typeof options.cookies !== "undefined")
this.cookies(options.cookies);
if (typeof options.output !== "undefined")
this.output(options.output);
}
return this;
}
/* set path to CLI binary */
Prince.prototype.binary = function (binary) {
if (arguments.length !== 1)
throw new Error("Prince#binary: invalid number of arguments");
this.config.binary = binary;
this.config.prefix = "";
return this;
};
/* set path to installation tree */
Prince.prototype.prefix = function (prefix) {
if (arguments.length !== 1)
throw new Error("Prince#prefix: invalid number of arguments");
this.config.prefix = prefix;
return this;
};
/* set path to license file */
Prince.prototype.license = function (filename) {
if (arguments.length !== 1)
throw new Error("Prince#license: invalid number of arguments");
this.config.license = filename;
return this;
};
/* set timeout for CLI execution */
Prince.prototype.timeout = function (timeout) {
if (arguments.length !== 1)
throw new Error("Prince#timeout: invalid number of arguments");
this.config.timeout = timeout;
return this;
};
/* set maxmimum stdout/stderr buffer for CLI execution */
Prince.prototype.maxbuffer = function (maxbuffer) {
if (arguments.length !== 1)
throw new Error("Prince#maxbuffer: invalid number of arguments");
this.config.maxbuffer = maxbuffer;
return this;
};
/* set current working directory for CLI execution */
Prince.prototype.cwd = function (cwd) {
if (arguments.length !== 1)
throw new Error("Prince#cwd: invalid number of arguments");
this.config.cwd = cwd;
return this;
};
/* set input file(s) */
Prince.prototype.inputs = function (inputs) {
if (arguments.length !== 1)
throw new Error("Prince#inputs: invalid number of arguments");
this.config.inputs = util.isArray(inputs) ? inputs : [ inputs ];
return this;
};
/* set cookie(s) */
Prince.prototype.cookies = function (cookies) {
if (arguments.length !== 1)
throw new Error("Prince#cookies: invalid number of arguments");
this.config.cookies = util.isArray(cookies) ? cookies : [ cookies ];
return this;
};
/* set output file */
Prince.prototype.output = function (output) {
if (arguments.length !== 1)
throw new Error("Prince#output: invalid number of arguments");
this.config.output = output;
return this;
};
/* set CLI options */
Prince.prototype.option = function (name, value, forced) {
if (arguments.length < 1 || arguments.length > 3)
throw new Error("Prince#option: invalid number of arguments");
if (arguments.length < 2)
value = true;
if (arguments.length < 3)
forced = false;
if (!forced && typeof princeOptions[name] === "undefined")
throw new Error("Prince#option: invalid prince(1) option: \"" + name + "\" (but can be forced)");
if (!forced && princeOptions[name] === true && arguments.length === 1)
throw new Error("Prince#option: prince(1) option \"" + name + "\" required argument");
this.config.option[name] = value;
return this;
};
/* execute the CLI binary */
Prince.prototype._execute = function (method, args) {
/* determine path to prince(1) binary */
var prog = this.config.binary;
if (!fs.existsSync(prog)) {
var findInPath = function (name) {
var p = process.env.PATH.split(path.delimiter).map(function(item) {
return path.join(item, name);
});
for (var i = 0, len = p.length; i < len; i++)
if (fs.existsSync(p[i]))
return p[i];
return undefined;
};
prog = findInPath(prog);
if (typeof prog === "undefined")
throw new Error("Prince#" + method + ": cannot resolve binary \"" +
this.config.binary + "\" to a filesystem path");
}
/* return promise for executing CLI */
var self = this;
return new promise(function (resolve, reject) {
try {
var options = {};
options.timeout = self.config.timeout;
options.maxBuffer = self.config.maxbuffer;
options.cwd = self.config.cwd;
options.encoding = "buffer";
child_process.execFile(prog, args, options,
function (error, stdout, stderr) {
var m;
if (error === null && (m = stderr.toString().match(/prince:\s+error:\s+([^\n]+)/)))
reject({ error: m[1], stdout: stdout, stderr: stderr });
else if (error !== null)
reject({ error: error, stdout: stdout, stderr: stderr });
else
resolve({ stdout: stdout, stderr: stderr });
}
);
}
catch (exception) {
reject({ error: exception, stdout: "", stderr: "" });
}
});
};
/* execute the CLI binary */
Prince.prototype.execute = function () {
/* determine arguments to prince(1) binary */
var args = [];
if (this.config.prefix !== "") {
args.push("--prefix");
args.push(this.config.prefix);
}
if (this.config.license !== "") {
args.push("--license-file");
args.push(this.config.license);
}
_.forOwn(this.config.option, function (value, name) {
args.push("--" + name);
if (value !== true)
args.push(value);
});
this.config.inputs.forEach(function (input) {
args.push(input);
});
/* supported since Prince 10 */
this.config.cookies.forEach(function (cookie) {
args.push("--cookie");
args.push(cookie);
});
/* required from Prince 11 on, supported since Prince 7 */
if (this.config.output !== "") {
args.push("--output");
args.push(this.config.output);
}
else if (this.config.option["raster-output"] === undefined)
throw new Error("Prince#execute: require either \"output\" or \"raster-output\" options");
/* return promise for executing CLI */
return this._execute("execute", args);
};
/* export API constructor */
module.exports = Prince;