prawn-core 0.7.2 → 0.8.4

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 (65) hide show
  1. data/Rakefile +1 -1
  2. data/examples/general/background.rb +1 -1
  3. data/examples/general/measurement_units.rb +2 -2
  4. data/examples/general/outlines.rb +50 -0
  5. data/examples/general/repeaters.rb +11 -7
  6. data/examples/general/stamp.rb +6 -6
  7. data/examples/graphics/basic_images.rb +1 -1
  8. data/examples/graphics/curves.rb +1 -1
  9. data/examples/graphics/rounded_polygons.rb +19 -0
  10. data/examples/graphics/rounded_rectangle.rb +20 -0
  11. data/examples/graphics/transformations.rb +52 -0
  12. data/examples/m17n/win_ansi_charset.rb +1 -1
  13. data/examples/text/font_calculations.rb +3 -3
  14. data/examples/text/indent_paragraphs.rb +18 -0
  15. data/examples/text/kerning.rb +4 -4
  16. data/examples/text/rotated.rb +98 -0
  17. data/examples/text/simple_text.rb +3 -3
  18. data/examples/text/simple_text_ttf.rb +1 -1
  19. data/lib/prawn/byte_string.rb +1 -0
  20. data/lib/prawn/core.rb +12 -5
  21. data/lib/prawn/core/object_store.rb +99 -0
  22. data/lib/prawn/core/page.rb +96 -0
  23. data/lib/prawn/core/text.rb +75 -0
  24. data/lib/prawn/document.rb +71 -78
  25. data/lib/prawn/document/annotations.rb +2 -2
  26. data/lib/prawn/document/bounding_box.rb +19 -9
  27. data/lib/prawn/document/column_box.rb +13 -12
  28. data/lib/prawn/document/graphics_state.rb +49 -0
  29. data/lib/prawn/document/internals.rb +5 -40
  30. data/lib/prawn/document/page_geometry.rb +1 -18
  31. data/lib/prawn/document/snapshot.rb +12 -7
  32. data/lib/prawn/errors.rb +18 -0
  33. data/lib/prawn/font.rb +4 -2
  34. data/lib/prawn/font/afm.rb +8 -0
  35. data/lib/prawn/font/dfont.rb +12 -4
  36. data/lib/prawn/font/ttf.rb +9 -0
  37. data/lib/prawn/graphics.rb +66 -9
  38. data/lib/prawn/graphics/color.rb +1 -1
  39. data/lib/prawn/graphics/transformation.rb +156 -0
  40. data/lib/prawn/graphics/transparency.rb +3 -7
  41. data/lib/prawn/images.rb +4 -3
  42. data/lib/prawn/images/png.rb +2 -2
  43. data/lib/prawn/outline.rb +278 -0
  44. data/lib/prawn/pdf_object.rb +5 -3
  45. data/lib/prawn/repeater.rb +25 -13
  46. data/lib/prawn/stamp.rb +6 -29
  47. data/lib/prawn/text.rb +139 -121
  48. data/lib/prawn/text/box.rb +168 -102
  49. data/spec/bounding_box_spec.rb +7 -2
  50. data/spec/document_spec.rb +7 -5
  51. data/spec/font_spec.rb +9 -1
  52. data/spec/graphics_spec.rb +229 -0
  53. data/spec/object_store_spec.rb +5 -5
  54. data/spec/outline_spec.rb +229 -0
  55. data/spec/repeater_spec.rb +18 -1
  56. data/spec/snapshot_spec.rb +7 -7
  57. data/spec/span_spec.rb +6 -2
  58. data/spec/spec_helper.rb +7 -3
  59. data/spec/stamp_spec.rb +13 -0
  60. data/spec/text_at_spec.rb +119 -0
  61. data/spec/text_box_spec.rb +257 -4
  62. data/spec/text_spec.rb +278 -180
  63. data/vendor/pdf-inspector/lib/pdf/inspector/graphics.rb +12 -0
  64. metadata +16 -3
  65. data/lib/prawn/object_store.rb +0 -92
@@ -21,9 +21,9 @@ module Prawn
21
21
  # +options+ must be a Hash describing the annotation.
22
22
  #
23
23
  def annotate(options)
