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
@@ -8,7 +8,6 @@ module Prawn
8
8
  module Wrap #:nodoc:
9
9
 
10
10
  def initialize(array, options)
11
- super(array, options)
12
11
  @line_wrap = Prawn::Core::Text::Formatted::LineWrap.new
13
12
  @arranger = Prawn::Core::Text::Formatted::Arranger.new(@document)
14
13
  end
@@ -16,8 +15,7 @@ module Prawn
16
15
 
17
16
  # See the developer documentation for Prawn::Core::Text#wrap
18
17
  #
19
- # Formatted#wrap should set some of the variables slightly differently
20
- # than Text#wrap;
18
+ # Formatted#wrap should set the following variables:
21
19
  # <tt>@line_height</tt>::
22
20
  # the height of the tallest fragment in the last printed line
23
21
  # <tt>@descender</tt>::
@@ -26,56 +24,93 @@ module Prawn
26
24
  # <tt>@ascender</tt>::
27
25
  # the ascender heigth of the tallest fragment in the last
28
26
  # printed line
27
+ # <tt>@baseline_y</tt>::
28
+ # the baseline of the current line
29
+ # <tt>@nothing_printed</tt>::
30
+ # set to true until something is printed, then false
31
+ # <tt>@everything_printed</tt>::
32
+ # set to false until everything printed, then true
29
33
  #
30
34
  # Returns any formatted text that was not printed
31
35
  #
32
36
  def wrap(array) #:nodoc:
33
37
  initialize_wrap(array)
34
38
 
35
- move_baseline = true
36
- while @arranger.unfinished?
37
- printed_fragments = []
38
-
39
- line_to_print = @line_wrap.wrap_line(:document => @document,
40
- :kerning => @kerning,
41
- :width => available_width,
42
- :arranger => @arranger)
43
-
44
- move_baseline = false
45
- break unless enough_height_for_this_line?
46
- move_baseline_down
47
-
48
- accumulated_width = 0
49
- word_spacing = word_spacing_for_this_line
50
- while fragment = @arranger.retrieve_fragment
51
- fragment.word_spacing = word_spacing
52
- if fragment.text == "\n"
53
- printed_fragments << "\n" if @printed_lines.last == ""
54
- break
55
- end
56
- printed_fragments << fragment.text
57
- format_and_draw_fragment(fragment, accumulated_width,
58
- @line_wrap.width, word_spacing)
59
- accumulated_width += fragment.width
39
+ stop = false
40
+ while !stop
41
+ @line_wrap.wrap_line(:document => @document,
42
+ :kerning => @kerning,
43
+ :width => available_width,
44
+ :arranger => @arranger)
45
+
46
+ if enough_height_for_this_line?
47
+ move_baseline_down
48
+ print_line
49
+ else
50
+ stop = true
60
51
  end
61
- @printed_lines << printed_fragments.join("")
62
- break if @single_line
63
- move_baseline = true unless @arranger.finished?
52
+
53
+ stop ||= @single_line || @arranger.finished?
64
54
  end
65
- move_baseline_down if move_baseline
66
55
  @text = @printed_lines.join("\n")
67
-
56
+ @everything_printed = @arranger.finished?
68
57
  @arranger.unconsumed
69
58
  end
70
59
 
71
60
  private
72
61
 
