diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..0bf931f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,22 @@ +FROM node:10 + +RUN apt-get update && \ + apt-get -y install xvfb gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 \ + libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 \ + libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 \ + libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 \ + libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget curl && \ + rm -rf /var/lib/apt/lists/* + +RUN npm install puppeteer markdown-it mustache markdown-it-named-headers cheerio +COPY makepdfs.js / +COPY package.json / +COPY template/ template/ +COPY styles/ styles/ + +RUN fc-cache -fv && \ + chmod +x /makepdfs.js && \ + mkdir /pdf && \ + chmod 777 /pdf && \ + ln -s /makepdfs.js /usr/local/bin/makepdfs +CMD [ "makepdfs" ] diff --git a/LICENSE b/LICENSE index 3cb6634..9e9def9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019 Merritt Krakowitzer +Copyright (c) 2020 Merritt Krakowitzer Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md new file mode 100644 index 0000000..b8884b6 --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +# A github action for creating PDFs from github markdown + +This action creates PDF documents from github markdown + +## Inputs + +### `markdown_dir` + +**Required** Location of markdown files in github repository. Default `doc`. + +### `output_dir` + +**Required** Location to output PDF files to. Default `tmp`. + +## Example usage + +``` +on: [push] + +name: CreatePDFs + +jobs: + makepdfs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - uses: mkrakowitzer/actions-makepdfs@master + if: github.ref == 'refs/heads/master' + - uses: actions/upload-artifact@v1 + with: + name: platform-architecture-docs + path: tmp +``` diff --git a/action.yml b/action.yml new file mode 100644 index 0000000..f30ca37 --- /dev/null +++ b/action.yml @@ -0,0 +1,19 @@ +# action.yml +name: 'Create PDF' +description: 'Creates PDF files from github markdown' +runs: + using: 'docker' + image: 'Dockerfile' +inputs: + markdown_dir: + description: 'Location of markdown files in github repository' + required: true + default: 'doc' + output_dir: + description: 'Location to output PDF files to' + required: true + default: 'tmp' + +branding: + icon: 'activity' + color: 'green' diff --git a/makepdfs.js b/makepdfs.js new file mode 100755 index 0000000..d9e850b --- /dev/null +++ b/makepdfs.js @@ -0,0 +1,127 @@ +#!/usr/bin/env node + +const markdown_dir = process.env.INPUT_MARKDOWN_DIR; +const output_dir = process.env.INPUT_OUTPUT_DIR; + +'use strict'; +var fs = require( 'fs' ); +var mddir = '/github/workspace/' + markdown_dir; +var dir = '/github/workspace/' + output_dir + '/'; + +/* + * Show an error message + */ +function showErrorMessage(msg, error) { + console.log('ERROR: ' + msg); + if (error) { + console.log(error); + } +} + +/* + * make html + */ +function makeHtml(data) { + try { + // read files that make up template + var style = fs.readFileSync("/styles/markdown.css", ).toString('utf-8') + fs.readFileSync("/styles/markdown-pdf.css", ).toString('utf-8'); + var template = fs.readFileSync("/template/template.html").toString('utf-8'); + // compile template + var mustache = require('mustache'); + + var view = { + style: style, + content: data + }; + return mustache.render(template, view); + } catch (error) { + showErrorMessage('makeHtml()', error); + } +} + +/* + * make PDF + */ +function makePdf(data,file) { + try { + file = file.replace('.md',''); + const puppeteer = require('puppeteer'); + (async () => { + const browser = await puppeteer.launch( { + executablePath:'/node_modules/puppeteer/.local-chromium/linux-706915/chrome-linux/chrome', + args: [ + '--headless', + '--no-sandbox', + '--disable-setuid-sandbox' + ] + } ) + const page = await browser.newPage(); + await page.goto(data, {waitUntil: 'networkidle2'}); + await page.pdf({ + path: dir + file + '.pdf', + format: 'A4', + scale: .9, + displayHeaderFooter: true, + margin: {top: 100, bottom:100, right: '50', left: '50'}, + footerTemplate: '
', + headerTemplate: '
' + }); + + await browser.close(); + })(); + } catch (error) { + showErrorMessage('makeHtml()', error); + } +} + +function Slug(string) { + try { + var stg = encodeURI(string.trim() + .toLowerCase() + .replace(/[\]\[\!\"\#\$\%\&\'\(\)\*\+\,\.\/\:\;\<\=\>\?\@\\\^\_\{\|\}\~\`]/g, '') + .replace(/\s+/g, '-') + .replace(/^\-+/, '') + .replace(/\-+$/, '')); + return stg; + } catch (error) { + showErrorMessage('Slug()', error); + } +} + +var path = require('path'); + +if (!fs.existsSync(dir)){ + fs.mkdirSync(dir); +} + +fs.readdir (mddir,function(err, files) { + for (let file of files) { + if (path.extname(file) == '.md') { + var text = fs.readFileSync('doc/' + file).toString('utf-8'); + var md = require('markdown-it')({ + html: true, + breaks: true, + highlight: function (str, lang) { + if (lang && hljs.getLanguage(lang)) { + try { + str = hljs.highlight(lang, str, true).value; + } catch (error) { + str = md.utils.escapeHtml(str); + + showErrorMessage('markdown-it:highlight', error); + } + } else { + str = md.utils.escapeHtml(str); + } + return '
' + str + '
'; + } + }); + var options = { + slugify: Slug + } + md.use(require('markdown-it-named-headers'), options); + var body = md.render(text); + makePdf('data:text/html;,' + encodeURIComponent(makeHtml(body)),file); + } + } +}); diff --git a/package.json b/package.json new file mode 100644 index 0000000..309b6ef --- /dev/null +++ b/package.json @@ -0,0 +1,7 @@ +{ + "dependencies": { + "puppeteer": "1.15.0", + "commander": "2.20.0", + "markdown-it": "8.4.2" + } +} diff --git a/styles/markdown-pdf.css b/styles/markdown-pdf.css new file mode 100644 index 0000000..9c34cc0 --- /dev/null +++ b/styles/markdown-pdf.css @@ -0,0 +1,55 @@ +/* + * Markdown PDF CSS + */ + + body { + font-family: "Meiryo", "Segoe WPC", "Segoe UI", "SFUIText-Light", "HelveticaNeue-Light", sans-serif, "Droid Sans Fallback"; +} + +img { + display: block; + margin-left: auto; + margin-right: auto; + width: 50%; +} + +pre { + background-color: #f8f8f8; + border: 1px solid #cccccc; + border-radius: 3px; + overflow-x: auto; + white-space: pre-wrap; + overflow-wrap: break-word; +} + +pre:not(.hljs) { + padding: 23px; + line-height: 19px; +} + +blockquote { + background: rgba(127, 127, 127, 0.1); + border-color: rgba(0, 122, 204, 0.5); +} + +.emoji { + height: 1.4em; +} + +/* for inline code */ +:not(pre):not(.hljs) > code { + color: #C9AE75; /* Change the old color so it seems less like an error */ + font-size: inherit; +} + +/* Page Break : use
to insert page break +-------------------------------------------------------- */ +.page { + page-break-after: always; +} + +@media print { + p {page-break-inside: avoid;} + h1 {page-break-before: always;} + footer {page-break-after: always;} +} diff --git a/styles/markdown.css b/styles/markdown.css new file mode 100644 index 0000000..45464f7 --- /dev/null +++ b/styles/markdown.css @@ -0,0 +1,155 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +body { + font-family: "Segoe WPC", "Segoe UI", "SFUIText-Light", "HelveticaNeue-Light", sans-serif, "Droid Sans Fallback"; + font-size: 14px; + padding: 0 12px; + line-height: 22px; + word-wrap: break-word; +} + +#code-csp-warning { + position: fixed; + top: 0; + right: 0; + color: white; + margin: 16px; + text-align: center; + font-size: 12px; + font-family: sans-serif; + background-color:#444444; + cursor: pointer; + padding: 6px; + box-shadow: 1px 1px 1px rgba(0,0,0,.25); +} + +#code-csp-warning:hover { + text-decoration: none; + background-color:#007acc; + box-shadow: 2px 2px 2px rgba(0,0,0,.25); +} + + +body.scrollBeyondLastLine { + margin-bottom: calc(100vh - 22px); +} + +body.showEditorSelection .code-line { + position: relative; +} + +body.showEditorSelection .code-active-line:before, +body.showEditorSelection .code-line:hover:before { + content: ""; + display: block; + position: absolute; + top: 0; + left: -12px; + height: 100%; +} + +body.showEditorSelection li.code-active-line:before, +body.showEditorSelection li.code-line:hover:before { + left: -30px; +} + +img { + max-width: 100%; + max-height: 100%; +} + +a { + color: #4080D0; + text-decoration: none; +} + +a:focus, +input:focus, +select:focus, +textarea:focus { + outline: 1px solid -webkit-focus-ring-color; + outline-offset: -1px; +} + +hr { + border: 0; + height: 2px; + border-bottom: 2px solid; +} + +h1 { + padding-bottom: 0.3em; + line-height: 1.2; + border-bottom-width: 1px; + border-bottom-style: solid; +} + +h1, h2, h3 { + font-weight: normal; +} + +h1 code, +h2 code, +h3 code, +h4 code, +h5 code, +h6 code { + font-size: inherit; + line-height: auto; +} + +a:hover { + color: #4080D0; + text-decoration: underline; +} + +table { + border-collapse: collapse; +} + +table > thead > tr > th { + text-align: left; + border-bottom: 1px solid; +} + +table > thead > tr > th, +table > thead > tr > td, +table > tbody > tr > th, +table > tbody > tr > td { + padding: 5px 10px; +} + +table > tbody > tr + tr > td { + border-top: 1px solid; +} + +blockquote { + margin: 0 7px 0 5px; + padding: 0 16px 0 10px; + border-left: 5px solid; +} + +code { + font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Courier New", monospace, "Droid Sans Fallback"; + font-size: 14px; + line-height: 19px; +} + +body.wordWrap pre { + white-space: pre-wrap; +} + +.mac code { + font-size: 12px; + line-height: 18px; +} + +pre:not(.hljs), +pre.hljs code > div { + padding: 16px; + border-radius: 3px; + overflow: auto; +} diff --git a/template/template.html b/template/template.html new file mode 100644 index 0000000..04a4409 --- /dev/null +++ b/template/template.html @@ -0,0 +1,13 @@ + + + +{{{title}}} + + + + +{{{content}}} + +