Skip to content

Commit 5ad70a3

Browse files
committed
Initial commit
0 parents  commit 5ad70a3

File tree

8 files changed

+3602
-0
lines changed

8 files changed

+3602
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/node_modules
2+
/.vagrant
3+
/ubuntu-xenial-16.04-cloudimg-console.log

.nvmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
11.9

README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Doküman
2+
3+
Doküman is a web server that can convert your Markdown into PDF files
4+
— useful for producing decent looking API docs and more.
5+
6+
## API
7+
8+
`POST /api/pdf` converts Markdown to a PDF file.
9+
10+
### Parameters
11+
12+
- `markdown` (required): the Markdown content.
13+
- `title` (optional): title of HTML page.
14+
15+
## Run
16+
17+
A Vagrantfile is included in this repo and probably provides the easiest way to get the server up and running.
18+
Start server from Vagrant box:
19+
20+
vagrant up
21+
vagrant ssh
22+
cd /vagrant
23+
nvm install
24+
npm install
25+
node app.js
26+
27+
Now we are ready to use the server:
28+
29+
# From your local machine
30+
curl --data-urlencode "markdown@./README.md" \
31+
http://localhost:8080/api/pdf > README.pdf

README.pdf

45.2 KB
Binary file not shown.

Vagrantfile

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# -*- mode: ruby -*-
2+
# vi: set ft=ruby :
3+
4+
# All Vagrant configuration is done below. The "2" in Vagrant.configure
5+
# configures the configuration version (we support older styles for
6+
# backwards compatibility). Please don't change it unless you know what
7+
# you're doing.
8+
Vagrant.configure("2") do |config|
9+
config.vm.box = "ubuntu/xenial64"
10+
11+
config.vm.network "forwarded_port", guest: 3000, host: 8080, host_ip: "127.0.0.1"
12+
13+
config.vm.provision "shell", inline: <<-SHELL
14+
apt-get update
15+
apt-get install -y 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
16+
echo 'LC_ALL=en_US.UTF-8' >> /etc/environment
17+
echo 'LANG=en_US.UTF-8' >> /etc/environment
18+
SHELL
19+
config.vm.provision "shell", privileged: false, inline: <<-SHELL
20+
curl --silent -o- https://raw.githubusercontent.com/creationix/nvm/v0.34.0/install.sh | bash
21+
SHELL
22+
end

app.js

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
const express = require('express')
2+
const bodyParser = require('body-parser')
3+
const path = require('path')
4+
const fs = require('fs')
5+
const port = process.env.PORT || 3000
6+
7+
const app = express()
8+
9+
app.use(bodyParser.urlencoded({ extended: false }))
10+
11+
app.get('/', (req, res) => {
12+
res.type('html')
13+
const readmeMarkdownPath = path.join(__dirname, 'README.md')
14+
const readmeMarkdownContent = fs.readFileSync(readmeMarkdownPath, 'utf8')
15+
res.send(toHTML({ markdownContent: readmeMarkdownContent, title: 'Doküman' }))
16+
})
17+
18+
app.post('/api/pdf', async (req, res) => {
19+
if (!req.body.markdown) {
20+
res.status(400).send('Required markdown parameter is not set.\n')
21+
return
22+
}
23+
24+
const pdfBuffer = await toPDF({
25+
markdownContent: req.body.markdown,
26+
title: req.body.title
27+
})
28+
const pdfFileName = req.body.pdfFileName || 'markdown.pdf'
29+
30+
res.type('pdf')
31+
res.set('Content-Disposition', `attachment; filename=${pdfFileName}`)
32+
res.send(pdfBuffer)
33+
})
34+
35+
app.listen(port, () => console.log(`Doküman listening on port ${port}!`))
36+
37+
// -----------------------------------------------------------------------------
38+
39+
const md = require('markdown-it')()
40+
const sass = require('node-sass')
41+
const mustache = require('mustache')
42+
const puppeteer = require('puppeteer')
43+
44+
const mustacheTemplate = `<!DOCTYPE HTML>
45+
<html>
46+
<head>
47+
<meta charset="UTF-8">
48+
<title>{{title}}</title>
49+
<style media="all">{{{css}}}</style>
50+
</head>
51+
<body>
52+
<div class="markdown-body">
53+
{{{htmlFragment}}}
54+
</div>
55+
</body>
56+
</html>
57+
`
58+
59+
const sassStylesheet = `
60+
@import 'primer-base/index.scss';
61+
@import 'primer-markdown/index.scss';
62+
@media only screen {
63+
body {
64+
margin: 2em 1em;
65+
}
66+
67+
.markdown-body {
68+
max-width: 800px;
69+
margin: 0 auto;
70+
}
71+
}
72+
`
73+
const sassRenderResult = sass.renderSync({
74+
data: sassStylesheet,
75+
outputStyle: 'compressed',
76+
includePaths: ['./node_modules']
77+
})
78+
79+
const pdfOptions = {
80+
printBackground: true,
81+
margin: {
82+
top: '2.00 cm',
83+
left: '1.75 cm',
84+
right: '1.75 cm',
85+
bottom: '2.50 cm'
86+
}
87+
}
88+
89+
async function toPDF ({ markdownContent, title }) {
90+
const htmlContent = toHTML({ markdownContent, title })
91+
const browser = await puppeteer.launch()
92+
const page = await browser.newPage()
93+
await page.setContent(htmlContent)
94+
const pdfBuffer = await page.pdf(pdfOptions)
95+
await browser.close()
96+
97+
return pdfBuffer
98+
}
99+
100+
function toHTML ({ markdownContent, title }) {
101+
const htmlFragment = md.render(markdownContent)
102+
const html = mustache.render(mustacheTemplate, {
103+
title: title,
104+
css: sassRenderResult.css,
105+
htmlFragment: htmlFragment
106+
})
107+
108+
return html
109+
}

0 commit comments

Comments
 (0)