62
+ def print_line
63
+ @nothing_printed = false
64
+ printed_fragments = []
65
+ fragments_this_line = []
66
+
67
+ word_spacing = word_spacing_for_this_line
68
+ while fragment = @arranger.retrieve_fragment
69
+ fragment.word_spacing = word_spacing
70
+ if fragment.text == "\n"
71
+ printed_fragments << "\n" if @printed_lines.last == ""
72
+ break
73
+ end
74
+ printed_fragments << fragment.text
75
+ fragments_this_line << fragment
76
+ end
77
+
78
+ accumulated_width = 0
79
+ fragments_this_line.reverse! if @direction == :rtl
80
+ fragments_this_line.each do |fragment|
81
+ fragment.default_direction = @direction
82
+ format_and_draw_fragment(fragment, accumulated_width,
83
+ @line_wrap.width, word_spacing)
84
+ accumulated_width += fragment.width
85
+ end
86
+
87
+ if "".respond_to?(:force_encoding)
88
+ printed_fragments.map! { |s| s.force_encoding("utf-8") }
89
+ end
90
+ @printed_lines << printed_fragments.join
91
+ end
92
+
93
+ def word_spacing_for_this_line
94
+ if @align == :justify &&
95
+ @line_wrap.space_count > 0 &&
96
+ !@line_wrap.paragraph_finished?
97
+ (available_width - @line_wrap.width) / @line_wrap.space_count
98
+ else
99
+ 0
100
+ end
101
+ end
102
+
73
103
  def enough_height_for_this_line?
74
104
  @line_height = @arranger.max_line_height
75
105
  @descender = @arranger.max_descender
76
106
  @ascender = @arranger.max_ascender
77
- required_height = @baseline_y == 0 ? @line_height : @line_height + @descender
78
- if @baseline_y.abs + required_height > @height
107
+ if @baseline_y == 0
108
+ diff = @ascender + @descender
109
+ else
110
+ diff = @descender + @line_height + @leading
111
+ end
112
+ required_total_height = @baseline_y.abs + diff
113
+ if required_total_height > @height + 0.0001
79
114
  # no room for the full height of this line
80
115
  @arranger.repack_unretrieved
81
116
  false
@@ -95,6 +130,8 @@ module Prawn
95
130
  @baseline_y = 0
96
131
 
97
132
  @printed_lines = []
133
+ @nothing_printed = true
134
+ @everything_printed = false
98
135
  end
99
136
 
