prawn-table 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/COPYING +2 -0
  3. data/GPLv2 +340 -0
  4. data/GPLv3 +674 -0
  5. data/Gemfile +5 -0
  6. data/LICENSE +56 -0
  7. data/lib/prawn/table.rb +641 -0
  8. data/lib/prawn/table/cell.rb +772 -0
  9. data/lib/prawn/table/cell/image.rb +69 -0
  10. data/lib/prawn/table/cell/in_table.rb +33 -0
  11. data/lib/prawn/table/cell/span_dummy.rb +93 -0
  12. data/lib/prawn/table/cell/subtable.rb +66 -0
  13. data/lib/prawn/table/cell/text.rb +154 -0
  14. data/lib/prawn/table/cells.rb +255 -0
  15. data/lib/prawn/table/column_width_calculator.rb +182 -0
  16. data/manual/contents.rb +13 -0
  17. data/manual/example_helper.rb +8 -0
  18. data/manual/table/basic_block.rb +53 -0
  19. data/manual/table/before_rendering_page.rb +26 -0
  20. data/manual/table/cell_border_lines.rb +24 -0
  21. data/manual/table/cell_borders_and_bg.rb +31 -0
  22. data/manual/table/cell_dimensions.rb +30 -0
  23. data/manual/table/cell_text.rb +38 -0
  24. data/manual/table/column_widths.rb +30 -0
  25. data/manual/table/content_and_subtables.rb +39 -0
  26. data/manual/table/creation.rb +27 -0
  27. data/manual/table/filtering.rb +36 -0
  28. data/manual/table/flow_and_header.rb +17 -0
  29. data/manual/table/image_cells.rb +33 -0
  30. data/manual/table/position.rb +29 -0
  31. data/manual/table/row_colors.rb +20 -0
  32. data/manual/table/span.rb +30 -0
  33. data/manual/table/style.rb +22 -0
  34. data/manual/table/table.rb +52 -0
  35. data/manual/table/width.rb +27 -0
  36. data/prawn-table.gemspec +48 -0
  37. data/spec/cell_spec.rb +629 -0
  38. data/spec/extensions/encoding_helpers.rb +11 -0
  39. data/spec/extensions/mocha.rb +46 -0
  40. data/spec/spec_helper.rb +53 -0
  41. data/spec/table/span_dummy_spec.rb +17 -0
  42. data/spec/table_spec.rb +1527 -0
  43. metadata +240 -0