24
- current_page.data[:Annots] ||= []
24
+ page.dictionary.data[:Annots] ||= []
25
25
  options = sanitize_annotation_hash(options)
26
- current_page.data[:Annots] << ref!(options)
26
+ page.dictionary.data[:Annots] << ref!(options)
27
27
  return options
28
28
  end
29
29
 
@@ -15,6 +15,9 @@ module Prawn
15
15
  # A bounding box serves two important purposes:
16
16
  # * Provide bounds for flowing text, starting at a given point
17
17
  # * Translate the origin (0,0) for graphics primitives
18
+ #
19
+ # A point and :width must be provided. :height is optional.
20
+ # (See stretchyness below)
18
21
  #
19
22
  # ==Positioning
20
23
  #
@@ -153,7 +156,7 @@ module Prawn
153
156
  #
154
157
  def bounding_box(*args, &block)
155
158
  init_bounding_box(block) do |_|
156
- translate!(args[0])
159
+ map_to_absolute!(args[0])
157
160
  @bounding_box = BoundingBox.new(self, *args)
158
161
  end
159
162
  end
@@ -167,9 +170,9 @@ module Prawn
167
170
  #
168
171
  def canvas(&block)
169
172
  init_bounding_box(block, :hold_position => true) do |_|
170
- @bounding_box = BoundingBox.new(self, [0,page_dimensions[3]],
171
- :width => page_dimensions[2],
172
- :height => page_dimensions[3]
173
+ @bounding_box = BoundingBox.new(self, [0,page.dimensions[3]],
174
+ :width => page.dimensions[2],
175
+ :height => page.dimensions[3]
173
176
  )
174
177
  end
175
178
  end
@@ -198,10 +201,17 @@ module Prawn
198
201
  class BoundingBox
199
202
 
200
203
  def initialize(parent, point, options={}) #:nodoc:
204
+ unless options[:width]
205
+ raise ArgumentError, "BoundingBox needs the :width option to be set"
206
+ end
207
+
201
208
  @parent = parent
202
209
  @x, @y = point
203
210
  @width, @height = options[:width], options[:height]
204
- end
211
+ @stretched_height = nil
212
+ end
213
+
214
+ attr_reader :parent
205
215
 
206
216
  # The translated origin (x,y-height) which describes the location
207
217
  # of the bottom left corner of the bounding box
@@ -214,7 +224,7 @@ module Prawn
214
224
  #
215
225
  # Example, position some text 3 pts from the left of the containing box:
216
226
  #
217
- # text('hello', :at => [(bounds.left + 3), 0])
227
+ # draw_text('hello', :at => [(bounds.left + 3), 0])
218
228
  #
219
229
  def left
220
230
  0
@@ -245,7 +255,7 @@ module Prawn
245
255
  #
246
256
  # Example, position some text 3 pts from the right of the containing box:
247
257
  #
248
- # text('hello', :at => [(bounds.right - 3), 0])
258
+ # draw_text('hello', :at => [(bounds.right - 3), 0])
249
259
  #
250
260
  def right
251
261
  @width
@@ -255,7 +265,7 @@ module Prawn
255
265
  #
256
266
  # Example, position some text 3 pts from the top of the containing box:
257
267
  #
258
- # text('hello', :at => [0, (bounds.top - 3)])
268
+ # draw_text('hello', :at => [0, (bounds.top - 3)])
259
269
  #
260
270
  def top
261
271
  height
@@ -265,7 +275,7 @@ module Prawn
265
275
  #
266
276
  # Example, position some text 3 pts from the bottom of the containing box:
267
277
  #
268
- # text('hello', :at => [0, (bounds.bottom + 3)])
278
+ # draw_text('hello', :at => [0, (bounds.bottom + 3)])
269
279
  #
270
280
  def bottom
271
281
  0
@@ -9,20 +9,21 @@ require "prawn/document/bounding_box"
9
9
  module Prawn
10
10
  class Document
11
11
 
12
- # A column box is a bounding box with the additional property that when
13
- # text flows past the bottom, it will wrap first to another column on the
14
- # same page, and only flow to the next page when all the columns are
15
- # filled.
16
- #
17
- # column_box accepts the same parameters as bounding_box, as well as the
18
- # number of :columns and a :spacer (in points) between columns.
19
- #
20
- # Defaults are :columns = 3 and :spacer = font_size
21
- #
22
- # Under PDF::Writer, "spacer" was known as "gutter"
12
+ # A column box is a bounding box with the additional property that when
13
+ # text flows past the bottom, it will wrap first to another column on the
14
+ # same page, and only flow to the next page when all the columns are
15
+ # filled.
16
+ #
17
+ # column_box accepts the same parameters as bounding_box, as well as the
18
+ # number of :columns and a :spacer (in points) between columns.
19
+ #
20
+ # Defaults are :columns = 3 and :spacer = font_size
21
+ #
22
+ # Under PDF::Writer, "spacer" was known as "gutter"
23
+ #
23
24
  def column_box(*args, &block)
24
25
  init_column_box(block) do |_|
25
- translate!(args[0])
26
+ map_to_absolute!(args[0])
26
27
  @bounding_box = ColumnBox.new(self, *args)
27
28
  end
28
29
  end
@@ -0,0 +1,49 @@
1
+ # encoding: utf-8
2
+ #
3
+ # graphics_state.rb: Implements graphics state saving and restoring
4
+ #
5
+ # Copyright January 2010, Michael Witrant. All Rights Reserved.
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+ #
9
+
10
+ module Prawn
11
+ class Document
12
+ module GraphicsState
13
+
14
+ # Pushes the current graphics state on to the graphics state stack so we
15
+ # can restore it when finished with a change we want to isolate (such as
16
+ # modifying the transformation matrix). Used in pairs with
17
+ # restore_graphics_state or passed a block
18
+ #
19
+ # Example without a block:
20
+ #
21
+ # save_graphics_state
22
+ # rotate 30
23
+ # text "rotated text"
24
+ # restore_graphics_state
25
+ #
26
+ # Example with a block:
27
+ #
28
+ # save_graphics_state do
29
+ # rotate 30
30
+ # text "rotated text"
31
+ # end
32
+ #
33
+ def save_graphics_state
34
+ add_content "q"
35
+ if block_given?
36
+ yield
37
+ restore_graphics_state
38
+ end
39
+ end
40
+
41
+ # Pops the last saved graphics state off the graphics state stack and
42
+ # restores the state to those values
43
+ def restore_graphics_state
44
+ add_content "Q"
45
+ end
46
+
47
+ end
48
+ end
49
+ end
@@ -37,18 +37,6 @@ module Prawn
37
37
  @store.ref(data)
38
38
  end
39
39
 
40
- # Grabs the reference for the current page content
41
- #
42
- def page_content
43
- @active_stamp_stream || @store[@page_content]
44
- end
45
-
46
- # Grabs the reference for the current page
47
- #
48
- def current_page
49
- @active_stamp_dictionary || @store[@current_page]
50
- end
51
-
52
40
  # Appends a raw string to the current page content.
53
41
  #
54
42
  # # Raw line drawing example:
@@ -58,32 +46,8 @@ module Prawn
58
46
  # pdf.add_content("S") # stroke
59
47
  #
60
48
  def add_content(str)
61
- page_content << str << "\n"
49
+ page.content << str << "\n"
62
50
  end
63
-
64
- # The Resources dictionary for the current page
65
- #
66
- def page_resources
67
- current_page.data[:Resources] ||= {}
68
- end
69
-
70
- # The Font dictionary for the current page
71
- #
72
- def page_fonts
73
- page_resources[:Font] ||= {}
74
- end
75
-
76
- # The XObject dictionary for the current page
77
- #
78
- def page_xobjects
79
- page_resources[:XObject] ||= {}
80
- end
81
-
82
- # The ExtGState dictionary for the current page
83
- #
84
- def page_ext_gstates
85
- page_resources[:ExtGState] ||= {}
86
- end
87
51
 
88
52
  # The Name dictionary (PDF spec 3.6.3) for this document. It is
89
53
  # lazily initialized, so that documents that do not need a name
@@ -127,15 +91,16 @@ module Prawn
127
91
  (1..page_count).each do |i|
128
92
  go_to_page i
129
93
  repeaters.each { |r| r.run(i) }
130
- add_content "Q"
131
- page_content.compress_stream if compression_enabled?
132
- page_content.data[:Length] = page_content.stream.size
94
+ restore_graphics_state
95
+ page.content.compress_stream if compression_enabled?
96
+ page.content.data[:Length] = page.content.stream.size
133
97
  end
134
98
  end
135
99
 
136
100
  # raise the PDF version of the file we're going to generate.
137
101
  # A private method, designed for internal use when the user adds a feature
138
102
  # to their document that requires a particular version.
103
+ #
139
104
  def min_version(min)
140
105
  @version = min if min > @version
141
106
  end
@@ -131,23 +131,6 @@ module Prawn
131
131
  "LETTER" => [612.00, 792.00],
132
132
  "TABLOID" => [792.00, 1224.00] }
