prawn 0.11.1.pre → 0.11.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (204) hide show
  1. data/COPYING +2 -340
  2. data/HACKING +1 -1
  3. data/LICENSE +3 -3
  4. data/Rakefile +17 -6
  5. data/data/encodings/win_ansi.txt +1 -1
  6. data/data/images/prawn.png +0 -0
  7. data/data/pdfs/form.pdf +820 -0
  8. data/data/pdfs/multipage_template.pdf +127 -0
  9. data/examples/bounding_box/bounding_boxes.rb +4 -3
  10. data/examples/bounding_box/indentation.rb +2 -1
  11. data/examples/bounding_box/russian_boxes.rb +3 -2
  12. data/examples/bounding_box/stretched_nesting.rb +2 -1
  13. data/examples/general/background.rb +2 -1
  14. data/examples/general/canvas.rb +2 -1
  15. data/examples/general/context_sensitive_headers.rb +2 -1
  16. data/examples/general/float.rb +2 -1
  17. data/examples/general/margin.rb +2 -1
  18. data/examples/general/measurement_units.rb +2 -1
  19. data/examples/general/metadata-info.rb +2 -1
  20. data/examples/general/multi_page_layout.rb +2 -1
  21. data/examples/general/outlines.rb +2 -1
  22. data/examples/general/page_geometry.rb +2 -1
  23. data/examples/general/page_numbering.rb +27 -2
  24. data/examples/general/page_templates.rb +20 -0
  25. data/examples/general/repeaters.rb +2 -1
  26. data/examples/general/stamp.rb +4 -3
  27. data/examples/general/templates.rb +2 -1
  28. data/examples/graphics/basic_images.rb +2 -1
  29. data/examples/graphics/cmyk.rb +2 -1
  30. data/examples/graphics/curves.rb +4 -3
  31. data/examples/graphics/gradient.rb +23 -0
  32. data/examples/graphics/hexagon.rb +3 -2
  33. data/examples/graphics/image_fit.rb +3 -2
  34. data/examples/graphics/image_flow.rb +2 -1
  35. data/examples/graphics/image_position.rb +3 -2
  36. data/examples/graphics/line.rb +2 -1
  37. data/examples/graphics/png_types.rb +3 -2
  38. data/examples/graphics/polygons.rb +3 -2
  39. data/examples/graphics/remote_images.rb +2 -1
  40. data/examples/graphics/rounded_polygons.rb +2 -1
  41. data/examples/graphics/rounded_rectangle.rb +2 -1
  42. data/examples/graphics/ruport_style_helpers.rb +3 -2
  43. data/examples/graphics/stroke_bounds.rb +2 -1
  44. data/examples/graphics/stroke_cap_and_join.rb +2 -1
  45. data/examples/graphics/stroke_dash.rb +2 -1
  46. data/examples/graphics/transformations.rb +2 -1
  47. data/examples/graphics/transparency.rb +4 -3
  48. data/examples/grid/bounding_boxes.rb +2 -1
  49. data/examples/grid/column_gutter_grid.rb +2 -1
  50. data/examples/grid/multi_boxes.rb +2 -1
  51. data/examples/grid/show_grid.rb +2 -1
  52. data/examples/grid/simple_grid.rb +2 -1
  53. data/examples/m17n/chinese_text_wrapping.rb +2 -1
  54. data/examples/m17n/euro.rb +3 -2
  55. data/examples/m17n/full_win_ansi_character_list.rb +20 -0
  56. data/examples/m17n/sjis.rb +2 -1
  57. data/examples/m17n/utf8.rb +3 -2
  58. data/examples/m17n/win_ansi_charset.rb +2 -1
  59. data/examples/security/hello_foo.rb +2 -1
  60. data/examples/table/bill.rb +2 -1
  61. data/examples/table/borders.rb +25 -0
  62. data/examples/table/cell.rb +3 -2
  63. data/examples/table/checkerboard.rb +2 -1
  64. data/examples/table/header.rb +3 -2
  65. data/examples/table/inline_format_table.rb +2 -1
  66. data/examples/table/multi_page_table.rb +2 -1
  67. data/examples/table/simple_table.rb +2 -1
  68. data/examples/table/subtable.rb +2 -1
  69. data/examples/table/widths.rb +2 -1
  70. data/examples/text/alignment.rb +2 -1
  71. data/examples/text/character_spacing.rb +2 -1
  72. data/examples/text/dfont.rb +2 -1
  73. data/examples/text/family_based_styling.rb +3 -2
  74. data/examples/text/font_calculations.rb +2 -1
  75. data/examples/text/font_size.rb +2 -1
  76. data/examples/text/hyphenation.rb +2 -2
  77. data/examples/text/indent_paragraphs.rb +7 -5
  78. data/examples/text/inline_format.rb +7 -6
  79. data/examples/text/kerning.rb +2 -1
  80. data/examples/text/rendering_mode.rb +21 -0
  81. data/examples/text/rotated.rb +2 -1
  82. data/examples/text/shaped_text_box.rb +2 -1
  83. data/examples/text/simple_text.rb +2 -1
  84. data/examples/text/simple_text_ttf.rb +2 -1
  85. data/examples/text/span.rb +3 -2
  86. data/examples/text/text_box.rb +7 -5
  87. data/examples/text/text_box_returning_excess.rb +4 -3
  88. data/examples/text/text_flow.rb +2 -1
  89. data/lib/prawn.rb +1 -1
  90. data/lib/prawn/core/object_store.rb +42 -14
  91. data/lib/prawn/core/page.rb +22 -8
  92. data/lib/prawn/core/text.rb +141 -13
  93. data/lib/prawn/core/text/formatted/arranger.rb +39 -12
  94. data/lib/prawn/core/text/formatted/line_wrap.rb +205 -60
  95. data/lib/prawn/core/text/formatted/wrap.rb +72 -35
  96. data/lib/prawn/document.rb +174 -70
  97. data/lib/prawn/document/bounding_box.rb +122 -83
  98. data/lib/prawn/document/column_box.rb +113 -0
  99. data/lib/prawn/document/graphics_state.rb +90 -2
  100. data/lib/prawn/document/internals.rb +5 -3
  101. data/lib/prawn/errors.rb +5 -0
  102. data/lib/prawn/font.rb +4 -4
  103. data/lib/prawn/font/afm.rb +11 -0
  104. data/lib/prawn/font/ttf.rb +5 -0
  105. data/lib/prawn/graphics.rb +77 -14
  106. data/lib/prawn/graphics/cap_style.rb +13 -5
  107. data/lib/prawn/graphics/color.rb +54 -35
  108. data/lib/prawn/graphics/dash.rb +27 -16
  109. data/lib/prawn/graphics/gradient.rb +84 -0
  110. data/lib/prawn/graphics/join_style.rb +12 -3
  111. data/lib/prawn/graphics/transparency.rb +4 -4
  112. data/lib/prawn/images.rb +18 -160
  113. data/lib/prawn/images/jpg.rb +39 -0
  114. data/lib/prawn/images/png.rb +130 -0
  115. data/lib/prawn/repeater.rb +6 -13
  116. data/lib/prawn/security.rb +6 -1
  117. data/lib/prawn/stamp.rb +12 -4
  118. data/lib/prawn/table.rb +36 -4
  119. data/lib/prawn/table/cell.rb +224 -63
  120. data/lib/prawn/table/cell/text.rb +20 -10
  121. data/lib/prawn/table/cells.rb +23 -6
  122. data/lib/prawn/text.rb +54 -91
  123. data/lib/prawn/text/box.rb +29 -283
  124. data/lib/prawn/text/formatted/box.rb +349 -24
  125. data/lib/prawn/text/formatted/fragment.rb +63 -2
  126. data/lib/prawn/text/formatted/parser.rb +2 -1
  127. data/prawn.gemspec +21 -5
  128. data/spec/bounding_box_spec.rb +61 -28
  129. data/spec/cell_spec.rb +168 -30
  130. data/spec/document_spec.rb +187 -3
  131. data/spec/extensions/mocha.rb +45 -0
  132. data/spec/font_spec.rb +32 -1
  133. data/spec/formatted_text_arranger_spec.rb +35 -40
  134. data/spec/formatted_text_box_spec.rb +287 -443
  135. data/spec/formatted_text_fragment_spec.rb +87 -0
  136. data/spec/graphics_spec.rb +128 -12
  137. data/spec/grid_spec.rb +1 -1
  138. data/spec/images_spec.rb +11 -3
  139. data/spec/inline_formatted_text_parser_spec.rb +8 -0
  140. data/spec/line_wrap_spec.rb +200 -208
  141. data/spec/object_store_spec.rb +10 -0
  142. data/spec/outline_spec.rb +7 -3
  143. data/spec/repeater_spec.rb +58 -10
  144. data/spec/security_spec.rb +6 -0
  145. data/spec/spec_helper.rb +12 -8
  146. data/spec/stamp_spec.rb +52 -1
  147. data/spec/stroke_styles_spec.rb +30 -0
  148. data/spec/table_spec.rb +93 -3
  149. data/spec/template_spec.rb +132 -6
  150. data/spec/text_at_spec.rb +14 -4
  151. data/spec/text_box_spec.rb +309 -70
  152. data/spec/text_rendering_mode_spec.rb +45 -0
  153. data/spec/text_spec.rb +60 -17
  154. data/spec/text_with_inline_formatting_spec.rb +4 -162
  155. metadata +241 -241
  156. data/lib/prawn/core/text/line_wrap.rb +0 -211
  157. data/lib/prawn/core/text/wrap.rb +0 -82
  158. data/vendor/pdf-inspector/README +0 -18
  159. data/vendor/pdf-inspector/lib/pdf/inspector.rb +0 -26
  160. data/vendor/pdf-inspector/lib/pdf/inspector/extgstate.rb +0 -18
  161. data/vendor/pdf-inspector/lib/pdf/inspector/graphics.rb +0 -131
  162. data/vendor/pdf-inspector/lib/pdf/inspector/page.rb +0 -25
  163. data/vendor/pdf-inspector/lib/pdf/inspector/text.rb +0 -46
  164. data/vendor/pdf-inspector/lib/pdf/inspector/xobject.rb +0 -19
  165. data/vendor/ttfunk/data/fonts/DejaVuSans.ttf +0 -0
  166. data/vendor/ttfunk/data/fonts/comicsans.ttf +0 -0
  167. data/vendor/ttfunk/example.rb +0 -45
  168. data/vendor/ttfunk/lib/ttfunk.rb +0 -102
  169. data/vendor/ttfunk/lib/ttfunk/directory.rb +0 -17
  170. data/vendor/ttfunk/lib/ttfunk/encoding/mac_roman.rb +0 -88
  171. data/vendor/ttfunk/lib/ttfunk/encoding/windows_1252.rb +0 -69
  172. data/vendor/ttfunk/lib/ttfunk/reader.rb +0 -44
  173. data/vendor/ttfunk/lib/ttfunk/resource_file.rb +0 -78
  174. data/vendor/ttfunk/lib/ttfunk/subset.rb +0 -18
  175. data/vendor/ttfunk/lib/ttfunk/subset/base.rb +0 -141
  176. data/vendor/ttfunk/lib/ttfunk/subset/mac_roman.rb +0 -50
  177. data/vendor/ttfunk/lib/ttfunk/subset/unicode.rb +0 -48
  178. data/vendor/ttfunk/lib/ttfunk/subset/unicode_8bit.rb +0 -63
  179. data/vendor/ttfunk/lib/ttfunk/subset/windows_1252.rb +0 -55
  180. data/vendor/ttfunk/lib/ttfunk/subset_collection.rb +0 -72
  181. data/vendor/ttfunk/lib/ttfunk/table.rb +0 -46
  182. data/vendor/ttfunk/lib/ttfunk/table/cmap.rb +0 -34
  183. data/vendor/ttfunk/lib/ttfunk/table/cmap/format00.rb +0 -54
  184. data/vendor/ttfunk/lib/ttfunk/table/cmap/format04.rb +0 -126
  185. data/vendor/ttfunk/lib/ttfunk/table/cmap/subtable.rb +0 -79
  186. data/vendor/ttfunk/lib/ttfunk/table/glyf.rb +0 -64
  187. data/vendor/ttfunk/lib/ttfunk/table/glyf/compound.rb +0 -81
  188. data/vendor/ttfunk/lib/ttfunk/table/glyf/simple.rb +0 -37
  189. data/vendor/ttfunk/lib/ttfunk/table/head.rb +0 -44
  190. data/vendor/ttfunk/lib/ttfunk/table/hhea.rb +0 -41
  191. data/vendor/ttfunk/lib/ttfunk/table/hmtx.rb +0 -47
  192. data/vendor/ttfunk/lib/ttfunk/table/kern.rb +0 -79
  193. data/vendor/ttfunk/lib/ttfunk/table/kern/format0.rb +0 -62
  194. data/vendor/ttfunk/lib/ttfunk/table/loca.rb +0 -43
  195. data/vendor/ttfunk/lib/ttfunk/table/maxp.rb +0 -40
  196. data/vendor/ttfunk/lib/ttfunk/table/name.rb +0 -125
  197. data/vendor/ttfunk/lib/ttfunk/table/os2.rb +0 -78
  198. data/vendor/ttfunk/lib/ttfunk/table/post.rb +0 -91
  199. data/vendor/ttfunk/lib/ttfunk/table/post/format10.rb +0 -43
  200. data/vendor/ttfunk/lib/ttfunk/table/post/format20.rb +0 -35
  201. data/vendor/ttfunk/lib/ttfunk/table/post/format25.rb +0 -23
  202. data/vendor/ttfunk/lib/ttfunk/table/post/format30.rb +0 -17
  203. data/vendor/ttfunk/lib/ttfunk/table/post/format40.rb +0 -17
  204. data/vendor/ttfunk/lib/ttfunk/table/simple.rb +0 -14
