わさっきhb

大学(教育研究)とか ,親馬鹿とか,和歌山とか,とか,とか.

C to PDF

今年度の講義では,授業資料を配ることにしました.
一つは,PowerPointファイルを6up両面1枚で印刷したもの.学生アンケートの「ノートが取りづらい」に配慮したものです*1
もう一つは,例題プログラムです.これまでは,配列を使うプログラムあたりから配り始めていましたが,座学でソースコードを「手元に置く」ほうがいいと考え,初回から配布しました.
これまで,例題プログラムはWindowsのメモ帳で開き,印刷していました.ゴシック体です.試験問題はTeXベースで作っていまして,有名な「\」を含め,字形が違います*2.さらにメモ帳ベースだと,行番号を付けるのに苦労します*3
何ができるといいか,整理してみると…

  • Cのソースファイルを入力として,Rubyスクリプトを一つ起動すれば,例題プログラムのPDFファイルが生成できるようにしたい.
  • 行番号を振りたい.できればソースファイルと別フォント(小さくするなど)にしたい.
  • Cのソースファイルが,1ページに収まらない行数でも,生成されるPDFファイルに問題がないようにしたい.

3番目の要請は,実はこれまでの試験問題作成で,このトラブルがありまして,いつも,1ページに収めるよう,プログラム読解問題を選定・修正していたのでした.
それと,これくらいならシェルスクリプトでも書けそうですが,これまで例題ソースファイルを,Webやその他への提供する(基本的にファイルコピーですが)ためRubyスクリプトを書いていて,require "src2pdf.rb" などとして連携したいとも考えました.
そんなこんなでプログラムです.src2pdf.rbというファイル名で,一見任意のファイルを変換できそうですが,実際には入力ファイル名は「.c」決め打ちです.

#!/usr/bin/env ruby
# -*- coding: utf-8 -*-

# src2pdf.rb

require "fileutils"

class Src2pdf
  def initialize(cf)
    @input_file = cf
    @input_file_fullpath = File.expand_path(@input_file)
    @targetdir = File.dirname(@input_file_fullpath)
    @workdir = File.dirname(__FILE__)
    @basename = File.basename(@input_file, ".c")
    @cfile = @basename + ".c"
    @texfile = @basename + ".tex"
    @pdffile = @basename + ".pdf"
    @opt_fu = {:verbose => true}
    @opt_keeptmpfile = false
  end
  
  def my_system(command)
    puts command
    system command
  end
  
  def create_texsrc
    # @cfile
    my_system("nkf -s -Lw #{@input_file_fullpath} > #{@cfile}")
    # @texfile
    body = open("src2pdf.tex").read
    body.sub!(/CFILENAME/, @cfile)
    open(@texfile, "w") do |fout|
      fout.print body
    end
  end

  def tex2dvi
    my_system("platex #{@basename}")
  end

  def dvi2pdf
    my_system("dvipdfmx #{@basename}")
  end

  def mv_pdf
    FileUtils.mv(@pdffile, @targetdir, @opt_fu)
  end

  def cleanup
    FileUtils.rm(Dir.glob("#{@basename}*"), @opt_fu)
  end

  def start
    Dir.chdir(@workdir) do
      create_texsrc
      tex2dvi
      dvi2pdf
      mv_pdf
      cleanup unless @opt_keeptmpfile
    end
  end
end

if __FILE__ == $0
  if ARGV.empty?
    puts "usage: ruby #{$0} C_file ..."
    exit
  end
  ARGV.each do |filename|
    Src2pdf.new(filename).start
  end
end

処理内容は次のとおり.

  1. 入力ファイル(.c),中間ファイル,出力ファイル(.pdf)の名前を求めます.なお,中間ファイルは,src2pdf.rbと同じディレクトリに作ります.
  2. 入力ファイルをWindows環境のplatexに通せるよう,文字コードを変換します.
  3. texファイルを作ります.
  4. platexコマンドを実行し,dviファイルを作ります.
  5. dvipdfmxコマンドを実行し,pdfファイルを作ります.
  6. pdfファイルを,入力ファイルと同じディレクトリに移動します.
  7. 中間ファイルを削除します.

スクリプトの中で,"src2pdf.tex" というファイルを開いています.その内容は以下のとおり.「CFILENAME」のところを置き換えて別ファイルで保存し,platexの入力とします.もう少し複雑な置き換えが必要なら,eRubyを使うべきですね.

% src2pdf.tex
\documentclass[12pt,a4paper]{jsarticle}
\usepackage{misc}
\parindent=0pt
\unitlength=1mm
\pagestyle{empty}
\addtolength{\evensidemargin}{-1cm}
\addtolength{\oddsidemargin}{-1cm}
\addtolength{\textwidth}{2cm}
\addtolength{\topmargin}{-2cm}
\addtolength{\textheight}{3cm}
\begin{document}
\baselineskip=.7\baselineskip
\listing{CFILENAME}
\end{document}

「\usepackage{misc}」ですが,TeXにソースコードを埋め込む. - ぴょぴょぴょ? - Linuxとかプログラミングの覚え書き -からmisc.styを知りました.このファイル,Windowsplatex環境には含まれていないようですが,Googleで探すと,ほどなく見つかりましたので,これもsrc2pdf.rbと同じディレクトリに置きました.二十数年前のファイルが今でも動くのは,すごいものです.
なのですが,src2pdf.rbを実行してPDF化したところ,行番号のフォントがソースと同じ形・大きさなのは,うれしくありません.そこで

    \everypar{\advance\lineno by 1 \llap{\the\lineno\ \ }}\input#1

のところを

    \everypar{\advance\lineno by 1 \llap{{\rm\scriptsize\the\lineno}\ \ }}\input#1

と変更してから,PDF化やり直し.いい感じになりました.
このRubyスクリプトには,「入力ファイルをsrc2pdf.rbがあるディレクトリに置いてから,実行すると,nkfのところでおかしな挙動になる」「入力ファイルの名前がsrc2pdf.cやmisc.cだと,src2pdf.texやmisc.styが中間ファイルと勘違いされて削除してしまう」というバグが含まれています.自分が使う際にはそういうことがないので放置なのですが,警告を出すか何かすべきですね….

*1:とはいえ,1回の授業のスライドは,6×2=12枚で収まらず,スクリーンには20〜30枚,そして資料は12枚こっきりです.資料にないページは,ノートをとるか,資料に書き加えてもらうことになります.この資料の位置付けや使い方を,きちんと説明しないといけないのですが.

*2:別の言い方をすると,「\」の字形について,試験やそれに近い段階ではなく,早い段階から認識しておいてほしいという意図があります.

*3:行番号なしでは,説明で時間のロスを伴います.配って,番号を付けるよう指示するのも,そこにタイムロスが生じます.