mack-pdf_writer 0.8.1 → 0.8.2

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 (74) hide show
  1. data/lib/gems/archive-tar-minitar-0.5.2/bin/minitar +27 -0
  2. data/lib/gems/archive-tar-minitar-0.5.2/lib/archive/tar/minitar/command.rb +814 -0
  3. data/lib/gems/archive-tar-minitar-0.5.2/lib/archive/tar/minitar.rb +979 -0
  4. data/lib/gems/color-1.4.0/lib/color/cmyk.rb +281 -0
  5. data/lib/gems/color-1.4.0/lib/color/css.rb +30 -0
  6. data/lib/gems/color-1.4.0/lib/color/grayscale.rb +214 -0
  7. data/lib/gems/color-1.4.0/lib/color/hsl.rb +223 -0
  8. data/lib/gems/color-1.4.0/lib/color/palette/adobecolor.rb +274 -0
  9. data/lib/gems/color-1.4.0/lib/color/palette/gimp.rb +118 -0
  10. data/lib/gems/color-1.4.0/lib/color/palette/monocontrast.rb +182 -0
  11. data/lib/gems/color-1.4.0/lib/color/palette.rb +18 -0
  12. data/lib/gems/color-1.4.0/lib/color/rgb/metallic.rb +45 -0
  13. data/lib/gems/color-1.4.0/lib/color/rgb-colors.rb +357 -0
  14. data/lib/gems/color-1.4.0/lib/color/rgb.rb +455 -0
  15. data/lib/gems/color-1.4.0/lib/color/yiq.rb +86 -0
  16. data/lib/gems/color-1.4.0/lib/color.rb +147 -0
  17. data/lib/gems/pdf-writer-1.1.8/bin/techbook +24 -0
  18. data/lib/gems/pdf-writer-1.1.8/lib/pdf/charts/stddev.rb +430 -0
  19. data/lib/gems/pdf-writer-1.1.8/lib/pdf/charts.rb +13 -0
  20. data/lib/gems/pdf-writer-1.1.8/lib/pdf/math.rb +108 -0
  21. data/lib/gems/pdf-writer-1.1.8/lib/pdf/quickref.rb +332 -0
  22. data/lib/gems/pdf-writer-1.1.8/lib/pdf/simpletable.rb +947 -0
  23. data/lib/gems/pdf-writer-1.1.8/lib/pdf/techbook.rb +901 -0
  24. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/arc4.rb +63 -0
  25. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/fontmetrics.rb +203 -0
  26. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/fonts/Courier-Bold.afm +342 -0
  27. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/fonts/Courier-BoldOblique.afm +342 -0
  28. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/fonts/Courier-Oblique.afm +342 -0
  29. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/fonts/Courier.afm +342 -0
  30. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/fonts/Helvetica-Bold.afm +2827 -0
  31. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/fonts/Helvetica-BoldOblique.afm +2827 -0
  32. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/fonts/Helvetica-Oblique.afm +3051 -0
  33. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/fonts/Helvetica.afm +3051 -0
  34. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/fonts/MustRead.html +19 -0
  35. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/fonts/Symbol.afm +213 -0
  36. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/fonts/Times-Bold.afm +2588 -0
  37. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/fonts/Times-BoldItalic.afm +2384 -0
  38. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/fonts/Times-Italic.afm +2667 -0
  39. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/fonts/Times-Roman.afm +2419 -0
  40. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/fonts/ZapfDingbats.afm +225 -0
  41. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/graphics/imageinfo.rb +365 -0
  42. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/graphics.rb +813 -0
  43. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/lang/en.rb +99 -0
  44. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/lang.rb +43 -0
  45. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/object/action.rb +35 -0
  46. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/object/annotation.rb +42 -0
  47. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/object/catalog.rb +39 -0
  48. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/object/contents.rb +65 -0
  49. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/object/destination.rb +40 -0
  50. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/object/encryption.rb +53 -0
  51. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/object/font.rb +72 -0
  52. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/object/fontdescriptor.rb +34 -0
  53. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/object/fontencoding.rb +40 -0
  54. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/object/image.rb +304 -0
  55. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/object/info.rb +51 -0
  56. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/object/outline.rb +30 -0
  57. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/object/outlines.rb +30 -0
  58. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/object/page.rb +195 -0
  59. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/object/pages.rb +115 -0
  60. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/object/procset.rb +46 -0
  61. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/object/viewerpreferences.rb +74 -0
  62. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/object.rb +23 -0
  63. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/ohash.rb +58 -0
  64. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/oreader.rb +25 -0
  65. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/state.rb +48 -0
  66. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer/strokestyle.rb +138 -0
  67. data/lib/gems/pdf-writer-1.1.8/lib/pdf/writer.rb +2729 -0
  68. data/lib/gems/transaction-simple-1.4.0/lib/transaction/simple/group.rb +146 -0
  69. data/lib/gems/transaction-simple-1.4.0/lib/transaction/simple/threadsafe/group.rb +36 -0
  70. data/lib/gems/transaction-simple-1.4.0/lib/transaction/simple/threadsafe.rb +68 -0
  71. data/lib/gems/transaction-simple-1.4.0/lib/transaction/simple.rb +486 -0
  72. data/lib/gems.rb +13 -0
  73. data/lib/mack-pdf_writer.rb +2 -0
  74. metadata +111 -16