@@ -14,9 +14,9 @@ module Prawn
14
14
  #
15
15
  class Text < Cell
16
16
 
17
- TextOptions = [:inline_format, :kerning, :size, :style,
18
- :align, :valign, :rotate, :rotate_around, :leading, :single_line,
19
- :skip_encoding, :overflow, :min_font_size]
17
+ TextOptions = [:inline_format, :kerning, :size, :align, :valign,
18
+ :rotate, :rotate_around, :leading, :single_line, :skip_encoding,
19
+ :overflow, :min_font_size]
20
20
 
21
21
  TextOptions.each do |option|
22
22
  define_method("#{option}=") { |v| @text_options[option] = v }
@@ -28,12 +28,6 @@ module Prawn
28
28
  def initialize(pdf, point, options={})
29
29
  @text_options = {}
30
30
  super
31
-
32
- # Sets a reasonable minimum width. If the cell has any content, make
33
- # sure we have enough width to be at least one character wide. This is
34
- # a bit of a hack, but it should work well enough.
35
- min_content_width = [natural_content_width, styled_width_of("M")].min
36
- @min_width = padding_left + padding_right + min_content_width
37
31
  end
38
32
 
39
33
  # Returns the font that will be used to draw this cell.
@@ -42,6 +36,13 @@ module Prawn
42
36
  with_font { @pdf.font }
