Commit 8d88a441 authored by Fred Chasen's avatar Fred Chasen

Refactored Renderer, moved Hyphenate to Hyphenator, Added first tests

parent a51e3e0d
......@@ -21,7 +21,7 @@ vivliostyle-electron ./path/to/index.html -o result.pdf
-V, --version output the version number
-i, --inputs [inputs] Inputs
-o, --output [output] Output
-d, --debug Debug
-d, --debug Show Electron Window to Debug
-l, --landscape Landscape printing
-s, --page-size [size] Print to Page Size [size]
-w, --width [size] Print to Page Width [width]
......@@ -42,7 +42,7 @@ Pass the abbreviation a language code (such as `en-us` or `de`) when calling the
```
vivliostyle-electron ./path/to/index.html --hyphenate hyphenation.en-us
vivliostyle-electron ./path/to/index.html --hyphenate en-us --output
```
## Running on Linux
......@@ -72,5 +72,12 @@ gulp watch
Then you can run
```
DEBUG=* vivliostyle-electron ./path/to/index.html
DEBUG=* vivliostyle-electron ./path/to/index.html --output
```
To display the output in the browser window before printing,
instead of outputting the file add the `--debug` flag.
```
vivliostyle-electron ./path/to/index.html --debug
```
#!/usr/bin/env node
var Renderer = require("./lib/index.js");
var Hyphenate = require("./lib/hyphenate.js");
var Hyphenate = require("./lib/hyphenator.js");
var program = require('commander');
var path = require('path');
var fs = require('fs');
var replaceExt = require('replace-ext');
var temp = require("temp").track();
program
.version('0.0.1')
......@@ -14,8 +17,8 @@ program
.option('-d, --debug', 'Debug')
.option('-l, --landscape', 'Landscape printing', false)
.option('-s, --page-size [size]', 'Print to Page Size [size]')
.option('-w, --width [size]', 'Print to Page Width [width]')
.option('-h --height [size]', 'Print to Page Height [weight]')
.option('-w, --width [size]', 'Print to Page Width [width] in MM')
.option('-h --height [size]', 'Print to Page Height [weight] in MM')
.option('-m, --page-margin [margin]', 'Print with margin [margin]')
.option('-n, --hyphenate [lang]', 'Hyphenate with language [language], defaults to "en-us"')
.option('-hi, --hypher_ignore [str]', 'Ignore passed element selectors, such as ".class_to_ignore, h1"')
......@@ -27,23 +30,40 @@ program
var input = program.inputs || program.args[0];
if (!input) {
return console.error("You must include an input path");
console.error("You must include an input path");
return process.exit(1);
}
var dir = process.cwd();
var relativePath = path.resolve(dir, input);
if (program.debug) {
process.env.ELECTRON_HTML_TO_DEBUGGING = true;
}
var output = program.output ? path.resolve(dir, program.output) : './' + path.basename(input).replace('.xhtml', '.pdf').replace('.html', '.pdf');
var renderer = new Renderer(program);
var render;
var dir = process.cwd();
var relativePath = path.resolve(dir, input);
var output;
var tmpPath;
var hyphenate;
var hyphenator;
var hyphenateOptions;
var tmpPath;
if (path.extname(relativePath) != ('.html' || '.xhtml')) {
return console.error("Must pass a html or xhtml file as input");
console.error("Must pass a html or xhtml file as input");
return process.exit(1);
}
try {
fs.accessSync(relativePath, fs.F_OK);
} catch (e) {
console.error("Input cannot be found", e);
return process.exit(1);
}
if (program.output) {
output = path.resolve(dir, program.output);
} else if (typeof(program.output) != "undefined") {
output = './' + replaceExt(path.basename(input), '.pdf');
}
if (program.hyphenate) {
......@@ -52,32 +72,35 @@ if (program.hyphenate) {
only: program.hypher_only || undefined,
encoding: program.encoding || undefined
}
tmpPath = relativePath.replace(".xhtml", ".hyphenated.xhtml").replace(".html", ".hyphenated.html");
hyphenate = new Hyphenate(program.hyphenate);
hyphenate.process(relativePath, tmpPath, hyphenateOptions);
}
// tmpPath = replaceExt(relativePath, ".hyphenated.html");
tmpPath = temp.openSync({suffix: '.html'});
renderer = new Renderer(tmpPath || input, output, {
pageSize: program.pageSize,
margin: program.pageMargin,
landscape: program.landscape,
width: program.width,
height: program.height,
debug: program.debug,
timeout: program.timeout
}).then(function (result) {
if (!program.debug && tmpPath && tmpPath != relativePath) {
fs.unlinkSync(tmpPath);
// Create a new Hyphenator, with passed language
hyphenator = new Hyphenator(program.hyphenate);
hyphenator.process(relativePath, tmpPath, hyphenateOptions);
if (program.debug && tmpPath) {
conole.log("Hyphenated at: ", tmpPath);
}
}).catch(function (err) {
console.error(err);
})
// if (program.display) {
// var childProcess = require("child_process");
// var electron = require('electron-prebuilt');
// var cwd = (process && process.cwd()) || __dirname;
//
// child = childProcess.spawn(electron, ["--path", "visual.js", "--url", ( tmpPath || relativePath)], { _showOutput: true });
// }
}
renderer.process(tmpPath || input, output)
.then(function (result) {
var msg = "Printed";
if (result) {
msg += " " + result.numberOfPages + " pages";
}
if (output) {
msg += " to " + ouput;
}
console.log(msg);
}, function (err) {
console.error(err);
// temp.cleanupSync();
});
......@@ -20,12 +20,13 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
var IGNORE = 'head, code, pre, script, style, [class^="pull-"], [class^="push-"], .small-caps';
function Hyphenate(lang) {
var pattern = require("hyphenation." + lang);
this.h = new _hypher2.default(pattern);
function Hyphenator(lang) {
this.lang = lang || "en-us";
this.pattern = require("hyphenation." + this.lang);
this.h = new _hypher2.default(this.pattern);
}
Hyphenate.prototype.hyphenateText = function (text) {
Hyphenator.prototype.hyphenateText = function (text) {
var _this = this;
// split the text in html entity and not entity
......@@ -35,20 +36,45 @@ Hyphenate.prototype.hyphenateText = function (text) {
}).join('');
};
Hyphenate.prototype.process = function (input, output, encoding, options) {
Hyphenator.prototype.process = function (input, output, encoding, options) {
var contents = _fs2.default.readFileSync(input, options && options.encoding || 'utf8');
var hyphenated = this.eachTextNode(contents, this.hyphenateText.bind(this), options);
var hyphenated = this.walk(contents, this.hyphenateText.bind(this), options);
if (output) {
_fs2.default.writeFileSync(output, hyphenated, encoding || 'utf8');
this.ouput(contents, output, encoding);
}
return hyphenated;
};
Hyphenate.prototype.eachTextNode = function (html, doThis, options) {
Hyphenator.prototype.output = function (contents, outputPath, encoding) {
_fs2.default.writeFileSync(outputPath, contents, encoding || 'utf8', encoding);
};
Hyphenator.prototype.findTextNodes = function (node, doThis, ignore) {
var hyphenator = this;
if ($(node).is(ignore)) return false;
$(node).contents().each(function () {
var childNode = $(this)[0];
// We've made it to a text node!
// apply the function which transforms
// its text content (childNode.data)
if (childNode.type === 'text' || !childNode.type) {
childNode.data = doThis(childNode.data);
} else {
hyphenator.findTextNodes(childNode, doThis, hyphenator);
}
});
};
Hyphenator.prototype.walk = function (html, doThis, options) {
var ignore = IGNORE;
var only = options && options.only || ':root';
var hyphenator = this;
if (options && options.ignore) ignore += ', ' + options.ignore;
......@@ -58,33 +84,14 @@ Hyphenate.prototype.eachTextNode = function (html, doThis, options) {
});
var processedText = $(only).each(function () {
findTextNodes(this);
hyphenator.findTextNodes(this, doThis, ignore).bind(hyphenator);
});
function findTextNodes(node) {
if ($(node).is(ignore)) return false;
$(node).contents().each(function () {
var childNode = $(this)[0];
// We've made it to a text node!
// apply the function which transforms
// its text content (childNode.data)
if (childNode.type === 'text' || !childNode.type) {
childNode.data = doThis(childNode.data);
} else {
findTextNodes(childNode, doThis);
}
});
}
return $.html({
decodeEntities: false,
xmlMode: true
});
};
exports.default = Hyphenate;
exports.default = Hyphenator;
module.exports = exports['default'];
\ No newline at end of file
......@@ -16,44 +16,97 @@ var _path = require('path');
var _path2 = _interopRequireDefault(_path);
var _debug = require('debug');
var _debug2 = _interopRequireDefault(_debug);
var _package = require('../package.json');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function Renderer(inputPath, outputPath, _options) {
var log = (0, _debug2.default)(_package.name + ':renderer');
var MICRON_TO_MM = 1000;
var conversion = (0, _electronHtmlTo2.default)({
function Renderer(options) {
this.settings = this.determineSettings(options);
this.converter = (0, _electronHtmlTo2.default)({
converterPath: _electronHtmlTo2.default.converters.PDF,
allowLocalFilesAccess: true,
timeout: 60000 * 5, // Allow five minutes to process
strategy: 'dedicated-process' // 'electron-ipc | electron-server | dedicated-process'
timeout: this.settings.timeout, // Allow five minutes to process
strategy: this.settings.strategy // 'electron-ipc | electron-server | dedicated-process'
});
}
var directoryPath = __dirname.replace('lib', '');
var basePath = directoryPath + 'renderer/';
var relativePath = _path2.default.relative(basePath, inputPath);
Renderer.prototype.determineSettings = function (_options) {
var options = _options || {};
var settings = {
return {
pageSize: options.pageSize || 'A4',
pageMargin: options.pageMargin || 0,
landscape: options.landscape || false,
width: options.width,
height: options.height,
debug: options.debug || false,
timeout: options.timeout || undefined
timeout: options.timeout || 60000 * 5,
strategy: options.strategy || 'dedicated-process'
};
};
var url = 'file://' + basePath + "renderer.html?url=" + encodeURIComponent(relativePath);
Renderer.prototype.process = function (inputPath, outputPath, options) {
var _this = this;
var size = settings.pageSize;
var settings = options ? this.determineSettings(options) : this.settings;
var result = this.convert(inputPath, settings);
result.then(function (result) {
log("Printed", result.numberOfPages, 'pages to', outputPath);
_this.converter.kill();
if (outputPath) {
_this.output(result.stream, outputPath);
}
}, function (err) {
log('An error has ocurred in in converting [%s]: %s', inputPath, err.message, err.stack);
_this.converter.kill();
});
return result;
};
Renderer.prototype.getRendererPath = function () {
var directoryPath = __dirname.replace('lib', '');
return directoryPath + 'renderer/';
};
Renderer.prototype.getRelativePath = function (basePath, inputPath) {
var relativePath = _path2.default.relative(basePath, inputPath);
return relativePath;
};
Renderer.prototype.encodeUrl = function (inputPath) {
var rendererPath = this.getRendererPath();
var relativePath = this.getRelativePath(rendererPath, inputPath);
var url = 'file://' + rendererPath + "renderer.html?url=" + encodeURIComponent(relativePath);
return url;
};
Renderer.prototype.settingsToUriComponent = function (settings) {
var url = '';
if (settings.width && settings.height) {
url += '&width=' + settings.width;
url += '&height=' + settings.height;
size = {
width: parseFloat(settings.width) * 25400,
height: parseFloat(settings.height) * 25400
};
} else {
url += '&pageSize=' + settings.pageSize;
}
......@@ -66,43 +119,50 @@ function Renderer(inputPath, outputPath, _options) {
url += '&debug=' + settings.debug;
}
if (settings.timeout) {
conversion.timeout = parseInt(settings.timeout);
return url;
};
Renderer.prototype.getSize = function (settings) {
var size = settings.pageSize;
if (settings.width && settings.height) {
size = {
width: parseFloat(settings.width) * MICRON_TO_MM,
height: parseFloat(settings.height) * MICRON_TO_MM
};
}
console.log("Processing", inputPath, url);
return new Promise(function (resolve, reject) {
return size;
};
Renderer.prototype.convert = function (inputPath, settings) {
var url = this.encodeUrl(inputPath, settings);
var fullUrl = url + this.settingsToUriComponent(settings);
conversion({
url: url,
return new Promise(function (resolve, reject) {
this.converter({
url: fullUrl,
waitForJS: true,
pdf: {
marginsType: settings.pageMargin,
pageSize: {
width: 9 * 25400,
height: 2 * 25400
},
pageSize: this.getSize(settings),
printBackground: true,
landscape: settings.landscape
}
}, function (err, result) {
if (err) {
conversion.kill();
return reject(err);
}
console.log("Printed", result.numberOfPages, 'pages to', outputPath);
if (outputPath) {
result.stream.pipe(_fs2.default.createWriteStream(outputPath));
}
conversion.kill();
resolve(result);
});
});
}
}.bind(this));
};
Renderer.prototype.output = function (stream, outputPath) {
stream.pipe(_fs2.default.createWriteStream(outputPath));
};
exports.default = Renderer;
module.exports = exports['default'];
\ No newline at end of file
......@@ -17,8 +17,7 @@
<style id="vivliostyle-page-rules"></style>
<style>
/* Uncomment for visual check of renderered pages */
/*
#vivliostyle-viewer-viewport [data-vivliostyle-outer-zoom-box],
#vivliostyle-viewer-viewport [data-vivliostyle-spread-container] {
width: auto !important;
......@@ -26,7 +25,7 @@
#vivliostyle-viewer-viewport [data-vivliostyle-page-container] {
display: block !important;
}*/
}
</style>
</head>
<body data-vivliostyle-viewer-status="loading">
......
......@@ -46,7 +46,6 @@ if (settings.debug) {
viewer = new vivliostyle.viewer.Viewer(viewerSettings, viewerOptions);
viewer.addListener("loaded", function() {
console.log("Loaded");
window.ELECTRON_HTML_TO_READY = true; // this will start the conversion
});
......
......@@ -4,12 +4,13 @@ import fs from 'fs';
const IGNORE = 'head, code, pre, script, style, [class^="pull-"], [class^="push-"], .small-caps';
function Hyphenate(lang) {
var pattern = require("hyphenation."+lang);
this.h = new hypher(pattern);
function Hyphenator(lang) {
this.lang = lang || "en-us";
this.pattern = require("hyphenation."+this.lang);
this.h = new hypher(this.pattern);
}
Hyphenate.prototype.hyphenateText = function (text) {
Hyphenator.prototype.hyphenateText = function (text) {
// split the text in html entity and not entity
return text.split(/(!?&[a-zA-Z]*;)/).map((textPart) => {
// immediately return html entities and hyphenate everything else
......@@ -19,20 +20,47 @@ Hyphenate.prototype.hyphenateText = function (text) {
}).join('');
};
Hyphenate.prototype.process = function (input, output, encoding, options) {
Hyphenator.prototype.process = function (input, output, encoding, options) {
var contents = fs.readFileSync(input, (options && options.encoding) || 'utf8');
var hyphenated = this.eachTextNode(contents, this.hyphenateText.bind(this), options);
var hyphenated = this.walk(contents, this.hyphenateText.bind(this), options);
if (output) {
fs.writeFileSync(output, hyphenated, encoding || 'utf8');
this.ouput(contents, output, encoding);
}
return hyphenated;
};
Hyphenate.prototype.eachTextNode = function(html, doThis, options) {
Hyphenator.prototype.output = function (contents, outputPath, encoding) {
fs.writeFileSync(outputPath, contents, encoding || 'utf8', encoding);
};
Hyphenator.prototype.findTextNodes = function (node, doThis, ignore) {
var hyphenator = this;
if ($(node).is(ignore)) return false;
$(node).contents().each(function(){
var childNode = $(this)[0];
// We've made it to a text node!
// apply the function which transforms
// its text content (childNode.data)
if (childNode.type === 'text' || !childNode.type) {
childNode.data = doThis(childNode.data);
} else {
hyphenator.findTextNodes(childNode, doThis, hyphenator);
}
});
}
Hyphenator.prototype.walk = function(html, doThis, options) {
var ignore = IGNORE;
var only = (options && options.only) || ':root';
var hyphenator = this;
if (options && options.ignore) ignore += ', ' + options.ignore;
......@@ -41,27 +69,9 @@ Hyphenate.prototype.eachTextNode = function(html, doThis, options) {
xmlMode: true
});
var processedText = $(only).each(function(){findTextNodes(this);});
function findTextNodes(node) {
if ($(node).is(ignore)) return false;
$(node).contents().each(function(){
var childNode = $(this)[0];
// We've made it to a text node!
// apply the function which transforms
// its text content (childNode.data)
if (childNode.type === 'text' || !childNode.type) {
childNode.data = doThis(childNode.data);
} else {
findTextNodes(childNode, doThis);
}
});
}
var processedText = $(only).each(function(){
hyphenator.findTextNodes(this, doThis, ignore).bind(hyphenator);
});
return $.html({
decodeEntities: false,
......@@ -69,4 +79,4 @@ Hyphenate.prototype.eachTextNode = function(html, doThis, options) {
});
};
export default Hyphenate;
export default Hyphenator;
import convertFactory from 'electron-html-to';
import fs from 'fs';
import path from 'path';
import debug from 'debug';
import { name as pkgName } from '../package.json';
function Renderer(inputPath, outputPath, _options) {
const log = debug(`${pkgName}:renderer`);
var conversion = convertFactory({
const MICRON_TO_MM = 1000;
function Renderer(options) {
this.settings = this.determineSettings(options);
this.converter = convertFactory({
converterPath: convertFactory.converters.PDF,
allowLocalFilesAccess: true,
timeout: 60000 * 5, // Allow five minutes to process
strategy: 'dedicated-process' // 'electron-ipc | electron-server | dedicated-process'
timeout: this.settings.timeout, // Allow five minutes to process
strategy: this.settings.strategy // 'electron-ipc | electron-server | dedicated-process'
});
}
var directoryPath = __dirname.replace('lib', '');
var basePath = directoryPath + 'renderer/';
var relativePath = path.relative(basePath, inputPath);
Renderer.prototype.determineSettings = function (_options) {
var options = _options || {};
var settings = {
return {
pageSize: options.pageSize || 'A4',
pageMargin: options.pageMargin || 0,
landscape: options.landscape || false,
width: options.width,
height: options.height,
debug: options.debug || false,
timeout: options.timeout || undefined
timeout: options.timeout || 60000 * 5,
strategy: options.strategy || 'dedicated-process'
};
};
var url = 'file://' + basePath + "renderer.html?url=" + encodeURIComponent(relativePath);
Renderer.prototype.process = function (inputPath, outputPath, options) {
var settings = options ? this.determineSettings(options) : this.settings;
var size = settings.pageSize;
var result = this.convert(inputPath, settings)
result.then((result) => {
log("Printed", result.numberOfPages, 'pages to', outputPath);
this.converter.kill();
if (outputPath) {
this.output(result.stream, outputPath);
}
}, (err) => {
log('An error has ocurred in in converting [%s]: %s', inputPath, err.message, err.stack);
this.converter.kill();
});
return result;
};
Renderer.prototype.getRendererPath = function () {
var directoryPath = __dirname.replace('lib', '');
return directoryPath + 'renderer/';
};
Renderer.prototype.getRelativePath = function (basePath, inputPath) {
var relativePath = path.relative(basePath, inputPath);
return relativePath;
};
Renderer.prototype.encodeUrl = function (inputPath) {
var rendererPath = this.getRendererPath();
var relativePath = this.getRelativePath(rendererPath, inputPath);
var url = 'file://' + rendererPath + "renderer.html?url=" + encodeURIComponent(relativePath);
return url;
}
Renderer.prototype.settingsToUriComponent = function (settings) {
var url = '';
if (settings.width && settings.height) {
url += '&width=' + settings.width;
url += '&height=' + settings.height;
size = {
width: parseFloat(settings.width) * 25400,
height: parseFloat(settings.height) * 25400
};
} else {
url += '&pageSize=' + settings.pageSize;
}
......@@ -50,46 +101,51 @@ function Renderer(inputPath, outputPath, _options) {
url += '&debug=' + settings.debug;
}
if (settings.timeout) {
conversion.timeout = parseInt(settings.timeout);
return url;
};