@@ -0,0 +1,947 @@
1
+ #--
2
+ # PDF::Writer for Ruby.
3
+ # http://rubyforge.org/projects/ruby-pdf/
4
+ # Copyright 2003 - 2005 Austin Ziegler.
5
+ #
6
+ # Licensed under a MIT-style licence. See LICENCE in the main distribution
7
+ # for full licensing information.
8
+ #
9
+ # $Id: simpletable.rb 186 2007-12-10 22:58:48Z sandal $
10
+ #++
11
+ require 'pdf/writer'
12
+ require 'transaction/simple/group'
13
+
14
+ # This class will create tables with a relatively simple API and internal
15
+ # implementation.
16
+ class PDF::SimpleTable
17
+
18
+ include Transaction::Simple
19
+
20
+ # Defines formatting options for a column.
21
+ class Column
22
+ def initialize(name)
23
+ @name = name
24
+
25
+ yield self if block_given?
26
+ end
27
+
28
+ # The heading of the column. This should be an instance of
29
+ # PDF::SimpleTable::Column::Heading. If it is not, it will be
30
+ # converted into one.
31
+ attr_accessor :heading
32
+ def heading=(hh) #:nodoc:
33
+ unless hh.kind_of?(Heading)
34
+ hh = Heading.new(hh)
35
+ end
36
+ @heading = hh
37
+ end
38
+ # The name of the column.
39
+ attr_reader :name
40
+ # The width of the column. If this value is set, the column will be
41
+ # exactly this number of units wide.
42
+ attr_accessor :width
43
+ # The data name that will be used to provide a hyperlink for values in
44
+ # this column.
45
+ attr_accessor :link_name
46
+ # The justification of the column. May be :left, :right, :center, or
47
+ # :full.
48
+ attr_accessor :justification
49
+
50
+ # Formatting options for heading rows. Each column can have a separate
51
+ # heading value.
52
+ class Heading
53
+ def initialize(title = nil)
54
+ @title = title
55
+ yield self if block_given?
56
+ end
57
+
58
+ # Indicates that the heading should be rendered bold.
59
+ attr_accessor :bold
60
+ # The justification of the heading of the column. May be :left,
61
+ # :center, :right, or :full.
62
+ attr_accessor :justification
63
+ # The title of the heading. If nothing is present, the name of the
64
+ # column will be used when headings are displayed.
65
+ attr_accessor :title
66
+ end
67
+ end
68
+
69
+ def initialize
70
+ @column_order = []
71
+ @data = []
72
+ @columns = {}
73
+
74
+ @show_lines = :outer
75
+ @show_headings = true
76
+ @shade_rows = :shaded
77
+ @shade_color = Color::RGB::Grey80
78
+ @shade_color2 = Color::RGB::Grey70
79
+ @shade_headings = false
80
+ @shade_heading_color = Color::RGB::Grey90
81
+ @font_size = 10
82
+ @heading_font_size = 12
83
+ @title_font_size = 12
84
+ @title_gap = 5
85
+ @title_color = Color::RGB::Black
86
+ @heading_color = Color::RGB::Black
87
+ @text_color = Color::RGB::Black
88
+ @line_color = Color::RGB::Black
89
+ @position = :center
90
+ @orientation = :center
91
+ @bold_headings = false
92
+
93
+ @cols = PDF::Writer::OHash.new
94
+ @width = 0
95
+ @maximum_width = 0
96
+
97
+ @gap = 5
98
+ @row_gap = 2
99
+ @column_gap = 5
100
+ @header_gap = 0
101
+
102
+ @minimum_space = 0
103
+ @protect_rows = 1
104
+ @split_rows = false
105
+
106
+ @inner_line_style = PDF::Writer::StrokeStyle.new(1)
107
+ @outer_line_style = PDF::Writer::StrokeStyle.new(1)
108
+
109
+ yield self if block_given?
110
+ end
111
+
112
+ # An array of Hash entries. Each row is a Hash where the keys are the
113
+ # names of the columns as specified in #column_order and the values are
114
+ # the values of the cell.
115
+ attr_accessor :data
116
+ # An array that defines the order of the columns in the table. The
117
+ # values in this array are the column names in #data. The columns will
118
+ # be presented in the order defined here.
119
+ attr_accessor :column_order
120
+ # An array that defines columns and column options for the table. The
121
+ # entries should be PDF::SimpleTable::Column objects.
122
+ attr_accessor :columns
123
+
124
+ # The title to be put on the top of the table.
125
+ attr_accessor :title
126
+
127
+ # Whether to display the lines on the table or not. Valid values are:
128
+ #
129
+ # <tt>:none</tt>:: Displays no lines.
130
+ # <tt>:outer</tt>:: Displays outer lines only. *Default*
131
+ # <tt>:inner</tt>:: Displays inner lines only.
132
+ # <tt>:all</tt>:: Displays all lines, inner and outer.
133
+ attr_accessor :show_lines
134
+ # Displays the headings for the table if +true+. The default is +true+.
135
+ attr_accessor :show_headings
136
+ # Controls row shading.
137
+ #
138
+ # <tt>:none</tt>:: No row shading; all rows are the standard
139
+ # background colour.
140
+ # <tt>:shaded</tt>:: Alternate lines will be shaded; half of the rows
141
+ # will be the standard background colour; the rest
142
+ # of the rows will be shaded with #shade_color.
143
+ # *Default*
144
+ # <tt>:striped</tt>:: Alternate lines will be shaded; half of the rows
145
+ # will be shaded with #shade_color; the rest of the
146
+ # rows will be shaded with #shade_color2.
147
+ attr_accessor :shade_rows
148
+ # The main row shading colour. Defaults to Color::RGB::Grey80. Used with
149
+ # #shade_rows of <tt>:shaded</tt> and <tt>:striped</tt>.
150
+ attr_accessor :shade_color
151
+ # The alternate row shading colour, used with #shade_rows of
152
+ # <tt>:striped</tt>. Defaults to Color::RGB::Grey70.
153
+ attr_accessor :shade_color2
154
+ # Places a background colour in the heading if +true+.
155
+ attr_accessor :shade_headings
156
+ # Defines the colour of the background shading for the heading if
157
+ # #shade_headings is +true+. Default is Color::RGB::Grey90.
158
+ attr_accessor :shade_heading_color
159
+ # The font size of the data cells, in points. Defaults to 10 points.
160
+ attr_accessor :font_size
161
+ # The font size of the heading cells, in points. Defaults to 12 points.
162
+ attr_accessor :heading_font_size
163
+ # The font size of the title, in points. Defaults to 12 points.
164
+ attr_accessor :title_font_size
165
+ # The gap, in PDF units, between the title and the table. Defaults to 5
166
+ # units.
167
+ attr_accessor :title_gap
168
+ # The text colour of the title. Defaults to Color::RGB::Black.
169
+ attr_accessor :title_color
170
+ # The text colour of the heading. Defaults to Color::RGB::Black.
171
+ attr_accessor :heading_color
172
+ # The text colour of the body cells. Defaults to Color::RGB::Black.
173
+ attr_accessor :text_color
174
+ # The colour of the table lines. Defaults to Color::RGB::Black.
175
+ attr_accessor :line_color
176
+ # The +x+ position of the table. This will be one of:
177
+ #
178
+ # <tt>:left</tt>:: Aligned with the left margin.
179
+ # <tt>:right</tt>:: Aligned with the right margin.
180
+ # <tt>:center</tt>:: Centered between the margins. *Default*.
181
+ # <em>offset</em>:: The absolute position of the table, relative from
182
+ # the left margin.
183
+ attr_accessor :position
184
+ # The orientation of the table relative to #position.
185
+ #
186
+ # <tt>:left</tt>:: The table is to the left of #position.
187
+ # <tt>:right</tt>:: The table is to the right of #position.
188
+ # <tt>:center</tt>:: The table is centred at #position.
189
+ # <em>offset</em>:: The left of the table is offset from #position.
190
+ attr_accessor :orientation
191
+ # Makes the heading text bold if +true+. Defaults to +false+.
192
+ attr_accessor :bold_headings
193
+ # Specifies the width of the table. If the table is smaller than the
194
+ # provided width, columns are proportionally stretched to fit the width
195
+ # of the table. If the table is wider than the provided width, columns
196
+ # are proportionally shrunk to fit the width of the table. Content may
197
+ # need to wrap in this case.
198
+ #
199
+ # Defaults to zero, which indicates that the size whould be determined
200
+ # automatically based on the content and the margins.
201
+ attr_accessor :width
202
+ # Specifies the maximum width of the table. The table will not grow
203
+ # larger than this width under any circumstances.
204
+ #
205
+ # Defaults to zero, which indicates that there is no maximum width
206
+ # (aside from the margin size).
207
+ attr_accessor :maximum_width
208
+ # The space, in PDF user units, added to the top and bottom of each row
209
+ # between the text and the lines of the cell. Default 2 units.
210
+ attr_accessor :row_gap
211
+ # The space, in PDF user units, on the left and right sides of each
212
+ # cell. Default 5 units.
213
+ attr_accessor :column_gap
214
+
215
+ # The minimum space between the bottom of each row and the bottom
216
+ # margin. If the amount of space is less than this, a new page will be
217
+ # started. Default is 100 PDF user units.
218
+ attr_accessor :minimum_space
219
+ # The number of rows to hold with the heading on the page. If there are
220
+ # less than this number of rows on the page, then move the whole lot
221
+ # onto the next page. Default is one row.
222
+ attr_accessor :protect_rows
223
+ # Allows a table's rows to be split across page boundaries if +true+.
224
+ # This defaults to +false+.
225
+ attr_accessor :split_rows
226
+ # The number of PDF user units to leave open at the top of a page after
227
+ # a page break. This is typically used for a repeating page header, etc.
228
+ # Defaults to zero units.
229
+ attr_accessor :header_gap
230
+ # Defines the inner line style. The default style is a solid line with a
231
+ # thickness of 1 unit.
232
+ attr_accessor :inner_line_style
233
+ # Defines the outer line style. The default style is a solid line with a
234
+ # thickness of 1 unit.
235
+ attr_accessor :outer_line_style
236
+
237
+ # Render the table on the PDF::Writer document provided.
238
+ def render_on(pdf)
239
+ if @column_order.empty?
240
+ raise TypeError, PDF::Writer::Lang[:simpletable_columns_undefined]
241
+ end
242
+ if @data.empty?
243
+ raise TypeError, PDF::Writer::Lang[:simpletable_data_empty]
244
+ end
245
+
246
+ low_y = descender = y0 = y1 = y = nil
247
+
248
+ @cols = PDF::Writer::OHash.new
249
+ @column_order.each do |name|
250
+ col = @columns[name]
251
+ if col
252
+ @cols[name] = col
253
+ else
254
+ @cols[name] = PDF::SimpleTable::Column.new(name)
255
+ end
256
+ end
257
+
258
+ @gap = 2 * @column_gap
259
+
260
+ max_width = __find_table_max_width__(pdf)
261
+ pos, t, x, adjustment_width, set_width = __find_table_positions__(pdf, max_width)
262
+
263
+ # if max_width is specified, and the table is too wide, and the width
264
+ # has not been set, then set the width.
265
+ if @width.zero? and @maximum_width.nonzero? and ((t - x) > @maximum_width)
266
+ @width = @maximum_width
267
+ end
268
+
269
+ if @width and (adjustment_width > 0) and (set_width < @width)
270
+ # First find the current widths of the columns involved in this
271
+ # mystery
272
+ cols0 = PDF::Writer::OHash.new
273
+ cols1 = PDF::Writer::OHash.new
274
+
275
+ xq = presentWidth = 0
276
+ last = nil
277
+
278
+ pos.each do |name, colpos|
279
+ if @cols[last].nil? or
280
+ @cols[last].width.nil? or
281
+ @cols[last].width <= 0
282
+ unless last.nil? or last.empty?
283
+ cols0[last] = colpos - xq - @gap
284
+ presentWidth += (colpos - xq - @gap)
285
+ end
286
+ else
287
+ cols1[last] = colpos - xq
288
+ end
289
+ last = name
290
+ xq = colpos
291
+ end
292
+
293
+ # cols0 contains the widths of all the columns which are not set
294
+ needed_width = @width - set_width
295
+
296
+ # If needed width is negative then add it equally to each column,
297
+ # else get more tricky.
298
+ if presentWidth < needed_width
299
+ diff = (needed_width - presentWidth) / cols0.size.to_f
300
+ cols0.each_key { |name| cols0[name] += diff }
301
+ else
302
+ cnt = 0
303
+ loop do
304
+ break if (presentWidth <= needed_width) or (cnt >= 100)
305
+ cnt += 1 # insurance policy
306
+ # Find the widest columns and the next to widest width
307
+ aWidest = []
308
+ nWidest = widest = 0
309
+ cols0.each do |name, w|
310
+ if w > widest
311
+ aWidest = [ name ]
312
+ nWidest = widest
313
+ widest = w
314
+ elsif w == widest
315
+ aWidest << name
316
+ end
317
+ end
318
+
319
+ # Then figure out what the width of the widest columns would
320
+ # have to be to take up all the slack.
321
+ newWidestWidth = widest - (presentWidth - needed_width) / aWidest.size.to_f
322
+ if newWidestWidth > nWidest
323
+ aWidest.each { |name| cols0[name] = newWidestWidth }
324
+ presentWidth = needed_width
325
+ else
326
+ # There is no space, reduce the size of the widest ones down
327
+ # to the next size down, and we will go round again
328
+ aWidest.each { |name| cols0[name] = nWidest }
329
+ presentWidth -= (widest - nWidest) * aWidest.size
330
+ end
331
+ end
332
+ end
333
+
334
+ # cols0 now contains the new widths of the constrained columns. now
335
+ # need to update the pos and max_width arrays
336
+ xq = 0
337
+ pos.each do |name, colpos|
338
+ pos[name] = xq
339
+
340
+ if @cols[name].nil? or
341
+ @cols[name].width.nil? or
342
+ @cols[name].width <= 0
343
+ if not cols0[name].nil?
344
+ xq += cols0[name] + @gap
345
+ max_width[name] = cols0[name]
346
+ end
347
+ else
348
+ xq += cols1[name] unless cols1[name].nil?
349
+ end
350
+ end
351
+
352
+ t = x + @width
353
+ pos[:__last_column__] = t
354
+ end
355
+
356
+ # now adjust the table to the correct location across the page
357
+ case @position
358
+ when :left
359
+ xref = pdf.absolute_left_margin
360
+ when :right
361
+ xref = pdf.absolute_right_margin
362
+ when :center
363
+ xref = pdf.margin_x_middle
364
+ else
365
+ xref = @position
366
+ end
367
+
368
+ case @orientation
369
+ when :left
370
+ dx = xref - t
371
+ when :right
372
+ dx = xref
373
+ when :center
374
+ dx = xref - (t / 2.0)
375
+ else
376
+ dx = xref + @orientation
377
+ end
378
+
379
+ pos.each { |k, v| pos[k] = v + dx }
380
+
381
+ base_x0 = x0 = x + dx
382
+ base_x1 = x1 = t + dx
383
+
384
+ base_left_margin = pdf.absolute_left_margin
385
+ base_pos = pos.dup
386
+
387
+ # Ok, just about ready to make me a table.
388
+ pdf.fill_color @text_color
389
+ pdf.stroke_color @shade_color
390
+
391
+ middle = (x0 + x1) / 2.0
392
+
393
+ # Start a transaction. This transaction will be used to regress the
394
+ # table if there are not enough rows protected.
395
+ tg = Transaction::Simple::Group.new(pdf, self)
396
+ tg.start_transaction(:table)
397
+ moved_once = false if @protect_rows.nonzero?
398
+
399
+ abortTable = true
400
+ loop do # while abortTable
401
+ break unless abortTable
402
+ abortTable = false
403
+
404
+ dm = pdf.absolute_left_margin - base_left_margin
405
+ base_pos.each { |k, v| pos[k] = v + dm }
406
+ x0 = base_x0 + dm
407
+ x1 = base_x1 + dm
408
+ middle = (x0 + x1) / 2.0
409
+
410
+ # If the title is set, then render it.
411
+ unless @title.nil? or @title.empty?
412
+ w = pdf.text_width(@title, @title_font_size)
413
+ _y = pdf.y - pdf.font_height(@title_font_size)
414
+ if _y < pdf.absolute_bottom_margin
415
+ pdf.start_new_page
416
+
417
+ # margins may have changed on the new page
418
+ dm = pdf.absolute_left_margin - base_left_margin
419
+ base_pos.each { |k, v| pos[k] = v + dm }
420
+ x0 = base_x0 + dm
421
+ x1 = base_x1 + dm
422
+ middle = (x0 + x1) / 2.0
423
+ end
424
+
425
+ pdf.y -= pdf.font_height(@title_font_size)
426
+ pdf.fill_color @title_color
427
+ pdf.add_text(middle - w / 2.0, pdf.y, title, @title_font_size)
428
+ pdf.y -= @title_gap
429
+ end
430
+
431
+ # Margins may have changed on the new_page.
432
+ dm = pdf.absolute_left_margin - base_left_margin
433
+ base_pos.each { |k, v| pos[k] = v + dm }
434
+ x0 = base_x0 + dm
435
+ x1 = base_x1 + dm
436
+ middle = (x0 + x1) / 2.0
437
+
438
+ y = pdf.y # simplifies the code a bit
439
+ low_y = y if low_y.nil? or y < low_y
440
+
441
+ # Make the table
442
+ height = pdf.font_height @font_size
443
+ descender = pdf.font_descender @font_size
444
+
445
+ y0 = y + descender
446
+ dy = 0
447
+
448
+ if @show_headings
449
+ # This function will move the start of the table to a new page if
450
+ # it does not fit on this one.
451
+ hOID = __open_new_object__(pdf) if @shade_headings
452
+ pdf.fill_color @heading_color
453
+ _height, y = __table_column_headings__(pdf, pos, max_width, height,
454
+ descender, @row_gap, @heading_font_size, y)
455
+ pdf.fill_color @text_color
456
+ y0 = y + _height
457
+ y1 = y
458
+
459
+ if @shade_headings
460
+ pdf.close_object
461
+ pdf.fill_color! @shade_heading_color
462
+ pdf.rectangle(x0 - @gap / 2.0, y, x1 - x0, _height).fill
463
+ pdf.reopen_object(hOID)
464
+ pdf.close_object
465
+ pdf.restore_state
466
+ end
467
+
468
+ # Margins may have changed on the new_page
469
+ dm = pdf.absolute_left_margin - base_left_margin
470
+ base_pos.each { |k, v| pos[k] = v + dm }
471
+ x0 = base_x0 + dm
472
+ x1 = base_x1 + dm
473
+ middle = (x0 + x1) / 2.0
474
+ else
475
+ y1 = y0
476
+ end
477
+
478
+ first_line = true
479
+
480
+ # open an object here so that the text can be put in over the
481
+ # shading
482
+ tOID = __open_new_object__(pdf) unless :none == @shade_rows
483
+
484
+ cnt = 0
485
+ cnt = 1 unless @shade_headings
486
+ newPage = false
487
+ @data.each do |row|
488
+ cnt += 1
489
+ # Start a transaction that will be used for this row to prevent it
490
+ # from being split.
491
+ unless @split_rows
492
+ pageStart = pdf.pageset.size
493
+
494
+ columnStart = pdf.column_number if pdf.columns?
495
+
496
+ tg.start_transaction(:row)
497
+ row_orig = row
498
+ y_orig = y
499
+ y0_orig = y0
500
+ y1_orig = y1
501
+ end # unless @split_rows
502
+
503
+ ok = false
504
+ second_turn = false
505
+ loop do # while !abortTable and !ok
506
+ break if abortTable or ok
507
+
508
+ mx = 0
509
+ newRow = true
510
+
511
+ loop do # while !abortTable and (newPage or newRow)
512
+ break if abortTable or not (newPage or newRow)
513
+
514
+ y -= height
515
+ low_y = y if low_y.nil? or y < low_y
516
+
517
+ if newPage or y < (pdf.absolute_bottom_margin + @minimum_space)
518
+ # check that enough rows are with the heading
519
+ moved_once = abortTable = true if @protect_rows.nonzero? and not moved_once and cnt <= @protect_rows
520
+
521
+ y2 = y - mx + (2 * height) + descender - (newRow ? 1 : 0) * height
522
+
523
+ unless :none == @show_lines
524
+ y0 = y1 unless @show_headings
525
+
526
+ __table_draw_lines__(pdf, pos, @gap, x0, x1, y0, y1, y2,
527
+ @line_color, @inner_line_style, @outer_line_style,
528
+ @show_lines)
529
+ end
530
+
531
+ unless :none == @shade_rows
532
+ pdf.close_object
533
+ pdf.restore_state
534
+ end
535
+
536
+ pdf.start_new_page
537
+ pdf.save_state
538
+
539
+ # and the margins may have changed, this is due to the
540
+ # possibility of the columns being turned on as the columns are
541
+ # managed by manipulating the margins
542
+ dm = pdf.absolute_left_margin - base_left_margin
543
+ base_pos.each { |k, v| pos[k] = v + dm }
544
+ x0 = base_x0 + dm
545
+ x1 = base_x1 + dm
546
+
547
+ tOID = __open_new_object__(pdf) unless :none == @shade_rows
548
+
549
+ pdf.fill_color! @text_color
550
+
551
+ y = pdf.absolute_top_margin - @header_gap
552
+ low_y = y
553
+ y0 = y + descender
554
+ mx = 0
555
+
556
+ if @show_headings
557
+ old_y = y
558
+
559
+ pdf.fill_color @heading_color
560
+ _height, y = __table_column_headings__(pdf, pos, max_width,
561
+ height, descender, @row_gap, @heading_font_size, y)
562
+ pdf.fill_color @text_color
563
+
564
+ y0 = y + _height
565
+ y1 = y
566
+
567
+ if @shade_headings
568
+ pdf.fill_color! @shade_heading_color
569
+ pdf.rectangle(x0 - @gap / 2, y, x1 - x0, _height).fill
570
+ pdf.fill_color @heading_color
571
+ __table_column_headings__(pdf, pos, max_width, height,
572
+ descender, @row_gap,
573
+ @heading_font_size, old_y)
574
+ pdf.fill_color @text_color
575
+ end
576
+
577
+ dm = pdf.absolute_left_margin - base_left_margin
578
+ base_pos.each { |k, v| pos[k] = v + dm }
579
+ x0 = base_x0 + dm
580
+ x1 = base_x1 + dm
581
+ middle = (x0 + x1) / 2.0
582
+ else
583
+ y1 = y0
584
+ end
585
+
586
+ first_line = true
587
+ y -= height
588
+ low_y = y if low_y.nil? or y < low_y
589
+ end
590
+
591
+ newRow = false
592
+
593
+ # Write the actual data. If these cells need to be split over
594
+ # a page, then newPage will be set, and the remaining text
595
+ # will be placed in leftOvers
596
+ newPage = false
597
+ leftOvers = PDF::Writer::OHash.new
598
+
599
+ @cols.each do |name, column|
600
+ pdf.pointer = y + height
601
+ colNewPage = false
602
+
603
+ unless row[name].nil?
604
+ lines = row[name].to_s.split(/\n/)
605
+ if column and column.link_name
606
+ lines.map! do |kk|
607
+ link = row[column.link_name]
608
+ if link
609
+ "<c:alink uri='#{link}'>#{kk}</c:alink>"
610
+ else
611
+ kk
612
+ end
613
+ end
614
+ end
615
+ else
616
+ lines = []
617
+ end
618
+
619
+ pdf.y -= @row_gap
620
+
621
+ lines.each do |line|
622
+ pdf.send(:preprocess_text, line)
623
+ start = true
624
+
625
+ loop do
626
+ break if (line.nil? or line.empty?) and not start
627
+ start = false
628
+
629
+ _y = pdf.y - height if not colNewPage
630
+
631
+ # a new page is required
632
+ newPage = colNewPage = true if _y < pdf.absolute_bottom_margin
633
+
634
+ if colNewPage
635
+ if leftOvers[name].nil?
636
+ leftOvers[name] = [line]
637
+ else
638
+ leftOvers[name] << "\n#{line}"
639
+ end
640
+ line = nil
641
+ else
642
+ if column and column.justification
643
+ just = column.justification
644
+ end
645
+ just ||= :left
646
+
647
+ pdf.y = _y
648
+ line = pdf.add_text_wrap(pos[name], pdf.y,
649
+ max_width[name], line,
650
+ @font_size, just)
651
+ end
652
+ end
653
+ end
654
+
655
+ dy = y + height - pdf.y + @row_gap
656
+ mx = dy - height * (newPage ? 1 : 0) if (dy - height * (newPage ? 1 : 0)) > mx
657
+ end
658
+
659
+ # Set row to leftOvers so that they will be processed onto the
660
+ # new page
661
+ row = leftOvers
662
+
663
+ # Now add the shading underneath
664
+ unless :none == @shade_rows
665
+ pdf.close_object
666
+
667
+ if (cnt % 2).zero?
668
+ pdf.fill_color!(@shade_color)
669
+ pdf.rectangle(x0 - @gap / 2.0, y + descender + height - mx, x1 - x0, mx).fill
670
+ elsif (cnt % 2).nonzero? and :striped == @shade_rows
671
+ pdf.fill_color!(@shade_color2)
672
+ pdf.rectangle(x0 - @gap / 2.0, y + descender + height - mx, x1 - x0, mx).fill
673
+ end
674
+ pdf.reopen_object(tOID)
675
+ end
676
+
677
+ if :inner == @show_lines or :all == @show_lines
678
+ # draw a line on the top of the block
679
+ pdf.save_state
680
+ pdf.stroke_color! @line_color
681
+ if first_line
682
+ pdf.stroke_style @outer_line_style
683
+ first_line = false
684
+ else
685
+ pdf.stroke_style @inner_line_style
686
+ end
687
+ pdf.line(x0 - @gap / 2.0, y + descender + height, x1 - @gap / 2.0, y + descender + height).stroke
688
+ pdf.restore_state
689
+ end
690
+ end
691
+
692
+ y = y - mx + height
693
+ pdf.y = y
694
+ low_y = y if low_y.nil? or y < low_y
695
+
696
+ # checking row split over pages
697
+ unless @split_rows
698
+ if (((pdf.pageset.size != pageStart) or (pdf.columns? and columnStart != pdf.column_number)) and not second_turn)
699
+ # then we need to go back and try that again!
700
+ newPage = second_turn = true
701
+ tg.rewind_transaction(:row)
702
+ row = row_orig
703
+ low_y = y = y_orig
704
+ y0 = y0_orig
705
+ y1 = y1_orig
706
+ ok = false
707
+
708
+ dm = pdf.absolute_left_margin - base_left_margin
709
+ base_pos.each { |k, v| pos[k] = v + dm }
710
+ x0 = base_x0 + dm
711
+ x1 = base_x1 + dm
712
+ else
713
+ tg.commit_transaction(:row)
714
+ ok = true
715
+ end
716
+ else
717
+ ok = true # don't go 'round the loop if splitting rows is allowed
718
+ end
719
+ end
720
+
721
+ if abortTable
722
+ # abort_transaction if not ok only the outer transaction should
723
+ # be operational.
724
+ tg.rewind_transaction(:table)
725
+ pdf.start_new_page
726
+ # fix a bug where a moved table will take up the whole page.
727
+ low_y = nil
728
+ pdf.save_state
729
+ break
730
+ end
731
+ end
732
+ end
733
+
734
+ if low_y <= y
735
+ y2 = low_y + descender
736
+ else
737
+ y2 = y + descender
738
+ end
739
+
740
+ unless :none == @show_lines
741
+ y0 = y1 unless @show_headings
742
+
743
+ __table_draw_lines__(pdf, pos, @gap, x0, x1, y0, y1, y2, @line_color,
744
+ @inner_line_style, @outer_line_style, @show_lines)
745
+ end
746
+
747
+ # close the object for drawing the text on top
748
+ unless :none == @shade_rows
749
+ pdf.close_object
750
+ pdf.restore_state
751
+ end
752
+
753
+ pdf.y = low_y
754
+
755
+ # Table has been put on the page, the rows guarded as required; commit.
756
+ tg.commit_transaction(:table)
757
+
758
+ y
759
+ rescue Exception => ex
760
+ begin
761
+ tg.abort_transaction(:table) if tg.transaction_open?
762
+ rescue
763
+ nil
764
+ end
765
+ raise ex
766
+ end
767
+
768
+ WIDTH_FACTOR = 1.01
769
+
770
+ # Find the maximum widths of the text within each column. Default to
771
+ # zero.
772
+ def __find_table_max_width__(pdf)
773
+ max_width = PDF::Writer::OHash.new(-1)
774
+
775
+ # Find the maximum cell widths based on the data and the headings.
776
+ # Passing through the data multiple times is unavoidable as we must
777
+ # do some analysis first.
778
+ @data.each do |row|
779
+ @cols.each do |name, column|
780
+ w = pdf.text_width(row[name].to_s, @font_size)
781
+ w *= PDF::SimpleTable::WIDTH_FACTOR
782
+
783
+ max_width[name] = w if w > max_width[name]
784
+ end
785
+ end
786
+
787
+ @cols.each do |name, column|
788
+ title = column.heading.title if column.heading
789
+ title ||= column.name
790
+ w = pdf.text_width(title, @heading_font_size)
791
+ w *= PDF::SimpleTable::WIDTH_FACTOR
792
+ max_width[name] = w if w > max_width[name]
793
+ end
794
+ max_width
795
+ end
796
+ private :__find_table_max_width__
797
+
798
+ # Calculate the start positions of each of the columns. This is based
799
+ # on max_width, but may be modified with column options.
800
+ def __find_table_positions__(pdf, max_width)
801
+ pos = PDF::Writer::OHash.new
802
+ x = t = adjustment_width = set_width = 0
803
+
804
+ max_width.each do |name, w|
805
+ pos[name] = t
806
+ # If the column width has been specified then set that here, also
807
+ # total the width avaliable for adjustment.
808
+ if not @cols[name].nil? and
809
+ not @cols[name].width.nil? and
810
+ @cols[name].width > 0
811
+ t += @cols[name].width
812
+ max_width[name] = @cols[name].width - @gap
813
+ set_width += @cols[name].width
814
+ else
815
+ t += w + @gap
816
+ adjustment_width += w
817
+ set_width += @gap
818
+ end
819
+ end
820
+ pos[:__last_column__] = t
821
+
822
+ [pos, t, x, adjustment_width, set_width]
823
+ end
824
+ private :__find_table_positions__
825
+
826
+ # Uses ezText to add the text, and returns the height taken by the
827
+ # largest heading. This page will move the headings to a new page if
828
+ # they will not fit completely on this one transaction support will be
829
+ # used to implement this.
830
+ def __table_column_headings__(pdf, pos, max_width, height, descender, gap, size, y)
831
+ mx = second_go = 0
832
+ start_page = pdf.pageset.size
833
+
834
+ # y is the position at which the top of the table should start, so the
835
+ # base of the first text, is y-height-gap-descender, but ezText starts
836
+ # by dropping height.
837
+
838
+ # The return from this function is the total cell height, including
839
+ # gaps, and y is adjusted to be the postion of the bottom line.
840
+ tg = Transaction::Simple::Group.new(pdf, self)
841
+ tg.start_transaction(:column_headings)
842
+
843
+ ok = false
844
+ y -= gap
845
+ loop do
846
+ break if ok
847
+ @cols.each do |name, column|
848
+ pdf.pointer = y
849
+
850
+ if column.heading
851
+ justification = column.heading.justification
852
+ bold = column.heading.bold
853
+ title = column.heading.title
854
+ end
855
+
856
+ justification ||= :left
857
+ bold ||= @bold_headings
858
+ title ||= column.name
859
+
860
+ title = "<b>#{title}</b>" if bold
861
+
862
+ pdf.text(title, :font_size => size, :absolute_left => pos[name],
863
+ :absolute_right => (max_width[name] + pos[name]),
864
+ :justification => justification)
865
+ dy = y - pdf.y
866
+ mx = dy if dy > mx
867
+ end
868
+
869
+ y -= (mx + gap) - descender # y = y - mx - gap + descender
870
+
871
+ # If this has been moved to a new page, then abort the transaction;
872
+ # move to a new page, and put it there. Do not check on the second
873
+ # time around to avoid an infinite loop.
874
+ if (pdf.pageset.size != start_page and not second_go)
875
+ tg.rewind_transaction(:column_headings)
876
+
877
+ pdf.start_new_page
878
+ save_state
879
+ y = @y - gap - descender
880
+ ok = false
881
+ second_go = true
882
+ mx = 0
883
+ else
884
+ tg.commit_transaction(:column_headings)
885
+ ok = true
886
+ end
887
+ end
888
+
889
+ return [mx + gap * 2 - descender, y]
890
+ rescue Exception => ex
891
+ begin
892
+ tg.abort_transaction(:column_headings) if tg.transaction_open?(:column_headings)
893
+ rescue
894
+ nil
895
+ end
896
+ raise ex
897
+ end
898
+ private :__table_column_headings__
899
+
900
+ def __table_draw_lines__(pdf, pos, gap, x0, x1, y0, y1, y2, col, inner, outer, opt = :outer)
901
+ x0 = 1000
902
+ x1 = 0
903
+
904
+ pdf.stroke_color(col)
905
+
906
+ cnt = 0
907
+ n = pos.size
908
+
909
+ pos.each do |name, x|
910
+ cnt += 1
911
+
912
+ if (cnt == 1 or cnt == n)
913
+ pdf.stroke_style outer
914
+ else
915
+ pdf.stroke_style inner
916
+ end
917
+
918
+ pdf.line(x - gap / 2.0, y0, x - gap / 2.0, y2).stroke
919
+ x1 = x if x > x1
920
+ x0 = x if x < x0
921
+ end
922
+
923
+ pdf.stroke_style outer
924
+
925
+ pdf.line(x0 - (gap / 2.0) - (outer.width / 2.0), y0,
926
+ x1 - (gap / 2.0) + (outer.width / 2.0), y0).stroke
927
+
928
+ # Only do the second line if it is different than the first AND each
929
+ # row does not have a line on it.
930
+ if y0 != y1 and @show_lines == :outer
931
+ pdf.line(x0 - gap / 2.0, y1, x1 - gap / 2.0, y1).stroke
932
+ end
933
+ pdf.line(x0 - (gap / 2.0) - (outer.width / 2.0), y2,
934
+ x1 - (gap / 2.0) + (outer.width / 2.0), y2).stroke
935
+ end
936
+ private :__table_draw_lines__
937
+
938
+ def __open_new_object__(pdf)
939
+ pdf.save_state
940
+ tOID = pdf.open_object
941
+ pdf.close_object
942
+ pdf.add_object(tOID)
943
+ pdf.reopen_object(tOID)
944
+ tOID
945
+ end
946
+ private :__open_new_object__
947
+ end