43
37
  end
44
38
 
39
+ # Sets the style of the font in use. Equivalent to the Text::Box
40
+ # +style+ option, but we already have a style method.
41
+ #
42
+ def font_style=(style)
43
+ @text_options[:style] = style
44
+ end
45
+
45
46
  # Returns the width of this text with no wrapping. This will be far off
46
47
  # from the final width if the text is long.
47
48
  #
@@ -56,7 +57,7 @@ module Prawn
56
57
  with_font do
57
58
  b = text_box(:width => content_width + FPTolerance)
58
59
  b.render(:dry_run => true)
59
- b.height
60
+ b.height + b.line_gap
60
61
  end
61
62
  end
62
63
 
@@ -75,6 +76,15 @@ module Prawn
75
76
 
76
77
  protected
77
78
 
79
+ def set_width_constraints
80
+ # Sets a reasonable minimum width. If the cell has any content, make
81
+ # sure we have enough width to be at least one character wide. This is
82
+ # a bit of a hack, but it should work well enough.
83
+ min_content_width = [natural_content_width, styled_width_of("M")].min
84
+ @min_width ||= padding_left + padding_right + min_content_width
85
+ super
86
+ end
87
+
78
88
  def with_font
79
89
  @pdf.save_font do
80
90
  options = {}
