prawn-core 0.5.1 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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