latex 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (5) hide show
  1. data/LICENSE.txt +58 -0
  2. data/README +57 -0
  3. data/examples/test-latex.rb +40 -0
  4. data/lib/latex.rb +516 -0
  5. metadata +39 -0
@@ -0,0 +1,58 @@
1
+ Ruby is copyrighted free software by Yukihiro Matsumoto <matz@netlab.co.jp>.
2
+ You can redistribute it and/or modify it under either the terms of the GPL
3
+ (see COPYING.txt file), or the conditions below:
4
+
5
+ 1. You may make and give away verbatim copies of the source form of the
6
+ software without restriction, provided that you duplicate all of the
7
+ original copyright notices and associated disclaimers.
8
+
9
+ 2. You may modify your copy of the software in any way, provided that
10
+ you do at least ONE of the following:
11
+
12
+ a) place your modifications in the Public Domain or otherwise
13
+ make them Freely Available, such as by posting said
14
+ modifications to Usenet or an equivalent medium, or by allowing
15
+ the author to include your modifications in the software.
16
+
17
+ b) use the modified software only within your corporation or
18
+ organization.
19
+
20
+ c) rename any non-standard executables so the names do not conflict
21
+ with standard executables, which must also be provided.
22
+
23
+ d) make other distribution arrangements with the author.
24
+
25
+ 3. You may distribute the software in object code or executable
26
+ form, provided that you do at least ONE of the following:
27
+
28
+ a) distribute the executables and library files of the software,
29
+ together with instructions (in the manual page or equivalent)
30
+ on where to get the original distribution.
31
+
32
+ b) accompany the distribution with the machine-readable source of
33
+ the software.
34
+
35
+ c) give non-standard executables non-standard names, with
36
+ instructions on where to get the original software distribution.
37
+
38
+ d) make other distribution arrangements with the author.
39
+
40
+ 4. You may modify and include the part of the software into any other
41
+ software (possibly commercial). But some files in the distribution
42
+ are not written by the author, so that they are not under this terms.
43
+
44
+ They are gc.c(partly), utils.c(partly), regex.[ch], st.[ch] and some
45
+ files under the ./missing directory. See each file for the copying
46
+ condition.
47
+
48
+ 5. The scripts and library files supplied as input to or produced as
49
+ output from the software do not automatically fall under the
50
+ copyright of the software, but belong to whomever generated them,
51
+ and may be sold commercially, and may be aggregated with this
52
+ software.
53
+
54
+ 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
55
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
56
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
57
+ PURPOSE.
58
+
data/README ADDED
@@ -0,0 +1,57 @@
1
+ Latex README
2
+ ============
3
+
4
+ Latex is a ruby library and contains LaTeX text generation support for Ruby.
5
+ Its main class LatexFile contains:
6
+ - support for the subfigure environment => LatexFile.figures
7
+ - complex tables with nested headers and subheaders => LatexFile.table
8
+ - the method LatexFile.puts indents the given lines according to begin...end
9
+ blocks.
10
+
11
+ The directory examples/ contains a demonstration of the table generating
12
+ features.
13
+
14
+
15
+ Requirements
16
+ ------------
17
+
18
+ * Ruby 1.8
19
+
20
+
21
+ Install
22
+ -------
23
+
24
+ De-compress archive and enter its top directory.
25
+ Then type:
26
+
27
+ ($ su)
28
+ # ruby setup.rb
29
+
30
+ This simple step installs this program under the default
31
+ location of Ruby libraries. You can also install files into
32
+ your favorite directory by supplying setup.rb some options.
33
+ Try "ruby setup.rb --help".
34
+
35
+
36
+ Alternatively you can use the remote installer RubyGems
37
+ [http://rubygems.rubyforge.org/] for installation. Having RubyGems installed
38
+ on your system, just type:
39
+
40
+ ($ su)
41
+ # gem install latex --remote
42
+
43
+
44
+ Usage
45
+ -----
46
+
47
+ In order to get an overview of the features you can generate
48
+ the RDoc documentation and hava a look at the examples/ directory.
49
+
50
+
51
+ License
52
+ -------
53
+
54
+ Ruby License
55
+
56
+
57
+ Christian Bang, cbang AT web.de
@@ -0,0 +1,40 @@
1
+ require 'latex'
2
+
3
+ tex=LatexFile.new("latextest.tex")
4
+ rows = {}
5
+ rows["row1"] = {"col1" => "r1c1", "col2"=>"r1c2", "col3"=>["r1c3","3"]}
6
+ rows["row2"] = {"col1" => "r2c1", "col2"=>"r2c2", "Col3"=>["r2c3","1.4","2"]}
7
+ tex.table(rows,"common table")
8
+
9
+ File.open("saved_table.dat","w") do |f|
10
+ Marshal::dump(rows, f)
11
+ end
12
+
13
+ rows = {}
14
+ rows["A1"] = {'B'=>
15
+ {'A'=> ['5',6],
16
+ 'C'=> {'F'=>'1','G'=>'2'},
17
+ 'D'=> {'H'=>'3','I'=>'4'}
18
+ },
19
+ 'X'=>[1,3,55.55]}
20
+ rows["A2"] = {'B'=>
21
+ {'C'=> {'F'=>'11','G2'=>'22'},
22
+ 'D'=> {'H'=>'33','I2'=>{'a'=>'44','b'=>'66'}},
23
+ 'E'=> '55'},
24
+ 'X'=>[2.3,5.111,1,4]}
25
+ tex.table(rows,"hierarchical headers", "Rows")
26
+
27
+ File.open("saved_table.dat","r") do |f|
28
+ old_rows = Marshal::load(f)
29
+ rows.merge!(old_rows)
30
+ end
31
+ tex.table(rows,"both tables",:rowTitle=>"Instance") do |x,y|
32
+ order = %w{X B C D E G F H col1 col2 col3 Col3}
33
+ if order.index(x) && order.index(y)
34
+ order.index(x) <=> order.index(y)
35
+ else
36
+ x <=> y
37
+ end
38
+ end
39
+ tex.close
40
+
@@ -0,0 +1,516 @@
1
+ # Copyright (c) 2004 Christian Bang <cbang AT web.de> and Frank Hutter
2
+ #
3
+ # Version 0.1.1
4
+ #
5
+ # All rights reserved. You can redistribute and/or modify it under the same terms as Ruby.
6
+ #
7
+ # This file provides a LatexFile class for Ruby.
8
+
9
+ class String
10
+ #Escapes underscores. Use this if you have a string that contains
11
+ #such but you don't mean the latex semantic of underscore.
12
+ def escape_
13
+ gsub(/_/,"\\_")
14
+ end
15
+ end
16
+
17
+ #examples: Figure.new("9cm", "image.eps", "Test image")
18
+ # Figure.new("angle=-90,width=9.5cm", "rotatedimage.eps", "Rotated image", "link01")
19
+ Figure = Struct.new(:format, :filename, :title, :label)
20
+
21
+ class FormatError < StandardError
22
+ end
23
+
24
+ # +LatexFile+ is a +File+ with special functions for easy generation of
25
+ # LaTeX code.
26
+ # Current mayor features are +table+ and +figures+ generation.
27
+ #
28
+ # You may want to quote underscores ('_') in some Latex code you create. For this use String.escape_ which
29
+ # is available with this file.
30
+ class LatexFile < File
31
+ Header = Struct.new(:name, :children)
32
+
33
+ # the default table font size, default is nil which means the Latex default. See #table for possible values
34
+ attr_accessor :defaultTableFontSize
35
+
36
+ @@defaultUsePackages = %w([latin1]{inputenc} [final]{graphics} [pdftex]{graphicx} [dvips]{color}
37
+ {amsfonts} {subfigure} {lscape} {hyperref})
38
+ @@default_extras = {:fontsize => "10pt" }
39
+ attr_reader :extras
40
+
41
+ # Open a new latex-file for writing.
42
+ # +extras+ is an optional hash that may contain the following flags
43
+ # <tt>:landscape</tt>:: turns all pages 90�
44
+ # <tt>:twocolumn</tt>:: the whole document has two columns
45
+ # <tt>extras[:fontsize]</tt>:: string containing the font size like "10pt"
46
+ # === Example:
47
+ # f = LatexFile.new("test.tex", {:twocolumn => true, :fontsize => "11pt"})
48
+ def initialize(filename, extras = nil)
49
+ super(filename,"w") # open a file for write access
50
+ @extras = extras || Hash.new
51
+ #use defaults for unused entries:
52
+ @extras.each_key{|key| @extras[key] |= @@default_extras[key] }
53
+ @indent = 0 # indent lines in blocks
54
+
55
+ @usePackages = @extras[:usePackages] || @@defaultUsePackages
56
+
57
+ writeDocumentHeader
58
+ @lastWasPrint = false
59
+ end
60
+
61
+ def writeDocumentHeader
62
+ twocolumn = @extras[:twocolumn] ? ",twocolumn" : ""
63
+ landscape = @extras[:landscape] ? "\\special{landscape}" : ""
64
+ puts "\\documentclass[#{@extras[:fontsize]}#{twocolumn}]{article}"
65
+ @usePackages.each{|package| puts "\\usepackage#{package}"}
66
+ puts "\\addtolength{\\oddsidemargin}{-3.5cm}"
67
+ puts "\\addtolength{\\textwidth}{7cm}"
68
+ puts "\\addtolength{\\topmargin}{-3cm}"
69
+ puts "\\addtolength{\\textheight}{5cm}"
70
+ puts "\\newcommand{\\hide}[1]{}"
71
+ puts "#{landscape}"
72
+ puts "\\begin{document}"
73
+ puts "\\DeclareGraphicsExtensions{.jpg,.pdf,.mps,.png}"
74
+ end
75
+
76
+ #Write the latex-file footer and closes the file.
77
+ def close
78
+ puts "\\end{document}"
79
+ super
80
+ end
81
+
82
+ #Prints a string to the latex file and indents each line with respect to the
83
+ #current indentation level that depends on nested blocks.
84
+ def puts(string)
85
+ lines = string.split("\n")
86
+ for line in lines
87
+ @indent -= 2 if line[0,5]=='\\end{' && @indent >= 2
88
+ if @lastWasPrint
89
+ super(line)
90
+ @lastWasPrint = false
91
+ else
92
+ super(" " * @indent + line)
93
+ end
94
+ @indent += 2 if line[0,7]=='\\begin{'
95
+ end
96
+ end
97
+
98
+ #Insert spaces up to the current indentation level
99
+ def indent
100
+ print " "*@indent
101
+ end
102
+
103
+ #Other than puts, print does NOT indent the text. If you want to do so
104
+ #call +indent+ before.
105
+ def print(string)
106
+ @lastWasPrint = true
107
+ @indent -= 2 if string[/\\end/]
108
+ @indent += 2 if string[/\\begin/]
109
+ super(string)
110
+ end
111
+
112
+ # Easy creation of environments.
113
+ # +name+:: the name of the environment.
114
+ # +args+:: an optional list of arguments that will be added after the environment definition.
115
+ # === Example:
116
+ # tex.env("table","[ht]") do
117
+ # tex.puts "<table body>"
118
+ # end
119
+ def env(name, *args)
120
+ puts "\\begin{#{name}}#{args}"
121
+ yield if block_given?
122
+ puts "\\end{#{name}}"
123
+ end
124
+
125
+ ########### Figure generation #################################################
126
+
127
+ #Inserts one (ore more) figure block(s) with given figures as subfigures.
128
+ #+figures+::
129
+ # is an array consisting of +Figure+ instances.
130
+ # <tt>Figure = Struct.new(:format, :filename, :title)</tt>
131
+ # If +format+ begins with a number, it is assumed to be the width of the image.
132
+ # But you can also set e.g. <tt>"height=10.5cm,angle=-90"</tt>.
133
+ # For more info see the documentation to <tt>\includegraphics </tt> in the
134
+ # +graphicx+ package of _LaTeX_.
135
+ #+caption+:: is placed on each figure block that is created.
136
+ #+newPageThreshold+::
137
+ # is maximum the number of figures in a block.
138
+ # If more figures are given then a new block is created with the same
139
+ # caption.
140
+ #+placement+:: an optional placement for the figure blocks.
141
+ #+label+:: an optional label of the figures block ('fig:' is added)
142
+ # You can also use label="\\hypertarget{...}" if you like.
143
+ # Otherwise a \label{fig:...} is generated.
144
+ #=== Example:
145
+ # Experiment = Struct.new(:caption, :figures)
146
+ # thisExperiment = Experiment.new
147
+ # thisExperiment.caption = "Instance: #{instance}_1.dat".escape_
148
+ # thisExperiment.figures = [
149
+ # Figure.new("6.5cm", epsDirectory, "#{instance}-a.eps"),
150
+ # Figure.new("6.5cm", epsDirectory, "#{instance}-b.eps"),
151
+ # Figure.new("14cm", epsDirectory, "#{instance}-c.eps", "Title for image c)")
152
+ # ]
153
+ # tex.figures(thisExperiment.caption, thisExperiment.figures, 3)
154
+ def figures(caption, figures, newPageThreshold, placement = "htbp", label = nil)
155
+ lastimagepath = ""
156
+ figures.each_with_index do |figure,i|
157
+ figure.title = "[{#{figure.title}}]" unless figure.title == "" or figure.title == nil
158
+ if i % newPageThreshold == 0
159
+ puts "\\begin{figure}[#{placement}]"
160
+ puts "\\caption{#{caption}}"
161
+ if label
162
+ if label[0,1]=='\\' #you can use \hypertarget here
163
+ puts label
164
+ else
165
+ puts "\\label{fig:#{label}}"
166
+ end
167
+ end
168
+ puts "\\centering"
169
+ end
170
+ imagepath = File.dirname(figure.filename)
171
+ puts "\\graphicspath{{#{imagepath}}}" if imagepath != "" and imagepath != "." and lastimagepath != imagepath
172
+ lastimagepath = imagepath
173
+ figure.format = "width="+figure.format if figure.format =~ /^[0-9\.]/
174
+ if figure.label
175
+ if figure.label[0,1]=='\\'
176
+ puts figure.label
177
+ else
178
+ puts "\\label{#{figure.label}}"
179
+ end
180
+ end
181
+ puts "\\subfigure#{figure.title}{\\includegraphics[#{figure.format}]{#{File.basename(figure.filename)}}}"
182
+ if i % newPageThreshold == newPageThreshold-1 or figure == figures[-1]
183
+ puts "\\end{figure}"
184
+ puts "\\clearpage"
185
+ end
186
+ end
187
+ end
188
+
189
+ ########### Table Generation functions #################################################
190
+
191
+ #This is a helper function for table generation.
192
+ #It computes the number of columns that are leaves of the tree with root +header_node+.
193
+ #You can also say, it computes the number of leaves of the given tree.
194
+ #+header+:: is of type +Header+: (name, children)
195
+ def getNumColsOfHeader(header)
196
+ header.children.inject(0) do |numChildren, child|
197
+ numChildren + ((child.class == Header) ? getNumColsOfHeader(child) : 1)
198
+ end
199
+ end
200
+ private :getNumColsOfHeader
201
+
202
+ #+hash+:: must contain hashes as values for all keys.
203
+ #These child hashes will be joined/merged to one big hash.
204
+ #The actual leafes will be ignored so that only the header remain.
205
+ def getHeaderMergedHash(list)
206
+ merged = {}
207
+ toMerge = Hash.new {|h,k| h[k] = [] }
208
+ nothingToMerge = true
209
+ for entry in list
210
+ if entry.class == Hash
211
+ entry.each_pair do |key,value|
212
+ toMerge[key] << value
213
+ end
214
+ nothingToMerge = false
215
+ end
216
+ end
217
+ #return list if nothingToMerge
218
+ return nil if nothingToMerge #return only header tree without any data
219
+ toMerge.each_pair do |key,value|
220
+ merged[key] = getHeaderMergedHash(value)
221
+ end
222
+ return merged
223
+ end
224
+ private :getHeaderMergedHash
225
+
226
+ @@default_sort = Proc.new { |x,y| x <=> y }
227
+
228
+ #Given a list of hashes, initially a hash-list of each row it
229
+ #returns a sorted tree representation - a headerList.
230
+ #+sort_block+:: a block that compares header names
231
+ def hash2HeaderList(hash, sort_block = @@default_sort)
232
+ headerList = []
233
+ entries = hash.to_a
234
+ entries.sort! {|x,y| sort_block.call(x[0],y[0])}
235
+ for key,value in entries
236
+ if value.class == Hash
237
+ subtree = hash2HeaderList(value, sort_block)
238
+ headerList << Header.new(key,subtree)
239
+ else
240
+ headerList << key
241
+ end
242
+ end
243
+ return headerList
244
+ end
245
+ private :hash2HeaderList
246
+
247
+ #Performes a breath first traversal of the given tree
248
+ #+headerList+:: a list of header nodes of class +Header+.
249
+ #Returns a list of headers (together with the number of columns they use) for each header line needed.
250
+ #example: <tt>[['B',['C',['F','G']], ['D',['H','I']],['E']]]</tt> =>
251
+ #<tt>[['B'], ['C','D','E'], ['F','G','H','I']]</tt> and with their column width information
252
+ #if the (sub)header covers more than one column =>
253
+ #<tt>[[['B',5]], [['C',2],['D',2],'E'], ['F','G','H','I']]</tt>
254
+ #The length of the returned list is the number of rows needed for the headers.
255
+ def getHeaderLines(headerList) #:nodoc:
256
+ line = []
257
+ nextLevel = [] # collects the children nodes of all nodes processed (BFS)
258
+ hasChildrenInNextLevel = false
259
+ for header in headerList
260
+ if header.class != Header
261
+ line << header #leaf node will not be put in an array with column width info
262
+ nextLevel += ["~"] # insert this for lines below this
263
+ else
264
+ line << [header.name, getNumColsOfHeader(header)]
265
+ nextLevel += header.children
266
+ hasChildrenInNextLevel = true
267
+ end
268
+ end
269
+ return hasChildrenInNextLevel ? [line] + getHeaderLines(nextLevel) : [line]
270
+ end
271
+ private :getHeaderLines
272
+
273
+ #Returns a list of paths to all leaf nodes of the given header-tree list. The Leaf nodes are the final
274
+ #columns. In order to compute the table value at (row,col) we want to access only the leaf cols and not
275
+ #the header columns.
276
+ #Example: let <tt>[['B',['C',['F','G']], ['D',['H','I']],['E']]]</tt> be our
277
+ #header tree. The real columns are F,G,H,I,E. In order to access e.g. 'I' we need to know the path [B,D,I].
278
+ #<tt> => [["B", "C", "F"], ["B", "C", "G"], ["B", "D", "H"], ["B", "D", "I"], ["B", "E"]]</tt>
279
+ def getLeafColumns(headerList, path_prefix = nil) #:nodoc:
280
+ paths = []
281
+ path_prefix = path_prefix || []
282
+ for header in headerList
283
+ if header.class == Header
284
+ paths += getLeafColumns(header.children, path_prefix + [header.name])
285
+ else #header is not of class Header but a leaf header (string)
286
+ paths << path_prefix + [header]
287
+ end
288
+ end
289
+ return paths
290
+ end
291
+ private :getLeafColumns
292
+
293
+ #Pretty print output of table entries. If an entry is an array then
294
+ #+prettyPrintCell+ will be called for every table item. You can override this
295
+ #function if you like a special behaviour.
296
+ def prettyPrintCell(x)
297
+ if x.kind_of?(Integer)
298
+ x = "%d" % x
299
+ elsif x.kind_of?(Float)
300
+ x = "%.2f" % x
301
+ else
302
+ x = x.to_s
303
+ end
304
+ raise(FormatError, "Invalid format: #{x.inspect} contains % that is not sufficiently quoted for latex.", caller) if x =~ /[^\\]%|^%/
305
+ x.gsub(/1.#J|Infinity|1.#INF0/, "$\\infty$")
306
+ end
307
+
308
+
309
+ # Generates a table with the specified entries.
310
+ # +entries+::
311
+ # the row-hash where the keys are the row names which are printed in the first column.
312
+ # The <tt>entries[row]</tt> is a column-hash. Its keys are the column names.
313
+ # A column-hash value can be either
314
+ # - an object (with a <tt>to_s</tt> method) that are printed
315
+ # - an array containing objects. In this case a new row is added for each array entry
316
+ # - a hash in which case the keys of this hash are subheaders.
317
+ # +caption+:: the caption of the table.
318
+ # +args+::
319
+ # is either a hash or further arguments
320
+ # Case 1::
321
+ # The hash is indexed by one of the argument symbols below, the values correspond to the
322
+ # argument values you want to set to the corresponding argument.
323
+ # Instead of symbols you can also use strings, e.g. <tt>:label</tt> or <tt>"label"<</tt>
324
+ # Case 2::
325
+ # args are some more arguments. Then they will be interpreted as the following arguments
326
+ # in the same order. You need not use all of them.
327
+ # <tt>:rowTitle</tt>:: the title of the first column that contains the row names.
328
+ # <tt>:label</tt>:: the label of the table. The "tab:" prefix is added automatically .
329
+ # <tt>:newTableThreshold</tt>::
330
+ # maximum number of lines per table. If reached then a new table will be created.
331
+ # Zero means no restriction. An empty row (entries[name] == nil) also toggles a new table.
332
+ # <tt>:placement</tt>:: the LaTeX placement for the table. See the LaTeX documentation for details.
333
+ # <tt>:empty</tt>:: a string that will be put in those fields that have no value.
334
+ # <tt>:sort</tt>::
335
+ # is used to sort the rows and columns. Default is alphabetical ordering.
336
+ # See the example below to get an idea on how to implement customized ordering.
337
+ # <tt>:landscape</tt>:: if true, it will turn the page by 90 degree.
338
+ # <tt>:fontSize</tt>::
339
+ # A latex fontsize modifier like tiny, scriptsize, footnotesize, small,
340
+ # normalsize (default), large, Large, LARGE, huge, Huge.
341
+ # <tt>:header_hlines</tt>::
342
+ # If +true+, then horizontal lines between headers of different depths are drawn.
343
+ # Default is +false+ (no horizontal header lines).
344
+ #
345
+ # === Example:
346
+ # require 'latex'
347
+ # tex = LatexFile.new("table.tex")
348
+ # rows["row1"] = {'Main Header'=>
349
+ # {'A'=> '5',
350
+ # 'B'=> {'D'=>'1','E'=>'2'},
351
+ # 'C'=> {'F'=>'3','G'=>'4'}},
352
+ # 'X'=> [1,3,55.55]}
353
+ # tex.table(rows,"table with hierarchical headers",:rowTitle=>"Instance")
354
+ # tableSort = Proc.new do |x,y|
355
+ # order = %w{A X B C D E}
356
+ # (order.index(x) && order.index(y)) ? order.index(x) <=> order.index(y) : x <=> y
357
+ # end
358
+ # tex.table(rows,"special ordering", :rowTitle=>"Instance", :sort => tableSort)
359
+ def table(entries, caption, *args)
360
+ tableValidArgs = [:rowTitle, :label, :newTableThreshold, :placement, :empty, :sort, :landscape, :fontSize, :grouping, :header_hlines]
361
+ # raise(ArgumentError,"Too many arguments Latex.table", caller) unless args.length <= 1
362
+ hash = {}
363
+ args = if args[0].kind_of?(Hash)
364
+ args[0].each_pair{|key,value| hash[key.to_sym]= value}; hash # allow string keys also
365
+ else
366
+ args.each_with_index{|arg,i| hash[tableValidArgs[i].to_sym] = arg}; hash
367
+ end
368
+ args.each_key{|key| raise(ArgumentError,"Invalid argument: #{key} for Latex.table", caller) unless tableValidArgs.include?(key)}
369
+ @tableCaption = caption
370
+ rowTitle = args[:rowTitle] || ""
371
+ @tableLabel = args[:label] || ""
372
+ newTableThreshold = args[:newTableThreshold] || (args[:landscape] ? 38 : 60)
373
+ placement = args[:placement] || "htbp"
374
+ empty = args[:empty] ||"-"
375
+ sort_block = args[:sort] ||@@default_sort
376
+ grouping = args[:grouping] || [entries.keys]
377
+ @tableLandscape = args[:landscape]
378
+ @tableFontSize = args[:fontSize] || @defaultTableFontSize
379
+ @header_hlines = args[:header_hlines]
380
+
381
+ #access an element in the table which is located in row _rowName_ and in a column
382
+ #that is defined by a _path_ through the headers that can be obtained with
383
+ #the function +getLeafColumns+.
384
+ def accessElement(entries, rowName, path) #:nodoc:
385
+ entry = entries[rowName]
386
+ for key in path
387
+ entry = entry[key]
388
+ return [] unless entry
389
+ end
390
+ return entry.is_a?(Array) ? entry : [entry]
391
+ end
392
+
393
+ def printTableHeader(placement, headerLines, leafColumns, rowTitle) #:nodoc:
394
+ puts "\\begin{landscape}" if @tableLandscape
395
+ puts "\n\\begin{table}[#{placement}]\n\\begin{center}"
396
+ puts "\\#{@tableFontSize}" if @tableFontSize
397
+ print " "*@indent + "\\begin{tabular}{|"
398
+ print "c|" * (leafColumns.length+1)
399
+ puts "}"
400
+ currentHeaderRowNumber = 0
401
+ puts "\\hline"
402
+ for headerLine in headerLines
403
+ print " "*@indent
404
+ if headerLine == headerLines[-1]
405
+ print "#{rowTitle} & " # row name title comes in the last header line
406
+ else
407
+ print "~ & "
408
+ end
409
+ currentColumn = 2
410
+ cline = ""
411
+ print headerLine.map {|headerName, columns|
412
+ raise(FormatError, "Invalid format: #{headerName.inspect} contains % that is not sufficiently quoted for latex.", caller) if headerName =~ /[^\\]%|^%/
413
+ if columns
414
+ cline += "\\cline{#{currentColumn}-#{(currentColumn+=columns)-1}}" if @header_hlines
415
+ "\\multicolumn{#{columns}}{c|}{#{headerName}}"
416
+ else
417
+ currentColumn += 1
418
+ if currentHeaderRowNumber == headerLines.length-1 || headerName == "~"
419
+ headerName
420
+ else
421
+ delta = "%.1f" % ((headerLines.length-currentHeaderRowNumber-1).to_f)
422
+ "\\raisebox{-#{delta}\\totalheight}[1ex][1ex]{#{headerName}}"
423
+ end
424
+ end
425
+ }.join(" & ")
426
+ puts "\\\\"+cline
427
+ currentHeaderRowNumber += 1
428
+ end
429
+ puts "\\hline\\hline"
430
+ end
431
+
432
+ def printTableFooter #:nodoc:
433
+ puts "\\end{tabular}"
434
+ puts "\\caption{#{@tableCaption}}\n\\label{tab:#@tableLabel}"
435
+ puts "\\end{center}\n\\end{table}\n"
436
+ puts "\\end{landscape}" if @tableLandscape
437
+ end
438
+
439
+ #====== Generate header
440
+ headerList = hash2HeaderList(getHeaderMergedHash(entries.values), sort_block)
441
+ headerLines = getHeaderLines(headerList)
442
+ leafColumns = getLeafColumns(headerList)
443
+ printTableHeader(placement, headerLines, leafColumns, rowTitle)
444
+
445
+ currentRow = 0
446
+ for row_names in grouping
447
+ #======= Compute table break for multiple groupings.
448
+ new_lines = 0
449
+ for row_name in row_names
450
+ max_lines_per_entry = 1
451
+ for col_header in leafColumns
452
+ max_lines_per_entry = [max_lines_per_entry, accessElement(entries,row_name,col_header).length].max
453
+ end
454
+ new_lines += max_lines_per_entry
455
+ end
456
+
457
+ if (currentRow > 0 and currentRow + new_lines >= newTableThreshold)
458
+ currentRow = 0
459
+ printTableFooter # close last table
460
+ printTableHeader(placement, headerLines, leafColumns, rowTitle) # open new table
461
+ else
462
+ unless currentRow == 0
463
+ puts "\\hline" unless row_names == []
464
+ currentRow += 0.5
465
+ end
466
+ end
467
+
468
+ row_names.sort! &sort_block
469
+ #====== Generate entries.
470
+ for row_name in row_names
471
+ if (newTableThreshold > 0 and currentRow >= newTableThreshold)
472
+ currentRow = 0
473
+ printTableFooter # close last table
474
+ printTableHeader(placement, headerLines, leafColumns, rowTitle) # open new table
475
+ end
476
+ max_lines_per_entry = 1
477
+ for col_header in leafColumns
478
+ max_lines_per_entry = [max_lines_per_entry, accessElement(entries,row_name,col_header).length].max
479
+ end
480
+
481
+ for i in 0...max_lines_per_entry
482
+ #print row-name
483
+ print " "*@indent
484
+ output = (i==0 ? row_name.to_s.gsub(/_/,"\\_") : "~")
485
+ delta = "%.1f" % ((max_lines_per_entry-1).to_f)
486
+ output = "\\raisebox{-#{delta}\\totalheight}[1ex][1ex]{#{output}}" if max_lines_per_entry > 1
487
+ print output
488
+
489
+ #print the other columns
490
+ for col_header in leafColumns
491
+ entry = accessElement(entries,row_name,col_header)
492
+ if entry[i]
493
+ output = prettyPrintCell(entry[i])
494
+ delta = "%.1f" % (max_lines_per_entry-entry.length).to_f
495
+ else
496
+ output = (i==0 ? empty : "~")
497
+ delta = max_lines_per_entry
498
+ end
499
+ #Center the entries vertically in the cell. Out-comment this if you don't like that.
500
+ if entry.length != max_lines_per_entry# && entry.length > 0
501
+ output = "\\raisebox{-#{delta}\\totalheight}[1ex][1ex]{#{output}}" unless output == "~"
502
+ end
503
+ print " & " + output
504
+ end
505
+ puts "\\\\"
506
+ end
507
+ puts "\\hline"
508
+ currentRow += max_lines_per_entry
509
+ end
510
+ end
511
+ #====== Generate footer.
512
+ printTableFooter
513
+ end
514
+ end
515
+
516
+
metadata ADDED
@@ -0,0 +1,39 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.8.1
3
+ specification_version: 1
4
+ name: latex
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.1.1
7
+ date: 2004-09-30
8
+ summary: Latex is a LaTeX text generation library for Ruby.
9
+ require_paths:
10
+ - lib
11
+ author: Christian Bang
12
+ email: cbang AT web.de
13
+ homepage: http://latex.rubyforge.org
14
+ rubyforge_project: latex
15
+ description:
16
+ autorequire: latex/latex
17
+ default_executable:
18
+ bindir: bin
19
+ has_rdoc: true
20
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
21
+ requirements:
22
+ -
23
+ - ">"
24
+ - !ruby/object:Gem::Version
25
+ version: 0.0.0
26
+ version:
27
+ platform: ruby
28
+ files:
29
+ - lib/latex.rb
30
+ - README
31
+ - LICENSE.txt
32
+ - examples/test-latex.rb
33
+ test_files: []
34
+ rdoc_options: []
35
+ extra_rdoc_files: []
36
+ executables: []
37
+ extensions: []
38
+ requirements: []
39
+ dependencies: []