@@ -54,6 +54,7 @@ module Prawn
54
54
  #
55
55
  def rows(row_spec)
56
56
  index_cells unless @indexed
57
+ row_spec = transform_spec(row_spec, @row_count)
57
58
  Cells.new(@rows[row_spec] ||= select{ |c| row_spec === c.row })
58
59
  end
59
60
  alias_method :row, :rows
@@ -65,9 +66,10 @@ module Prawn
65
66
  # table.column(0) # selects first column
66
67
  # table.columns(3..4) # selects columns four and five
67
68
  #
68
- def columns(row_spec)
69
+ def columns(col_spec)
69
70
  index_cells unless @indexed
70
- Cells.new(@columns[row_spec] ||= select{ |c| row_spec === c.column })
71
+ col_spec = transform_spec(col_spec, @column_count)
72
+ Cells.new(@columns[col_spec] ||= select{ |c| col_spec === c.column })
71
73
  end
72
74
  alias_method :column, :columns
73
75
 
@@ -104,10 +106,7 @@ module Prawn
104
106
  # table.cells.style { |cell| cell.border_width += 12 }
105
107
  #
106
108
  def style(options={}, &block)
107
- each do |cell|
108
- options.each { |k, v| cell.send("#{k}=", v) }
109
- block.call(cell) if block
110
- end
109
+ each { |cell| cell.style(options, &block) }
111
110
  end
112
111
 
113
112
  # Returns the total width of all columns in the selected set.
@@ -182,8 +181,26 @@ module Prawn
182
181
  @columns[cell.column] << cell
183
182
  end
184
183
 
184
+ @row_count = @rows.size
185
+ @column_count = @columns.size
186
+
185
187
  @indexed = true
186
188
  end
189
+
190
+ # Transforms +spec+, a column / row specification, into an object that
191
+ # can be compared against a row or column number using ===. Normalizes
192
+ # negative indices to be positive, given a total size of +total+.
193
+ #
194
+ def transform_spec(spec, total)
195
+ case spec
196
+ when Range
197
+ transform_spec(spec.begin, total)..transform_spec(spec.end, total)
198
+ when Integer
199
+ spec < 0 ? (total + spec) : spec
200
+ else # pass through
201
+ spec
202
+ end
203
+ end
187
204
  end
188
205
  end
189
206
  end
data/lib/prawn/text.rb CHANGED
@@ -6,9 +6,8 @@
6
6
  #
7
7
  # This is free software. Please see the LICENSE and COPYING files for details.
8
8
  require "prawn/core/text"
9
- require "prawn/core/text/wrap"
10
- require "prawn/text/box"
11
9
  require "prawn/text/formatted"
10
+ require "prawn/text/box"
12
11
  require "zlib"
13
12
 
14
13
  module Prawn
@@ -17,7 +16,12 @@ module Prawn
17
16
  include Prawn::Core::Text
18
17
  include Prawn::Text::Formatted
19
18
 
19
+ # No-Break Space
20
20
  Prawn::Text::NBSP = " "
21
+ # Zero Width Space (indicate word boundaries without a space)
22
+ Prawn::Text::ZWSP = [8203].pack("U")
23
+ # Soft Hyphen (invisible, except when causing a line break)
24
+ Prawn::Text::SHY = "­"
21
25
 
22
26
  # If you want text to flow onto a new page or between columns, this is the
23
27
  # method to use. If, instead, if you want to place bounded text outside of
@@ -100,7 +104,7 @@ module Prawn
100
104
  #
101
105
  # <tt>:kerning</tt>:: <tt>boolean</tt>. Whether or not to use kerning (if it
102
106
  # is available with the current font)
103
- # [value of default_kerning?]
107
+ # [value of document.default_kerning?]
104
108
  # <tt>:size</tt>:: <tt>number</tt>. The font size to use. [current font
105
109
  # size]
106
110
  # <tt>:character_spacing</tt>:: <tt>number</tt>. The amount of space to add
@@ -111,15 +115,33 @@ module Prawn
111
115
  # <tt>:indent_paragraphs</tt>:: <tt>number</tt>. The amount to indent the
112
116
  # first line of each paragraph. Omit this
113
117
  # option if you do not want indenting
114
- # <tt>:align</tt>:: <tt>:left</tt>, <tt>:center</tt>, <tt>:right</tt>, or
115
- # <tt>:justify</tt> Alignment within the bounding box [:left]
118
+ # <tt>:direction</tt>::
119
+ # <tt>:ltr</tt>, <tt>:rtl</tt>, Direction of the text (left-to-right
120
+ # or right-to-left) [value of document.text_direction]
121
+ # <tt>:fallback_fonts</tt>::
122
+ # An array of font names. Each name must be the name of an AFM font or
123
+ # the name that was used to register a family of TTF fonts (see
124
+ # Prawn::Document#font_families). If present, then each glyph will be
125
+ # rendered using the first font that includes the glyph, starting with
126
+ # the current font and then moving through :fallback_fonts from
127
+ # left to right.
128
+ # <tt>:align</tt>::
129
+ # <tt>:left</tt>, <tt>:center</tt>, <tt>:right</tt>, or
130
+ # <tt>:justify</tt> Alignment within the bounding box
131
+ # [:left if direction is :ltr, :right if direction is :rtl]
116
132
  # <tt>:valign</tt>:: <tt>:top</tt>, <tt>:center</tt>, or <tt>:bottom</tt>.
