latex-tools 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,165 @@
1
+ SER GENERAL PUBLIC LICENSE
2
+ Version 3, 29 June 2007
3
+
4
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
5
+ Everyone is permitted to copy and distribute verbatim copies
6
+ of this license document, but changing it is not allowed.
7
+
8
+
9
+ This version of the GNU Lesser General Public License incorporates
10
+ the terms and conditions of version 3 of the GNU General Public
11
+ License, supplemented by the additional permissions listed below.
12
+
13
+ 0. Additional Definitions.
14
+
15
+ As used herein, "this License" refers to version 3 of the GNU Lesser
16
+ General Public License, and the "GNU GPL" refers to version 3 of the GNU
17
+ General Public License.
18
+
19
+ "The Library" refers to a covered work governed by this License,
20
+ other than an Application or a Combined Work as defined below.
21
+
22
+ An "Application" is any work that makes use of an interface provided
23
+ by the Library, but which is not otherwise based on the Library.
24
+ Defining a subclass of a class defined by the Library is deemed a mode
25
+ of using an interface provided by the Library.
26
+
27
+ A "Combined Work" is a work produced by combining or linking an
28
+ Application with the Library. The particular version of the Library
29
+ with which the Combined Work was made is also called the "Linked
30
+ Version".
31
+
32
+ The "Minimal Corresponding Source" for a Combined Work means the
33
+ Corresponding Source for the Combined Work, excluding any source code
34
+ for portions of the Combined Work that, considered in isolation, are
35
+ based on the Application, and not on the Linked Version.
36
+
37
+ The "Corresponding Application Code" for a Combined Work means the
38
+ object code and/or source code for the Application, including any data
39
+ and utility programs needed for reproducing the Combined Work from the
40
+ Application, but excluding the System Libraries of the Combined Work.
41
+
42
+ 1. Exception to Section 3 of the GNU GPL.
43
+
44
+ You may convey a covered work under sections 3 and 4 of this License
45
+ without being bound by section 3 of the GNU GPL.
46
+
47
+ 2. Conveying Modified Versions.
48
+
49
+ If you modify a copy of the Library, and, in your modifications, a
50
+ facility refers to a function or data to be supplied by an Application
51
+ that uses the facility (other than as an argument passed when the
52
+ facility is invoked), then you may convey a copy of the modified
53
+ version:
54
+
55
+ a) under this License, provided that you make a good faith effort to
56
+ ensure that, in the event an Application does not supply the
57
+ function or data, the facility still operates, and performs
58
+ whatever part of its purpose remains meaningful, or
59
+
60
+ b) under the GNU GPL, with none of the additional permissions of
61
+ this License applicable to that copy.
62
+
63
+ 3. Object Code Incorporating Material from Library Header Files.
64
+
65
+ The object code form of an Application may incorporate material from
66
+ a header file that is part of the Library. You may convey such object
67
+ code under terms of your choice, provided that, if the incorporated
68
+ material is not limited to numerical parameters, data structure
69
+ layouts and accessors, or small macros, inline functions and templates
70
+ (ten or fewer lines in length), you do both of the following:
71
+
72
+ a) Give prominent notice with each copy of the object code that the
73
+ Library is used in it and that the Library and its use are
74
+ covered by this License.
75
+
76
+ b) Accompany the object code with a copy of the GNU GPL and this license
77
+ document.
78
+
79
+ 4. Combined Works.
80
+
81
+ You may convey a Combined Work under terms of your choice that,
82
+ taken together, effectively do not restrict modification of the
83
+ portions of the Library contained in the Combined Work and reverse
84
+ engineering for debugging such modifications, if you also do each of
85
+ the following:
86
+
87
+ a) Give prominent notice with each copy of the Combined Work that
88
+ the Library is used in it and that the Library and its use are
89
+ covered by this License.
90
+
91
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
92
+ document.
93
+
94
+ c) For a Combined Work that displays copyright notices during
95
+ execution, include the copyright notice for the Library among
96
+ these notices, as well as a reference directing the user to the
97
+ copies of the GNU GPL and this license document.
98
+
99
+ d) Do one of the following:
100
+
101
+ 0) Convey the Minimal Corresponding Source under the terms of this
102
+ License, and the Corresponding Application Code in a form
103
+ suitable for, and under terms that permit, the user to
104
+ recombine or relink the Application with a modified version of
105
+ the Linked Version to produce a modified Combined Work, in the
106
+ manner specified by section 6 of the GNU GPL for conveying
107
+ Corresponding Source.
108
+
109
+ 1) Use a suitable shared library mechanism for linking with the
110
+ Library. A suitable mechanism is one that (a) uses at run time
111
+ a copy of the Library already present on the user's computer
112
+ system, and (b) will operate properly with a modified version
113
+ of the Library that is interface-compatible with the Linked
114
+ Version.
115
+
116
+ e) Provide Installation Information, but only if you would otherwise
117
+ be required to provide such information under section 6 of the
118
+ GNU GPL, and only to the extent that such information is
119
+ necessary to install and execute a modified version of the
120
+ Combined Work produced by recombining or relinking the
121
+ Application with a modified version of the Linked Version. (If
122
+ you use option 4d0, the Installation Information must accompany
123
+ the Minimal Corresponding Source and Corresponding Application
124
+ Code. If you use option 4d1, you must provide the Installation
125
+ Information in the manner specified by section 6 of the GNU GPL
126
+ for conveying Corresponding Source.)
127
+
128
+ 5. Combined Libraries.
129
+
130
+ You may place library facilities that are a work based on the
131
+ Library side by side in a single library together with other library
132
+ facilities that are not Applications and are not covered by this
133
+ License, and convey such a combined library under terms of your
134
+ choice, if you do both of the following:
135
+
136
+ a) Accompany the combined library with a copy of the same work based
137
+ on the Library, uncombined with any other library facilities,
138
+ conveyed under the terms of this License.
139
+
140
+ b) Give prominent notice with the combined library that part of it
141
+ is a work based on the Library, and explaining where to find the
142
+ accompanying uncombined form of the same work.
143
+
144
+ 6. Revised Versions of the GNU Lesser General Public License.
145
+
146
+ The Free Software Foundation may publish revised and/or new versions
147
+ of the GNU Lesser General Public License from time to time. Such new
148
+ versions will be similar in spirit to the present version, but may
149
+ differ in detail to address new problems or concerns.
150
+
151
+ Each version is given a distinguishing version number. If the
152
+ Library as you received it specifies that a certain numbered version
153
+ of the GNU Lesser General Public License "or any later version"
154
+ applies to it, you have the option of following the terms and
155
+ conditions either of that published version or of any later version
156
+ published by the Free Software Foundation. If the Library as you
157
+ received it does not specify a version number of the GNU Lesser
158
+ General Public License, you may choose any version of the GNU Lesser
159
+ General Public License ever published by the Free Software Foundation.
160
+
161
+ If the Library as you received it specifies that a proxy can decide
162
+ whether future versions of the GNU Lesser General Public License shall
163
+ apply, that proxy's public statement of acceptance of any version is
164
+ permanent authorization for you to choose that version for the
165
+ Library.
data/README ADDED
@@ -0,0 +1,14 @@
1
+ =latex-tools
2
+ Tools for generating Latex code fragments
3
+
4
+ The latex-tools gem is a collection of classes that facilitate the writing of various elements of Latex code. It is not designed for authoring entire Latex files, merely their component parts. Right now, it only consists of one such 'tool', LatexTable, which provides an easy and convenient way of outputting Latex code for tables from data in a ruby script.
5
+
6
+ ==Installation
7
+ Install the gem just like any other gem:
8
+ gem install latex-tools
9
+
10
+
11
+ ==Documentation Status
12
+ The classes (right now only LatexTable) should be documented well enough to get started, but individual class methods
13
+ are mostly undocumented. This documentation will (hopefully) be added in the future.
14
+
@@ -0,0 +1,89 @@
1
+ # ---
2
+ # Copyright (c) 2011-2012 David Hollman
3
+ # This file is part of the LatexTools rubygem.
4
+ #
5
+ # The LatexTools rubygem is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Lesser Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # The LatexTools rubygem is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Lesser Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Lesser Public License
16
+ # along with the LatexTools rubygem. If not, see <http://www.gnu.org/licenses/>.
17
+ # +++
18
+ #
19
+ # Rakefile for project LatexTools
20
+ #
21
+ #
22
+
23
+ require 'rubygems'
24
+ require 'rake'
25
+ require 'rake/clean'
26
+ require 'rake/rdoctask'
27
+ require 'rake/testtask'
28
+ require 'rake/gempackagetask'
29
+
30
+
31
+
32
+ #----------------
33
+ # Gem packaging
34
+ #++++++++++++++++
35
+ spec = Gem::Specification.new do |s|
36
+ s.name = "latex-tools"
37
+ s.version = "0.1"
38
+ s.has_rdoc = true
39
+ s.extra_rdoc_files = ['README', 'LICENSE']
40
+ s.summary = "Tools for generating Latex code fragments."
41
+ s.description = "The latex-tools gem is a collection of classes that facilitate the writing of various elements of Latex code. It is not designed for authoring entire Latex files, merely their component parts. Right now, it only consists of one such 'tool', LatexTable, which provides an easy and convenient way of outputting Latex code for tables from data in a ruby script."
42
+ s.author = "David S. Hollman"
43
+ s.email = "david.s.hollman@gmail.com"
44
+ s.files = %w(LICENSE README Rakefile) + Dir.glob("{lib,test,examples}/**/*")
45
+ s.require_path = "lib"
46
+ s.test_files = Dir.glob("{test}/**/*")
47
+ s.homepage = "http://github.com/dhollman/ruby-latex-tools"
48
+ end
49
+
50
+ Rake::GemPackageTask.new(spec) do |p|
51
+ p.need_tar = true
52
+ p.need_zip = true
53
+ end
54
+
55
+
56
+
57
+ #----------------
58
+ # Testing
59
+ #++++++++++++++++
60
+ Rake::TestTask.new do |t|
61
+ t.test_files = FileList['test/**/*_test.rb']
62
+ t.libs << Dir["lib","test/lib"]
63
+ end
64
+
65
+
66
+ #----------------
67
+ # Code coverage
68
+ #++++++++++++++++
69
+ begin
70
+ require 'rcov/rcovtask'
71
+ Rcov::RcovTask.new do |t|
72
+ t.libs << Dir["lib","test/lib"]
73
+ t.test_files = FileList['test/**/*_test.rb']
74
+ t.verbose = true
75
+ end
76
+ rescue LoadError
77
+ # The user doesn't have rcov/rcovtask. No worries, we just won't make it available
78
+ end
79
+
80
+
81
+
82
+ #----------------
83
+ # Documentation
84
+ #++++++++++++++++
85
+ Rake::RDocTask.new do |t|
86
+ t.main = "README"
87
+ t.rdoc_files.include("README", "lib/**/*.rb")
88
+ t.rdoc_dir = 'doc/rdoc'
89
+ end
@@ -0,0 +1,7 @@
1
+ t = LatexTable.new(3)
2
+ t << " " << "Height" << "Weight" << endl
3
+ t << hline
4
+ t << "Person 1" << '156 cm' << '55 kg' << endl
5
+ t << "Person 2" << '183 cm' << '62 kg' << endl
6
+ t << hline
7
+ t.to_s
@@ -0,0 +1,9 @@
1
+ \begin{table}
2
+ \begin{tabular}{ccc}
3
+ & Height & Weight \\
4
+ \hline
5
+ Person 1 & 156 cm & 55 kg \\
6
+ Person 2 & 183 cm & 62 kg \\
7
+ \hline
8
+ \end{tabular}
9
+ \end{table}
@@ -0,0 +1,572 @@
1
+
2
+ require 'forwardable'
3
+
4
+ #---
5
+ #TODO Document this old thing
6
+ #+++
7
+
8
+ ##
9
+ # The +LatexTools+ module wraps everything in this package.
10
+ # For convenience of use (i.e. unless you have a good reason not to do so),
11
+ # you should include the following line at the beginning of your source code:
12
+ # include LatexTools
13
+ #
14
+ # Not doing so will result in much more verbose and much harder to read code, which
15
+ # is not how this module was intended to be used. For instance, when the module is
16
+ # included properly, use of the LatexTable class becomes quite clean and "c++ like":
17
+ # :include: examples/table/example1.rb
18
+ # (See the LatexTable class documentation for more.)
19
+ #
20
+ module LatexTools
21
+
22
+
23
+ ##
24
+ # The LatexTable class represents a table in Latex. It provides multiple means of creating, formatting, and
25
+ # outputting Latex tables.
26
+ #
27
+ # == Basic Examples
28
+ # The easiest way to create tables programmatically is to use the #<< method.
29
+ # :include:examples/table/example1.rb
30
+ # This produces
31
+ # :include:examples/table/example1.rb.out
32
+ #
33
+ # More documentation to come in the future, but hopefully this is enough to get you started.
34
+ #
35
+ class LatexTable
36
+ attr_reader(
37
+ :columns,
38
+ :rows,
39
+ :float_number_format,
40
+ :col_divider,
41
+ :col_dividers,
42
+ :v_bordered,
43
+ :v_double_bordered,
44
+ :h_borders,
45
+ :format
46
+ )
47
+
48
+ attr_accessor(
49
+ # if strict is true, don't allow adding of rows when the previous row hasn't been filled
50
+ :strict, #TODO move this to reader and implement a writer that goes back and checks
51
+ :auto_add_columns
52
+ )
53
+
54
+
55
+ class Row # :nodoc: all
56
+
57
+ extend Forwardable
58
+
59
+ attr_reader(
60
+ :special_tag
61
+ )
62
+
63
+ TAGS = [
64
+ :cline,
65
+ :hline,
66
+ :provisional
67
+ ]
68
+
69
+ def_delegators :@entries, :push, :size, :pop, :<<, :[], :[]=, :each, :each_with_index, :last
70
+
71
+ def initialize(tag = nil)
72
+ @entries = Array.new
73
+ if(tag)
74
+ raise ArgumentError unless TAGS.include?(tag)
75
+ @special_tag = tag
76
+ end
77
+ end
78
+
79
+ def num_columns
80
+ if(@special_tag == :cline)
81
+ return @entries.last.end_col
82
+ end
83
+ ret_val = 0
84
+ @entries.each do |entry|
85
+ add_val = entry.kind_of?(MultiColumn) ? entry.width : 1
86
+ if add_val == :fill
87
+ return -1
88
+ end
89
+ ret_val += add_val
90
+ end
91
+ return ret_val
92
+ end
93
+
94
+ def special_tag=(tag)
95
+ raise ArgumentError unless tag.nil? || TAGS.include?(tag)
96
+ @special_tag = tag
97
+ end
98
+ alias :tag :special_tag
99
+ alias :tag= :special_tag=
100
+ end
101
+
102
+ class MultiColumn # :nodoc: all
103
+ attr_accessor(
104
+ :width,
105
+ :alignment,
106
+ :content
107
+ )
108
+
109
+ ALIGNMENTS = [:r, :c, :l]
110
+
111
+ def initialize(nc, aln, cont)
112
+ @width = nc
113
+ @alignment = aln.to_sym
114
+ raise "Invalid alignment specification" unless ALIGNMENTS.include?(@alignment)
115
+ @content = cont
116
+ end
117
+
118
+ end
119
+
120
+ class Cline # :nodoc: all
121
+ attr_accessor(:begin_col, :end_col)
122
+
123
+ def initialize(firstcol, lastcol)
124
+ @begin_col = firstcol
125
+ @end_col = lastcol
126
+ end
127
+
128
+ end
129
+
130
+ def initialize(*args)
131
+ @col_dividers = Array.new
132
+ @v_bordered = false
133
+ @h_borders = 0
134
+ @auto_add_columns = false
135
+ @format = Hash.new{ Array.new }
136
+ @strict = true
137
+ case args.size
138
+ when 0
139
+ @rows = 0
140
+ @columns = 0
141
+ @data = Array.new
142
+ add_row(:provisional)
143
+ when 1
144
+ arg = args.shift
145
+ if(arg.kind_of?(Integer))
146
+ @columns = arg
147
+ @rows = 0
148
+ @data = Array.new
149
+ add_row(:provisional)
150
+ else
151
+ raise TypeError, "Invalid argument type in LatexTable#initialize"
152
+ end
153
+ else
154
+ raise ArgumentError, "Invalid argument count"
155
+ end
156
+ end
157
+
158
+ def tabular_string
159
+ ret_val = ""
160
+ unless(@col_dividers.empty?)
161
+ ret_val += @col_dividers[0]
162
+ end
163
+ 1.upto(@columns) do |i|
164
+ ret_val += "c"
165
+ if(@col_dividers.empty?)
166
+ unless(@col_divider.nil? || i == @columns)
167
+ ret_val += @col_divider
168
+ end
169
+ else
170
+ ret_val += @col_dividers[i]
171
+ end
172
+ end
173
+ return ret_val
174
+ end
175
+
176
+ def to_latex(fontsize = "")
177
+ ret_val = ""
178
+ ret_val = ret_val + "\\begin{table}#{fontsize}\n \\begin{tabular}{"
179
+ ret_val += tabular_string
180
+ ret_val = ret_val + "}\n"
181
+ 1.upto(@h_borders) { ret_val = ret_val + " \\hline\n" }
182
+ ret_val += " "
183
+ ret_val += to_latex_fragment
184
+ if(@h_borders != 0)
185
+ ret_val += "\\\\"
186
+ end
187
+ 1.upto(@h_borders) { ret_val = ret_val + " \n \\hline" }
188
+ ret_val = ret_val + "\n \\end{tabular}\n"
189
+ ret_val = ret_val + "\\end{table}\n"
190
+ ret_val
191
+ end
192
+
193
+ def to_latex_fragment
194
+ ret_val = ""
195
+ indent = " "
196
+ @data.each_with_index do |row, row_num|
197
+ cols_so_far = 0
198
+ if(row.tag == :hline)
199
+ ret_val = ret_val + "\\hline"
200
+ ret_val += " \n" + indent unless row_num == @rows - 1
201
+ elsif(row.tag == :cline)
202
+ row.each do |cline|
203
+ ret_val += "\\cline{" + cline.begin_col.to_s + "-" + cline.end_col.to_s + "} "
204
+ end
205
+ ret_val += " \n" + indent unless row_num == @rows - 1
206
+ elsif(row.tag == :provisional)
207
+ raise RuntimeError, "Programmer needs more coffee: Unhandled provisional" unless row_num == @data.size - 1
208
+ else
209
+ row.each_with_index do |entry, index|
210
+ if(entry)
211
+ content = entry.kind_of?(MultiColumn) ? entry.content : entry
212
+ if(!@format[row_num].nil? && !@format[row_num][index].nil? && content.respond_to?(:to_f))
213
+ content = @format[row_num][index] % content.to_f
214
+ elsif(content.respond_to?(:to_f) && (content.to_f.to_s == content.to_s || content.to_i.to_s == content.to_s) && !@float_number_format.nil?)
215
+ content = @float_number_format % content.to_f
216
+ end
217
+ val = content
218
+
219
+ if(entry.kind_of?(MultiColumn))
220
+ if(entry.width == :fill)
221
+ val = "\\multicolumn{#{@columns - cols_so_far}}{#{entry.alignment.to_s}}{#{val}}"
222
+ raise "Columns spanning to the end must be the last entry in a row." unless entry === row.last
223
+ cols_so_far = @columns
224
+ else
225
+ val = "\\multicolumn{#{entry.width}}{#{entry.alignment.to_s}}{#{val}}"
226
+ cols_so_far += entry.width
227
+ end
228
+ else
229
+ cols_so_far += 1
230
+ end
231
+
232
+ ret_val = ret_val + val.to_s + ((cols_so_far == @columns) ? _end_of_line(row_num, indent) : " & ")
233
+ end
234
+ end
235
+ (cols_so_far + 1).upto(@columns) do |index|
236
+ ret_val = ret_val + " " + ((index == @columns) ? _end_of_line(row_num, indent) : " & ")
237
+ end
238
+ end
239
+ end
240
+ return ret_val
241
+ end
242
+
243
+ def add_row(tag = nil)
244
+ if(@strict)
245
+ spot = @rows - 1
246
+ lastrow = nil
247
+ lastrow = @data[spot] if spot >= 0
248
+ skip_tags = [:hline,:cline,:provisional]
249
+ while(lastrow && skip_tags.include?(lastrow.tag))
250
+ spot -= 1
251
+ if(spot >= 0)
252
+ lastrow = @data[spot]
253
+ else
254
+ lastrow = nil
255
+ end
256
+ end
257
+ ncols = nil
258
+ if(lastrow && (ncols = lastrow.num_columns) != @columns && ncols != -1)
259
+ last_non = _last_non_provisional_row
260
+ unless(last_non && last_non.tag == :cline && @auto_add_columns && lastrow.num_columns < last_non.num_columns)
261
+ raise RuntimeError, "Tried to add new row when previous row had #{ncols} columns which is not the same as #{@columns} columns. Turn #strict off to ignore this, although your latex output may not compile."
262
+ end
263
+ end
264
+ end
265
+ add = !_provisional_remove
266
+ if(add)
267
+ @data.push(Row.new(tag))
268
+ else
269
+ @data.last.tag = tag
270
+ end
271
+ unless(tag == :provisional)
272
+ @rows += 1 if(add)
273
+ end
274
+ end
275
+
276
+ def add_column
277
+ @columns = @columns + 1
278
+ if(!@col_dividers.empty?)
279
+ border = @col_dividers.pop
280
+ @col_dividers.push(@col_divider.nil? ? "" : @col_divider)
281
+ @col_dividers.push(border)
282
+ end
283
+ end
284
+
285
+ def add_spanning_row(text, alignment = :c)
286
+ _cline_check
287
+ tmp = nil
288
+ add = !(_provisional_remove)
289
+ if(!add)
290
+ tmp = @data.last
291
+ else
292
+ tmp = Row.new
293
+ @rows += 1
294
+ @data.push(tmp)
295
+ end
296
+ tmp.push(MultiColumn.new(:fill, alignment, text))
297
+ add_row(:provisional)
298
+ end
299
+
300
+ def add_element_spanning_to_end(text, alignment = :c)
301
+ _cline_check
302
+ _provisional_remove
303
+ @data.last.push(MultiColumn.new(:fill, alignment, text))
304
+ add_row(:provisional)
305
+ end
306
+
307
+ def add_element_spanning(num, text, alignment = :c)
308
+ _cline_check
309
+ _provisional_remove
310
+ raise TypeError.new unless num.kind_of?(Integer) || num == :fill
311
+ if(@data.last.num_columns + num >= @columns)
312
+ if(@auto_add_columns)
313
+ @columns.upto(@data.last.num_columns + num - 1) { add_column }
314
+ else
315
+ _cant_add_column("add_element_spanning")
316
+ end
317
+ end
318
+ @data.last.push(MultiColumn.new(num, alignment, text))
319
+ end
320
+
321
+ def add_element(element)
322
+ _cline_check
323
+ _provisional_remove
324
+
325
+ @data.last.push(element)
326
+ if(@data.last.size > @columns)
327
+ if(@auto_add_columns)
328
+ add_column
329
+ else
330
+ _cant_add_column("add_element")
331
+ end
332
+ end
333
+ end
334
+
335
+ def << (element)
336
+ if(element == :endl)
337
+ add_row(:provisional)
338
+ elsif(element.kind_of?(Cline))
339
+ lastnon = _last_non_provisional_row
340
+ if(lastnon && lastnon.tag != :cline && @data.last.tag != :provisional)
341
+ raise "Invalid cline addition: Can't add a cline to a row that already has non-cline content. Use #add_row or #<<(:endl)"
342
+ end
343
+ self.c_line(element.begin_col, element.end_col)
344
+ elsif(element == :hline || element == :h_line)
345
+ h_line
346
+ else
347
+ add_element(element)
348
+ end
349
+ return self
350
+ end
351
+
352
+ def float_number_format=(format)
353
+ #TODO Check to make sure it is an okay format
354
+ @float_number_format = format
355
+ end
356
+ alias :set_float_number_format :float_number_format=
357
+
358
+ #Note: This method overwrites any non-border changes to the #col_dividers array
359
+ def col_divider=(divider)
360
+ raise ArgumentError, "Invalid column divider" unless _is_valid_col_divider(divider)
361
+ @col_divider = divider
362
+ unless(@col_dividers.nil? || @col_dividers.empty?)
363
+ 1.upto(@col_dividers.size - 2) do |i|
364
+ col_dividers[i] = @col_divider
365
+ end
366
+ end
367
+ end
368
+ alias :set_col_divider :col_divider=
369
+
370
+ #Note: This method resets #col_divider
371
+ def col_dividers=(div_array)
372
+ raise ArgumentError, "Invalid column divider array size. Must either be #columns - 1 (for dividers only) or #columns + 1 (for dividers and borders)" unless ([@columns - 1, @columns + 1].include?(div_array.size))
373
+ raise ArgumentError, "Invalid column divider." unless div_array.all? { |div| _is_valid_col_divider(div) }
374
+ @col_dividers = div_array
375
+ @col_divider = nil
376
+ if(@col_dividers.size == @columns - 1)
377
+ if(@v_double_bordered)
378
+ @col_dividers.unshift("||")
379
+ @col_dividers.push("||")
380
+ elsif(@v_bordered)
381
+ @col_dividers.unshift("|")
382
+ @col_dividers.push("|")
383
+ else
384
+ @col_dividers.unshift('')
385
+ @col_dividers.push('')
386
+ end
387
+ else
388
+ if(@col_dividers[0] == '||' && @col_dividers.last == "||")
389
+ @v_bordered = true
390
+ @v_double_bordered = true
391
+ elsif(@col_dividers[0] == '|' && @col_dividers.last == "|")
392
+ @v_bordered = true
393
+ else # Border will not be preserved in next "internal divider" col_dividers assignment
394
+ @v_bordered = false
395
+ @v_double_bordered = false
396
+ end
397
+ end
398
+ end
399
+ alias :set_col_dividers :col_dividers=
400
+
401
+ # This method ends the current row
402
+ def h_line
403
+ add_row(:hline)
404
+ add_row(:provisional)
405
+ end
406
+ alias :hline :h_line
407
+
408
+ # This method ends the current row
409
+ # NOTE that the numbering is 1 based, just as in Latex itself
410
+ def c_line(begincol, endcol)
411
+ last_row = _last_non_provisional_row
412
+ if(last_row.nil? || last_row.tag != :cline)
413
+ add_row(:cline)
414
+ end
415
+ last_row = _last_non_provisional_row
416
+
417
+ last_element = last_row.last
418
+ if(last_element)
419
+ if(last_element.end_col >= begincol)
420
+ raise ArgumentError, "Overlapping clines. Check the start of the LatexTable#cline call in your code and the end of the previous call"
421
+ end
422
+ end
423
+ if(endcol > @columns)
424
+ if(@auto_add_columns)
425
+ (@columns + 1).upto(endcol) { add_column }
426
+ else
427
+ _cant_add_column("c_line")
428
+ end
429
+ end
430
+
431
+ if(begincol < 1)
432
+ raise "Invalid cline specification (Begin column less than 1). Note that the cline column specification is one-based, just as in Latex"
433
+ end
434
+
435
+ last_row.push(Cline.new(begincol, endcol))
436
+ end
437
+ alias :cline :c_line
438
+
439
+ def to_s
440
+ to_latex
441
+ end
442
+
443
+ def to_str
444
+ to_latex
445
+ end
446
+
447
+ def add_v_border
448
+ if(@v_bordered)
449
+ add_v_double_border
450
+ elsif(@col_dividers.empty?)
451
+ @col_dividers[0] = "|"
452
+ 1.upto(@columns - 1) do |i|
453
+ @col_dividers[i] = @col_divider.nil? ? " " : @col_divider
454
+ end
455
+ @col_dividers[@columns] = "|"
456
+ else
457
+ @col_dividers[0] = "|"
458
+ @col_dividers[@columns] = "|"
459
+ end
460
+ @v_bordered = true
461
+ end
462
+
463
+ def add_v_double_border
464
+ raise "Too many vertical borders. A maximum of two is allowed" if @v_bordered && @col_dividers[0] == "||"
465
+ if(@col_dividers.empty?)
466
+ @col_dividers[0] = "||"
467
+ 1.upto(@columns - 1) do |i|
468
+ @col_dividers[i] = @col_divider.nil? ? '' : @col_divider
469
+ end
470
+ @col_dividers[@columns] = "||"
471
+ else
472
+ @col_dividers[0] = "||"
473
+ @col_dividers[@columns] = "||"
474
+ end
475
+ @v_double_bordered = true
476
+ end
477
+ alias :add_double_v_border :add_v_double_border
478
+
479
+ def add_h_border
480
+ @h_borders = @h_borders + 1
481
+ end
482
+
483
+ def add_border
484
+ add_h_border
485
+ add_v_border
486
+ end
487
+ alias :add_borders :add_border
488
+
489
+
490
+ private
491
+
492
+ def _last_non_provisional_row
493
+ if(@data.last.nil?)
494
+ return nil
495
+ elsif(@data.last.tag == :provisional)
496
+ return nil if(@data.size == 1)
497
+ if @data[@data.size - 2].tag == :provisional
498
+ raise "Internal error. Programmer needs more coffee: Two provisional rows at end of table. This should never happen"
499
+ end
500
+ return @data[@data.size - 2]
501
+ else
502
+ return @data.last
503
+ end
504
+ end
505
+
506
+ def _provisional_remove
507
+ if(@data.last && @data.last.tag == :provisional)
508
+ @data.last.tag = nil
509
+ @rows += 1
510
+ return true
511
+ end
512
+ return false
513
+ end
514
+
515
+ def _cline_check
516
+ if(@data.last && @data.last.tag == :cline)
517
+ add_row(:provisional)
518
+ end
519
+ end
520
+
521
+ def _cant_add_column(method_name)
522
+ raise IndexError, "Columns will not be automatically added in LatexTable##{method_name} when LatexTable#auto_add_columns is set to false."
523
+ end
524
+
525
+ def _end_of_line(row, indent)
526
+ if(row == @rows - 1)
527
+ return " "
528
+ else
529
+ return "\\\\ \n" + indent
530
+ end
531
+ end
532
+
533
+ def _is_valid_col_divider(div)
534
+ #TODO @{} dividers and such
535
+ ['','|','||'].include?(div)
536
+ end
537
+
538
+ end
539
+
540
+
541
+ def multicolumn(ncol, alignment_or_content, content = nil)
542
+ if(content.nil?)
543
+ return LatexTable::MultiColumn.new(ncol, :c, alignment_or_content)
544
+ else
545
+ return LatexTable::MultiColumn.new(ncol, alignment_or_content, content)
546
+ end
547
+ end
548
+ alias :multicol :multicolumn
549
+
550
+
551
+ def fill_to_end(alignment_or_content, content = nil)
552
+ if(content.nil?)
553
+ return LatexTable::MultiColumn.new(:fill, :c, alignment_or_content)
554
+ else
555
+ return LatexTable::MultiColumn.new(:fill, alignment_or_content, content)
556
+ end
557
+ end
558
+
559
+ def hline
560
+ :hline
561
+ end
562
+
563
+ def endl
564
+ :endl
565
+ end
566
+
567
+ def cline(begincol, endcol)
568
+ return LatexTable::Cline.new(begincol, endcol)
569
+ end
570
+
571
+
572
+ end