Skip to content

Instantly share code, notes, and snippets.

@namelessjon
Created May 16, 2012 10:19
Show Gist options
  • Save namelessjon/2709284 to your computer and use it in GitHub Desktop.
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.
#!/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' => '&alpha;',
'beta' => '&beta;',
'gamma' => '&gamma;',
'micro' => '&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