117
133
  # Vertical alignment within the bounding box [:top]
118
- # <tt>:leading</tt>:: <tt>number</tt>. Additional space between lines [0]
134
+ # <tt>:leading</tt>::
135
+ # <tt>number</tt>. Additional space between lines [value of
136
+ # document.default_leading]
119
137
  # <tt>:final_gap</tt>:: <tt>boolean</tt>. If true, then the space between
120
138
  # each line is included below the last line;
121
139
  # otherwise, document.y is placed just below the
122
140
  # descender of the last line printed [true]
141
+ # <tt>:mode</tt>:: The text rendering mode to use. Use this to specify if the
142
+ # text should render with the fill color, stroke color or
143
+ # both. See the comments to text_rendering_mode() to see
144
+ # a list of valid options. [0]
123
145
  #
124
146
  # == Exceptions
125
147
  #
@@ -135,30 +157,11 @@ module Prawn
135
157
  if options[:inline_format]
136
158
  options.delete(:inline_format)
137
159
  array = Text::Formatted::Parser.to_array(string)
138
- formatted_text(array, options)
139
- return
140
- end
141
-
142
- inspect_options_for_text(options)
143
-
144
- if @indent_paragraphs
145
- string.split("\n").each do |paragraph|
146
- options[:skip_encoding] = false
147
- remaining_text = draw_indented_line(paragraph, options)
148
- options[:skip_encoding] = true
149
- if remaining_text == paragraph
150
- # we were too close to the bottom of the page to print even one line
151
- @bounding_box.move_past_bottom
152
- remaining_text = draw_indented_line(paragraph, options)
153
- end
154
- remaining_text = fill_text_box(remaining_text, options)
155
- draw_remaining_text_on_new_pages(remaining_text, options)
156
- end
157
160
  else
158
- remaining_text = fill_text_box(string, options)
159
- options[:skip_encoding] = true
160
- draw_remaining_text_on_new_pages(remaining_text, options)
161
+ array = [{ :text => string }]
161
162
  end
163
+
164
+ formatted_text(array, options)
162
165
  end
163
166
 
164
167
 
@@ -183,21 +186,24 @@ module Prawn
183
186
  # Same as for #text
184
187
  #
185
188
  def formatted_text(array, options={})
186
- # we modify the options. don't change the user's hash
187
- options = options.dup
188
-
189
- inspect_options_for_text(options)
189
+ options = inspect_options_for_text(options.dup)
190
190
 
191
191
  if @indent_paragraphs
192
192
  Text::Formatted::Parser.array_paragraphs(array).each do |paragraph|
193
193
  options[:skip_encoding] = false
194
194
  remaining_text = draw_indented_formatted_line(paragraph, options)
195
195
  options[:skip_encoding] = true
196
- if remaining_text == paragraph
197
- # we were too close to the bottom of the page to print even one line
198
- @bounding_box.move_past_bottom
199
- remaining_text = draw_indented_formatted_line(paragraph, options)
196
+
197
+ if @no_text_printed
198
+ # unless this paragraph was an empty line
199
+ unless @all_text_printed
200
+ @bounding_box.move_past_bottom
201
+ options[:skip_encoding] = false
202
+ remaining_text = draw_indented_formatted_line(paragraph, options)
203
+ options[:skip_encoding] = true
204
+ end
200
205
  end
206
+
201
207
  remaining_text = fill_formatted_text_box(remaining_text, options)
202
208
  draw_remaining_formatted_text_on_new_pages(remaining_text, options)
203
209
  end
@@ -262,9 +268,8 @@ module Prawn
262
268
  # Raises <tt>ArgumentError</tt> if <tt>:align</tt> option included
263
269
  #
264
270
  def draw_text(text, options)
265
- # we modify the options. don't change the user's hash
266
- options = options.dup
267
- inspect_options_for_draw_text(options)
271
+ options = inspect_options_for_draw_text(options.dup)
272
+
268
273
  # dup because normalize_encoding changes the string
269
274
  text = text.to_s.dup
270
275
  save_font do
@@ -291,19 +296,7 @@ module Prawn
291
296
  # any text
292
297
  #
293
298
  def height_of(string, options={})
294
- if options[:indent_paragraphs]
295
- raise NotImplementedError, ":indent_paragraphs option not available" +
296
- "with height_of"
297
- end
298
- process_final_gap_option(options)
299
- box = Text::Box.new(string,
300
- options.merge(:height => 100000000,
301
- :document => self))
302
- printed = box.render(:dry_run => true)
303
-
304
- height = box.height - (box.line_height - box.ascender)
305
- height += box.line_height + box.leading - box.ascender if @final_gap
306
- height
299
+ height_of_formatted([{ :text => string }], options)
307
300
  end
308
301
 
309
302
  # Gets height of formatted text in PDF points.
@@ -327,44 +320,13 @@ module Prawn
327
320
  :document => self))
328
321
  printed = box.render(:dry_run => true)
329
322
 
330
- height = box.height - (box.line_height - box.ascender)
331
- height += box.line_height + box.leading - box.ascender if @final_gap
323
+ height = box.height
324
+ height += box.line_gap + box.leading if @final_gap
332
325
  height
