prawn-core 0.5.1 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. data/HACKING +46 -0
  2. data/README +9 -3
  3. data/Rakefile +7 -6
  4. data/examples/bounding_box/stretched_nesting.rb +67 -0
  5. data/examples/general/margin.rb +36 -0
  6. data/examples/general/multi_page_layout.rb +3 -1
  7. data/examples/general/page_numbering.rb +15 -0
  8. data/examples/general/stamp.rb +45 -0
  9. data/examples/graphics/stroke_cap_and_join.rb +45 -0
  10. data/examples/graphics/stroke_dash.rb +42 -0
  11. data/examples/graphics/transparency.rb +26 -0
  12. data/examples/text/text_box_returning_excess.rb +51 -0
  13. data/lib/prawn/byte_string.rb +7 -0
  14. data/lib/prawn/core.rb +7 -8
  15. data/lib/prawn/document/annotations.rb +3 -2
  16. data/lib/prawn/document/bounding_box.rb +15 -10
  17. data/lib/prawn/document/column_box.rb +1 -3
  18. data/lib/prawn/document/destinations.rb +11 -10
  19. data/lib/prawn/document/internals.rb +62 -19
  20. data/lib/prawn/document/snapshot.rb +71 -0
  21. data/lib/prawn/document/text/box.rb +7 -0
  22. data/lib/prawn/document/text/wrapping.rb +3 -0
  23. data/lib/prawn/document/text.rb +9 -2
  24. data/lib/prawn/document.rb +141 -25
  25. data/lib/prawn/errors.rb +12 -0
  26. data/lib/prawn/font/afm.rb +1 -1
  27. data/lib/prawn/font/ttf.rb +5 -5
  28. data/lib/prawn/font.rb +8 -5
  29. data/lib/prawn/graphics/cap_style.rb +35 -0
  30. data/lib/prawn/graphics/dash.rb +69 -0
  31. data/lib/prawn/graphics/join_style.rb +35 -0
  32. data/lib/prawn/graphics/transparency.rb +56 -0
  33. data/lib/prawn/graphics.rb +9 -1
  34. data/lib/prawn/images.rb +4 -4
  35. data/lib/prawn/name_tree.rb +2 -1
  36. data/lib/prawn/object_store.rb +63 -0
  37. data/lib/prawn/pdf_object.rb +4 -0
  38. data/lib/prawn/reference.rb +18 -5
  39. data/lib/prawn/stamp.rb +87 -0
  40. data/spec/bounding_box_spec.rb +9 -0
  41. data/spec/document_spec.rb +58 -5
  42. data/spec/images_spec.rb +1 -1
  43. data/spec/name_tree_spec.rb +14 -5
  44. data/spec/object_store_spec.rb +42 -0
  45. data/spec/pdf_object_spec.rb +5 -0
  46. data/spec/reference_spec.rb +40 -0
  47. data/spec/snapshot_spec.rb +115 -0
  48. data/spec/spec_helper.rb +1 -4
  49. data/spec/stamp_spec.rb +98 -0
  50. data/spec/stroke_styles_spec.rb +152 -0
  51. data/spec/text_box_spec.rb +26 -0
  52. data/spec/text_spec.rb +8 -1
  53. data/spec/transparency_spec.rb +61 -0
  54. data/vendor/pdf-inspector/lib/pdf/inspector/extgstate.rb +18 -0
  55. data/vendor/pdf-inspector/lib/pdf/inspector/graphics.rb +40 -1
  56. data/vendor/pdf-inspector/lib/pdf/inspector/page.rb +12 -3
  57. data/vendor/pdf-inspector/lib/pdf/inspector.rb +2 -1
  58. metadata +26 -2
@@ -222,7 +222,7 @@ module Prawn
222
222
 
223
223
  def register(subset)
224
224
  temp_name = @ttf.name.postscript_name.gsub("\0","").to_sym
225
- @document.ref(:Type => :Font, :BaseFont => temp_name) { |ref| embed(ref, subset) }
225
+ @document.ref!(:Type => :Font, :BaseFont => temp_name) { |ref| embed(ref, subset) }
226
226
  end
