From 084653e382e49279466e2d803c28fb9bad25d42b Mon Sep 17 00:00:00 2001 From: Ben Firshman Date: Sun, 17 Jun 2018 18:00:31 +0300 Subject: [PATCH] Add option to specify external CSS Closes #10 --- bin/engrafo | 7 ++++++- src/converter/index.js | 19 +++++++++++++------ src/converter/latexml.js | 20 ++++++++++++++------ src/converter/postprocessor/css.js | 13 +++++++++++++ src/converter/postprocessor/index.js | 1 + src/converter/postprocessor/utils.js | 14 ++++++++++++++ src/converter/server.js | 5 ++++- 7 files changed, 65 insertions(+), 14 deletions(-) create mode 100644 src/converter/postprocessor/css.js create mode 100644 src/converter/postprocessor/utils.js diff --git a/bin/engrafo b/bin/engrafo index 791afbc2..5e313934 100755 --- a/bin/engrafo +++ b/bin/engrafo @@ -13,6 +13,10 @@ program "--no-post-processing", "Raw LaTeXML output, without filters or postprocessors (for debugging)" ) + .option( + "--css ", + "URL or path to external CSS to use instead of the default CSS. If passed, the defaut CSS is not copied to the output directory." + ) .parse(process.argv); if (program.args.length != 1) { @@ -28,7 +32,8 @@ var output = var options = { input: input, output: output, - postProcessing: program.postProcessing + postProcessing: program.postProcessing, + externalCSS: program.css }; converter diff --git a/src/converter/index.js b/src/converter/index.js index 17bf3357..6a1d5ae3 100644 --- a/src/converter/index.js +++ b/src/converter/index.js @@ -7,12 +7,13 @@ const math = require("./math"); const postprocessors = require("./postprocessor"); // Run postprocessing against a string of HTML -function postprocess(htmlString) { +function postprocess(htmlString, options) { var dom = jsdom.jsdom(htmlString, { features: { ProcessExternalResources: false, FetchExternalResources: false } }); // Run all processing on document. + postprocessors.css(dom, options); postprocessors.figures(dom); postprocessors.headings(dom); postprocessors.links(dom); @@ -22,9 +23,9 @@ function postprocess(htmlString) { } // Do all processing on the file that LaTeXML produces -async function processHTML(htmlPath) { +async function processHTML(htmlPath, options) { let htmlString = await fs.readFile(htmlPath, "utf8"); - htmlString = postprocess(htmlString); + htmlString = postprocess(htmlString, options); htmlString = await math.renderMath(htmlString); await fs.writeFile(htmlPath, htmlString); } @@ -32,7 +33,7 @@ async function processHTML(htmlPath) { // Render and postprocess a LaTeX file into outputDir (created if does not // exist). Calls callback with an error on failure or a path to an HTML file // on success. -async function render({ input, output, postProcessing }) { +async function render({ input, output, postProcessing, externalCSS }) { if (postProcessing === undefined) { postProcessing = true; } @@ -41,10 +42,16 @@ async function render({ input, output, postProcessing }) { const texPath = await io.pickLatexFile(inputDir); const outputDir = await io.prepareOutputDirectory(output); + // If there are external assets, don't let LaTeXML copy it to the output + // directory - we will handle it ourselves + // Otherwise, link directly to the built asset. Absolute path, assuming + // latexml is always run in Docker. + const cssPath = externalCSS ? null : "/app/dist/css/index.css"; + console.log(`Rendering tex file ${texPath} to ${outputDir}`); - const htmlPath = await latexml.render({ texPath, outputDir }); + const htmlPath = await latexml.render({ texPath, outputDir, cssPath }); - await processHTML(htmlPath); + await processHTML(htmlPath, { externalCSS }); if (output.startsWith("s3://")) { await io.uploadOutputToS3(outputDir, output); diff --git a/src/converter/latexml.js b/src/converter/latexml.js index 89a1a72e..699f648c 100644 --- a/src/converter/latexml.js +++ b/src/converter/latexml.js @@ -13,12 +13,11 @@ function unlinkIfExists(path) { } } -function createChildProcess({ htmlPath, texPath, outputDir }) { +function createChildProcess({ cssPath, htmlPath, texPath, outputDir }) { // prettier-ignore const latexmlArgs = [ "--format", "html5", "--nodefaultresources", - "--css", "/app/dist/index.css", "--mathtex", "--svg", "--verbose", @@ -27,6 +26,10 @@ function createChildProcess({ htmlPath, texPath, outputDir }) { "--preload", "/usr/src/latexml/lib/LaTeXML/Package/hyperref.sty.ltxml", ]; + if (cssPath) { + latexmlArgs.push("--css", cssPath); + } + latexmlArgs.push(path.basename(texPath)); if (process.env.LATEXML_DOCKER) { @@ -55,14 +58,19 @@ function createChildProcess({ htmlPath, texPath, outputDir }) { } // render a document with latexml -function render({ texPath, outputDir }) { +function render({ texPath, outputDir, cssPath }) { const htmlPath = path.join(outputDir, "index.html"); - const latexmlc = createChildProcess({ texPath, outputDir, htmlPath }); + const latexmlc = createChildProcess({ + texPath, + outputDir, + htmlPath, + cssPath + }); - var stdoutReadline = readline.createInterface({ input: latexmlc.stdout }); + const stdoutReadline = readline.createInterface({ input: latexmlc.stdout }); stdoutReadline.on("line", console.log); - var stderrReadline = readline.createInterface({ input: latexmlc.stderr }); + const stderrReadline = readline.createInterface({ input: latexmlc.stderr }); stderrReadline.on("line", console.error); return new Promise((resolve, reject) => { diff --git a/src/converter/postprocessor/css.js b/src/converter/postprocessor/css.js new file mode 100644 index 00000000..190267fd --- /dev/null +++ b/src/converter/postprocessor/css.js @@ -0,0 +1,13 @@ +const utils = require("./utils"); + +module.exports = function(dom, { externalCSS }) { + if (externalCSS) { + const head = dom.querySelector("head"); + const link = utils.nodeFromString( + dom, + '' + ); + link.setAttribute("href", externalCSS); + head.appendChild(link); + } +}; diff --git a/src/converter/postprocessor/index.js b/src/converter/postprocessor/index.js index f6707606..ae236d54 100644 --- a/src/converter/postprocessor/index.js +++ b/src/converter/postprocessor/index.js @@ -1,4 +1,5 @@ module.exports = { + css: require("./css"), figures: require("./figures"), headings: require("./headings"), links: require("./links"), diff --git a/src/converter/postprocessor/utils.js b/src/converter/postprocessor/utils.js new file mode 100644 index 00000000..06d7001f --- /dev/null +++ b/src/converter/postprocessor/utils.js @@ -0,0 +1,14 @@ +exports.removeAll = function(els) { + Array.from(els).forEach(el => { + el.parentNode.removeChild(el); + }); +}; + +exports.nodeFromString = function(dom, str) { + var div = dom.createElement("div"); + div.innerHTML = str; + if (!div.firstChild) { + throw new Error("Invalid HTML passed to nodeFromString"); + } + return div.firstChild; +}; diff --git a/src/converter/server.js b/src/converter/server.js index 894eba49..d6278c76 100644 --- a/src/converter/server.js +++ b/src/converter/server.js @@ -8,7 +8,10 @@ module.exports.start = async input => { const tmpDir = await tmp.dir({ dir: "/tmp" }); const htmlPath = await render({ input: input, - output: tmpDir.path + output: tmpDir.path, + // This absolute filesystem path will be passed unmodified through + // to the parcel bundler, which will compile the SCSS + externalCSS: path.join(__dirname, "../assets/css/index.scss") }); console.log("💅 Starting server at http://localhost:8000");