333
326
  end
334
327
 
335
328
  private
336
329
 
337
- def draw_remaining_text_on_new_pages(remaining_text, options)
338
- while remaining_text.length > 0
339
- @bounding_box.move_past_bottom
340
- previous_remaining_text = remaining_text
341
- remaining_text = fill_text_box(remaining_text, options)
342
- break if remaining_text == previous_remaining_text
343
- end
344
- end
345
-
346
- def draw_indented_line(string, options)
347
- indent(@indent_paragraphs) do
348
- fill_text_box(string, options.dup.merge(:single_line => true))
349
- end
350
- end
351
-
352
- def fill_text_box(text, options)
353
- merge_text_box_positioning_options(options)
354
-
355
- box = Text::Box.new(text, options)
356
- remaining_text = box.render
357
-
358
- self.y -= box.height - (box.line_height - box.ascender)
359
- if @final_gap
360
- self.y -= box.line_height + box.leading - box.ascender
361
- end
362
- remaining_text
363
- end
364
-
365
-
366
-
367
-
368
330
  def draw_remaining_formatted_text_on_new_pages(remaining_text, options)
369
331
  while remaining_text.length > 0
370
332
  @bounding_box.move_past_bottom
@@ -384,16 +346,15 @@ module Prawn
384
346
  merge_text_box_positioning_options(options)
385
347
  box = Text::Formatted::Box.new(text, options)
386
348
  remaining_text = box.render
349
+ @no_text_printed = box.nothing_printed?
350
+ @all_text_printed = box.everything_printed?
351
+
352
+ self.y -= box.height
353
+ self.y -= box.line_gap + box.leading if @final_gap
387
354
 
388
- self.y -= box.height - (box.line_height - box.ascender)
389
- if @final_gap
390
- self.y -= box.line_height + box.leading - box.ascender
391
- end
392
355
  remaining_text
393
356
  end
394
357
 
395
-
396
-
397
358
  def merge_text_box_positioning_options(options)
398
359
  bottom = @bounding_box.stretchy? ? @margin_box.absolute_bottom :
399
360
  @bounding_box.absolute_bottom
@@ -415,6 +376,7 @@ module Prawn
415
376
  end
416
377
  valid_options = Prawn::Core::Text::VALID_OPTIONS + [:at, :rotate]
417
378
  Prawn.verify_options(valid_options, options)
379
+ options
418
380
  end
419
381
 
420
382
  def inspect_options_for_text(options)
@@ -425,6 +387,7 @@ module Prawn
425
387
  process_final_gap_option(options)
426
388
  process_indent_paragraphs_option(options)
427
389
  options[:document] = self
390
+ options
428
391
  end
429
392
 
430
393
  def process_final_gap_option(options)
@@ -11,9 +11,8 @@ module Prawn
11
11
  module Text
12
12
 
13
13
  # Draws the requested text into a box. When the text overflows
14
- # the rectangle, you can display ellipses, shrink to fit, or
15
- # truncate the text. Text boxes are independent of the document
16
- # y position.
14
+ # the rectangle, you shrink to fit, or truncate the text. Text
15
+ # boxes are independent of the document y position.
17
16
  #
18
17
  # == Encoding
19
18
  #
@@ -38,6 +37,9 @@ module Prawn
38
37
  # <tt>:character_spacing</tt>:: <tt>number</tt>. The amount of space to add
39
38
  # to or remove from the default character
40
39
  # spacing. [0]
40
+ # <tt>:mode</tt>:: <tt>symbol</tt>. The text rendering mode. See
41
+ # documentation for Prawn::Document#text_rendering_mode
42
+ # for a list of valid options. [:fill]
41
43
  # <tt>:style</tt>:: The style to use. The requested style must be part of
42
44
  # the current font familly. [current style]
43
45
  #
@@ -47,10 +49,21 @@ module Prawn
47
49
  # <tt>:width</tt>::
48
50
  # <tt>number</tt>. The width of the box [@document.bounds.right - @at[0]]
49
51
  # <tt>:height</tt>::
50
- # <tt>number</tt>. The height of the box [@at[1] - @document.bounds.bottom]
52
+ # <tt>number</tt>. The height of the box [default_height()]
53
+ # <tt>:direction</tt>::
54
+ # <tt>:ltr</tt>, <tt>:rtl</tt>, Direction of the text (left-to-right
55
+ # or right-to-left) [value of document.text_direction]
56
+ # <tt>:fallback_fonts</tt>::
57
+ # An array of font names. Each name must be the name of an AFM font or
58
+ # the name that was used to register a family of TTF fonts (see
59
+ # Prawn::Document#font_families). If present, then each glyph will be
60
+ # rendered using the first font that includes the glyph, starting with
61
+ # the current font and then moving through :fallback_fonts from
62
+ # left to right.
51
63
  # <tt>:align</tt>::
52
64
  # <tt>:left</tt>, <tt>:center</tt>, <tt>:right</tt>, or
53
- # <tt>:justify</tt> Alignment within the bounding box [:left]
65
+ # <tt>:justify</tt> Alignment within the bounding box
66
+ # [:left if direction is :ltr, :right if direction is :rtl]
54
67
  # <tt>:valign</tt>::
55
68
  # <tt>:top</tt>, <tt>:center</tt>, or <tt>:bottom</tt>. Vertical
56
69
  # alignment within the bounding box [:top]