100
137
  def format_and_draw_fragment(fragment, accumulated_width,
@@ -9,36 +9,37 @@
9
9
  require "stringio"
10
10
  require "prawn/document/page_geometry"
11
11
  require "prawn/document/bounding_box"
12
+ require "prawn/document/column_box"
12
13
  require "prawn/document/internals"
13
14
  require "prawn/document/span"
14
15
  require "prawn/document/snapshot"
15
16
  require "prawn/document/graphics_state"
16
17
 
17
18
  module Prawn
18
-
19
+
19
20
  # The Prawn::Document class is how you start creating a PDF document.
20
- #
21
- # There are three basic ways you can instantiate PDF Documents in Prawn, they
21
+ #
22
+ # There are three basic ways you can instantiate PDF Documents in Prawn, they
22
23
  # are through assignment, implicit block or explicit block. Below is an exmple
23
24
  # of each type, each example does exactly the same thing, makes a PDF document
24
25
  # with all the defaults and puts in the default font "Hello There" and then
25
26
  # saves it to the current directory as "example.pdf"
26
- #
27
+ #
27
28
  # For example, assignment can be like this:
28
- #
29
+ #
29
30
  # pdf = Prawn::Document.new
30
31
  # pdf.text "Hello There"
31
32
  # pdf.render_file "example.pdf"
32
- #
33
+ #
33
34
  # Or you can do an implied block form:
34
- #
35
+ #
35
36
  # Prawn::Document.generate "example.pdf" do
36
37
  # text "Hello There"
37
38
  # end
38
- #
39
+ #
39
40
  # Or if you need to access a variable outside the scope of the block, the
40
41
  # explicit block form:
41
- #
42
+ #
42
43
  # words = "Hello There"
43
44
  # Prawn::Document.generate "example.pdf" do |pdf|
44
45
  # pdf.text words
@@ -46,7 +47,7 @@ module Prawn
46
47
  #
47
48
  # Usually, the block forms are used when you are simply creating a PDF document
48
49
  # that you want to immediately save or render out.
49
- #
50
+ #
50
51
  # See the new and generate methods for further details on the above.
51
52
  #
52
53
  class Document
@@ -68,11 +69,11 @@ module Prawn
68
69
  # Example:
69
70
  #
70
71
  # module MyFancyModule
71
- #
72
+ #
72
73
  # def party!
73
74
  # text "It's a big party!"
74
75
  # end
75
- #
76
+ #
76
77
  # end
77
78
  #
78
79
  # Prawn::Document.extensions << MyFancyModule
@@ -152,7 +153,7 @@ module Prawn
152
153
  #
153
154
  # Additionally, :page_size can be specified as a simple two value array giving
154
155
  # the width and height of the document you need in PDF Points.
155
- #
156
+ #
156
157
  # Usage:
157
158
  #
158
159
  # # New document, US Letter paper, portrait orientation
@@ -167,9 +168,9 @@ module Prawn
167
168
  # # New document, with background
168
169
  # pdf = Prawn::Document.new(:background => "#{Prawn::BASEDIR}/data/images/pigs.jpg")
169
170
  #
170
- def initialize(options={},&block)
171
- Prawn.verify_options [:page_size, :page_layout, :margin, :left_margin,
172
- :right_margin, :top_margin, :bottom_margin, :skip_page_creation,
171
+ def initialize(options={},&block)
172
+ Prawn.verify_options [:page_size, :page_layout, :margin, :left_margin,
173
+ :right_margin, :top_margin, :bottom_margin, :skip_page_creation,
173
174
  :compress, :skip_encoding, :background, :info,
174
175
  :optimize_objects, :template], options
175
176
 
@@ -204,7 +205,7 @@ module Prawn
204
205
  end
205
206
 
206
207
  @bounding_box = @margin_box
207
-
208
+
208
209
  if block
209
210
  block.arity < 1 ? instance_eval(&block) : block[self]
210
211
  end
@@ -233,6 +234,11 @@ module Prawn
233
234
  # pdf.start_new_page(:left_margin => 50, :right_margin => 50)
234
235
  # pdf.start_new_page(:margin => 100)
235
236
  #
237
+ # A template for a page can be specified by pointing to the path of and existing pdf.
238
+ # One can also specify which page of the template which defaults otherwise to 1.
239
+ #
240
+ # pdf.start_new_page(:template => multipage_template.pdf, :template_page => 2)
241
+ #
236
242
  def start_new_page(options = {})
237
243
  if last_page = state.page
238
244
  last_page_size = last_page.size
@@ -240,29 +246,36 @@ module Prawn
240
246
  last_page_margins = last_page.margins
241
247
  end
242
248
 
243
- state.page = Prawn::Core::Page.new(self,
244
- :size => options[:size] || last_page_size,
245
- :layout => options[:layout] || last_page_layout,
246
- :margins => last_page_margins )
249
+ page_options = {:size => options[:size] || last_page_size,
250
+ :layout => options[:layout] || last_page_layout,
251
+ :margins => last_page_margins}
252
+ if last_page
253
+ new_graphic_state = last_page.graphic_state.dup
254
+ #erase the color space so that it gets reset on new page for fussy pdf-readers
255
+ new_graphic_state.color_space = {}
256
+ page_options.merge!(:graphic_state => new_graphic_state)
257
+ end
258
+ merge_template_options(page_options, options) if options[:template]
259
+
260
+ state.page = Prawn::Core::Page.new(self, page_options)
247
261
 
248
262
  apply_margin_options(options)
263
+ state.page.new_content_stream if options[:template]
264
+ use_graphic_settings(options[:template])
249
265
 
250
- use_graphic_settings
251
-
252
266
  unless options[:orphan]
253
267
  state.insert_page(state.page, @page_number)
254
268
  @page_number += 1
255
269
 
256
- save_graphics_state
257
-
258
- canvas { image(@background, :at => bounds.top_left) } if @background
270
+ canvas { image(@background, :at => bounds.top_left) } if @background
259
271
  @y = @bounding_box.absolute_top
260
272
 
261
273
  float do
262
274
  state.on_page_create_action(self)
263
275
  end
264
276
  end
265
- end
277
+
278
+ end
266
279
 
267
280
  # Returns the number of pages in the document
268
281
  #
@@ -274,9 +287,9 @@ module Prawn
274
287
  def page_count
275
288
  state.page_count
276
289
  end
277
-
290
+
278
291
  # Re-opens the page with the given (1-based) page number so that you can
279
- # draw on it.
292
+ # draw on it.
280
293
  #
281
294
  # See Prawn::Document#number_pages for a sample usage of this capability.
282
295
  #
@@ -300,7 +313,7 @@ module Prawn
300
313
  end
301
314
 
302
315
  # Moves to the specified y position in relative terms to the bottom margin.
303
- #
316
+ #
304
317
  def move_cursor_to(new_y)
305
318
  self.y = new_y + bounds.absolute_bottom
306
319
  end
@@ -314,9 +327,9 @@ module Prawn
314
327
  # pdf.text "C"
315
328
  # end
316
329
  #
317
- # pdf.text "B"
318
- #
319
- def float
330
+ # pdf.text "B"
331
+ #
332
+ def float
320
333
  mask(:y) { yield }
321
334
  end
322
335
 
@@ -353,12 +366,12 @@ module Prawn
353
366
  # Another important point about bounding boxes is that all x and y measurements
354
367
  # within a bounding box code block are relative to the bottom left corner of the
355
368
  # bounding box.
356
- #
369
+ #
357
370
  # For example:
358
- #
371
+ #
359
372
  # Prawn::Document.new do
360
373
  # # In the default "margin box" of a Prawn document of 0.5in along each edge
361
- #
374
+ #
362
375
  # # Draw a border around the page (the manual way)
363
376
  # stroke do
364
377
  # line(bounds.bottom_left, bounds.bottom_right)
@@ -366,17 +379,17 @@ module Prawn
366
379
  # line(bounds.top_right, bounds.top_left)
367
380
  # line(bounds.top_left, bounds.bottom_left)
368
381
  # end
369
- #
382
+ #
370
383
  # # Draw a border around the page (the easy way)
371
384
  # stroke_bounds
372
385
  # end
373
- #
386
+ #
374
387
  def bounds
375
388
  @bounding_box
376
389
  end
377
390
 
378
391
  # Sets Document#bounds to the BoundingBox provided. See above for a brief
379
- # description of what a bounding box is. This function is useful if you
392
+ # description of what a bounding box is. This function is useful if you
380
393
  # really need to change the bounding box manually, but usually, just entering
381
394
  # and exiting bounding box code blocks is good enough.
382
395
  #
@@ -386,14 +399,14 @@ module Prawn
386
399
 
387
400
  # Moves up the document by n points relative to the current position inside
388
401
  # the current bounding box.
389
- #
402
+ #
390
403
  def move_up(n)
391
404
  self.y += n
392
405
  end
393
406
 
394
407
  # Moves down the document by n points relative to the current position inside
395
408
  # the current bounding box.
396
- #
409
+ #
397
410
  def move_down(n)
398
411
  self.y -= n
399
412
  end
@@ -438,8 +451,8 @@ module Prawn
438
451
  yield
439
452
  move_down(y)
440
453
  end
441
-
442
-
454
+
455
+
443
456
  # Indents the specified number of PDF points for the duration of the block
444
457
  #
445
458
  # pdf.text "some text"
@@ -448,11 +461,14 @@ module Prawn
448
461
  # end
449
462
  # pdf.text "This starts 20 points left of the above line " +
450
463
  # "and is flush with the first line"
464
+ # pdf.indent 20, 20 do
465
+ # pdf.text "This line is indented on both sides."
466
+ # end
451
467
  #
452
- def indent(x, &block)
453
- bounds.indent(x, &block)
468
+ def indent(left, right = 0, &block)
469
+ bounds.indent(left, right, &block)
454
470
  end
455
-
471
+
456
472
 
457
473
  def mask(*fields) # :nodoc:
458
474
  # Stores the current state of the named attributes, executes the block, and
@@ -467,7 +483,8 @@ module Prawn
467
483
  # Attempts to group the given block vertically within the current context.
468
484
  # First attempts to render it in the current position on the current page.
469
485
  # If that attempt overflows, it is tried anew after starting a new context
470
- # (page or column).
486
+ # (page or column). Returns a logically true value if the content fits in
487
+ # one page/column, false if a new page or column was needed.
471
488
  #
472
489
  # Raises CannotGroup if the provided content is too large to fit alone in
473
490
  # the current page or column.
@@ -488,48 +505,128 @@ module Prawn
488
505
  raise Prawn::Errors::CannotGroup if second_attempt
489
506
  old_bounding_box.move_past_bottom
490
507
  group(second_attempt=true) { yield }
491
- end
508
+ end
509
+
510
+ success
492
511
  end
493
512
 
494
- # Specify a template for page numbering. This should be called
513
+ # Places a text box on specified pages for page numbering. This should be called
495
514
  # towards the end of document creation, after all your content is already in
496
515
  # place. In your template string, <page> refers to the current page, and
497
- # <total> refers to the total amount of pages in the doucment.
516
+ # <total> refers to the total amount of pages in the document. Page numbering should
517
+ # occur at the end of your Prawn::Document.generate block because the method iterates
518
+ # through existing pages after they are created.
498
519
  #
499
- # Example:
520
+ # Parameters are:
521
+ #
522
+ # <tt>string</tt>:: Template string for page number wording.
523
+ # Should include '<page>' and, optionally, '<total>'.
524
+ # <tt>options</tt>:: A hash for page numbering and text box options.
525
+ # <tt>:page_filter</tt>:: A filter to specify which pages to place page numbers on.
526
+ # Refer to the method 'page_match?'
527
+ # <tt>:start_count_at</tt>:: The starting count to increment pages from.
528
+ # <tt>:total_pages</tt>:: If provided, will replace <total> with the value given.
529
+ # Useful to override the total number of pages when using
530
+ # the start_count_at option.
531
+ # <tt>:color</tt>:: Text fill color.
532
+ #
533
+ # Please refer to Prawn::Text::text_box for additional options concerning text
534
+ # formatting and placement.
535
+ #
536
+ # Example: Print page numbers on every page except for the first. Start counting from
537
+ # five.
500
538
  #
501
539
  # Prawn::Document.generate("page_with_numbering.pdf") do
502
- # text "Hai"
503
- # start_new_page
504
- # text "bai"
505
- # start_new_page
506
- # text "-- Hai again"
507
- # number_pages "<page> in a total of <total>", [bounds.right - 50, 0]
540
+ # number_pages "<page> in a total of <total>",
541
+ # {:start_count_at => 5,
542
+ # :page_filter => lambda{ |pg| pg != 1 },
543
+ # :at => [bounds.right - 50, 0],
544
+ # :align => :right,
545
+ # :size => 14}
508
546
  # end
509
547
  #
510
- def number_pages(string, position)
511
- page_count.times do |i|
512
- go_to_page(i+1)
513
- str = string.gsub("<page>","#{i+1}").gsub("<total>","#{page_count}")
514
- draw_text str, :at => position
548
+ def number_pages(string, options={})
549
+ opts = options.dup
550
+ start_count_at = opts.delete(:start_count_at).to_i
551
+ page_filter = opts.delete(:page_filter)
552
+ total_pages = opts.delete(:total_pages)
553
+ txtcolor = opts.delete(:color)
554
+ # An explicit height so that we can draw page numbers in the margins
555
+ opts[:height] = 50
556
+
557
+ start_count = false
558
+ pseudopage = 0
559
+ (1..page_count).each do |p|
560
+ unless start_count
561
+ pseudopage = case start_count_at
562
+ when 0
563
+ 1
564
+ else
565
+ start_count_at.to_i
566
+ end
567
+ end
568
+ if page_match?(page_filter, p)
569
+ go_to_page(p)
570
+ # have to use fill_color here otherwise text reverts back to default fill color
571
+ fill_color txtcolor unless txtcolor.nil?
572
+ total_pages = total_pages.nil? ? page_count : total_pages
573
+ str = string.gsub("<page>","#{pseudopage}").gsub("<total>","#{total_pages}")
574
+ text_box str, opts
575
+ start_count = true # increment page count as soon as first match found
576
+ end
577
+ pseudopage += 1 if start_count
515
578
  end
516
579
  end
517
580
 
581
+ # Provides a way to execute a block of code repeatedly based on a
582
+ # page_filter.
583
+ #
584
+ # Available page filters are:
585
+ # :all repeats on every page
586
+ # :odd repeats on odd pages
587
+ # :even repeats on even pages
588
+ # some_array repeats on every page listed in the array
589
+ # some_range repeats on every page included in the range
590
+ # some_lambda yields page number and repeats for true return values
591
+ def page_match?(page_filter, page_number)
592
+ case page_filter
593
+ when :all
594
+ true
595
+ when :odd
596
+ page_number % 2 == 1
597
+ when :even
598
+ page_number % 2 == 0
599
+ when Range, Array
600
+ page_filter.include?(page_number)
601
+ when Proc
602
+ page_filter.call(page_number)
603
+ end
604
+ end
605
+
606
+
518
607
  # Returns true if content streams will be compressed before rendering,
519
608
  # false otherwise
520
609
  #
521
610
  def compression_enabled?
522
611
  !!state.compress
523
612
  end
524
-
613
+
525
614
  private
526
615
 
527
- def use_graphic_settings
528
- update_colors
529
- line_width(line_width) unless line_width == 1
530
- cap_style(cap_style) unless cap_style == :butt
531
- join_style(join_style) unless join_style == :miter
532
- dash(dash[:dash], dash) if dashed?
616
+ def merge_template_options(page_options, options)
617
+ object_id = state.store.import_page(options[:template], options[:template_page] || 1)
618
+ page_options.merge!(:object_id => object_id )
619
+ end
620
+
621
+ # setting override_settings to true ensures that a new graphic state does not end up using
622
+ # previous settings especially from imported template streams
623
+ def use_graphic_settings(override_settings = false)
624
+ set_fill_color if current_fill_color != "000000" || override_settings
625
+ set_stroke_color if current_stroke_color != "000000" || override_settings
626
+ write_line_width if line_width != 1 || override_settings
627
+ write_stroke_cap_style if cap_style != :butt || override_settings
628
+ write_stroke_join_style if join_style != :miter || override_settings
629
+ write_stroke_dash if dashed? || override_settings
533
630
  end
534
631
 
535
632
  def generate_margin_box
@@ -538,18 +635,25 @@ module Prawn
538
635
 
539
636
  @margin_box = BoundingBox.new(
540
637
  self,
638
+ nil, # margin box has no parent
541
639
  [ page.margins[:left], page.dimensions[-1] - page.margins[:top] ] ,
542
640
  :width => page.dimensions[-2] - (page.margins[:left] + page.margins[:right]),
543
641
  :height => page.dimensions[-1] - (page.margins[:top] + page.margins[:bottom])
544
642
  )
545
643
 
644
+ # This check maintains indentation settings across page breaks
645
+ if (old_margin_box)
646
+ @margin_box.add_left_padding(old_margin_box.total_left_padding)
647
+ @margin_box.add_right_padding(old_margin_box.total_right_padding)
648
+ end
649
+
546
650
  # we must update bounding box if not flowing from the previous page
547
651
  #
548
652
  # FIXME: This may have a bug where the old margin is restored
549
653
  # when the bounding box exits.
550
654
  @bounding_box = @margin_box if old_margin_box == @bounding_box
551
655
  end
552
-
656
+
553
657
  def apply_margin_options(options)
554
658
  if options[:margin]
555
659
  # Treat :margin as CSS shorthand with 1-4 values.