133
133
 
134
-
135
- # Returns the width and height of the current page in PDF points.
136
- # Usually, you'll want the dimensions of the bounding_box or
137
- # margin_box instead.
138
- #
139
- def page_dimensions
140
- coords = SIZES[page_size] || page_size
141
- [0,0] + case(page_layout)
142
- when :portrait
143
- coords
144
- when :landscape
145
- coords.reverse
146
- else
147
- raise Prawn::Errors::InvalidPageLayout,
148
- "Layout must be either :portrait or :landscape"
149
- end
150
- end
151
- end
134
+ end
152
135
  end
153
136
  end
@@ -5,6 +5,7 @@
5
5
  # Copyright August 2009, Brad Ediger. All Rights Reserved.
6
6
  #
7
7
  # This is free software. Please see the LICENSE and COPYING files for details.
8
+
8
9
  require 'delegate'
9
10
 
10
11
  module Prawn
@@ -43,9 +44,10 @@ module Prawn
43
44
 
44
45
  # Takes a current snapshot of the document's state, sufficient to
45
46
  # reconstruct it after it was amended.
47
+ #
46
48
  def take_snapshot
47
- {:page_content => Marshal.load(Marshal.dump(page_content)),
48
- :current_page => Marshal.load(Marshal.dump(current_page)),
49
+ {:page_content => Marshal.load(Marshal.dump(page.content)),
50
+ :current_page => Marshal.load(Marshal.dump(page.dictionary)),
49
51
  :page_number => @page_number,
50
52
  :page_kids => @store.pages.data[:Kids].map{|kid| kid.identifier},
51
53
  :dests => names? &&
@@ -53,17 +55,20 @@ module Prawn
53
55
  end
54
56
 
55
57
  # Rolls the page state back to the state of the given snapshot.
58
+ #
56
59
  def restore_snapshot(shot)
57
60
  # Because these objects are referenced by identifier from the Pages
58
61
  # dictionary, we can't just restore them over the current refs in
59
62
  # page_content and current_page. We have to restore them over the old
60
63
  # ones.
61
- @page_content = shot[:page_content].identifier
62
- page_content.replace shot[:page_content]
64
+ page.content = shot[:page_content].identifier
65
+ page.content.replace shot[:page_content]
63
66
 
64
- @current_page = shot[:current_page].identifier
65
- current_page.replace shot[:current_page]
66
- current_page.data[:Contents] = page_content
67
+ page.dictionary = shot[:current_page].identifier
68
+ page.dictionary.replace shot[:current_page]
69
+ page.dictionary.data[:Contents] = page.content
70
+
71
+ @page_number = shot[:page_number]
67
72
 
68
73
  @page_number = shot[:page_number]
69
74
 
@@ -32,6 +32,11 @@ module Prawn
32
32
  #
33
33
  CannotFit = Class.new(StandardError)
34
34
 
35
+ # Raised if group() is called with a block that is too big to be
36
+ # rendered in the current context.
37
+ #
38
+ CannotGroup = Class.new(StandardError)
39
+
35
40
  # This error is raised when Prawn is being used on a M17N aware VM,
36
41
  # and the user attempts to add text that isn't compatible with UTF-8
37
42
  # to their document
@@ -61,5 +66,18 @@ module Prawn
61
66
  # This error is raised when an object is attempted to be
62
67
  # referenced by name, but no such name is associated with an object
63
68
  UndefinedObjectName = Class.new(StandardError)
69
+
70
+ # This error is raised when a required option has not been set
71
+ RequiredOption = Class.new(StandardError)
72
+
73
+ # This error is raised when a requested outline item with a given title does not exist
74
+ UnknownOutlineTitle = Class.new(StandardError)
75
+
76
+ # This error is raised when a block is required, but not provided
77
+ BlockRequired = Class.new(StandardError)
78
+
79
+ # This error is rased when a graphics method is called with improper arguments
80
+ InvalidGraphicsPath = Class.new(StandardError)
81
+
64
82
  end
65
83
  end
@@ -5,6 +5,7 @@
5
5
  # Copyright May 2008, Gregory Brown / James Healy. All Rights Reserved.
6
6
  #
7
7
  # This is free software. Please see the LICENSE and COPYING files for details.
8
+ #
8
9
  require "prawn/font/afm"
9
10
  require "prawn/font/ttf"
10
11
  require "prawn/font/dfont"
@@ -47,7 +48,7 @@ module Prawn
47
48
  def font(name=nil, options={})
48
49
  return((defined?(@font) && @font) || font("Helvetica")) if name.nil?
49
50
 
50
- raise Errors::NotOnPage unless current_page
51
+ raise Errors::NotOnPage if pages.empty? && !page.in_stamp_stream?
51
52
  new_font = find_font(name, options)
52
53
 
53
54
  if block_given?
@@ -277,6 +278,7 @@ module Prawn
277
278
  # font. The string is expected to be UTF-8 going in. It will be re-encoded
278
279
  # and the new string will be returned. For an in-place (destructive)
279
280
  # version, see normalize_encoding!.
281
+ #
280
282
  def normalize_encoding(string)
281
283
  raise NotImplementedError, "subclasses of Prawn::Font must implement #normalize_encoding"
282
284
  end
@@ -307,7 +309,7 @@ module Prawn
307
309
  #
308
310
  def add_to_current_page(subset)
309
311
  @references[subset] ||= register(subset)
310
- @document.page_fonts.merge!(identifier_for(subset) => @references[subset])
312
+ @document.page.fonts.merge!(identifier_for(subset) => @references[subset])
311
313
  end
312
314
 
313
315
  def identifier_for(subset) #:nodoc:
@@ -1,3 +1,11 @@
1
+ # encoding: utf-8
2
+
3
+ # prawn/font/afm.rb : Implements AFM font support for Prawn
4
+ #
5
+ # Copyright May 2008, Gregory Brown / James Healy. All Rights Reserved.
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+
1
9
  require 'prawn/encoding'
2
10
 
3
11
  module Prawn
@@ -1,3 +1,11 @@
1
+ # encoding: utf-8
2
+ #
3
+ # font.rb : The Prawn font class
4
+ #
5
+ # Copyright November 2008, Jamis Buck. All Rights Reserved.
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+ #
1
9
  require 'prawn/font/ttf'
2
10
 
3
11
  module Prawn
@@ -11,16 +19,16 @@ module Prawn
11
19
  # list is not necessarily the font at index 0 in the file.
12
20
  #
13
21
  def self.named_fonts(file)
14
- TTFunk::ResourceFile.open(file) do |file|
15
- return file.resources_for("sfnt")
22
+ TTFunk::ResourceFile.open(file) do |f|
23
+ return f.resources_for("sfnt")
16
24
  end
17
25
  end
18
26
 
19
27
  # Returns the number of fonts contained in the dfont file.
20
28
  #
21
29
  def self.font_count(file)
22
- TTFunk::ResourceFile.open(file) do |file|
23
- return file.map["sfnt"][:list].length
30
+ TTFunk::ResourceFile.open(file) do |f|
31
+ return f.map["sfnt"][:list].length
24
32
  end
25
33
  end
26
34
 
@@ -1,3 +1,12 @@
1
+ # encoding: utf-8
2
+
3
+ # prawn/font/ttf.rb : Implements AFM font support for Prawn
4
+ #
5
+ # Copyright May 2008, Gregory Brown / James Healy / Jamis Buck
6
+ # All Rights Reserved.
7
+ #
8
+ # This is free software. Please see the LICENSE and COPYING files for details.
9
+
1
10
  require 'ttfunk'
2
11
  require 'ttfunk/subset_collection'
3
12