1 # The LaTeX class.
  2 # Note that methods with a ! in them work like the * versions of the LaTeX
  3 # functions, and methods with a ? in them do not print a newline.
  4 # Alternatively, you can use _star and _suppress, if you prefer a more
  5 # English-like syntax.
  6 class LaTeX
  7   def initialize()
  8     @nesting = 0
  9     @indent = "  "
 10   end
 11 
 12   # Allows the user to change the default indenting (default is two spaces
 13   # per nesting)
 14   attr_accessor :indent
 15 
 16   # Return the string for a newline
 17   def newline()
 18     return "\\\n"
 19   end
 20 
 21   # Return the string for a newline that won't break a page
 22   def newline!()
 23     return "\\*\n"
 24   end
 25 
 26   # Return a string for the LaTeX symbol
 27   def latex()
 28     return "\\LaTeX"
 29   end
 30 
 31   # Return a string for a comment, ended with a carriage-return
 32   def comment(comment)
 33     return "% #{comment}\n"
 34   end
 35 
 36   # Let % be an alternative way to specify a comment
 37   alias_method :%, :comment #,
 38 
 39   # Return an indented string, terminated by a newline if it isn't a comment
 40   # that is already newline-terminated
 41   def indent(str)
 42     s = indent?(str)
 43     if not s =~ /^\s*%.*\n/ then
 44       s << "\n"
 45     end
 46     return s
 47   end
 48 
 49   # Return an indented string that is never newline-terminated
 50   def indent?(str)
 51     s = ""
 52     @nesting.times do
 53       s << @indent
 54     end
 55     s << str
 56     return s
 57   end
 58 
 59   # Create a new LaTeX command
 60   def newcommand(cmd, func)
 61     method_missing("newcommand", "\\#{cmd}", func)
 62   end
 63 
 64   # Print a LaTeX command using the rules specified above
 65   def method_missing(symbol, *args)
 66     symbol = symbol.to_s.gsub('!', '*')
 67     symbol = symbol.to_s.gsub('_star', '*')
 68     nl = !(symbol =~ /(\?|_suppress)\*$/)
 69     symbol = symbol.gsub('\?', '')
 70     symbol = symbol.gsub('_suppress', '')
 71     s = ""
 72     if block_given? then
 73       s << __begin__(symbol, *args) << __gen_newline__(nl)
 74       nesting_tmp = @nesting
 75       @nesting = @nesting.succ
 76       s << proc.call()
 77       @nesting = nesting_tmp
 78       s << __end__(symbol) << __gen_newline__(nl)
 79     else
 80       s << indent?("\\#{symbol}#{__arg_list__(args)}#{__gen_newline__(nl)}")
 81     end
 82     return s
 83   end
 84 
 85   alias_method :__cmd__, :method_missing
 86   alias_method :__env__, :method_missing
 87 
 88   # Return the arguments of a LaTeX function; Use an array to specify
 89   # optional arguments
 90   def __arg_list__(args)
 91     s = ""
 92     args.each do |arg|
 93       case arg
 94       when Array
 95         s << "["
 96         arg.each do |a|
 97           s << "#{a},"
 98         end
 99         s.chop!
100         s << "]"
101       else
102         s << "{#{arg}}"
103       end
104     end
105     return s
106   end
107 
108   # Return a newline if nl is true, otherwise return an empty string
109   def __gen_newline__(nl)
110     return nl ? "\n" : ""
111   end
112 
113   def __begin__(symbol, *args)
114     return indent?("\\begin{#{symbol}}#{__arg_list__(args)}")
115   end
116 
117   def __end__(symbol)
118     return indent?("\\end{#{symbol}}")
119   end
120 end
121 
122 if __FILE__ == $0 then
123   l = LaTeX.new
124   s =
125   l.documentclass("article") +
126   l.newcommand("foo", l.latex) +
127   l.newenvironment("myitemize", l.__begin__("itemize"), l.__end__("itemize")) +
128   l.document {
129     l.title("#{l.LARGE?} This is a test") +
130     l.author("Me") +
131     l.date() +
132     l.maketitle() +
133 
134     l.section("Writing #{l.foo_suppress} documents in Ruby") {
135       l.indent(l % "This is a comment.") +
136       l.indent("I can put text here.")
137     } +
138 
139     l.section("More about #{l.foo_suppress} and Ruby") {
140       l.myitemize {
141         l.item?(["a)"]) + "This is item a.\n" +
142         l.item?(["b)"]) + "This is item b.\n"
143       }
144     }
145   }
146   puts s
147 end