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.
- data/LICENSE.txt +58 -0
- data/README +57 -0
- data/examples/test-latex.rb +40 -0
- data/lib/latex.rb +516 -0
- metadata +39 -0
data/LICENSE.txt
ADDED
@@ -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
|
+
|
data/lib/latex.rb
ADDED
@@ -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: []
|