Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates

version: 2
updates:
- package-ecosystem: "github-actions" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
- package-ecosystem: "npm" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
- package-ecosystem: "composer" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ Creates PDF and HTML files from Markdown using the GitHub (or custom) theme.
- Tables
- Images (see docs)
- Internal and external links
- Task Lists
- [x] Completed
- [ ] Not completed

## GitHub Action Inputs

Expand Down
61 changes: 59 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"markdown-it": "12.3.2",
"markdown-it-anchor": "^8.1.0",
"markdown-it-emoji": "^2.0.2",
"markdown-it-task-lists": "^2.1.1",
"markdown-it-toc-done-right": "^4.2.0",
"mustache": "4.0.1",
"puppeteer": "13.5.1",
Expand Down
70 changes: 36 additions & 34 deletions src/markdown-to-pdf.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const markdownIt = require('markdown-it');
const markdownItAnchor = require('markdown-it-anchor');
const markdownItTOC = require('markdown-it-toc-done-right');
const markdownItEmoji = require('markdown-it-emoji');
const markdownTaskLists = require('markdown-it-task-lists');

function nullCoalescing(value, fallback) {
return value !== undefined && value !== null ? value : fallback;
Expand All @@ -38,11 +39,11 @@ function GetMarkdownIt() {
}catch(__) {
}
}

return ''; // use external default escaping
}
});

md.use(markdownItAnchor, {
permalink: markdownItAnchor.permalink.ariaHidden({
class: 'anchor',
Expand All @@ -57,7 +58,8 @@ function GetMarkdownIt() {
slugify: slugify,
});
md.use(markdownItEmoji);

md.use(markdownTaskLists, {enabled: true});

return md;
}

Expand All @@ -67,18 +69,18 @@ async function encodeImage(url) {
request.get(url, function(error, response, body) {
if(error) {
console.log(error);

return resolve(null);
}

if(response.statusCode !== 200) {
console.log('Image not found, is the image folder route correct? [' + url + ']');

return resolve(null);
}

let data = 'data:' + response.headers['content-type'].replace(' ', '') + ';base64,' + new Buffer.from(body).toString('base64');

return resolve(data);
});
});
Expand All @@ -94,13 +96,13 @@ function slugify(string) {
.replace(/\s+/g, '-')
.replace(/^-+/, '')
.replace(/-+$/, ''));

if(used_headers[slug]) {
slug += '-' + ++used_headers[slug];
}else {
used_headers[slug] = 0;
}

return slug;
}

Expand All @@ -113,38 +115,38 @@ const PDFLayout = {
};

class MarkdownToPDF {

constructor(options) {
this._image_import = options.image_import;
this._image_dir = nullCoalescing(options.image_dir, this._image_import);

this._style = options.style;
this._template = options.template;

this._table_of_contents = options.table_of_contents;
}

start() {
this._image_server_app = express();
this._image_server_app.use(express.static(this._image_dir));
this._image_server = this._image_server_app.listen(3000);

console.log("Started image server with image folder route '" + this._image_dir + "'.");
console.log();
}

async convert(data, title) {
if(typeof data !== 'string') throw "Parameter 'data' has to be a string containing Markdown content";
if(typeof title !== 'string' && title !== undefined) throw "Parameter 'title' has to be a string";

// Convert MD to HTML
let preHTML = this._convertToHtml(data, nullCoalescing(title, ''));
let html = await this._convertImageRoutes(preHTML).then(function (html) {
return html;
}).catch(function (err) {
throw `Error while converting images: ${err}`;
})

// Build the PDF file
const browser = await puppeteer.launch({
args: [
Expand Down Expand Up @@ -186,51 +188,51 @@ class MarkdownToPDF {

return new Result(html, pdf);
}

close() {
// Shutdown the image server
this._image_server.close(function() {
console.log();
console.log('Gracefully shut down image server.');
});
}

// This converts the markdown string to it's HTML values # => h1 etc.
_convertToHtml(text, title) {
if(this._table_of_contents) text = '[toc]\n' + text;

let md = GetMarkdownIt();
let body = md.render(text);
let doc = cheerio.load(body);
let toc = doc('nav#table-of-contents').html();

doc('nav#table-of-contents').remove();
body = doc('body').html();

let view = {
title: title,
style: this._style,
toc: toc,
content: body,
};

// Compile the template
return mustache.render(this._template, view);
}

// ConvertImageRoutes this function changed all instances of the ImageImport path to localhost,
// it then fetches this URL and encodes it to base64 so we can include it in both the HTML and
// PDF files without having to lug around an images folder
async _convertImageRoutes(html) {
if(this._image_import === null) {
return html;
}

let imagePath = this._image_import.replace(/[-\[\]{}()*+?.,\\^$|#]/g, '\\$&');
let imagePathRegex = new RegExp(imagePath, 'g');
let imgTagRegex = /<img[^>]+src="([^">]+)"/g;
let encoded = html;

let m;
while(m = imgTagRegex.exec(html)) {
try {
Expand All @@ -240,36 +242,36 @@ class MarkdownToPDF {
}).catch(function (err) {
throw `Error while converting image: ${err}`;
})

if(image !== null) {
encoded = encoded.replace(m[1], image);
}
}catch(error) {
console.log('ERROR:', error);
}
}

return encoded;
}


static nullCoalescing = nullCoalescing;
static getFileContent = getFileContent;
}

class Result {
html;
pdf;

constructor(html, pdf) {
this.html = html;
this.pdf = pdf;
}

writeHTML(file) {
fs.writeFileSync(file, this.html);
}

writePDF(file) {
fs.writeFileSync(file, this.pdf)
}
Expand Down
3 changes: 3 additions & 0 deletions styles/markdown.css
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ body {
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial,
sans-serif, Apple Color Emoji, Segoe UI Emoji;
font-size: 16px;
Expand Down Expand Up @@ -828,6 +829,7 @@ pre > code {
padding: 0.5em;
color: #333;
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
background: #f8f8f8;
}

Expand All @@ -844,6 +846,7 @@ pre {
line-height: 1.45;
background-color: #f6f8fa;
-webkit-print-color-adjust: exact;
print-color-adjust: exact;

border-radius: 3px;
}
Expand Down