prawn 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. data/Rakefile +3 -1
  2. data/data/fonts/Action Man.dfont +0 -0
  3. data/examples/general/measurement_units.rb +2 -2
  4. data/examples/graphics/image_flow.rb +2 -2
  5. data/examples/graphics/stroke_bounds.rb +1 -1
  6. data/examples/m17n/win_ansi_charset.rb +3 -3
  7. data/examples/text/dfont.rb +49 -0
  8. data/examples/text/flowing_text_with_header_and_footer.rb +2 -48
  9. data/examples/text/font_calculations.rb +7 -6
  10. data/examples/text/font_size.rb +4 -4
  11. data/examples/text/text_flow.rb +1 -1
  12. data/lib/prawn.rb +6 -3
  13. data/lib/prawn/compatibility.rb +12 -17
  14. data/lib/prawn/document.rb +10 -10
  15. data/lib/prawn/document/internals.rb +8 -3
  16. data/lib/prawn/document/text.rb +39 -57
  17. data/lib/prawn/document/text/box.rb +1 -2
  18. data/lib/prawn/document/text/wrapping.rb +59 -0
  19. data/lib/prawn/errors.rb +0 -8
  20. data/lib/prawn/font.rb +192 -277
  21. data/lib/prawn/font/afm.rb +199 -0
  22. data/lib/prawn/font/dfont.rb +31 -0
  23. data/lib/prawn/font/ttf.rb +318 -0
  24. data/lib/prawn/graphics.rb +7 -2
  25. data/lib/prawn/images/png.rb +1 -1
  26. data/lib/prawn/reference.rb +7 -4
  27. data/spec/font_spec.rb +154 -61
  28. data/spec/text_spec.rb +47 -6
  29. data/vendor/pdf-inspector/lib/pdf/inspector.rb +1 -1
  30. data/vendor/ttfunk/example.rb +42 -2
  31. data/vendor/ttfunk/lib/ttfunk.rb +96 -42
  32. data/vendor/ttfunk/lib/ttfunk/directory.rb +17 -0
  33. data/vendor/ttfunk/lib/ttfunk/encoding/mac_roman.rb +88 -0
  34. data/vendor/ttfunk/lib/ttfunk/encoding/windows_1252.rb +69 -0
  35. data/vendor/ttfunk/lib/ttfunk/reader.rb +44 -0
  36. data/vendor/ttfunk/lib/ttfunk/resource_file.rb +78 -0
  37. data/vendor/ttfunk/lib/ttfunk/subset.rb +18 -0
  38. data/vendor/ttfunk/lib/ttfunk/subset/base.rb +141 -0
  39. data/vendor/ttfunk/lib/ttfunk/subset/mac_roman.rb +46 -0
  40. data/vendor/ttfunk/lib/ttfunk/subset/unicode.rb +48 -0
  41. data/vendor/ttfunk/lib/ttfunk/subset/unicode_8bit.rb +63 -0
  42. data/vendor/ttfunk/lib/ttfunk/subset/windows_1252.rb +51 -0
  43. data/vendor/ttfunk/lib/ttfunk/subset_collection.rb +72 -0
  44. data/vendor/ttfunk/lib/ttfunk/table.rb +37 -18
  45. data/vendor/ttfunk/lib/ttfunk/table/cmap.rb +24 -84
  46. data/vendor/ttfunk/lib/ttfunk/table/cmap/format00.rb +54 -0
  47. data/vendor/ttfunk/lib/ttfunk/table/cmap/format04.rb +126 -0
  48. data/vendor/ttfunk/lib/ttfunk/table/cmap/subtable.rb +79 -0
  49. data/vendor/ttfunk/lib/ttfunk/table/glyf.rb +64 -0
  50. data/vendor/ttfunk/lib/ttfunk/table/glyf/compound.rb +81 -0
  51. data/vendor/ttfunk/lib/ttfunk/table/glyf/simple.rb +37 -0
  52. data/vendor/ttfunk/lib/ttfunk/table/head.rb +38 -19
  53. data/vendor/ttfunk/lib/ttfunk/table/hhea.rb +35 -21
  54. data/vendor/ttfunk/lib/ttfunk/table/hmtx.rb +40 -13
  55. data/vendor/ttfunk/lib/ttfunk/table/kern.rb +69 -38
  56. data/vendor/ttfunk/lib/ttfunk/table/kern/format0.rb +62 -0
  57. data/vendor/ttfunk/lib/ttfunk/table/loca.rb +43 -0
  58. data/vendor/ttfunk/lib/ttfunk/table/maxp.rb +34 -11
  59. data/vendor/ttfunk/lib/ttfunk/table/name.rb +109 -42
  60. data/vendor/ttfunk/lib/ttfunk/table/os2.rb +78 -0
  61. data/vendor/ttfunk/lib/ttfunk/table/post.rb +91 -0
  62. data/vendor/ttfunk/lib/ttfunk/table/post/format10.rb +43 -0
  63. data/vendor/ttfunk/lib/ttfunk/table/post/format20.rb +35 -0
  64. data/vendor/ttfunk/lib/ttfunk/table/post/format25.rb +23 -0
  65. data/vendor/ttfunk/lib/ttfunk/table/post/format30.rb +17 -0
  66. data/vendor/ttfunk/lib/ttfunk/table/post/format40.rb +17 -0
  67. data/vendor/ttfunk/lib/ttfunk/table/simple.rb +14 -0
  68. metadata +54 -25
  69. data/examples/table/addressbook.csv +0 -6
  70. data/examples/table/cell.rb +0 -40
  71. data/examples/table/currency.csv +0 -1834
  72. data/examples/table/fancy_table.rb +0 -62
  73. data/examples/table/ruport_formatter.rb +0 -53
  74. data/examples/table/table.rb +0 -51
  75. data/examples/table/table_alignment.rb +0 -18
  76. data/examples/table/table_border_color.rb +0 -17
  77. data/examples/table/table_colspan.rb +0 -19
  78. data/examples/table/table_header_color.rb +0 -19
  79. data/examples/table/table_header_underline.rb +0 -15
  80. data/lib/prawn/document/table.rb +0 -338
  81. data/lib/prawn/font/cmap.rb +0 -59
  82. data/lib/prawn/font/metrics.rb +0 -378
  83. data/lib/prawn/font/wrapping.rb +0 -47
  84. data/lib/prawn/graphics/cell.rb +0 -264
  85. data/spec/metrics_spec.rb +0 -62
  86. data/spec/table_spec.rb +0 -179
  87. data/vendor/ttfunk/lib/ttfunk/table/directory.rb +0 -25
