Commit 95dbed66 authored by Fred Chasen's avatar Fred Chasen

Merge branch 'refactor' into 'master'

Refactored Renderer, moved Hyphenate to Hyphenator, Added first tests



See merge request !1
parents a51e3e0d 8d88a441
......@@ -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;
};
Renderer.prototype.getSize = function(settings) {
var size = settings.pageSize;
if (settings.width && settings.height) {
size = {