Created
May 16, 2012 10:19
-
-
Save namelessjon/2709284 to your computer and use it in GitHub Desktop.
My redcarpet-using script to convert my markdown notes into a html file. Is customised a little to cope with textile sections, making figures, and some basic html entity replacement.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/ruby | |
# Now with a hacky outline too, generated after: http://blog.steveklabnik.com/posts/2011-12-21-redcarpet-is-awesome | |
filename = ARGV.first | |
filename = File.basename(filename) if filename | |
require 'redcloth' | |
require 'redcarpet' | |
require 'yaml' | |
require 'mustache' | |
class Renderer < Redcarpet::Render::SmartyHTML | |
Data = Struct.new(:metadata, :html) | |
attr_reader :headings, :current_level | |
def initialize(*) | |
super | |
@current_level = 1 | |
@figure_count = 0 | |
@headings = [] | |
end | |
def block_code(code, language) | |
if language == 'textile' | |
RedCloth.new(code).to_html | |
else | |
"<pre><code>#{code}</code></pre>" | |
end | |
end | |
def next_fig | |
@figure_count += 1 | |
end | |
def image(url, title, alttext) | |
if title | |
fig = next_fig | |
@headings << [current_level + 1, "#figure_#{fig}", "Figure #{fig}"] | |
"<figure id='figure_#{fig}'> | |
<img src='#{url}' alt='#{alttext}' /> | |
<figcaption>Figure #{fig}: #{title}</figcaption> | |
</figure>" | |
else | |
"<img src='#{url}' alt='#{alttext}' />" | |
end | |
end | |
# This hook is provided to us by Redcarpet. We get access | |
# to the whole text before anything else kicks off, which | |
# means we can snag out the YAML at the beginning. | |
def preprocess(full_document) | |
full_document =~ /\A(---\s*\n.*?\n?)^(---\s*$\n?)/m | |
@metadata = YAML.load($1) if $1 | |
text = Mustache.render(($' || full_document), @metadata) | |
end | |
def format_outline(outline=headings) | |
prev_level = 2 | |
"<ul>" + outline.inject("") do |html, data| | |
level, link, text = data | |
if prev_level < level | |
html += "<ul>" | |
elsif prev_level > level | |
html += "</ul>" | |
end | |
prev_level = level | |
html += %Q|<li><a href="#{link}">#{text}</a></li>| | |
html | |
end + "</ul>" | |
end | |
def postprocess(full_document) | |
replace_common_entities(super) | |
end | |
ENTITY_MAP = { | |
'alpha' => 'α', | |
'beta' => 'β', | |
'gamma' => 'γ', | |
'micro' => 'µ' | |
} | |
ENTITY_REGEX = Regexp.new("\\\\(#{Regexp.union(ENTITY_MAP.keys)})") | |
def replace_common_entities(text) | |
text.gsub!(ENTITY_REGEX) do |match| | |
ENTITY_MAP[$1] || match | |
end | |
text | |
end | |
# This accessor lets us access our metadata after the | |
# processing is all done. | |
def metadata | |
@metadata ||= {} | |
end | |
def header(text, level) | |
if level == 1 | |
metadata['title'] ||= replace_common_entities(text) | |
else | |
@current_level = level | |
@headings << [level, "##{dash_and_lowercase(text)}", text] | |
end | |
"<h#{level} id='#{dash_and_lowercase(text)}'>#{text}</h#{level}>" | |
end | |
def dash_and_lowercase(string) | |
string.dup.downcase.gsub(/\s+/,'-') | |
end | |
end | |
renderer = Renderer.new() | |
markdown = Redcarpet::Markdown.new(renderer, {fenced_code_blocks:true,autolink:true,strikethrough:true,superscript:true}) | |
begin | |
output = markdown.render(ARGF.read) | |
rescue => e | |
puts e.message | |
end | |
output = Renderer::Data.new(renderer.metadata, output) | |
puts <<-eos | |
<html> | |
<head> | |
<title>#{output.metadata['title'] || filename}</title> | |
<style type="text/css"> | |
body { | |
line-height: 1.3; | |
text-align: center; | |
font-size: 110%; | |
} | |
div#container { | |
max-width: 42em; | |
margin: 4em auto; | |
text-align: left; | |
} | |
hr { | |
border: 0px; | |
background-color: #aaa; | |
height: 1px; | |
margin-top: 18pt; | |
margin-bottom: 18pt; | |
} | |
pre { | |
border: 1px solid #aaa; | |
padding: 2px; | |
background: #eee; | |
line-height: 100%; | |
} | |
code { color: #070 } | |
pre code { color: #000 } | |
blockquote { font-style: italic ; color: #333 } | |
h1 { text-align: center } | |
img { max-width: 34em; max-height: 34em; } | |
#menu { | |
position: fixed; | |
top: 4em; | |
left: 2em; | |
width: 14em; | |
text-align: left; | |
border: 2px solid #ccc; | |
font-size: 100% | |
} | |
#menu a { | |
color: #222; | |
text-decoration: none; | |
} | |
#menu ul { | |
list-style-type: none; | |
padding: 0 1em 0 1em; | |
} | |
#menu h2 { | |
padding: 0 1em 0 1em; | |
} | |
@media print { | |
/* taken from html5-boilerplate css (https://github.com/h5bp/html5-boilerplate/blob/1d320b59a390041f25cf87d790f6472a25a512ef/css/style.css) */ | |
* { background: transparent !important; color: black !important; box-shadow:none !important; text-shadow: none !important; } /* Black prints faster: h5bp.com/s */ | |
a, a:visited { text-decoration: underline; } | |
a[href]:after { content: " (" attr(href) ")"; } | |
abbr[title]:after { content: " (" attr(title) ")"; } | |
.ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; } /* Don't show links for images, or javascript/internal links */ | |
pre, blockquote { border: 1px solid #999; page-break-inside: avoid; } | |
thead { display: table-header-group; } /* h5bp.com/t */ | |
tr, img { page-break-inside: avoid; } | |
img { max-width: 100% !important; } | |
@page { margin: 0.5cm; } | |
p, h2, h3 { orphans: 3; widows: 3; } | |
h2, h3 { page-break-after: avoid; } | |
#menu { display: none; | |
} | |
} | |
</style> | |
<body> | |
<nav id='menu'> | |
<h2>Contents</h2> | |
eos | |
puts renderer.format_outline | |
puts <<-EOF | |
</nav> | |
<div id="container"> | |
EOF | |
puts output.html | |
puts <<-EOF | |
</div> | |
</body> | |
</html> | |
EOF |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment