prawn-core 0.7.2 → 0.8.4

Sign up to get free protection for your applications and to get access to all the features.
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