@@ -0,0 +1,39 @@
1
+ # encoding: utf-8
2
+ #
3
+ # There are five kinds of objects which can be put in table cells:
4
+ # 1. String: produces a text cell (the most common usage)
5
+ # 2. <code>Prawn::Table::Cell</code>
6
+ # 3. <code>Prawn::Table</code>
7
+ # 4. Array
8
+ # 5. Images
9
+ #
10
+ # Whenever a table or an array is provided as a cell, a subtable will be created
11
+ # (a table within a cell).
12
+ #
13
+ # If you'd like to provide a cell or table directly, the best way is to
14
+ # use the <code>make_cell</code> and <code>make_table</code> methods as they
15
+ # don't call <code>draw</code> on the created object.
16
+ #
17
+ # To insert an image just provide a hash with an with an <code>:image</code> key
18
+ # pointing to the image path.
19
+ #
20
+ require File.expand_path(File.join(File.dirname(__FILE__),
21
+ %w[.. example_helper]))
22
+
23
+ filename = File.basename(__FILE__).gsub('.rb', '.pdf')
24
+ Prawn::ManualBuilder::Example.generate(filename) do
25
+ cell_1 = make_cell(:content => "this row content comes directly ")
26
+ cell_2 = make_cell(:content => "from cell objects")
27
+
28
+ two_dimensional_array = [ ["..."], ["subtable from an array"], ["..."] ]
29
+
30
+ my_table = make_table([ ["..."], ["subtable from another table"], ["..."] ])
31
+
32
+ image_path = "#{Prawn::DATADIR}/images/stef.jpg"
33
+
34
+ table([ ["just a regular row", "", "", "blah blah blah"],
35
+ [cell_1, cell_2, "", ""],
36
+ ["", "", two_dimensional_array, ""],
37
+ ["just another regular row", "", "", ""],
38
+ [{:image => image_path}, "", my_table, ""]])
39
+ end
@@ -0,0 +1,27 @@
1
+ # encoding: utf-8
2
+ #
3
+ # Creating tables with Prawn is fairly easy. There are two methods that will
4
+ # create tables for us <code>table</code> and <code>make_table</code>.
5
+ #
6
+ # Both are wrappers that create a new <code>Prawn::Table</code> object. The
7
+ # difference is that <code>table</code> calls the <code>draw</code> method
8
+ # after creating the table and <code>make_table</code> only returns the created
9
+ # table, so you have to call the <code>draw</code> method yourself.
10
+ #
11
+ # The most simple table can be created by providing only an array of arrays
12
+ # containing your data where each inner array represents one row.
13
+ #
14
+ require File.expand_path(File.join(File.dirname(__FILE__),
15
+ %w[.. example_helper]))
16
+
17
+ filename = File.basename(__FILE__).gsub('.rb', '.pdf')
18
+ Prawn::ManualBuilder::Example.generate(filename) do
19
+ t = make_table([ ["this is the first row"],
20
+ ["this is the second row"] ])
21
+ t.draw
22
+ move_down 20
23
+
24
+ table([ ["short", "short", "loooooooooooooooooooong"],
25
+ ["short", "loooooooooooooooooooong", "short"],
26
+ ["loooooooooooooooooooong", "short", "short"] ])
27
+ end
@@ -0,0 +1,36 @@
1
+ # encoding: utf-8
2
+ #
3
+ # Another way to reduce the number of cells is to <code>filter</code> the table.
4
+ #
5
+ # <code>filter</code> is just like <code>Enumerable#select</code>. Pass it a
6
+ # block and it will iterate through the cells returning a new
7
+ # <code>Prawn::Table::Cells</code> instance containing only those cells for
8
+ # which the block was not false.
9
+ #
10
+ require File.expand_path(File.join(File.dirname(__FILE__),
11
+ %w[.. example_helper]))
12
+
13
+ filename = File.basename(__FILE__).gsub('.rb', '.pdf')
14
+ Prawn::ManualBuilder::Example.generate(filename) do
15
+ data = [ ["Item", "Jan Sales", "Feb Sales"],
16
+ ["Oven", 17, 89],
17
+ ["Fridge", 62, 30],
18
+ ["Microwave", 71, 47]
19
+ ]
20
+
21
+ table(data) do
22
+ values = cells.columns(1..-1).rows(1..-1)
23
+
24
+ bad_sales = values.filter do |cell|
25
+ cell.content.to_i < 40
26
+ end
27
+
28
+ bad_sales.background_color = "FFAAAA"
29
+
30
+ good_sales = values.filter do |cell|
31
+ cell.content.to_i > 70
32
+ end
33
+
34
+ good_sales.background_color = "AAFFAA"
35
+ end
36
+ end
@@ -0,0 +1,17 @@
1
+ # encoding: utf-8
2
+ #
3
+ # If the table cannot fit on the current page it will flow to the next page just
4
+ # like free flowing text. If you would like to have the first row treated as a
5
+ # header which will be repeated on subsequent pages set the <code>:header</code>
6
+ # option to true.
7
+ #
8
+ require File.expand_path(File.join(File.dirname(__FILE__),
9
+ %w[.. example_helper]))
10
+
11
+ filename = File.basename(__FILE__).gsub('.rb', '.pdf')
12
+ Prawn::ManualBuilder::Example.generate(filename) do
13
+ data = [["This row should be repeated on every new page"]]
14
+ data += [["..."]] * 30
15
+
16
+ table(data, :header => true)
17
+ end
@@ -0,0 +1,33 @@
1
+ # encoding: utf-8
2
+ #
3
+ # Prawn can insert images into a table. Just pass a hash into
4
+ # <code>table()</code> with an <code>:image</code> key pointing to the image.
5
+ #
6
+ # You can pass the <code>:scale</code>, <code>:fit</code>,
7
+ # <code>:position</code>, and <code>:vposition</code> arguments in alongside
8
+ # <code>:image</code>; these will function just as in <code>image()</code>.
9
+ #
10
+ # The <code>:image_width</code> and <code>:image_height</code> arguments set
11
+ # the width/height of the image within the cell, as opposed to the
12
+ # <code>:width</code> and <code>:height</code> arguments, which set the table
13
+ # cell's dimensions.
14
+ #
15
+ require File.expand_path(File.join(File.dirname(__FILE__),
16
+ %w[.. example_helper]))
17
+
18
+ filename = File.basename(__FILE__).gsub('.rb', '.pdf')
19
+ Prawn::ManualBuilder::Example.generate(filename) do
20
+ image = "#{Prawn::DATADIR}/images/prawn.png"
21
+
22
+ table [
23
+ ["Standard image cell", {:image => image}],
24
+ [":scale => 0.5", {:image => image, :scale => 0.5}],
25
+ [":fit => [100, 200]", {:image => image, :fit => [100, 200]}],
26
+ [":image_height => 50,
27
+ :image_width => 100", {:image => image, :image_height => 50,
28
+ :image_width => 100}],
29
+ [":position => :center", {:image => image, :position => :center}],
30
+ [":vposition => :center", {:image => image, :vposition => :center,
31
+ :height => 200}]
32
+ ], :width => bounds.width
33
+ end
@@ -0,0 +1,29 @@
1
+ # encoding: utf-8
2
+ #
3
+ # The <code>table()</code> method accepts a <code>:position</code> argument to
4
+ # determine horizontal position of the table within its bounding box. It can be
5
+ # <code>:left</code> (the default), <code>:center</code>, <code>:right</code>,
6
+ # or a number specifying a distance in PDF points from the left side.
7
+ #
8
+ require File.expand_path(File.join(File.dirname(__FILE__),
9
+ %w[.. example_helper]))
10
+
11
+ filename = File.basename(__FILE__).gsub('.rb', '.pdf')
12
+ Prawn::ManualBuilder::Example.generate(filename) do
13
+ data = [["The quick brown fox jumped over the lazy dogs."]] * 2
14
+
15
+ text "Left:"
16
+ table data, :position => :left
17
+ move_down 10
18
+
19
+ text "Center:"
20
+ table data, :position => :center
21
+ move_down 10
22
+
23
+ text "Right:"
24
+ table data, :position => :right
25
+ move_down 10
26
+
27
+ text "100pt:"
28
+ table data, :position => 100
29
+ end
@@ -0,0 +1,20 @@
1
+ # encoding: utf-8
2
+ #
3
+ # One of the most common table styling techniques is to stripe the rows with
4
+ # alternating colors.
5
+ #
6
+ # There is one helper just for that. Just provide the <code>:row_colors</code>
7
+ # option an array with color values.
8
+ #
9
+ require File.expand_path(File.join(File.dirname(__FILE__),
10
+ %w[.. example_helper]))
11
+
12
+ filename = File.basename(__FILE__).gsub('.rb', '.pdf')
13
+ Prawn::ManualBuilder::Example.generate(filename) do
14
+ data = [["This row should have one color"],
15
+ ["And this row should have another"]]
16
+
17
+ data += [["..."]] * 10
18
+
19
+ table(data, :row_colors => ["F0F0F0", "FFFFCC"])
20
+ end
@@ -0,0 +1,30 @@
1
+ # encoding: utf-8
2
+ #
3
+ # Table cells can span multiple columns, rows, or both. When building a cell,
4
+ # use the hash argument constructor with a <code>:colspan</code> and/or
5
+ # <code>:rowspan</code> argument. Row or column spanning must be specified when
6
+ # building the data array; you can't set the span in the table's initialization
7
+ # block. This is because cells are laid out in the grid before that block is
8
+ # called, so that references to row and column numbers make sense.
9
+ #
10
+ # Cells are laid out in the order given, skipping any positions spanned by
11
+ # previously instantiated cells. Therefore, a cell with <code>rowspan: 2</code>
12
+ # will be missing at least one cell in the row below it. See the code and table
13
+ # below for an example.
14
+ #
15
+ # It is illegal to overlap cells via spanning. A
16
+ # <code>Prawn::Errors::InvalidTableSpan</code> error will be raised if spans
17
+ # would cause cells to overlap.
18
+ #
19
+ require File.expand_path(File.join(File.dirname(__FILE__),
20
+ %w[.. example_helper]))
21
+
22
+ filename = File.basename(__FILE__).gsub('.rb', '.pdf')
23
+ Prawn::ManualBuilder::Example.generate(filename) do
24
+ table([
25
+ ["A", {:content => "2x1", :colspan => 2}, "B"],
26
+ [{:content => "1x2", :rowspan => 2}, "C", "D", "E"],
27
+ [{:content => "2x2", :colspan => 2, :rowspan => 2}, "F"],
28
+ ["G", "H"]
29
+ ])
30
+ end
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+ #
3
+ # We've seen how to apply styles to a selection of cells by setting the
4
+ # individual properties. Another option is to use the <code>style</code> method
5
+ #
6
+ # <code>style</code> lets us define multiple properties at once with a hash. It
7
+ # also accepts a block that will be called for each cell and can be used for
8
+ # some complex styling.
9
+ #
10
+ require File.expand_path(File.join(File.dirname(__FILE__),
11
+ %w[.. example_helper]))
12
+
13
+ filename = File.basename(__FILE__).gsub('.rb', '.pdf')
14
+ Prawn::ManualBuilder::Example.generate(filename) do
15
+ table([[""] * 8] * 8) do
16
+ cells.style(:width => 24, :height => 24)
17
+
18
+ cells.style do |c|
19
+ c.background_color = ((c.row + c.column) % 2).zero? ? '000000' : 'ffffff'
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,52 @@
1
+ # encoding: utf-8
2
+ #
3
+ # Examples for tables.
4
+ #
5
+ require File.expand_path(File.join(File.dirname(__FILE__),
6
+ %w[.. example_helper]))
7
+
8
+ Prawn::ManualBuilder::Example.generate("table.pdf", :page_size => "FOLIO") do
9
+
10
+ package "table" do |p|
11
+
12
+ p.section "Basics" do |s|
13
+ s.example "creation"
14
+ s.example "content_and_subtables"
15
+ s.example "flow_and_header"
16
+ s.example "position"
17
+ end
18
+
19
+ p.section "Styling" do |s|
20
+ s.example "column_widths"
21
+ s.example "width"
22
+ s.example "row_colors"
23
+ s.example "cell_dimensions"
24
+ s.example "cell_borders_and_bg"
25
+ s.example "cell_border_lines"
26
+ s.example "cell_text"
27
+ s.example "image_cells"
28
+ s.example "span"
29
+ s.example "before_rendering_page"
30
+ end
31
+
32
+ p.section "Initializer Block" do |s|
33
+ s.example "basic_block"
34
+ s.example "filtering"
35
+ s.example "style"
36
+ end
37
+
38
+ p.intro do
39
+ prose("Prawn comes with table support out of the box. Tables can be styled in whatever way you see fit. The whole table, rows, columns and cells can be styled independently from each other.
40
+
41
+ The examples show:")
42
+
43
+ list( "How to create tables",
44
+ "What content can be placed on tables",
45
+ "Subtables (or tables within tables)",
46
+ "How to style the whole table",
47
+ "How to use initializer blocks to style only specific portions of the table"
48
+ )
49
+ end
50
+
51
+ end
52
+ end
@@ -0,0 +1,27 @@
1
+ # encoding: utf-8
2
+ #
3
+ # The default table width depends on the content provided. It will expand up
4
+ # to the current bounding box width to fit the content. If you want the table to
5
+ # have a fixed width no matter the content you may use the <code>:width</code>
6
+ # option to manually set the width.
7
+ #
8
+ require File.expand_path(File.join(File.dirname(__FILE__),
9
+ %w[.. example_helper]))
10
+
11
+ filename = File.basename(__FILE__).gsub('.rb', '.pdf')
12
+ Prawn::ManualBuilder::Example.generate(filename) do
13
+ text "Normal width:"
14
+ table [%w[A B C]]
15
+ move_down 20
16
+
17
+ text "Fixed width:"
18
+ table([%w[A B C]], :width => 300)
19
+ move_down 20
20
+
21
+ text "Normal width:"
22
+ table([["A", "Blah " * 20, "C"]])
23
+ move_down 20
24
+
25
+ text "Fixed width:"
26
+ table([["A", "Blah " * 20, "C"]], :width => 300)
27
+ end
@@ -0,0 +1,48 @@
1
+ Gem::Specification.new do |spec|
2
+ spec.name = "prawn-table"
3
+ spec.version = "0.0.1" #File.read(File.expand_path('VERSION', File.dirname(__FILE__))).strip
4
+ spec.platform = Gem::Platform::RUBY
5
+ spec.summary = "Provides tables for PrawnPDF"
6
+ spec.files = Dir.glob("{examples,lib,spec,manual}/**/**/*") +
7
+ ["prawn-table.gemspec", "Gemfile",
8
+ "COPYING", "LICENSE", "GPLv2", "GPLv3"]
9
+ spec.require_path = "lib"
10
+ spec.required_ruby_version = '>= 1.9.3'
11
+ spec.required_rubygems_version = ">= 1.3.6"
12
+
13
+ spec.test_files = Dir[ "spec/*_spec.rb" ]
14
+ spec.authors = ["Gregory Brown","Brad Ediger","Daniel Nelson","Jonathan Greenberg","James Healy", "Hartwig Brandl"]
15
+ spec.email = ["gregory.t.brown@gmail.com","brad@bradediger.com","dnelson@bluejade.com","greenberg@entryway.net","jimmy@deefa.com", "mail@hartwigbrandl.at"]
16
+ spec.rubyforge_project = "prawn"
17
+ spec.licenses = ['RUBY', 'GPL-2', 'GPL-3']
18
+
19
+ spec.add_development_dependency('prawn', '~> 1.2.0')
20
+ spec.add_development_dependency('pdf-inspector', '~> 1.1.0')
21
+ spec.add_development_dependency('yard')
22
+ spec.add_development_dependency('rspec', '2.14.1')
23
+ spec.add_development_dependency('mocha')
24
+ spec.add_development_dependency('rake')
25
+ spec.add_development_dependency('simplecov')
26
+ spec.add_development_dependency('prawn-manual_builder', ">= 0.1.2")
27
+ spec.add_development_dependency('pdf-reader', '~>1.2')
28
+
29
+ spec.homepage = "http://prawn.majesticseacreature.com"
30
+ spec.description = <<END_DESC
31
+ Prawn is a fast, tiny, and nimble PDF generator for Ruby
32
+ END_DESC
33
+ spec.post_install_message = <<END_DESC
34
+
35
+ ********************************************
36
+
37
+
38
+ A lot has changed recently in Prawn.
39
+
40
+ Please read the changelog for details:
41
+
42
+ https://github.com/prawnpdf/prawn/wiki/CHANGELOG
43
+
44
+
45
+ ********************************************
46
+
47
+ END_DESC
48
+ end
data/spec/cell_spec.rb ADDED
@@ -0,0 +1,629 @@
1
+ # encoding: utf-8
2
+
3
+ require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper")
4
+ require_relative "../lib/prawn/table"
5
+
6
+ module CellHelpers
7
+
8
+ # Build, but do not draw, a cell on @pdf.
9
+ def cell(options={})
10
+ at = options[:at] || [0, @pdf.cursor]
11
+ Prawn::Table::Cell::Text.new(@pdf, at, options)
12
+ end
13
+
14
+ end
15
+
16
+ describe "Prawn::Table::Cell" do
17
+ before(:each) do
18
+ @pdf = Prawn::Document.new
19
+ end
20
+
21
+ describe "Prawn::Document#cell" do
22
+ include CellHelpers
23
+
24
+ it "should draw the cell" do
25
+ Prawn::Table::Cell::Text.any_instance.expects(:draw).once
26
+ @pdf.cell(:content => "text")
27
+ end
28
+
29
+ it "should return a Cell" do
30
+ @pdf.cell(:content => "text").should be_a_kind_of Prawn::Table::Cell
31
+ end
32
+
33
+ it "accepts :content => nil in a hash" do
34
+ @pdf.cell(:content => nil).should be_a_kind_of(Prawn::Table::Cell::Text)
35
+ @pdf.make_cell(:content => nil).should be_a_kind_of(Prawn::Table::Cell::Text)
36
+ end
37
+
38
+ it "should convert nil, Numeric, and Date values to strings" do
39
+ [nil, 123, 123.45, Date.today].each do |value|
40
+ c = @pdf.cell(:content => value)
41
+ c.should be_a_kind_of Prawn::Table::Cell::Text
42
+ c.content.should == value.to_s
43
+ end
44
+ end
45
+
46
+ it "should allow inline styling with a hash argument" do
47
+ # used for table([[{:text => "...", :font_style => :bold, ...}, ...]])
48
+ c = Prawn::Table::Cell.make(@pdf,
49
+ {:content => 'hello', :font_style => :bold})
50
+ c.should be_a_kind_of Prawn::Table::Cell::Text
51
+ c.content.should == "hello"
52
+ c.font.name.should == 'Helvetica-Bold'
53
+ end
54
+
55
+ it "should draw text at the given point plus padding, with the given " +
56
+ "size and style" do
57
+ @pdf.expects(:bounding_box).yields
58
+ @pdf.expects(:move_down)
59
+ @pdf.expects(:draw_text!).with { |text, options| text == "hello world" }
60
+
61
+ @pdf.cell(:content => "hello world",
62
+ :at => [10, 20],
63
+ :padding => [30, 40],
64
+ :size => 7,
65
+ :font_style => :bold)
66
+ end
67
+ end
68
+
69
+ describe "Prawn::Document#make_cell" do
70
+ it "should not draw the cell" do
71
+ Prawn::Table::Cell::Text.any_instance.expects(:draw).never
72
+ @pdf.make_cell("text")
73
+ end
74
+
75
+ it "should return a Cell" do
76
+ @pdf.make_cell("text", :size => 7).should be_a_kind_of Prawn::Table::Cell
77
+ end
78
+ end
79
+
80
+ describe "#style" do
81
+ include CellHelpers
82
+
83
+ it "should set each property in turn" do
84
+ c = cell(:content => "text")
85
+
86
+ c.expects(:padding=).with(50)
87
+ c.expects(:size=).with(7)
88
+
89
+ c.style(:padding => 50, :size => 7)
90
+ end
91
+
92
+ it "ignores unknown properties" do
93
+ c = cell(:content => 'text')
94
+
95
+ c.style(:foobarbaz => 'frobnitz')
96
+ end
97
+ end
98
+
99
+ describe "cell width" do
100
+ include CellHelpers
101
+
102
+ it "should be calculated for text" do
103
+ c = cell(:content => "text")
104
+ c.width.should == @pdf.width_of("text") + c.padding[1] + c.padding[3]
105
+ end
106
+
107
+ it "should be overridden by manual :width" do
108
+ c = cell(:content => "text", :width => 400)
109
+ c.width.should == 400
110
+ end
111
+
112
+ it "should incorporate padding when specified" do
113
+ c = cell(:content => "text", :padding => [1, 2, 3, 4])
114
+ c.width.should be_within(0.01).of(@pdf.width_of("text") + 6)
115
+ end
116
+
117
+ it "should allow width to be reset after it has been calculated" do
118
+ # to ensure that if we memoize width, it can still be overridden
119
+ c = cell(:content => "text")
120
+ c.width
121
+ c.width = 400
122
+ c.width.should == 400
123
+ end
124
+
125
+ it "should return proper width with size set" do
126
+ text = "text " * 4
127
+ c = cell(:content => text, :size => 7)
128
+ c.width.should ==
129
+ @pdf.width_of(text, :size => 7) + c.padding[1] + c.padding[3]
130
+ end
131
+
132
+ it "content_width should exclude padding" do
133
+ c = cell(:content => "text", :padding => 10)
134
+ c.content_width.should == @pdf.width_of("text")
135
+ end
136
+
137
+ it "content_width should exclude padding even with manual :width" do
138
+ c = cell(:content => "text", :padding => 10, :width => 400)
139
+ c.content_width.should be_within(0.01).of(380)
140
+ end
141
+
142
+ it "should have a reasonable minimum width that can fit @content" do
143
+ c = cell(:content => "text", :padding => 10)
144
+ min_content_width = c.min_width - c.padding[1] - c.padding[3]
145
+
146
+ @pdf.height_of("text", :width => min_content_width).should be <
147
+ (5 * @pdf.height_of("text"))
148
+ end
149
+
150
+ it "should defer min_width's evaluation of padding" do
151
+ c = cell(:content => "text", :padding => 100)
152
+ c.padding = 0
153
+
154
+ # Make sure we use the new value of padding in calculating min_width
155
+ c.min_width.should be < 100
156
+ end
157
+
158
+ it "should defer min_width's evaluation of size" do
159
+ c = cell(:content => "text", :size => 50)
160
+ c.size = 8
161
+ c.padding = 0
162
+ c.min_width.should be < 10
163
+ end
164
+
165
+ end
166
+
167
+ describe "cell height" do
168
+ include CellHelpers
169
+
170
+ it "should be calculated for text" do
171
+ c = cell(:content => "text")
172
+ c.height.should ==
173
+ @pdf.height_of("text", :width => @pdf.width_of("text")) +
174
+ c.padding[0] + c.padding[3]
175
+ end
176
+
177
+ it "should be overridden by manual :height" do
178
+ c = cell(:content => "text", :height => 400)
179
+ c.height.should == 400
180
+ end
181
+
182
+ it "should incorporate :padding when specified" do
183
+ c = cell(:content => "text", :padding => [1, 2, 3, 4])
184
+ c.height.should be_within(0.01).of(1 + 3 +
185
+ @pdf.height_of("text", :width => @pdf.width_of("text")))
186
+ end
187
+
188
+ it "should allow height to be reset after it has been calculated" do
189
+ # to ensure that if we memoize height, it can still be overridden
190
+ c = cell(:content => "text")
191
+ c.height
192
+ c.height = 400
193
+ c.height.should == 400
194
+ end
195
+
196
+ it "should return proper height for blocks of text" do
197
+ content = "words " * 10
198
+ c = cell(:content => content, :width => 100)
199
+ c.height.should == @pdf.height_of(content, :width => 100) +
200
+ c.padding[0] + c.padding[2]
201
+ end
202
+
203
+ it "should return proper height for blocks of text with size set" do
204
+ content = "words " * 10
205
+ c = cell(:content => content, :width => 100, :size => 7)
206
+
207
+ correct_content_height = nil
208
+ @pdf.font_size(7) do
209
+ correct_content_height = @pdf.height_of(content, :width => 100)
210
+ end
211
+
212
+ c.height.should == correct_content_height + c.padding[0] + c.padding[2]
213
+ end
214
+
215
+ it "content_height should exclude padding" do
216
+ c = cell(:content => "text", :padding => 10)
217
+ c.content_height.should == @pdf.height_of("text")
218
+ end
219
+
220
+ it "content_height should exclude padding even with manual :height" do
221
+ c = cell(:content => "text", :padding => 10, :height => 400)
222
+ c.content_height.should be_within(0.01).of(380)
223
+ end
224
+ end
225
+
226
+ describe "cell padding" do
227
+ include CellHelpers
228
+
229
+ it "should default to zero" do
230
+ c = cell(:content => "text")
231
+ c.padding.should == [5, 5, 5, 5]
232
+ end
233
+
234
+ it "should accept a numeric value, setting all padding" do
235
+ c = cell(:content => "text", :padding => 10)
236
+ c.padding.should == [10, 10, 10, 10]
237
+ end
238
+
239
+ it "should accept [v,h]" do
240
+ c = cell(:content => "text", :padding => [20, 30])
241
+ c.padding.should == [20, 30, 20, 30]
242
+ end
243
+
244
+ it "should accept [t,h,b]" do
245
+ c = cell(:content => "text", :padding => [10, 20, 30])
246
+ c.padding.should == [10, 20, 30, 20]
247
+ end
248
+
249
+ it "should accept [t,l,b,r]" do
250
+ c = cell(:content => "text", :padding => [10, 20, 30, 40])
251
+ c.padding.should == [10, 20, 30, 40]
252
+ end
253
+
254
+ it "should reject other formats" do
255
+ lambda{
256
+ cell(:content => "text", :padding => [10])
257
+ }.should raise_error(ArgumentError)
258
+ end
259
+ end
260
+
261
+ describe "background_color" do
262
+ include CellHelpers
263
+
264
+ it "should fill a rectangle with the given background color" do
265
+ @pdf.stubs(:mask).yields
266
+ @pdf.expects(:mask).with(:fill_color).yields
267
+
268
+ @pdf.stubs(:fill_color)
269
+ @pdf.expects(:fill_color).with('123456')
270
+ @pdf.expects(:fill_rectangle).checking do |(x, y), w, h|
271
+ x.should be_within(0.01).of(0)
272
+ y.should be_within(0.01).of(@pdf.cursor)
273
+ w.should be_within(0.01).of(29.344)
274
+ h.should be_within(0.01).of(23.872)
275
+ end
276
+ @pdf.cell(:content => "text", :background_color => '123456')
277
+ end
278
+
279
+ it "should draw the background in the right place if cell is drawn at a " +
280
+ "different location" do
281
+ @pdf.stubs(:mask).yields
282
+ @pdf.expects(:mask).with(:fill_color).yields
283
+
284
+ @pdf.stubs(:fill_color)
285
+ @pdf.expects(:fill_color).with('123456')
286
+ @pdf.expects(:fill_rectangle).checking do |(x, y), w, h|
287
+ x.should be_within(0.01).of(12.0)
288
+ y.should be_within(0.01).of(34.0)
289
+ w.should be_within(0.01).of(29.344)
290
+ h.should be_within(0.01).of(23.872)
291
+ end
292
+ c = @pdf.make_cell(:content => "text", :background_color => '123456')
293
+ c.draw([12.0, 34.0])
294
+ end
295
+ end
296
+
297
+ describe "color" do
298
+ it "should set fill color when :text_color is provided" do
299
+ pdf = Prawn::Document.new
300
+ pdf.stubs(:fill_color)
301
+ pdf.expects(:fill_color).with('555555')
302
+ pdf.cell :content => 'foo', :text_color => '555555'
303
+ end
304
+
305
+ it "should reset the fill color to the original one" do
306
+ pdf = Prawn::Document.new
307
+ pdf.fill_color = '333333'
308
+ pdf.cell :content => 'foo', :text_color => '555555'
309
+ pdf.fill_color.should == '333333'
310
+ end
311
+ end
312
+
313
+ describe "Borders" do
314
+ it "should draw all borders by default" do
315
+ @pdf.expects(:stroke_line).times(4)
316
+ @pdf.cell(:content => "text")
317
+ end
318
+
319
+ it "should draw all borders when requested" do
320
+ @pdf.expects(:stroke_line).times(4)
321
+ @pdf.cell(:content => "text", :borders => [:top, :right, :bottom, :left])
322
+ end
323
+
324
+ # Only roughly verifying the integer coordinates so that we don't have to
325
+ # do any FP closeness arithmetic. Can plug in that math later if this goes
326
+ # wrong.
327
+ it "should draw top border when requested" do
328
+ @pdf.expects(:stroke_line).checking do |from, to|
329
+ @pdf.map_to_absolute(from).map{|x| x.round}.should == [36, 756]
330
+ @pdf.map_to_absolute(to).map{|x| x.round}.should == [65, 756]
331
+ end
332
+ @pdf.cell(:content => "text", :borders => [:top])
333
+ end
334
+
335
+ it "should draw bottom border when requested" do
336
+ @pdf.expects(:stroke_line).checking do |from, to|
337
+ @pdf.map_to_absolute(from).map{|x| x.round}.should == [36, 732]
338
+ @pdf.map_to_absolute(to).map{|x| x.round}.should == [65, 732]
339
+ end
340
+ @pdf.cell(:content => "text", :borders => [:bottom])
341
+ end
342
+
343
+ it "should draw left border when requested" do
344
+ @pdf.expects(:stroke_line).checking do |from, to|
345
+ @pdf.map_to_absolute(from).map{|x| x.round}.should == [36, 756]
346
+ @pdf.map_to_absolute(to).map{|x| x.round}.should == [36, 732]
347
+ end
348
+ @pdf.cell(:content => "text", :borders => [:left])
349
+ end
350
+
351
+ it "should draw right border when requested" do
352
+ @pdf.expects(:stroke_line).checking do |from, to|
353
+ @pdf.map_to_absolute(from).map{|x| x.round}.should == [65, 756]
354
+ @pdf.map_to_absolute(to).map{|x| x.round}.should == [65, 732]
355
+ end
356
+ @pdf.cell(:content => "text", :borders => [:right])
357
+ end
358
+
359
+ it "should draw borders at the same location when in or out of bbox" do
360
+ @pdf.expects(:stroke_line).checking do |from, to|
361
+ @pdf.map_to_absolute(from).map{|x| x.round}.should == [36, 756]
362
+ @pdf.map_to_absolute(to).map{|x| x.round}.should == [65, 756]
363
+ end
364
+ @pdf.bounding_box([0, @pdf.cursor], :width => @pdf.bounds.width) do
365
+ @pdf.cell(:content => "text", :borders => [:top])
366
+ end
367
+ end
368
+
369
+ it "should set border color with :border_..._color" do
370
+ @pdf.ignores(:stroke_color=).with("000000")
371
+ @pdf.expects(:stroke_color=).with("ff0000")
372
+
373
+ c = @pdf.cell(:content => "text", :border_top_color => "ff0000")
374
+ c.border_top_color.should == "ff0000"
375
+ c.border_colors[0].should == "ff0000"
376
+ end
377
+
378
+ it "should set border colors with :border_color" do
379
+ @pdf.ignores(:stroke_color=).with("000000")
380
+ @pdf.expects(:stroke_color=).with("ff0000")
381
+ @pdf.expects(:stroke_color=).with("00ff00")
382
+ @pdf.expects(:stroke_color=).with("0000ff")
383
+ @pdf.expects(:stroke_color=).with("ff00ff")
384
+
385
+ c = @pdf.cell(:content => "text",
386
+ :border_color => %w[ff0000 00ff00 0000ff ff00ff])
387
+
388
+ c.border_colors.should == %w[ff0000 00ff00 0000ff ff00ff]
389
+ end
390
+
391
+ it "border_..._width should return 0 if border not selected" do
392
+ c = @pdf.cell(:content => "text", :borders => [:top])
393
+ c.border_bottom_width.should == 0
394
+ end
395
+
396
+ it "should set border width with :border_..._width" do
397
+ @pdf.ignores(:line_width=).with(1)
398
+ @pdf.expects(:line_width=).with(2)
399
+
400
+ c = @pdf.cell(:content => "text", :border_bottom_width => 2)
401
+ c.border_bottom_width.should == 2
402
+ c.border_widths[2].should == 2
403
+ end
404
+
405
+ it "should set border widths with :border_width" do
406
+ @pdf.ignores(:line_width=).with(1)
407
+ @pdf.expects(:line_width=).with(2)
408
+ @pdf.expects(:line_width=).with(3)
409
+ @pdf.expects(:line_width=).with(4)
410
+ @pdf.expects(:line_width=).with(5)
411
+
412
+ c = @pdf.cell(:content => "text",
413
+ :border_width => [2, 3, 4, 5])
414
+ c.border_widths.should == [2, 3, 4, 5]
415
+ end
416
+
417
+ it "should set default border lines to :solid" do
418
+ c = @pdf.cell(:content => "text")
419
+ c.border_top_line.should == :solid
420
+ c.border_right_line.should == :solid
421
+ c.border_bottom_line.should == :solid
422
+ c.border_left_line.should == :solid
423
+ c.border_lines.should == [:solid] * 4
424
+ end
425
+
426
+ it "should set border line with :border_..._line" do
427
+ c = @pdf.cell(:content => "text", :border_bottom_line => :dotted)
428
+ c.border_bottom_line.should == :dotted
429
+ c.border_lines[2].should == :dotted
430
+ end
431
+
432
+ it "should set border lines with :border_lines" do
433
+ c = @pdf.cell(:content => "text",
434
+ :border_lines => [:solid, :dotted, :dashed, :solid])
435
+ c.border_lines.should == [:solid, :dotted, :dashed, :solid]
436
+ end
437
+ end
438
+
439
+
440
+
441
+
442
+
443
+
444
+ describe "Text cell attributes" do
445
+ include CellHelpers
446
+
447
+ it "should pass through text options like :align to Text::Box" do
448
+ c = cell(:content => "text", :align => :right)
449
+
450
+ box = Prawn::Text::Box.new("text", :document => @pdf)
451
+
452
+ Prawn::Text::Box.expects(:new).checking do |text, options|
453
+ text.should == "text"
454
+ options[:align].should == :right
455
+ end.at_least_once.returns(box)
456
+
457
+ c.draw
458
+ end
459
+
460
+ it "should use font_style for Text::Box#style" do
461
+ c = cell(:content => "text", :font_style => :bold)
462
+
463
+ box = Prawn::Text::Box.new("text", :document => @pdf)
464
+
465
+ Prawn::Text::Box.expects(:new).checking do |text, options|
466
+ text.should == "text"
467
+ options[:style].should == :bold
468
+ end.at_least_once.returns(box)
469
+
470
+ c.draw
471
+ end
472
+
473
+ it "supports variant styles of the current font" do
474
+ font_path = "#{Prawn::BASEDIR}/data/fonts/Panic+Sans.dfont"
475
+ @pdf.font_families.merge!("Panic Sans" => {
476
+ :normal => { :file => font_path, :font => "PanicSans" },
477
+ :bold => { :file => font_path, :font => "PanicSans-Bold" },
478
+ })
479
+ @pdf.font "Panic Sans"
480
+
481
+ c = cell(:content => "text", :font_style => :bold)
482
+
483
+ box = Prawn::Text::Box.new("text", :document => @pdf)
484
+ Prawn::Text::Box.expects(:new).checking do |text, options|
485
+ text.should == "text"
486
+ options[:style].should == :bold
487
+ @pdf.font.family.should == 'Panic Sans'
488
+ end.at_least_once.returns(box)
489
+
490
+ c.draw
491
+ end
492
+
493
+
494
+ it "uses the style of the current font if none given" do
495
+ font_path = "#{Prawn::BASEDIR}/data/fonts/Panic+Sans.dfont"
496
+ @pdf.font_families.merge!("Panic Sans" => {
497
+ :normal => { :file => font_path, :font => "PanicSans" },
498
+ :bold => { :file => font_path, :font => "PanicSans-Bold" },
499
+ })
500
+ @pdf.font "Panic Sans", :style => :bold
501
+
502
+ c = cell(:content => "text")
503
+
504
+ box = Prawn::Text::Box.new("text", :document => @pdf)
505
+ Prawn::Text::Box.expects(:new).checking do |text, options|
506
+ text.should == "text"
507
+ @pdf.font.family.should == 'Panic Sans'
508
+ @pdf.font.options[:style].should == :bold
509
+ end.at_least_once.returns(box)
510
+
511
+ c.draw
512
+ end
513
+
514
+ it "should allow inline formatting in cells" do
515
+ c = cell(:content => "foo <b>bar</b> baz", :inline_format => true)
516
+
517
+ box = Prawn::Text::Formatted::Box.new([], :document => @pdf)
518
+
519
+ Prawn::Text::Formatted::Box.expects(:new).checking do |array, options|
520
+ array[0][:text].should == "foo "
521
+ array[0][:styles].should == []
522
+
523
+ array[1][:text].should == "bar"
524
+ array[1][:styles].should == [:bold]
525
+
526
+ array[2][:text].should == " baz"
527
+ array[2][:styles].should == []
528
+ end.at_least_once.returns(box)
529
+
530
+ c.draw
531
+ end
532
+
533
+ end
534
+
535
+ describe "Font handling" do
536
+ include CellHelpers
537
+
538
+ it "should allow only :font_style to be specified, defaulting to the " +
539
+ "document's font" do
540
+ c = cell(:content => "text", :font_style => :bold)
541
+ c.font.name.should == 'Helvetica-Bold'
542
+ end
543
+
544
+ it "should accept a font name for :font" do
545
+ c = cell(:content => "text", :font => 'Helvetica-Bold')
546
+ c.font.name.should == 'Helvetica-Bold'
547
+ end
548
+
549
+ it "should allow style to be changed after initialize" do
550
+ c = cell(:content => "text")
551
+ c.font_style = :bold
552
+ c.font.name.should == 'Helvetica-Bold'
553
+ end
554
+
555
+ it "should default to the document's font, if none is specified" do
556
+ c = cell(:content => "text")
557
+ c.font.should == @pdf.font
558
+ end
559
+
560
+ it "should use the metrics of the selected font (even if it is a variant " +
561
+ "of the document's font) to calculate width" do
562
+ c = cell(:content => "text", :font_style => :bold)
563
+ font = @pdf.find_font('Helvetica-Bold')
564
+ c.content_width.should == font.compute_width_of("text")
565
+ end
566
+
567
+ it "should properly calculate inline-formatted text" do
568
+ c = cell(:content => "<b>text</b>", :inline_format => true)
569
+ font = @pdf.find_font('Helvetica-Bold')
570
+ c.content_width.should == font.compute_width_of("text")
571
+ end
572
+ end
573
+ end
574
+
575
+ describe "Image cells" do
576
+ before(:each) do
577
+ create_pdf
578
+ end
579
+
580
+ describe "with default options" do
581
+ before(:each) do
582
+ @cell = Prawn::Table::Cell.make(@pdf,
583
+ :image => "#{Prawn::DATADIR}/images/prawn.png")
584
+ end
585
+
586
+ it "should create a Cell::Image" do
587
+ @cell.should be_a_kind_of(Prawn::Table::Cell::Image)
588
+ end
589
+
590
+ it "should pull the natural width and height from the image" do
591
+ @cell.natural_content_width.should == 141
592
+ @cell.natural_content_height.should == 142
593
+ end
594
+ end
595
+
596
+ describe "hash syntax" do
597
+ before(:each) do
598
+ @table = @pdf.make_table([[{
599
+ :image => "#{Prawn::DATADIR}/images/prawn.png",
600
+ :scale => 2,
601
+ :fit => [100, 200],
602
+ :image_width => 123,
603
+ :image_height => 456,
604
+ :position => :center,
605
+ :vposition => :center
606
+ }]])
607
+ @cell = @table.cells[0, 0]
608
+ end
609
+
610
+
611
+ it "should create a Cell::Image" do
612
+ @cell.should be_a_kind_of(Prawn::Table::Cell::Image)
613
+ end
614
+
615
+ it "should pass through image options" do
616
+ @pdf.expects(:embed_image).checking do |_, _, options|
617
+ options[:scale].should == 2
618
+ options[:fit].should == [100, 200]
619
+ options[:width].should == 123
620
+ options[:height].should == 456
621
+ options[:position].should == :center
622
+ options[:vposition].should == :center
623
+ end
624
+
625
+ @table.draw
626
+ end
627
+ end
628
+
629
+ end