@@ -62,15 +75,16 @@ module Prawn
62
75
  # <tt>:lower_right</tt>, or <tt>:lower_left</tt>. The point around which
63
76
  # to rotate the text [:upper_left]
64
77
  # <tt>:leading</tt>::
65
- # <tt>number</tt>. Additional space between lines [0]
78
+ # <tt>number</tt>. Additional space between lines [value of
79
+ # document.default_leading]
66
80
  # <tt>:single_line</tt>::
67
81
  # <tt>boolean</tt>. If true, then only the first line will be drawn [false]
68
82
  # <tt>:skip_encoding</tt>::
69
83
  # <tt>boolean</tt> [false]
70
84
  # <tt>:overflow</tt>::
71
- # <tt>:truncate</tt>, <tt>:shrink_to_fit</tt>, <tt>:expand</tt>, or
72
- # <tt>:ellipses</tt>. This controls the behavior when the amount of text
73
- # exceeds the available space. <tt>:ellipses</tt> [:truncate]
85
+ # <tt>:truncate</tt>, <tt>:shrink_to_fit</tt>, or <tt>:expand</tt>
86
+ # This controls the behavior when the amount of text
87
+ # exceeds the available space. [:truncate]
74
88
  # <tt>:min_font_size</tt>::
75
89
  # <tt>number</tt>. The minimum font size to use when :overflow is set to
76
90
  # :shrink_to_fit (that is the font size will not be reduced to less than
@@ -90,7 +104,7 @@ module Prawn
90
104
  # Raises <tt>Prawn::Errrors::CannotFit</tt> if not wide enough to print
91
105
  # any text
92
106
  #
93
- def text_box(string, options)
107
+ def text_box(string, options={})
94
108
  Text::Box.new(string, options.merge(:document => self)).render
95
109
  end
96
110
 
@@ -100,283 +114,15 @@ module Prawn
100
114
  # to placing text on the page, or to determine how much vertical space was
101
115
  # consumed by the printed text
102
116
  #
103
- class Box
104
- include Prawn::Core::Text::Wrap
117
+ class Box < Prawn::Text::Formatted::Box
105
118
 
106
- def valid_options
107
- Prawn::Core::Text::VALID_OPTIONS + [:at, :height, :width,
108
- :align, :valign,
109
- :rotate, :rotate_around,
110
- :overflow, :min_font_size,
111
- :leading, :character_spacing,
112
- :single_line,
113
- :skip_encoding,
114
- :document]
119
+ def initialize(string, options={})
120
+ super([{ :text => string }], options)
115
121
  end
116
-
117
- # The text that was successfully printed (or, if <tt>dry_run</tt> was
118
- # used, the test that would have been successfully printed)
119
- attr_reader :text
120
- # The upper left corner of the text box
121
- attr_reader :at
122
- # The line height of the last line printed
123
- attr_reader :line_height
124
- # The height of the ascender of the last line printed
125
- attr_reader :ascender
126
- # The height of the descender of the last line printed
127
- attr_reader :descender
128
- # The leading used during printing
129
- attr_reader :leading
130
122
 
131
-
132
- # Extend Prawn::Text::Box
133
- #
134
- # Example (see Prawn::Text::Core::Wrap for what is required
135
- # of the wrap method if you want to override the default
136
- # wrapping algorithm):
137
- #
138
- # module MyWrap
139
- #
140
- # def wrap
141
- # @text = nil
142
- # @line_height = @document.font.height
143
- # @descender = @document.font.descender
144
- # @ascender = @document.font.ascender
145
- # @baseline_y = -@ascender
146
- # draw_line("all your base are belong to us")
147
- # ""
148
- # end
149
- #
150
- # end
151
- #
152
- # Prawn::Text::Box.extensions << MyWrap
153
- #
154
- # box = Prawn::Text::Box.new('hello world')
155
- # box.render('why can't I print anything other than' +
156
- # '"all your base are belong to us"?')
157
- #
158
- #
159
- def self.extensions
160
- @extensions ||= []
161
- end
162
-
163
- def self.inherited(base) #:nodoc:
164
- extensions.each { |e| base.extensions << e }
165
- end
166
-
167
- # See Prawn::Text#text_box for valid options
168
- #
169
- def initialize(text, options={})
170
- @inked = false
171
- Prawn.verify_options(valid_options, options)
172
- options = options.dup
173
-
174
- self.class.extensions.reverse_each { |e| extend e }
175
-
176
- @overflow = options[:overflow] || :truncate
177
-
178
- self.original_text = text
179
- @text = nil
180
-
181
- @document = options[:document]
182
- @at = options[:at] ||
183
- [@document.bounds.left, @document.bounds.top]
184
- @width = options[:width] ||
185
- @document.bounds.right - @at[0]
186
- @height = options[:height] ||
187
- @at[1] - @document.bounds.bottom
188
- @align = options[:align] || :left
189
- @vertical_align = options[:valign] || :top
190
- @leading = options[:leading] || @document.default_leading?
191
- @character_spacing = options[:character_spacing] ||
192
- @document.character_spacing
193
- @rotate = options[:rotate] || 0
194
- @rotate_around = options[:rotate_around] || :upper_left
195
- @single_line = options[:single_line]
196
- @skip_encoding = options[:skip_encoding] || @document.skip_encoding
197
-
198
- if @overflow == :expand
199
- # if set to expand, then we simply set the bottom
200
- # as the bottom of the document bounds, since that
201
- # is the maximum we should expand to
202
- @height = @at[1] - @document.bounds.bottom
203
- @overflow = :truncate
204
- end
205
- @min_font_size = options[:min_font_size] || 5
206
- if options[:kerning].nil? then
207
- options[:kerning] = @document.default_kerning?
208
- end
209
- @options = { :kerning => options[:kerning],
210
- :size => options[:size],
211
- :style => options[:style] }
212
-
213
- super(text, options)
214
- end
215
-
216
- # Render text to the document based on the settings defined in initialize.
217
- #
218
- # In order to facilitate look-ahead calculations, <tt>render</tt> accepts
219
- # a <tt>:dry_run => true</tt> option. If provided, then everything is
220
- # executed as if rendering, with the exception that nothing is drawn on
221
- # the page. Useful for look-ahead computations of height, unprinted text,
222
- # etc.
223
- #
224
- # Returns any text that did not print under the current settings
225
- #
226
123
  def render(flags={})
