Fixes and stylesheets

- added `[!stylesheet]: file.css` command to include custom stylesheets
- changed images to be included as base64 url
- removed markdown task from gulpfile
- fixed commands not being recognized
develop
Trivernis 5 years ago
parent 6f39c36f56
commit 62d75c3a27

1
.gitignore vendored

@ -5,3 +5,4 @@ test.md
test.html
testchapter.md
git
testfiles

@ -5,8 +5,10 @@
- Changelog
- markdown parsing with markdown it
- command to include plugins `[!use]: plugin`
- command to include markdown files `[!include]: file`
- command to include markdown files `[!include]: file.md`
- command to start a new page `[!newpage]`
- command to include a stylesheet `[!stylesheet]: file.css`
- option to export to pdf `--pdf`
- option to watch (and export to html) `--watch`
- stylesheets supporting math and code highlighting (with highlightjs)
- auto base64 converting for images for a standalone html

@ -81,3 +81,15 @@ Warning: be careful not to put too much content in this environment
because it will be rendered as only ONE page in the pdf output.
:::
```
## Stylesheets
You can include your own stylesheet. It is applied after the default style. The stylesheets are applied in the order they are declared.
```markdown
[!stylesheet]: path/to/style.css
```
## Other stuff
Images and stylesheets are included within the html file. All image urls are converted into base64 urls.

@ -3,9 +3,6 @@ const sass = require('gulp-sass');
const ts = require('gulp-typescript');
const del = require('delete');
const cleanCss = require('gulp-clean-css');
const renderer = require('./dist/Renderer');
const fsx = require("fs-extra");
const path = require("path");
function clearDist(cb) {
del('dist/*', cb);
@ -25,18 +22,10 @@ function compileSass() {
.pipe(dest('dist/styles'));
}
async function renderMarkdown() {
let filename = 'test.md';
let rend = new renderer.Renderer();
let html = await rend.render(filename);
await fsx.writeFile(filename.replace(path.extname(filename), '') + '.html', html);
}
task('cleanBuild', series(clearDist, compileTypescript, compileSass));
task('default', series(compileTypescript, compileSass));
task('watch', () => {
series(compileSass, compileTypescript, renderMarkdown);
watch('src/**/*.sass', series(compileSass, renderMarkdown));
series(compileSass, compileTypescript);
watch('src/**/*.sass', series(compileSass));
watch('**/*.ts', compileTypescript);
watch('**/*.md', renderMarkdown);
});

14
package-lock.json generated

@ -67,6 +67,15 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.6.8.tgz",
"integrity": "sha512-aX+gFgA5GHcDi89KG5keey2zf0WfZk/HAQotEamsK2kbey+8yGKcson0hbK8E+v0NArlCJQCqMP161YhV6ZXLg=="
},
"@types/node-fetch": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.0.tgz",
"integrity": "sha512-TLFRywthBgL68auWj+ziWu+vnmmcHCDFC/sqCOQf1xTz4hRq8cu79z8CtHU9lncExGBsB8fXA4TiLDLt6xvMzw==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/puppeteer": {
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/@types/puppeteer/-/puppeteer-1.19.0.tgz",
@ -3753,6 +3762,11 @@
"integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=",
"dev": true
},
"node-fetch": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
"integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA=="
},
"node-gyp": {
"version": "3.8.0",
"resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz",

@ -16,6 +16,7 @@
"@types/jsdom": "^12.2.4",
"@types/markdown-it": "0.0.8",
"@types/node": "^12.6.8",
"@types/node-fetch": "^2.5.0",
"@types/winston": "^2.4.4",
"delete": "^1.1.0",
"gulp": "^4.0.2",
@ -50,6 +51,7 @@
"markdown-it-smartarrows": "^1.0.1",
"markdown-it-sub": "^1.0.0",
"markdown-it-toc-done-right": "^4.0.2",
"node-fetch": "^2.6.0",
"puppeteer": "^1.19.0",
"uninstall": "0.0.0",
"winston": "^3.2.1"

@ -5,14 +5,11 @@ import {markdownPlugins} from './plugins';
import {pageFormats} from "./formats";
import {PDFFormat} from "puppeteer";
namespace Matchers {
export const commandMatcher: RegExp = /\[ *!(\w+) *]:? *(.*)/g;
}
export class CommandParser {
public projectFiles: string[];
public pageFormat: PDFFormat;
public loadedPlugins: string[];
public stylesheets: string[];
private resolvePath: {path: string, lines: number}[];
@ -20,9 +17,15 @@ export class CommandParser {
this.projectFiles = [];
this.loadedPlugins = [];
this.resolvePath = [];
this.stylesheets = [];
}
async processCommands(doc: string, docpath: string, renderer: Renderer) {
if (process.platform === 'darwin') {
doc = doc.replace(/\r/g, '\n'); // mac uses \r as linebreak
} else {
doc = doc.replace(/\r/g, ''); // linux uses \n, windows \r\n
}
const inputLines: string[] = doc.split('\n');
let outputLines: string[] = [];
let mainDir: string = path.dirname(docpath);
@ -31,7 +34,7 @@ export class CommandParser {
this.resolvePath.push({path: docpath, lines: inputLines.length});
while (inputLines.length > 0) {
let inputLine = inputLines.shift().replace(/\r/, '');
let inputLine = inputLines.shift();
let currentFile = this.resolvePath[this.resolvePath.length - 1]; // keeping track of the current file
if (currentFile.lines > 0) {
currentFile.lines--;
@ -41,7 +44,7 @@ export class CommandParser {
currentFile = this.resolvePath[this.resolvePath.length - 1];
}
}
let match: RegExpExecArray = Matchers.commandMatcher.exec(inputLine);
let match: RegExpExecArray = /\[ *!(\w+) *\]:? *(.*)/gu.exec(inputLine.replace(/\s/, ''));
if (match && match[0]) { // TODO: stylesheets
switch(match[1]) {
@ -78,6 +81,9 @@ export class CommandParser {
this.addMarkdownPlugin('div', renderer);
outputLines.push('::: .newpage \n:::');
break;
case 'stylesheet':
await this.addStylesheet(match[2], mainDir);
break;
default:
outputLines.push(inputLine);
}
@ -134,4 +140,20 @@ export class CommandParser {
throw new Error(`The file ${filepath} can not be included (not found).`);
}
}
/**
* Adds a stylesheet to the result markdown.
* @param filepath
* @param mainDir
*/
private async addStylesheet(filepath: string, mainDir: string) {
let stylepath: string;
if (path.isAbsolute(filepath)) {
stylepath = path.normalize(filepath);
} else {
stylepath = path.join(mainDir, filepath);
}
if ((await fsx.pathExists(stylepath)))
this.stylesheets.push(stylepath);
}
}

@ -7,11 +7,12 @@ import {JSDOM} from 'jsdom';
import {CommandParser} from "./CommandParser";
import {EventEmitter} from "events";
import {PDFFormat} from "puppeteer";
import fetch from 'node-fetch';
export class Renderer extends EventEmitter {
private md: MarkdownIt;
private beforeRendering: Function[];
private afterRendering: Function[];
private readonly beforeRendering: Function[];
private readonly afterRendering: Function[];
private commandParser: CommandParser;
constructor() {
@ -111,12 +112,54 @@ export class Renderer extends EventEmitter {
*/
private configure() {
this.useBefore((a: string, b: string, c: Renderer) => this.commandParser.processCommands(a, b, c));
this.useAfter(async (doc: string) => {
let dom: JSDOM = new JSDOM(doc);
this.useAfter((doc: string) => new JSDOM(doc));
// include default style
this.useAfter(async (dom: JSDOM) => {
let styleTag = dom.window.document.createElement('style');
// append the default style
styleTag.innerHTML = await fsx.readFile(path.join(__dirname, 'styles/default.css'), 'utf-8');
dom.window.document.head.appendChild(styleTag);
return dom.serialize();
return dom;
});
// include user defined styles
this.useAfter(async (dom: JSDOM) => {
let userStyles = dom.window.document.createElement('style');
userStyles.setAttribute('id', 'user-style');
// append all user defined stylesheets
for (let stylesheet of this.commandParser.stylesheets) {
userStyles.innerHTML += await fsx.readFile(stylesheet, 'utf-8');
}
dom.window.document.head.appendChild(userStyles);
return dom;
});
// include all images as base64
this.useAfter(async (dom: JSDOM, mainfile: string) => {
let document = dom.window.document;
let mainFolder = path.dirname(mainfile);
let imgs = document.querySelectorAll('img');
for (let img of imgs) {
let source = img.src;
let filepath = source;
let base64Url = source;
if (!path.isAbsolute(filepath))
filepath = path.join(mainFolder, filepath);
if (await fsx.pathExists(filepath)) {
let type = path.extname(source).slice(1);
base64Url = `data:image/${type};base64,`;
base64Url += (await fsx.readFile(filepath)).toString('base64');
} else {
try {
let response = await fetch(source);
base64Url = `data:${response.headers.get('content-type')};base64,`;
base64Url += (await response.buffer()).toString('base64');
} catch (error) {
console.error(error);
}
}
img.src = base64Url;
}
return dom;
});
this.useAfter((dom: JSDOM) => dom.serialize());
}
}

@ -22,6 +22,10 @@ nav
border: 2px solid $backgroundSecondary
page-break-after: always
img
width: 100%
height: auto
.page
page-break-before: always
page-break-after: always

Loading…
Cancel
Save