Chienomi

Markdownで書かれたスライドをHTMLとポータブルPDFと印刷用PDFにする

書き捨てユーティリティコード

  • TOP
  • Old Archives
  • Markdownで書かれたスライドをHTMLとポータブルPDFと印刷用PDFにする

Aki SI&EのPR用に書いたものだが、Markdownで書き、Web(HTML)、配布用PDF、印刷用PDFの3種類を生成する考えでいた。

手でやろうかと思っていたのだが、再生成の回数も多そうだったので、自動化できるようにした。

Markdownで記述したドキュメントと、オリジナルサイズの画像ディレクトリ、mogrify -geometry x300でリサイズした画像ディレクトリが用意されている。 Web用のものについてはHTMLで出力し、リサイズされた画像のWebサーバー上のコピーを参照しなければならない。

PDFについては、MarkdownをLaTeXに変換してという方法もあるにはあったが、画像を含めた細かなデザインを施したかったため、HTMLをベースにPDFに変換することにした。

MarkdownコンバータはRubyのKramdownを使用。改ページはないため、wkhtmltopdfを用いてPDFに変換することとした。 参照しているディレクトリの違いで2回ループを回す。

それぞれHTMLは別に出力し、テンポラリファイルとして出力したHTMLを元にPDFのテンポラリファイルを生成、unitepdfを使ってこれを結合する。

専用のコードなので多くをハードコーディングしているが、これで正常に機能する。

#!/usr/bin/ruby
# -*- mode: ruby; coding: UTF-8 -*-

require 'kramdown'
require 'erb'

def treat_md(file, findex)
  $article_body = doc = Kramdown::Document.new(File.read(file)).to_html
  
  $article_title = doc[/<h1[^>]*>(.*?)<\/h1>/, 1]
  
  foot = Array.new
  if findex > 0
    foot << '<a href="' + ( SOURCEFILES[findex - 1].sub(/\.md$/, '')  + ".html" ) + '">前へ</a>'
  end
  
  foot << '<a href="/si/" target="_top">Aki SI&Eトップページへ</a>'
  
  if findex < ( SOURCEFILES.length - 1 )
    foot << '<a href="' + ( SOURCEFILES[findex + 1].sub(/\.md$/, '')  + ".html" ) + '">次へ</a>'
  end
  
  $article_footer = foot.join
end


TEMPLATE = <<'END'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" http://www.w3.org/TR/xhtml1/dtd/xhtml1-transitional.dtd">
<html>
  <head>
    <title><%= $article_title %></title>
    <meta http-equiv="content-language" content="ja" />
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <style>
body {
% case $mode
% when :Web
  font-size: 130%;
  color: #333;
  font-weight: bold;
  margin: 0px;
  padding: 0px;
% else
  font-size: 18pt;
  color: #000;
  font-weight: bold;
  margin: 2cm auto;
  padding: auto;
% end
  background-color: #fff;
  font-family: "Migu 1C"
}   

h3 {
  font-size: 135%;
}

#Container {
  margin: auto auto 100px;
  max-width: 950px;
  text-align: center;
}

img {
% case $mode
% when :Web
  height: 300px;
% else
  height: 18em;
  max-width: 100%;
% end
}

ul {
  text-align: left;
  margin: auto 1.8em;
}


#ArticleFooter {
% if $mode != :Web
  display:none;
% end
  position: fixed;
  bottom: 0px;
  margin: 0px;
  width: 100%;
  max-height: 100px;
  background-color: #339;
  color: #fff;
  text-align: center;
}

#ArticleFooter a {
  padding: 0 0.8em;
  text-decoration: none;
  color: #fff
}

#ArticleFooter a:hover {
  text-decoration: underline;
}
    </style>
  </head>
  <body>
    <div id="Container">
    <%= $article_body.gsub(
      %r{src="img/},
      %(src="#{$img_type}_img/)
    ) 
    %>
  </div>
    <div id="ArticleFooter">
      <%= $article_footer %>
    </div>
  </body>
</html>
END

$article_body = nil
$article_footer = nil
$article_title = nil

SOURCEFILES = Dir.entries(".").select {|i| i =~ /\.md$/ }.sort

# HTML
$img_type = "/img/si/intro_slideshow/resized"
$mode = :Web

SOURCEFILES.each_with_index do |x, i|
  begin
  treat_md(x, i)
  File.open("out/#{x.sub(".md", ".html")}", "w") do |f|
    f.puts(ERB.new(TEMPLATE, nil, "%<").result)
  end
  ensure
    $article_body = nil
    $article_footer = nil
    $article_title = nil
  end
end

# Web PDF
$img_type = "resized"
$mode = :PDF

SOURCEFILES.each_with_index do |x, i|
  begin
  treat_md(x, i)
  fn = "out/_tmp_#{x.sub(".md", ".html")}"
    File.open(fn, "w") do |f|
      f.puts(ERB.new(TEMPLATE, nil, "%<").result)
    end
  system('wkhtmltopdf "%s" "%s"' % [ fn, fn.gsub(".html", ".pdf") ])
  ensure
    $article_body = nil
    $article_footer = nil
    $article_title = nil
  end
end

system 'pdfunite out/_tmp_*.pdf out/portable.pdf'
system 'rm out/_tmp_*'

# Printable
$img_type = "original"
$mode = :PDF

SOURCEFILES.each_with_index do |x, i|
  begin
  treat_md(x, i)
  fn = "out/_tmp_#{x.sub(".md", ".html")}"
    File.open(fn, "w") do |f|
      f.puts(ERB.new(TEMPLATE, nil, "%<").result)
    end
  system('wkhtmltopdf "%s" "%s"' % [ fn, fn.gsub(".html", ".pdf") ])
  ensure
    $article_body = nil
    $article_footer = nil
    $article_title = nil
  end
end

system 'pdfunite out/_tmp_*.pdf out/printable.pdf'
system 'rm out/_tmp_*'

完成したものについては

Google DOCs Viewerはスライドショー向きではないので、ウェブ版を使うか、PDFをダウンロードするのがお勧め。