227
227
 
228
228
  def embed(reference, subset)
@@ -241,12 +241,12 @@ module Prawn
241
241
 
242
242
  compressed_font = Zlib::Deflate.deflate(font_content)
243
243
 
244
- fontfile = @document.ref(:Length => compressed_font.size,
244
+ fontfile = @document.ref!(:Length => compressed_font.size,
245
245
  :Length1 => font_content.size,
246
246
  :Filter => :FlateDecode )
247
247
  fontfile << compressed_font
248
248
 
249
- descriptor = @document.ref(:Type => :FontDescriptor,
249
+ descriptor = @document.ref!(:Type => :FontDescriptor,
250
250
  :FontName => basename.to_sym,
251
251
  :FontFile2 => fontfile,
252
252
  :FontBBox => bbox,
@@ -287,7 +287,7 @@ module Prawn
287
287
 
288
288
  to_unicode_cmap = UNICODE_CMAP_TEMPLATE % range_blocks.strip
289
289
 
290
- cmap = @document.ref({})
290
+ cmap = @document.ref!({})
291
291
  cmap << to_unicode_cmap
292
292
  cmap.compress_stream
293
293
 
@@ -296,7 +296,7 @@ module Prawn
296
296
  :FontDescriptor => descriptor,
297
297
  :FirstChar => 32,
298
298
  :LastChar => 255,
299
- :Widths => @document.ref(widths),
299
+ :Widths => @document.ref!(widths),
300
300
  :ToUnicode => cmap)
301
301
  end
302
302
 
data/lib/prawn/font.rb CHANGED
@@ -28,9 +28,9 @@ module Prawn
28
28
  # make it more portable.
29
29
  #
30
30
  def font(name=nil, options={})
31
- return @font || font("Helvetica") if name.nil?
31
+ return((defined?(@font) && @font) || font("Helvetica")) if name.nil?
32
32
 
33
- raise Errors::NotOnPage unless @current_page
33
+ raise Errors::NotOnPage unless defined?(@current_page) && @current_page
34
34
  new_font = find_font(name, options)
35
35
 
36
36
  if block_given?
@@ -252,13 +252,16 @@ module Prawn
252
252
  "#{self.class.name}< #{name}: #{size} >"
253
253
  end
254
254
 
255
- # Normalizes the encoding of the string to an encoding supported by the font.
256
- # The string is expected to be UTF-8 going in, and will be reencoded in-place
257
- # (the argument will be modified directly). The return value is not defined.
255
+ # Normalizes the encoding of the string to an encoding supported by the
256
+ # font. The string is expected to be UTF-8 going in. It will be re-encoded
257
+ # and the new string will be returned. For an in-place (destructive)
258
+ # version, see normalize_encoding!.
258
259
  def normalize_encoding(string)
259
260
  raise NotImplementedError, "subclasses of Prawn::Font must implement #normalize_encoding"
260
261
  end
261
262
 
263
+ # Destructive version of normalize_encoding; normalizes the encoding of a
264
+ # string in place.
262
265
  def normalize_encoding!(str)
263
266
  str.replace(normalize_encoding(str))
264
267
  end
@@ -0,0 +1,35 @@
1
+ # encoding: utf-8
2
+
3
+ # cap_style.rb : Implements stroke cap styling
4
+ #
5
+ # Contributed by Daniel Nelson. October, 2009
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+ #
9
+ module Prawn
10
+ module Graphics
11
+ module CapStyle
12
+ # Sets the cap_style for stroked lines and curves
13
+ #
14
+
15
+ CAP_STYLES = { :butt => 0, :round => 1, :projecting_square => 2 }
16
+
17
+ # style is one of :butt, :round, or :projecting_square
18
+ def cap_style(style=nil)
19
+ return @cap_style || :butt if style.nil?
20
+
21
+ @cap_style = style
22
+
23
+ write_stroke_cap_style
24
+ end
25
+
26
+ alias_method :cap_style=, :cap_style
27
+
28
+ private
29
+
30
+ def write_stroke_cap_style
31
+ add_content "#{CAP_STYLES[@cap_style]} J"
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,69 @@
1
+ # encoding: utf-8
2
+
3
+ # dash.rb : Implements stroke dashing
4
+ #
5
+ # Contributed by Daniel Nelson. October, 2009
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+ #
9
+ module Prawn
10
+ module Graphics
11
+ module Dash
12
+
13
+ # Sets the dash pattern for stroked lines and curves
14
+ #
15
+ # length is the length of the dash. If options is not present,
16
+ # or options[:space] is nil, then length is also the length of
17
+ # the space between dashes
18
+ #
19
+ # options may contain :space and :phase
20
+ # :space is the space between the dashes
21
+ # :phase is where in the cycle to begin dashing. For
22
+ # example, a phase of 0 starts at the beginning of
23
+ # the dash; whereas, if the phase is equal to the
24
+ # length of the dash, then stroking will begin at
25
+ # the beginning of the space. Default is 0
26
+ #
27
+ # integers or floats may be used for length and the options
28
+ #
29
+ # dash units are in PDF points ( 1/72 in )
30
+ #
31
+ def dash(length=nil, options={})
32
+ return @dash || undash_hash if length.nil?
33
+
34
+ @dash = { :dash => length,
35
+ :space => options[:space] || length,
36
+ :phase => options[:phase] || 0 }
37
+
38
+ write_stroke_dash
39
+ end
40
+
41
+ alias_method :dash=, :dash
42
+
43
+ # Restores solid stroking
44
+ def undash
45
+ @dash = undash_hash
46
+ write_stroke_dash
47
+ end
48
+
49
+ # Returns true iff the stroke is dashed
50
+ def dashed?
51
+ dash != undash_hash
52
+ end
53
+
54
+ private
55
+
56
+ def undash_hash
57
+ { :dash => nil, :space => nil, :phase => 0 }
58
+ end
59
+
60
+ def write_stroke_dash
61
+ if @dash[:dash].nil?
62
+ add_content "[] 0 d"
63
+ return
64
+ end
65
+ add_content "[#{@dash[:dash]} #{@dash[:space]}] #{@dash[:phase]} d"
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,35 @@
1
+ # encoding: utf-8
2
+
3
+ # join_style.rb : Implements stroke join styling
4
+ #
5
+ # Contributed by Daniel Nelson. October, 2009
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+ #
9
+ module Prawn
10
+ module Graphics
11
+ module JoinStyle
12
+ # Sets the join_style for stroked lines and curves
13
+ #
14
+
15
+ JOIN_STYLES = { :miter => 0, :round => 1, :bevel => 2 }
16
+
17
+ # style is one of :miter, :round, or :bevel
18
+ def join_style(style=nil)
19
+ return @join_style || :miter if style.nil?
20
+
21
+ @join_style = style
22
+
23
+ write_stroke_join_style
24
+ end
25
+
26
+ alias_method :join_style=, :join_style
27
+
28
+ private
29
+
30
+ def write_stroke_join_style
31
+ add_content "#{JOIN_STYLES[@join_style]} j"
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,56 @@
1
+ # encoding: utf-8
2
+ #
3
+ # transparency.rb : Implements transparency
4
+ #
5
+ # Copyright October 2009, Daniel Nelson. All Rights Reserved.
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+ #
9
+
10
+ module Prawn
11
+ module Graphics
12
+ module Transparency
13
+
14
+ def transparent(opacity, stroke_opacity=opacity, &block)
15
+ min_version(1.4)
16
+
17
+ key = "#{opacity}_#{stroke_opacity}"
18
+
19
+ if opacity_dictionary_registry[key]
20
+ opacity_dictionary = opacity_dictionary_registry[key][:obj]
21
+ opacity_dictionary_name = opacity_dictionary_registry[key][:name]
22
+ else
23
+ opacity_dictionary = ref!(:Type => :ExtGState,
24
+ :CA => stroke_opacity,
25
+ :ca => opacity
26
+ )
27
+
28
+ opacity_dictionary_name = "Tr#{next_opacity_dictionary_id}"
29
+ opacity_dictionary_registry[key] = { :name => opacity_dictionary_name,
30
+ :obj => opacity_dictionary }
31
+ end
32
+
33
+ page_ext_gstates.merge!(opacity_dictionary_name => opacity_dictionary)
34
+
35
+ # push a new graphics context onto the graphics context stack
36
+ add_content "q"
37
+ add_content "/#{opacity_dictionary_name} gs"
38
+
39
+ yield if block_given?
40
+
41
+ add_content "Q"
42
+ end
43
+
44
+ private
45
+
46
+ def opacity_dictionary_registry
47
+ @opacity_dictionary_registry ||= {}
48
+ end
49
+
50
+ def next_opacity_dictionary_id
51
+ opacity_dictionary_registry.length + 1
52
+ end
53
+
54
+ end
55
+ end
56
+ end
@@ -7,6 +7,10 @@
7
7
  # This is free software. Please see the LICENSE and COPYING files for details.
8
8
 
9
9
  require "prawn/graphics/color"
10
+ require "prawn/graphics/dash"
11
+ require "prawn/graphics/cap_style"
12
+ require "prawn/graphics/join_style"
13
+ require "prawn/graphics/transparency"
10
14
 
11
15
  module Prawn
12
16
 
@@ -19,6 +23,10 @@ module Prawn
19
23
  module Graphics
20
24
 
21
25
  include Color
26
+ include Dash
27
+ include CapStyle
28
+ include JoinStyle
29
+ include Transparency
22
30
 
23
31
  #######################################################################
24
32
  # Low level drawing operations must translate to absolute coords! #
@@ -94,7 +102,7 @@ module Prawn
94
102
  if width
95
103
  self.line_width = width
96
104
  else
97
- @line_width || 1
105
+ (defined?(@line_width) && @line_width) || 1
98
106
  end
99
107
  end
100
108
 
data/lib/prawn/images.rb CHANGED
@@ -154,7 +154,7 @@ module Prawn
154
154
  else
155
155
  raise ArgumentError, 'JPG uses an unsupported number of channels'
156
156
  end
157
- obj = ref(:Type => :XObject,
157
+ obj = ref!(:Type => :XObject,
158
158
  :Subtype => :Image,
159
159
  :Filter => :DCTDecode,
160
160
  :ColorSpace => color_space,
@@ -202,7 +202,7 @@ module Prawn
202
202
  end
203
203
 
204
204
  # build the image dict
205
- obj = ref(:Type => :XObject,
205
+ obj = ref!(:Type => :XObject,
206
206
  :Subtype => :Image,
207
207
  :Height => png.height,
208
208
  :Width => png.width,
@@ -226,7 +226,7 @@ module Prawn
226
226
  obj.data[:ColorSpace] = color
227
227
  else
228
228
  # embed the colour palette in the PDF as a object stream
229
- palette_obj = ref(:Length => png.palette.size)
229
+ palette_obj = ref!(:Length => png.palette.size)
230
230
  palette_obj << png.palette
231
231
 
232
232
  # build the color space array for the image
@@ -266,7 +266,7 @@ module Prawn
266
266
  # channel mixed in with the main image data. The PNG class seperates
267
267
  # it out for us and makes it available via the alpha_channel attribute
268
268
  if png.alpha_channel
269
- smask_obj = ref(:Type => :XObject,
269
+ smask_obj = ref!(:Type => :XObject,
270
270
  :Subtype => :Image,
271
271
  :Height => png.height,
272
272
  :Width => png.width,
@@ -79,6 +79,7 @@ module Prawn
79
79
  split! if children.length > limit
80
80
  else
81
81
  fit = children.detect { |child| child >= value }
82
+ fit = children.last unless fit
82
83
  fit << value
83
84
  end
84
85
 
@@ -113,7 +114,7 @@ module Prawn
113
114
 
114
115
  def new_node(parent=nil)
115
116
  node = Node.new(document, limit, parent)
116
- node.ref = document.ref(node)
117
+ node.ref = document.ref!(node)
117
118
  return node
118
119
  end
119
120
 
@@ -0,0 +1,63 @@
1
+ # encoding: utf-8
2
+
3
+ # object_store.rb : Implements PDF object repository for Prawn
4
+ #
5
+ # Copyright August 2008, Brad Ediger. All Rights Reserved.
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+ module Prawn
9
+ class ObjectStore
10
+ include Enumerable
11
+
12
+ def initialize(info={})
13
+ @objects = {}
14
+ @identifiers = []
15
+
16
+ # Create required PDF roots
17
+ @info = ref(info).identifier
18
+ @pages = ref(:Type => :Pages, :Count => 0, :Kids => []).identifier
19
+ @root = ref(:Type => :Catalog, :Pages => pages).identifier
20
+ end
21
+
22
+ def ref(data, &block)
23
+ push(size + 1, data, &block)
24
+ end
25
+
26
+ %w[info pages root].each do |name|
27
+ define_method(name) do
28
+ @objects[instance_variable_get("@#{name}")]
29
+ end
30
+ end
31
+
32
+ # Adds the given reference to the store and returns the reference object.
33
+ # If the object provided is not a Prawn::Reference, one is created from the
34
+ # arguments provided.
35
+ def push(*args, &block)
36
+ reference = if args.first.is_a?(Prawn::Reference)
37
+ args.first
38
+ else
39
+ Prawn::Reference.new(*args, &block)
40
+ end
41
+ @objects[reference.identifier] = reference
42
+ @identifiers << reference.identifier
43
+ reference
44
+ end
45
+ alias_method :<<, :push
46
+
47
+ def each
48
+ @identifiers.each do |id|
49
+ yield @objects[id]
50
+ end
51
+ end
52
+
53
+ def [](id)
54
+ @objects[id]
55
+ end
56
+
57
+ def size
58
+ @identifiers.size
59
+ end
60
+ alias_method :length, :size
61
+
62
+ end
63
+ end
@@ -6,6 +6,8 @@
6
6
  #
7
7
  # This is free software. Please see the LICENSE and COPYING files for details.
8
8
 
9
+ require 'prawn/byte_string'
10
+
9
11
  # Top level Module
10
12
  #
11
13
  module Prawn
@@ -41,6 +43,8 @@ module Prawn
41
43
  obj = obj.strftime("D:%Y%m%d%H%M%S%z").chop.chop + "'00'"
42
44
  obj = obj.gsub(/[\\\n\(\)]/) { |m| "\\#{m}" }
43
45
  "(#{obj})"
46
+ when Prawn::ByteString
47
+ "<" << obj.unpack("H*").first << ">"
44
48
  when String
45
49
  obj = "\xFE\xFF" + obj.unpack("U*").pack("n*") unless in_content_stream
46
50
  "<" << obj.unpack("H*").first << ">"
@@ -12,15 +12,16 @@ module Prawn
12
12
 
13
13
  class Reference #:nodoc:
14
14
 
15
- attr_accessor :gen, :data, :offset
16
- attr_reader :identifier, :stream
15
+ attr_accessor :gen, :data, :offset, :stream
16
+ attr_reader :identifier
17
17
 
18
18
  def initialize(id, data, &block)
19
19
  @identifier = id
20
- @gen = 0
21
- @data = data
20
+ @gen = 0
21
+ @data = data
22
22
  @compressed = false
23
- @on_encode = block
23
+ @on_encode = block
24
+ @stream = nil
24
25
  end
25
26
 
26
27
  def object
@@ -48,6 +49,18 @@ module Prawn
48
49
  @data[:Length] ||= @stream.length
49
50
  @compressed = true
50
51
  end
52
+
53
+ def compressed?
54
+ @compressed
55
+ end
56
+
57
+ # Replaces the data and stream with that of other_ref. Preserves compressed
58
+ # status.
59
+ def replace(other_ref)
60
+ @data = other_ref.data
61
+ @stream = other_ref.stream
62
+ @compressed = other_ref.compressed?
63
+ end
51
64
  end
52
65
 
53
66
  module_function
@@ -0,0 +1,87 @@
1
+ # encoding: utf-8
2
+ #
3
+ # stamp.rb : Implements a repeatable stamp
4
+ #
5
+ # Copyright October 2009, Daniel Nelson. All Rights Reserved.
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+ #
9
+
10
+ module Prawn
11
+ module Stamp
12
+
13
+ def stamp(user_defined_name)
14
+ stamp_at(user_defined_name, [0, 0])
15
+ end
16
+
17
+ def stamp_at(user_defined_name, point)
18
+ raise Prawn::Errors::InvalidName if user_defined_name.empty?
19
+ unless stamp_dictionary_registry[user_defined_name]
20
+ raise Prawn::Errors::UndefinedObjectName
21
+ end
22
+
23
+ add_content "q"
24
+
25
+ x,y = point
26
+ translate_position = "1 0 0 1 %.3f %.3f cm" % [x, y]
27
+ add_content translate_position
28
+
29
+ dict = stamp_dictionary_registry[user_defined_name]
30
+
31
+ stamp_dictionary_name = dict[:stamp_dictionary_name]
32
+ stamp_dictionary = dict[:stamp_dictionary]
33
+
34
+ add_content "/#{stamp_dictionary_name} Do"
35
+ add_content "Q"
36
+
37
+ page_xobjects.merge!(stamp_dictionary_name => stamp_dictionary)
38
+ end
39
+
40
+ def create_stamp(user_defined_name="", &block)
41
+ raise Prawn::Errors::InvalidName if user_defined_name.empty?
42
+
43
+ if stamp_dictionary_registry[user_defined_name]
44
+ raise Prawn::Errors::NameTaken
45
+ end
46
+
47
+ stamp_dictionary = ref!(:Type => :XObject,
48
+ :Subtype => :Form,
49
+ :BBox => [0, 0, bounds.width, bounds.height])
50
+
51
+ stamp_dictionary_name = "Stamp#{next_stamp_dictionary_id}"
52
+
53
+ stamp_dictionary_registry[user_defined_name] =
54
+ { :stamp_dictionary_name => stamp_dictionary_name,
55
+ :stamp_dictionary => stamp_dictionary}
56
+
57
+
58
+ @active_stamp_stream = ""
59
+ @active_stamp_dictionary = stamp_dictionary
60
+
61
+ yield if block_given?
62
+
63
+ stamp_dictionary.data[:Length] = @active_stamp_stream.length + 1
64
+ stamp_dictionary << @active_stamp_stream
65
+
66
+ @active_stamp_stream = nil
67
+ # The ProcSet needs to be assigned at the page level
68
+ procs = @active_stamp_dictionary.data[:ProcSet]
69
+ @active_stamp_dictionary.data.delete(:ProcSet)
70
+ @active_stamp_dictionary = nil
71
+
72
+ # The ProcSet needs to be assigned at the page level
73
+ proc_set(procs) if procs
74
+ end
75
+
76
+ private
77
+
78
+ def stamp_dictionary_registry
79
+ @stamp_dictionary_registry ||= {}
80
+ end
81
+
82
+ def next_stamp_dictionary_id
83
+ stamp_dictionary_registry.length + 1
84
+ end
85
+
86
+ end
87
+ end
@@ -125,6 +125,15 @@ describe "drawing bounding boxes" do
125
125
 
126
126
  @pdf.y.should.be.close 458.384, 0.001
127
127
  end
128
+
129
+ it "should keep track of the max height the box was stretched to" do
130
+ box = @pdf.bounding_box(@pdf.bounds.top_left, :width => 100) do
131
+ @pdf.move_down 100
132
+ @pdf.move_up 15
133
+ end
134
+
135
+ assert_equal 100, box.height
136
+ end
128
137
 
129
138
  end
130
139
 
@@ -11,6 +11,14 @@ describe "The cursor" do
11
11
  pdf.y = 300
12
12
  pdf.cursor.should == pdf.y - pdf.bounds.absolute_bottom
13
13
  end
14
+
15
+ it "should be able to move relative to the bottom margin" do
16
+ pdf = Prawn::Document.new
17
+ pdf.move_cursor_to(10)
18
+
19
+ pdf.cursor.should == 10
20
+ pdf.y.should == pdf.cursor + pdf.bounds.absolute_bottom
21
+ end
14
22
  end
15
23
 
16
24
  describe "when generating a document from a subclass" do
@@ -18,7 +26,7 @@ describe "when generating a document from a subclass" do
18
26
  custom_document = Class.new(Prawn::Document)
19
27
  custom_document.generate(Tempfile.new("generate_test").path) do |e|
20
28
  e.class.should == custom_document
21
- assert e.kind_of?(Prawn::Document)
29
+ e.should.be.kind_of(Prawn::Document)
22
30
  end
23
31
  end
24
32
  end
@@ -71,11 +79,11 @@ describe "When ending each page" do
71
79
  it "should not compress the page content stream if compression is disabled" do
72
80
 
73
81
  pdf = Prawn::Document.new(:compress => false)
74
- content_stub = pdf.ref({})
82
+ content_stub = pdf.ref!({})
75
83
  content_stub.stubs(:compress_stream).returns(true)
76
84
  content_stub.expects(:compress_stream).never
77
85
 
78
- pdf.instance_variable_set("@page_content", content_stub)
86
+ pdf.instance_variable_set("@page_content", content_stub.identifier)
79
87
  pdf.text "Hi There" * 20
80
88
  pdf.render
81
89
  end
@@ -83,11 +91,11 @@ describe "When ending each page" do
83
91
  it "should compress the page content stream if compression is enabled" do
84
92
 
85
93
  pdf = Prawn::Document.new(:compress => true)
86
- content_stub = pdf.ref({})
94
+ content_stub = pdf.ref!({})
87
95
  content_stub.stubs(:compress_stream).returns(true)
88
96
  content_stub.expects(:compress_stream).once
89
97
 
90
- pdf.instance_variable_set("@page_content", content_stub)
98
+ pdf.instance_variable_set("@page_content", content_stub.identifier)
91
99
  pdf.text "Hi There" * 20
92
100
  pdf.render
93
101
  end
@@ -151,6 +159,51 @@ describe "The mask() feature" do
151
159
  end
152
160
  end
153
161
 
162
+ describe "The group() feature" do
163
+ it "should group a simple block on a single page" do
164
+ pdf = Prawn::Document.new do
165
+ self.y = 50
166
+ group do
167
+ text "Hello"
168
+ text "World"
169
+ end
170
+ end
171
+
172
+ pages = PDF::Inspector::Page.analyze(pdf.render).pages
173
+ pages.size.should == 2
174
+ pages[0][:strings].should == []
175
+ pages[1][:strings].should == ["Hello", "World"]
176
+ end
177
+
178
+ it "should raise CannotGroup if the content is too tall" do
179
+ lambda {
180
+ Prawn::Document.new do
181
+ group do
182
+ 100.times { text "Too long" }
183
+ end
184
+ end.render
185
+ }.should.raise(Prawn::Document::CannotGroup)
186
+ end
187
+
188
+ it "should group within individual column boxes" do
189
+ pdf = Prawn::Document.new do
190
+ # Set up columns with grouped blocks of 0..49. 0 to 49 is slightly short
191
+ # of the height of one page / column, so each column should get its own
192
+ # group (every column should start with zero).
193
+ column_box([0, bounds.top], :width => bounds.width, :columns => 7) do
194
+ 10.times do
195
+ group { 50.times { |i| text(i.to_s) } }
196
+ end
197
+ end
198
+ end
199
+
200
+ # Second page should start with a 0 because it's a new group.
201
+ pages = PDF::Inspector::Page.analyze(pdf.render).pages
202
+ pages.size.should == 2
203
+ pages[1][:strings].first.should == '0'
204
+ end
205
+ end
206
+
154
207
  describe "The render() feature" do
155
208
  if "spec".respond_to?(:encode!)
156
209
  it "should return a 8 bit encoded string on a m17n aware VM" do