diff options
author | Robin Appelman <icewind@owncloud.com> | 2013-05-04 22:45:05 +0400 |
---|---|---|
committer | Robin Appelman <icewind@owncloud.com> | 2013-05-04 22:45:05 +0400 |
commit | 4b31f5cd0fe7277f0cb756026f6816e601a587da (patch) | |
tree | 29d2f7e3dfcc1c2890a622eb2526a97412c8969d | |
parent | 9d5d8283be0140595a0c52cd04b1128c7cb1cb90 (diff) |
Allow using wkhtmltopdf to download markdown files as pdfwkhtmltopdf
-rw-r--r-- | ajax/checkconvert.php | 18 | ||||
-rw-r--r-- | ajax/download.php | 55 | ||||
-rw-r--r-- | bin/.gitignore | 1 | ||||
-rw-r--r-- | bin/README | 2 | ||||
-rw-r--r-- | css/render.css | 386 | ||||
-rw-r--r-- | js/editor.js | 40 | ||||
-rw-r--r-- | lib/wkhtmltopdf.php | 25 | ||||
-rw-r--r-- | templates/render.php | 14 |
8 files changed, 540 insertions, 1 deletions
diff --git a/ajax/checkconvert.php b/ajax/checkconvert.php new file mode 100644 index 0000000..03aa197 --- /dev/null +++ b/ajax/checkconvert.php @@ -0,0 +1,18 @@ +<?php +/** + * Copyright (c) 2013 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +\OCP\JSON::checkLoggedIn(); +\OCP\JSON::callCheck(); +\OCP\JSON::setContentTypeHeader(); + +try { + $converter = new \OCA\Files_Markdown\WKHTMLtoPDF(); + echo 'true'; +} catch (Exception $e) { + echo 'false'; +} diff --git a/ajax/download.php b/ajax/download.php new file mode 100644 index 0000000..878d5f4 --- /dev/null +++ b/ajax/download.php @@ -0,0 +1,55 @@ +<?php +/** + * Copyright (c) 2013 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +\OCP\JSON::checkLoggedIn(); +\OCP\JSON::callCheck(); + +try { + $converter = new \OCA\Files_Markdown\WKHTMLtoPDF(); +} catch (Exception $e) { + \OC_JSON::error(array('error' => 'binary not found')); + exit(); +} + +$sourceName = \OC\Files\Filesystem::getLocalFile($_POST['name']); +$sourceName = basename($sourceName); +$sourceName = substr($sourceName, 0, strrpos($sourceName, '.')); +$targetName = $sourceName . '.pdf'; + +// we can't use /tmp on all system since systemd's PriveTmp prevents apache's tmp files from being accessed by wkpdftohtml +$basedir = \OC_User::getHome(\OC_User::getUser()) . '/files_markdown'; +if (!is_dir($basedir)) { + mkdir($basedir); +} + +$targetFile = $basedir . '/' . $targetName; +$sourceFile = $basedir . '/' . $sourceName . '.html'; +$source = $_POST['html']; + +$css = realpath(__DIR__ . '/../css/render.css'); +$template = new \OC_Template('files_markdown', 'render'); +$template->assign('css', file_get_contents($css)); +$template->assign('mathjaxcss', $_POST['mathjaxcss']); +$template->assign('html', $source); +$html = $template->fetchPage(); +file_put_contents($sourceFile, $html); + +$converter->renderHTMLFile($sourceFile, $targetFile); + +if (preg_match("/MSIE/", $_SERVER["HTTP_USER_AGENT"])) { + header('Content-Disposition: attachment; filename="' . rawurlencode($targetName) . '"'); +} else { + header('Content-Disposition: attachment; filename*=UTF-8\'\'' . rawurlencode($targetName) + . '; filename="' . rawurlencode($targetName) . '"'); +} +header('Content-Transfer-Encoding: binary'); +header('Content-Type: application/pdf'); +header('Content-Length: ' . filesize($targetFile)); +readfile($targetFile); +unlink($targetFile); +unlink($sourceFile); diff --git a/bin/.gitignore b/bin/.gitignore new file mode 100644 index 0000000..d01a0cc --- /dev/null +++ b/bin/.gitignore @@ -0,0 +1 @@ +wkhtmltopdf diff --git a/bin/README b/bin/README new file mode 100644 index 0000000..2d2f392 --- /dev/null +++ b/bin/README @@ -0,0 +1,2 @@ +download the latest version of wkhtmltopdf from https://code.google.com/p/wkhtmltopdf/downloads/list +, place it in this folder with the name "whtmltopdf" and make it executable. diff --git a/css/render.css b/css/render.css new file mode 100644 index 0000000..8b74a7c --- /dev/null +++ b/css/render.css @@ -0,0 +1,386 @@ +#preview { + font-family: Helvetica, arial, sans-serif; + font-size: 14px; + line-height: 1.6; + padding-top: 10px; + padding-bottom: 10px; + padding-left: 20px; + padding-right: 20px; + background-color: white; + margin-left: 10px; +} + +#preview > *:first-child { + margin-top: 0 !important; +} + +#preview > *:last-child { + margin-bottom: 0 !important; +} + +#preview a { + color: #4183C4; +} + +#preview a.absent { + color: #cc0000; +} + +#preview a.anchor { + display: block; + padding-left: 30px; + margin-left: -30px; + cursor: pointer; + position: absolute; + top: 0; + left: 0; + bottom: 0; +} + +#preview h1, #preview h2, #preview h3, #preview h4, #preview h5, #preview h6 { + margin: 20px 0 10px; + padding: 0; + font-weight: bold; + -webkit-font-smoothing: antialiased; + cursor: text; + position: relative; +} + +#preview h1:hover a.anchor, #preview h2:hover a.anchor, +#preview h3:hover a.anchor, #preview h4:hover a.anchor, +#preview h5:hover a.anchor, #preview h6:hover a.anchor { + background: url("../../images/modules/styleguide/para.png") no-repeat 10px center; + text-decoration: none; +} + +#preview h1 tt, #preview h1 code { + font-size: inherit; +} + +#preview h2 tt, #preview h2 code { + font-size: inherit; +} + +#preview h3 tt, #preview h3 code { + font-size: inherit; +} + +#preview h4 tt, #preview h4 code { + font-size: inherit; +} + +#preview h5 tt, #preview h5 code { + font-size: inherit; +} + +#preview h6 tt, #preview h6 code { + font-size: inherit; +} + +#preview h1 { + font-size: 28px; + color: black; +} + +#preview h2 { + font-size: 24px; + border-bottom: 1px solid #cccccc; + color: black; +} + +#preview h3 { + font-size: 18px; +} + +#preview h4 { + font-size: 16px; +} + +#preview h5 { + font-size: 14px; +} + +#preview h6 { + color: #777777; + font-size: 14px; +} + +#preview p, #preview blockquote, #preview ul, #preview ol, #preview dl, #preview li, #preview table, #preview pre { + margin: 15px 0; +} + +#preview hr { + background: transparent url("../../images/modules/pulls/dirty-shade.png") repeat-x 0 0; + border: 0 none; + color: #cccccc; + height: 4px; + padding: 0; +} + +#preview > h2:first-child { + margin-top: 0; + padding-top: 0; +} + +#preview > h1:first-child { + margin-top: 0; + padding-top: 0; +} + +#preview > h1:first-child + h2 { + margin-top: 0; + padding-top: 0; +} + +#preview > h3:first-child, #preview > h4:first-child, #preview > h5:first-child, #preview > h6:first-child { + margin-top: 0; + padding-top: 0; +} + +#preview a:first-child h1, #preview a:first-child h2, #preview a:first-child h3, #preview a:first-child h4, #preview a:first-child h5, #preview a:first-child h6 { + margin-top: 0; + padding-top: 0; +} + +#preview h1 p, #preview h2 p, #preview h3 p, #preview h4 p, #preview h5 p, #preview h6 p { + margin-top: 0; +} + +#preview li p.first { + display: inline-block; +} + +#preview ul, #preview ol { + padding-left: 30px; +} + +#preview ul :first-child, #preview ol :first-child { + margin-top: 0; +} + +#preview ul :last-child, #preview ol :last-child { + margin-bottom: 0; +} + +#preview dl { + padding: 0; +} + +#preview dl dt { + font-size: 14px; + font-weight: bold; + font-style: italic; + padding: 0; + margin: 15px 0 5px; +} + +#preview dl dt:first-child { + padding: 0; +} + +#preview dl dt > :first-child { + margin-top: 0; +} + +#preview dl dt > :last-child { + margin-bottom: 0; +} + +#preview dl dd { + margin: 0 0 15px; + padding: 0 15px; +} + +#preview dl dd > :first-child { + margin-top: 0; +} + +#preview dl dd > :last-child { + margin-bottom: 0; +} + +#preview blockquote { + border-left: 4px solid #dddddd; + padding: 0 15px; + color: #777777; +} + +#preview blockquote > :first-child { + margin-top: 0; +} + +#preview blockquote > :last-child { + margin-bottom: 0; +} + +#preview table { + padding: 0; +} + +#preview table tr { + border-top: 1px solid #cccccc; + background-color: white; + margin: 0; + padding: 0; +} + +#preview table tr:nth-child(2n) { + background-color: #f8f8f8; +} + +#preview table tr th { + font-weight: bold; + border: 1px solid #cccccc; + text-align: left; + margin: 0; + padding: 6px 13px; +} + +#preview table tr td { + border: 1px solid #cccccc; + text-align: left; + margin: 0; + padding: 6px 13px; +} + +#preview table tr th :first-child, #preview table tr td :first-child { + margin-top: 0; +} + +#preview table tr th :last-child, #preview table tr td :last-child { + margin-bottom: 0; +} + +#preview img { + max-width: 100%; +} + +#preview span.frame { + display: block; + overflow: hidden; +} + +#preview span.frame > span { + border: 1px solid #dddddd; + display: block; + float: left; + overflow: hidden; + margin: 13px 0 0; + padding: 7px; + width: auto; +} + +#preview span.frame span img { + display: block; + float: left; +} + +#preview span.frame span span { + clear: both; + color: #333333; + display: block; + padding: 5px 0 0; +} + +#preview span.align-center { + display: block; + overflow: hidden; + clear: both; +} + +#preview span.align-center > span { + display: block; + overflow: hidden; + margin: 13px auto 0; + text-align: center; +} + +#preview span.align-center span img { + margin: 0 auto; + text-align: center; +} + +#preview span.align-right { + display: block; + overflow: hidden; + clear: both; +} + +#preview span.align-right > span { + display: block; + overflow: hidden; + margin: 13px 0 0; + text-align: right; +} + +#preview span.align-right span img { + margin: 0; + text-align: right; +} + +#preview span.float-left { + display: block; + margin-right: 13px; + overflow: hidden; + float: left; +} + +#preview span.float-left span { + margin: 13px 0 0; +} + +#preview span.float-right { + display: block; + margin-left: 13px; + overflow: hidden; + float: right; +} + +#preview span.float-right > span { + display: block; + overflow: hidden; + margin: 13px auto 0; + text-align: right; +} + +#preview code, #preview tt { + margin: 0 2px; + padding: 0 5px; + white-space: nowrap; + border: 1px solid #eaeaea; + background-color: #f8f8f8; + border-radius: 3px; +} + +#preview pre code { + margin: 0; + padding: 0; + white-space: pre; + border: none; + background: transparent; +} + +#preview .highlight pre { + background-color: #f8f8f8; + border: 1px solid #cccccc; + font-size: 13px; + line-height: 19px; + overflow: auto; + padding: 6px 10px; + border-radius: 3px; +} + +#preview pre { + background-color: #f8f8f8; + border: 1px solid #cccccc; + font-size: 13px; + line-height: 19px; + overflow: auto; + padding: 6px 10px; + border-radius: 3px; +} + +#preview pre code, #preview pre tt { + background-color: transparent; + border: none; +} diff --git a/js/editor.js b/js/editor.js index 48e3c38..2a4e4e1 100644 --- a/js/editor.js +++ b/js/editor.js @@ -45,7 +45,15 @@ $(document).ready(function () { editor.width(halfWidth - 10); preview.height(editor.height()); }); - }) + }); + $.get(OC.filePath('files_markdown', 'ajax', 'checkconvert.php')).then(function (result) { + if (result) { + var downloadButton = $('<button/>'); + downloadButton.click(downloadPDF); + downloadButton.text(t('files_markdown', 'Download PDF')); + $('#editorcontrols').append(downloadButton); + } + }); }); }); FileActions.setDefault('text/markdown', 'Edit'); @@ -72,4 +80,34 @@ $(document).ready(function () { } }); +function downloadPDF() { + var html = $('#preview').html(), + form = $('<form/>'), + textfield = $('<textarea/>'), + input = $('<input/>'); + + form.attr('action', OC.filePath('files_markdown', 'ajax', 'download.php')).attr('method', 'post'); + input.attr('name', 'name'); + input.val($('div.crumb.last').text()) + form.append(input); + + textfield.attr('name', 'html'); + textfield.val(html); + form.append(textfield); + + input = $('<input/>') + input.attr('name', 'requesttoken'); + input.val(oc_requesttoken); + form.append(input); + + input = $('<input/>') + input.attr('name', 'mathjaxcss');//we need the css mathjax inserts for fonts and such + input.val($('head style').last().text()); + form.append(input); + + form.hide(); + $('body').append(form); + form.submit(); +} + var mathJaxLoaded = false; diff --git a/lib/wkhtmltopdf.php b/lib/wkhtmltopdf.php new file mode 100644 index 0000000..2a74ed7 --- /dev/null +++ b/lib/wkhtmltopdf.php @@ -0,0 +1,25 @@ +<?php +/** + * Copyright (c) 2013 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OCA\Files_Markdown; + +class WKHTMLtoPDF { + private $bin; + + public function __construct() { + $this->bin = realpath(__DIR__ . '/../bin/wkhtmltopdf'); + if (!file_exists($this->bin)) { + throw new \Exception('binary not found'); + } + } + + public function renderHTMLFile($source, $target) { + $cmd = $this->bin . ' --output-format pdf ' . escapeshellarg($source) . ' ' . escapeshellarg($target); + exec($cmd); + } +} diff --git a/templates/render.php b/templates/render.php new file mode 100644 index 0000000..5c38736 --- /dev/null +++ b/templates/render.php @@ -0,0 +1,14 @@ +<html> + <head> + <meta charset='utf-8'> + <style> + <?php echo $_['mathjaxcss'];?> + </style> + <style> + <?php echo $_['css'];?> + </style> + </head> + <body id='preview'> + <?php echo $_['html']; ?> + </body> +</html> |