227
- unprinted_text = ''
228
- @document.save_font do
229
- @document.character_spacing(@character_spacing) do
230
- process_options
231
-
232
- if @skip_encoding
233
- text = original_text
234
- else
235
- text = normalize_encoding
236
- end
237
-
238
- @document.font_size(@font_size) do
239
- shrink_to_fit(text) if @overflow == :shrink_to_fit
240
- process_vertical_alignment(text)
241
- @inked = true unless flags[:dry_run]
242
- if @rotate != 0 && @inked
243
- unprinted_text = render_rotated(text)
244
- else
245
- unprinted_text = wrap(text)
246
- end
247
- @inked = false
248
- end
249
- end
250
- end
251
-
252
- unprinted_text
253
- end
254
-
255
- # The height actually used during the previous <tt>render</tt>
256
- #
257
- def height
258
- return 0 if @baseline_y.nil? || @descender.nil?
259
- # baseline is already pushed down one line below the current
260
- # line, so we need to subtract line line_height and leading,
261
- # but we need to add in the descender since baseline is
262
- # above the descender
263
- @baseline_y.abs - @ascender - @leading
264
- end
265
-
266
- # The width available at this point in the box
267
- #
268
- def available_width
269
- @width
270
- end
271
-
272
- def draw_line(line_to_print, line_width=0, word_spacing=0, include_ellipses=false) #:nodoc:
273
- insert_ellipses(line_to_print) if include_ellipses
274
-
275
- case(@align)
276
- when :left, :justify
277
- x = @at[0]
278
- when :center
279
- x = @at[0] + @width * 0.5 - line_width * 0.5
280
- when :right
281
- x = @at[0] + @width - line_width
282
- end
283
-
284
- y = @at[1] + @baseline_y
285
-
286
- if @inked
287
- @document.word_spacing(word_spacing) {
288
- @document.character_spacing(@character_spacing) {
289
- @document.draw_text!(line_to_print, :at => [x, y],
290
- :kerning => @kerning)
291
- }
292
- }
293
- end
294
-
295
- line_to_print
296
- end
297
-
298
- private
299
-
300
- def normalize_encoding
301
- @document.font.normalize_encoding(@original_string)
302
- end
303
-
304
- def original_text
305
- @original_string
306
- end
307
-
308
- def original_text=(string)
309
- @original_string = string.dup
310
- end
311
-
312
- def process_vertical_alignment(text)
313
- return if @vertical_align == :top
314
- wrap(text)
315
- case @vertical_align
316
- when :center
317
- @at[1] = @at[1] - (@height - height) * 0.5
318
- when :bottom
319
- @at[1] = @at[1] - (@height - height)
320
- end
321
- @height = height
322
- end
323
-
324
- # Decrease the font size until the text fits or the min font
325
- # size is reached
326
- def shrink_to_fit(text)
327
- while (unprinted_text = wrap(text)).length > 0 &&
328
- @font_size > @min_font_size
329
- @font_size -= 0.5
330
- @document.font_size = @font_size
331
- end
332
- end
333
-
334
- def process_options
335
- # must be performed within a save_font bock because
336
- # document.process_text_options sets the font
337
- @document.process_text_options(@options)
338
- @font_size = @options[:size]
339
- @kerning = @options[:kerning]
340
- end
341
-
342
- def render_rotated(text)
343
- unprinted_text = ''
344
-
345
- case @rotate_around
346
- when :center
347
- x = @at[0] + @width * 0.5
348
- y = @at[1] - @height * 0.5
349
- when :upper_right
350
- x = @at[0] + @width
351
- y = @at[1]
352
- when :lower_right
353
- x = @at[0] + @width
354
- y = @at[1] - @height
355
- when :lower_left
356
- x = @at[0]
357
- y = @at[1] - @height
358
- else
359
- x = @at[0]
360
- y = @at[1]
361
- end
362
-
363
- @document.rotate(@rotate, :origin => [x, y]) do
364
- unprinted_text = wrap(text)
365
- end
366
- unprinted_text
367
- end
368
-
369
- def last_line?
370
- @baseline_y.abs + @descender > @height - @line_height
371
- end
372
-
373
- def insert_ellipses(line_to_print)
374
- if @document.width_of(line_to_print + "...",
375
- :kerning => @kerning) < available_width
376
- line_to_print.insert(-1, "...")
377
- else
378
- line_to_print[-3..-1] = "..." if line_to_print.length > 3
379
- end
124
+ leftover = super(flags)
125
+ leftover.collect { |hash| hash[:text] }.join
380
126
  end
381
127
 
382
128
  end