@@ -1,264 +0,0 @@
1
- # encoding: utf-8
2
-
3
- # cell.rb : Table support functions
4
- #
5
- # Copyright June 2008, Gregory Brown. All Rights Reserved.
6
- #
7
- # This is free software. Please see the LICENSE and COPYING files for details.
8
- module Prawn
9
-
10
- class Document
11
- # Builds and renders a Graphics::Cell. A cell is essentially a
12
- # special-purpose bounding box designed for flowing text within a bordered
13
- # area. For available options, see Graphics::Cell#new.
14
- #
15
- # Prawn::Document.generate("cell.pdf") do
16
- # cell [100,500],
17
- # :width => 200,
18
- # :text => "The rain in Spain falls mainly on the plains"
19
- # end
20
- #
21
- def cell(point, options={})
22
- Prawn::Graphics::Cell.new(
23
- options.merge(:document => self, :point => point)).draw
24
- end
25
- end
26
-
27
- module Graphics
28
- # A cell is a special-purpose bounding box designed to flow text within a
29
- # bordered area. This is used by Prawn's Document::Table implementation but
30
- # can also be used standalone for drawing text boxes via Document#cell
31
- #
32
- class Cell
33
-
34
- # Creates a new cell object. Generally used indirectly via Document#cell
35
- #
36
- # Of the available options listed below, <tt>:point</tt>, <tt>:width</tt>,
37
- # and <tt>:text</tt> must be provided. If you are not using the
38
- # Document#cell shortcut, the <tt>:document</tt> must also be provided.
39
- #
40
- # <tt>:point</tt>:: Absolute [x,y] coordinate of the top-left corner of the cell.
41
- # <tt>:document</tt>:: The Prawn::Document object to render on.
42
- # <tt>:text</tt>:: The text to be flowed within the cell
43
- # <tt>:text_color</tt>:: The color of the text to be displayed
44
- # <tt>:width</tt>:: The width in PDF points of the cell.
45
- # <tt>:height</tt>:: The height in PDF points of the cell.
46
- # <tt>:horizontal_padding</tt>:: The horizontal padding in PDF points
47
- # <tt>:vertical_padding</tt>:: The vertical padding in PDF points
48
- # <tt>:padding</tt>:: Overrides both horizontal and vertical padding
49
- # <tt>:align</tt>:: One of <tt>:left</tt>, <tt>:right</tt>, <tt>:center</tt>
50
- # <tt>:borders</tt>:: An array of sides which should have a border. Any of <tt>:top</tt>, <tt>:left</tt>, <tt>:right</tt>, <tt>:bottom</tt>
51
- # <tt>:border_width</tt>:: The border line width. Defaults to 1.
52
- # <tt>:border_style</tt>:: One of <tt>:all</tt>, <tt>:no_top</tt>, <tt>:no_bottom</tt>, <tt>:sides</tt>, <tt>:none</tt>, <tt>:bottom_only</tt>. Defaults to :all.
53
- # <tt>:border_color</tt>:: The color of the cell border.
54
- # <tt>:font_size</tt>:: The font size for the cell text.
55
- #
56
- def initialize(options={})
57
- @point = options[:point]
58
- @document = options[:document]
59
- @text = options[:text].to_s
60
- @text_color = options[:text_color]
61
- @width = options[:width]
62
- @height = options[:height]
63
- @borders = options[:borders]
64
- @border_width = options[:border_width] || 1
65
- @border_style = options[:border_style] || :all
66
- @border_color = options[:border_color]
67
- @background_color = options[:background_color]
68
- @align = options[:align] || :left
69
- @font_size = options[:font_size]
70
-
71
- @horizontal_padding = options[:horizontal_padding] || 0
72
- @vertical_padding = options[:vertical_padding] || 0
73
-
74
- if options[:padding]
75
- @horizontal_padding = @vertical_padding = options[:padding]
76
- end
77
- end
78
-
79
- attr_accessor :point, :border_style, :border_width, :background_color,
80
- :document, :horizontal_padding, :vertical_padding, :align,
81
- :borders, :text_color, :border_color
82
-
83
- attr_writer :height, :width #:nodoc:
84
-
85
- # Returns the cell's text as a string.
86
- #
87
- def to_s
88
- @text
89
- end
90
-
91
- # The width of the text area excluding the horizonal padding
92
- #
93
- def text_area_width
94
- width - 2*@horizontal_padding
95
- end
96
-
97
- # The width of the cell in PDF points
98
- #
99
- def width
100
- @width || (@document.font.metrics.string_width(@text,
101
- @font_size || @document.font.size)) + 2*@horizontal_padding
102
- end
103
-
104
- # The height of the cell in PDF points
105
- #
106
- def height
107
- @height || text_area_height + 2*@vertical_padding
108
- end
109
-
110
- # The height of the text area excluding the vertical padding
111
- #
112
- def text_area_height
113
- @document.font.height_of(@text, :line_width => text_area_width)
114
- end
115
-
116
- # Draws the cell onto the PDF document
117
- #
118
- def draw
119
- rel_point = @point
120
-
121
- if @background_color
122
- @document.mask(:fill_color) do
123
- @document.fill_color @background_color
124
- h = borders.include?(:bottom) ?
125
- height - border_width : height + border_width / 2.0
126
- @document.fill_rectangle [rel_point[0] + border_width / 2.0,
127
- rel_point[1] - border_width / 2.0 ],
128
- width - border_width, h
129
- end
130
- end
131
-
132
- if @border_width > 0
133
- @document.mask(:line_width) do
134
- @document.line_width = @border_width
135
-
136
- @document.mask(:stroke_color) do
137
- @document.stroke_color @border_color if @border_color
138
-
139
- if borders.include?(:left)
140
- @document.stroke_line [rel_point[0], rel_point[1] + (@border_width / 2.0)],
141
- [rel_point[0], rel_point[1] - height - @border_width / 2.0 ]
142
- end
143
-
144
- if borders.include?(:right)
145
- @document.stroke_line(
146
- [rel_point[0] + width, rel_point[1] + (@border_width / 2.0)],
147
- [rel_point[0] + width, rel_point[1] - height - @border_width / 2.0] )
148
- end
149
-
150
- if borders.include?(:top)
151
- @document.stroke_line(
152
- [ rel_point[0] + @border_width / 2.0, rel_point[1] ],
153
- [ rel_point[0] - @border_width / 2.0 + width, rel_point[1] ])
154
- end
155
-
156
- if borders.include?(:bottom)
157
- @document.stroke_line [rel_point[0], rel_point[1] - height ],
158
- [rel_point[0] + width, rel_point[1] - height]
159
- end
160
- end
161
-
162
- end
163
-
164
- borders
165
-
166
- end
167
-
168
- @document.bounding_box( [@point[0] + @horizontal_padding,
169
- @point[1] - @vertical_padding],
170
- :width => text_area_width,
171
- :height => height - @vertical_padding) do
172
- @document.move_up @document.font.line_gap
173
-
174
- options = {:align => @align}
175
-
176
- options[:size] = @font_size if @font_size
177
-
178
- @document.mask(:fill_color) do
179
- @document.fill_color @text_color if @text_color
180
- @document.text @text, options
181
- end
182
- end
183
- end
184
-
185
- private
186
-
187
- def borders
188
- @borders ||= case @border_style
189
- when :all
190
- [:top,:left,:right,:bottom]
191
- when :sides
192
- [:left,:right]
193
- when :no_top
194
- [:left,:right,:bottom]
195
- when :no_bottom
196
- [:left,:right,:top]
197
- when :bottom_only
198
- [:bottom]
199
- when :none
200
- []
201
- end
202
- end
203
-
204
- end
205
-
206
- class CellBlock #:nodoc:
207
-
208
- # Not sure if this class is something I want to expose in the public API.
209
-
210
- def initialize(document)
211
- @document = document
212
- @cells = []
213
- @width = 0
214
- @height = 0
215
- end
216
-
217
- attr_reader :width, :height, :cells
218
- attr_accessor :background_color, :text_color, :border_color
219
-
220
- def <<(cell)
221
- @cells << cell
222
- @height = cell.height if cell.height > @height
223
- @width += cell.width
224
- self
225
- end
226
-
227
- def draw
228
- y = @document.y
229
- x = @document.bounds.absolute_left
230
-
231
- @cells.each do |e|
232
- e.point = [x - @document.bounds.absolute_left,
233
- y - @document.bounds.absolute_bottom]
234
- e.height = @height
235
- e.background_color ||= @background_color
236
- e.text_color ||= @text_color
237
- e.border_color ||= @border_color
238
- e.draw
239
- x += e.width
240
- end
241
-
242
- @document.y = y - @height
243
- end
244
-
245
- def border_width
246
- @cells[0].border_width
247
- end
248
-
249
- def border_style=(s)
250
- @cells.each { |e| e.border_style = s }
251
- end
252
-
253
- def align=(align)
254
- @cells.each { |e| e.align = align }
255
- end
256
-
257
- def border_style
258
- @cells[0].border_style
259
- end
260
-
261
- end
262
- end
263
-
264
- end
data/spec/metrics_spec.rb DELETED
@@ -1,62 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper")
4
- require "iconv"
5
-
6
- describe "adobe font metrics" do
7
-
8
- setup do
9
- @times = Prawn::Font::Metrics["Times-Roman"]
10
- @iconv = ::Iconv.new('ISO-8859-1', 'utf-8')
11
- end
12
-
13
- it "should calculate string width taking into account accented characters" do
14
- @times.string_width(@iconv.iconv("é"), 12).should == @times.string_width("e", 12)
15
- end
16
-
17
- it "should calculate string width taking into account kerning pairs" do
18
- @times.string_width(@iconv.iconv("To"), 12).should == 13.332
19
- @times.string_width(@iconv.iconv("To"), 12, :kerning => true).should == 12.372
20
- @times.string_width(@iconv.iconv("Tö"), 12, :kerning => true).should == 12.372
21
- end
22
-
23
- it "should kern a string" do
24
- @times.kern(@iconv.iconv("To")).should == ["T", 80, "o"]
25
- @times.kern(@iconv.iconv("Télé")).should == ["T", 70, @iconv.iconv("élé")]
26
- @times.kern(@iconv.iconv("Technology")).should == ["T", 70, "echnology"]
27
- @times.kern(@iconv.iconv("Technology...")).should == ["T", 70, "echnology", 65, "..."]
28
- end
29
-
30
- end
31
-
32
- describe "ttf font metrics" do
33
-
34
- setup do
35
- @activa = Prawn::Font::Metrics["#{Prawn::BASEDIR}/data/fonts/Activa.ttf"]
36
- end
37
-
38
- it "should calculate string width taking into account accented characters" do
39
- @activa.string_width("é", 12).should == @activa.string_width("e", 12)
40
- end
41
-
42
- it "should calculate string width taking into account kerning pairs" do
43
- @activa.string_width("To", 12).should == 15.228
44
- @activa.string_width("To", 12, :kerning => true).should.to_s == 12.996.to_s
45
- end
46
-
47
- it "should kern a string" do
48
- @activa.kern("To").should == ["\0007", 186.0, "\000R"]
49
-
50
- # Does activa use kerning classes here? Ruby/TTF doesn't support
51
- # format 2 kerning tables, so don't bother for now.
52
-
53
- # @activa.kern("Télé").should == ["T", -186, "élé"]
54
-
55
- @activa.kern("Technology").should == ["\0007", 186.0,
56
- "\000H\000F\000K\000Q\000R\000O\000R\000J\000\\"]
57
- @activa.kern("Technology...").should == ["\0007", 186.0,
58
- "\000H\000F\000K\000Q\000R\000O\000R\000J\000\\", 88.0,
59
- "\000\021\000\021\000\021"]
60
- end
61
-
62
- end
data/spec/table_spec.rb DELETED
@@ -1,179 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper")
4
-
5
- describe "A table's width" do
6
- it "should equal sum(col_widths)" do
7
- pdf = Prawn::Document.new
8
- table = Prawn::Document::Table.new( [%w[ a b c ], %w[d e f]], pdf,
9
- :widths => { 0 => 50, 1 => 100, 2 => 150 })
10
-
11
- table.width.should == 300
12
- end
13
-
14
- it "should calculate unspecified column widths as "+
15
- "(max(string_width).ceil + 2*horizontal_padding)" do
16
- pdf = Prawn::Document.new
17
- hpad, fs = 3, 12
18
- columns = 2
19
- table = Prawn::Document::Table.new( [%w[ foo b ], %w[d foobar]], pdf,
20
- :horizontal_padding => hpad, :font_size => fs)
21
-
22
- col0_width = pdf.font.metrics.string_width("foo",fs)
23
- col1_width = pdf.font.metrics.string_width("foobar",fs)
24
-
25
- table.width.should == col0_width.ceil + col1_width.ceil + 2*columns*hpad
26
- end
27
-
28
- it "should allow mixing autocalculated and preset"+
29
- "column widths within a single table" do
30
-
31
- pdf = Prawn::Document.new
32
- hpad, fs = 10, 6
33
- stretchy_columns = 2
34
-
35
- col0_width = 50
36
- col1_width = pdf.font.metrics.string_width("foo",fs)
37
- col2_width = pdf.font.metrics.string_width("foobar",fs)
38
- col3_width = 150
39
-
40
- table = Prawn::Document::Table.new( [%w[snake foo b apple],
41
- %w[kitten d foobar banana]], pdf,
42
- :horizontal_padding => hpad, :font_size => fs,
43
- :widths => { 0 => col0_width, 3 => col3_width } )
44
-
45
- table.width.should == col1_width.ceil + col2_width.ceil +
46
- 2*stretchy_columns*hpad +
47
- col0_width.ceil + col3_width.ceil
48
-
49
- end
50
-
51
- end
52
-
53
- describe "A table's height" do
54
-
55
- before :each do
56
- data = [["foo"],["bar"],["baaaz"]]
57
- pdf = Prawn::Document.new
58
- @num_rows = data.length
59
-
60
- @vpad = 4
61
- origin = pdf.y
62
- pdf.table data, :vertical_padding => @vpad
63
-
64
- @table_height = origin - pdf.y
65
-
66
- @font_height = pdf.font.height
67
- end
68
-
69
- it "should have a height of n rows" do
70
- @table_height.should.be.close(
71
- @num_rows*@font_height + 2*@vpad*@num_rows, 0.001 )
72
- end
73
-
74
- end
75
-
76
- describe "A table's content" do
77
-
78
- it "should not cause an error if rendering the very first row causes a page break" do
79
- Prawn::Document.new( :page_layout => :portrait ) do
80
- arr = Array(1..5).collect{|i| ["cell #{i}"] }
81
-
82
- move_down( y - (bounds.absolute_bottom + 3) )
83
-
84
- lambda {
85
- table( arr,
86
- :font_size => 9,
87
- :horizontal_padding => 3,
88
- :vertical_padding => 3,
89
- :border_width => 0.05,
90
- :border_style => :none,
91
- :row_colors => %w{ffffff eeeeee},
92
- :widths => {0 =>110},
93
- :position => :left,
94
- :headers => ["exploding header"],
95
- :align => :left,
96
- :align_headers => :center)
97
- }.should.not.raise
98
- end
99
- end
100
-
101
- it "should output content cell by cell, row by row" do
102
- data = [["foo","bar"],["baz","bang"]]
103
- @pdf = Prawn::Document.new
104
- @pdf.table(data)
105
- output = PDF::Inspector::Text.analyze(@pdf.render)
106
- output.strings.should == data.flatten
107
- end
108
-
109
- it "should add headers to output when specified" do
110
- data = [["foo","bar"],["baz","bang"]]
111
- headers = %w[a b]
112
- @pdf = Prawn::Document.new
113
- @pdf.table(data, :headers => headers)
114
- output = PDF::Inspector::Text.analyze(@pdf.render)
115
- output.strings.should == headers + data.flatten
116
- end
117
-
118
- it "should repeat headers across pages" do
119
- data = [["foo","bar"]]*30
120
- headers = ["baz","foobar"]
121
- @pdf = Prawn::Document.new
122
- @pdf.table(data, :headers => headers)
123
- output = PDF::Inspector::Text.analyze(@pdf.render)
124
- output.strings.should == headers + data.flatten[0..-3] + headers +
125
- data.flatten[-2..-1]
126
- end
127
-
128
- it "should allow empty fields" do
129
- lambda {
130
- data = [["foo","bar"],["baz",""]]
131
- @pdf = Prawn::Document.new
132
- @pdf.table(data)
133
- }.should.not.raise
134
- end
135
-
136
- it "should paginate for large tables" do
137
- # 30 rows fit on the table with default setting, 31 exceed.
138
- data = [["foo"]] * 31
139
- pdf = Prawn::Document.new
140
-
141
- pdf.table data
142
- pdf.page_count.should == 2
143
-
144
- pdf.table data
145
- pdf.page_count.should == 3
146
- end
147
-
148
- end
149
-
150
- describe "An invalid table" do
151
-
152
- before(:each) do
153
- @pdf = Prawn::Document.new
154
- @bad_data = ["Single Nested Array"]
155
- end
156
-
157
- it "should raise error when invalid table data is given" do
158
- assert_raises(Prawn::Errors::InvalidTableData) do
159
- @pdf.table(@bad_data)
160
- end
161
- end
162
-
163
- it "should raise an EmptyTableError with empty table data" do
164
- lambda {
165
- data = []
166
- @pdf = Prawn::Document.new
167
- @pdf.table(data)
168
- }.should.raise( Prawn::Errors::EmptyTable )
169
- end
170
-
171
- it "should raise an EmptyTableError with nil table data" do
172
- lambda {
173
- data = nil
174
- @pdf = Prawn::Document.new
175
- @pdf.table(data)
176
- }.should.raise( Prawn::Errors::EmptyTable )
177
- end
178
-
179
- end