prawn 2.3.0 → 2.4.0

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 (91) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/Rakefile +2 -31
  5. data/lib/prawn.rb +1 -0
  6. data/lib/prawn/document.rb +20 -15
  7. data/lib/prawn/document/bounding_box.rb +10 -2
  8. data/lib/prawn/document/span.rb +2 -1
  9. data/lib/prawn/font.rb +6 -4
  10. data/lib/prawn/font_metric_cache.rb +7 -6
  11. data/lib/prawn/fonts/afm.rb +18 -16
  12. data/lib/prawn/fonts/ttf.rb +55 -29
  13. data/lib/prawn/graphics.rb +11 -11
  14. data/lib/prawn/graphics/color.rb +18 -16
  15. data/lib/prawn/graphics/join_style.rb +1 -1
  16. data/lib/prawn/graphics/patterns.rb +57 -49
  17. data/lib/prawn/graphics/transformation.rb +3 -3
  18. data/lib/prawn/grid.rb +36 -1
  19. data/lib/prawn/images.rb +29 -27
  20. data/lib/prawn/images/jpg.rb +4 -1
  21. data/lib/prawn/images/png.rb +2 -1
  22. data/lib/prawn/outline.rb +6 -5
  23. data/lib/prawn/repeater.rb +1 -1
  24. data/lib/prawn/security.rb +39 -36
  25. data/lib/prawn/text.rb +18 -18
  26. data/lib/prawn/text/box.rb +10 -9
  27. data/lib/prawn/text/formatted/arranger.rb +38 -19
  28. data/lib/prawn/text/formatted/box.rb +20 -15
  29. data/lib/prawn/text/formatted/line_wrap.rb +18 -16
  30. data/lib/prawn/text/formatted/parser.rb +28 -26
  31. data/lib/prawn/text/formatted/wrap.rb +15 -11
  32. data/lib/prawn/version.rb +1 -1
  33. data/lib/prawn/view.rb +2 -2
  34. data/manual/basic_concepts/measurement.rb +1 -1
  35. data/manual/bounding_box/canvas.rb +3 -3
  36. data/manual/bounding_box/nesting.rb +1 -1
  37. data/manual/document_and_page_options/background.rb +6 -2
  38. data/manual/document_and_page_options/document_and_page_options.rb +3 -3
  39. data/manual/document_and_page_options/print_scaling.rb +2 -1
  40. data/manual/graphics/common_lines.rb +1 -1
  41. data/manual/graphics/fill_and_stroke.rb +1 -1
  42. data/manual/graphics/fill_rules.rb +4 -3
  43. data/manual/graphics/helper.rb +11 -4
  44. data/manual/graphics/lines_and_curves.rb +1 -1
  45. data/manual/graphics/stroke_dash.rb +5 -5
  46. data/manual/graphics/translate.rb +2 -1
  47. data/manual/images/horizontal.rb +2 -2
  48. data/manual/images/scale.rb +3 -3
  49. data/manual/images/vertical.rb +6 -3
  50. data/manual/images/width_and_height.rb +3 -3
  51. data/manual/layout/content.rb +2 -2
  52. data/manual/outline/outline.rb +1 -1
  53. data/manual/security/permissions.rb +4 -2
  54. data/manual/security/security.rb +1 -1
  55. data/manual/text/alignment.rb +2 -2
  56. data/manual/text/column_box.rb +2 -2
  57. data/manual/text/font_style.rb +5 -2
  58. data/manual/text/formatted_callbacks.rb +17 -12
  59. data/manual/text/formatted_text.rb +7 -4
  60. data/manual/text/free_flowing_text.rb +3 -3
  61. data/manual/text/kerning_and_character_spacing.rb +4 -4
  62. data/manual/text/paragraph_indentation.rb +4 -5
  63. data/manual/text/rendering_and_color.rb +1 -1
  64. data/manual/text/right_to_left_text.rb +6 -6
  65. data/manual/text/rotation.rb +8 -3
  66. data/manual/text/text_box_extensions.rb +1 -1
  67. data/manual/text/text_box_overflow.rb +11 -9
  68. data/manual/text/win_ansi_charset.rb +9 -9
  69. data/prawn.gemspec +4 -10
  70. data/spec/prawn/document/bounding_box_spec.rb +82 -78
  71. data/spec/prawn/document/security_spec.rb +1 -1
  72. data/spec/prawn/document_span_spec.rb +14 -6
  73. data/spec/prawn/document_spec.rb +29 -26
  74. data/spec/prawn/font_spec.rb +16 -14
  75. data/spec/prawn/graphics_spec.rb +79 -44
  76. data/spec/prawn/images_spec.rb +13 -8
  77. data/spec/prawn/outline_spec.rb +127 -27
  78. data/spec/prawn/repeater_spec.rb +9 -8
  79. data/spec/prawn/soft_mask_spec.rb +1 -1
  80. data/spec/prawn/stamp_spec.rb +3 -2
  81. data/spec/prawn/text/box_spec.rb +57 -59
  82. data/spec/prawn/text/formatted/arranger_spec.rb +10 -10
  83. data/spec/prawn/text/formatted/box_spec.rb +8 -5
  84. data/spec/prawn/text/formatted/line_wrap_spec.rb +2 -1
  85. data/spec/prawn/text_draw_text_spec.rb +9 -8
  86. data/spec/prawn/text_spacing_spec.rb +2 -2
  87. data/spec/prawn/text_spec.rb +9 -9
  88. data/spec/prawn/text_with_inline_formatting_spec.rb +1 -1
  89. data/spec/prawn_manual_spec.rb +4 -4
  90. metadata +14 -98
  91. metadata.gz.sig +0 -0
@@ -212,7 +212,7 @@ module Prawn
212
212
  move_to(x + radius1, y)
213
213
 
214
214
  # Upper right hand corner
215
- curve_to [x, y + radius2],
215
+ curve_to [x, y + radius2],
216
216
  bounds: [[x + radius1, y + l2], [x + l1, y + radius2]]
217
217
 
218
218
  # Upper left hand corner
@@ -341,10 +341,8 @@ module Prawn
341
341
  }.merge(options)
342
342
 
343
343
  Prawn.verify_options(
344
- %i[
345
- at width height step_length
346
- negative_axes_length color
347
- ], options
344
+ %i[at width height step_length negative_axes_length color],
345
+ options
348
346
  )
349
347
 
350
348
  save_graphics_state do
@@ -354,11 +352,13 @@ module Prawn
354
352
  dash(1, space: 4)
355
353
  stroke_horizontal_line(
356
354
  options[:at][0] - options[:negative_axes_length],
357
- options[:at][0] + options[:width], at: options[:at][1]
355
+ options[:at][0] + options[:width],
356
+ at: options[:at][1]
358
357
  )
359
358
  stroke_vertical_line(
360
359
  options[:at][1] - options[:negative_axes_length],
361
- options[:at][1] + options[:height], at: options[:at][0]
360
+ options[:at][1] + options[:height],
361
+ at: options[:at][0]
362
362
  )
363
363
  undash
364
364
 
@@ -633,10 +633,10 @@ module Prawn
633
633
 
634
634
  ops.product(shapes).each do |operation, shape|
635
635
  class_eval <<-METHOD, __FILE__, __LINE__ + 1
636
- def #{operation}_#{shape}(*args)
637
- #{shape}(*args)
638
- #{operation}
639
- end
636
+ def #{operation}_#{shape}(*args) # def fill_polygon(*args)
637
+ #{shape}(*args) # polygon(*args)
638
+ #{operation} # fill
639
+ end # end
640
640
  METHOD
641
641
  end
642
642
 
@@ -154,27 +154,29 @@ module Prawn
154
154
  raise ArgumentError, "unknown color space: '#{color_space}'"
155
155
  end
156
156
 
157
- operator = case type
158
- when :fill
159
- 'cs'
160
- when :stroke
161
- 'CS'
162
- else
163
- raise ArgumentError, "unknown type '#{type}'"
164
- end
157
+ operator =
158
+ case type
159
+ when :fill
160
+ 'cs'
161
+ when :stroke
162
+ 'CS'
163
+ else
164
+ raise ArgumentError, "unknown type '#{type}'"
165
+ end
165
166
 
166
167
  renderer.add_content "/#{color_space} #{operator}"
167
168
  end
168
169
 
169
170
  def set_color(type, color, options = {})
170
- operator = case type
171
- when :fill
172
- 'scn'
173
- when :stroke
174
- 'SCN'
175
- else
176
- raise ArgumentError, "unknown type '#{type}'"
177
- end
171
+ operator =
172
+ case type
173
+ when :fill
174
+ 'scn'
175
+ when :stroke
176
+ 'SCN'
177
+ else
178
+ raise ArgumentError, "unknown type '#{type}'"
179
+ end
178
180
 
179
181
  if options[:pattern]
180
182
  set_color_space type, :Pattern
@@ -28,7 +28,7 @@ module Prawn
28
28
  unless JOIN_STYLES.key?(current_join_style)
29
29
  raise Prawn::Errors::InvalidJoinStyle,
30
30
  "#{style} is not a recognized join style. Valid styles are " +
31
- JOIN_STYLES.keys.join(', ')
31
+ JOIN_STYLES.keys.join(', ')
32
32
  end
33
33
 
34
34
  write_stroke_join_style
@@ -95,14 +95,15 @@ module Prawn
95
95
  patterns["SP#{registry_key}"] = shading
96
96
  end
97
97
 
98
- operator = case type
99
- when :fill
100
- 'scn'
101
- when :stroke
102
- 'SCN'
103
- else
104
- raise ArgumentError, "unknown type '#{type}'"
105
- end
98
+ operator =
99
+ case type
100
+ when :fill
101
+ 'scn'
102
+ when :stroke
103
+ 'SCN'
104
+ else
105
+ raise ArgumentError, "unknown type '#{type}'"
106
+ end
106
107
 
107
108
  set_color_space type, :Pattern
108
109
  renderer.add_content "/SP#{registry_key} #{operator}"
@@ -113,6 +114,7 @@ module Prawn
113
114
  *arguments, from: nil, to: nil, r1: nil, r2: nil, stops: nil,
114
115
  apply_transformations: nil
115
116
  )
117
+
116
118
  case arguments.length
117
119
  when 0
118
120
  apply_transformations = true if apply_transformations.nil?
@@ -128,22 +130,23 @@ module Prawn
128
130
  raise ArgumentError, 'At least two stops must be specified'
129
131
  end
130
132
 
131
- stops = stops.map.with_index do |stop, index|
132
- case stop
133
- when Array, Hash
134
- position, color = stop
135
- else
136
- position = index / (stops.length.to_f - 1)
137
- color = stop
133
+ stops =
134
+ stops.map.with_index do |stop, index|
135
+ case stop
136
+ when Array, Hash
137
+ position, color = stop
138
+ else
139
+ position = index / (stops.length.to_f - 1)
140
+ color = stop
141
+ end
142
+
143
+ unless (0..1).cover?(position)
144
+ raise ArgumentError, 'position must be between 0 and 1'
145
+ end
146
+
147
+ GradientStop.new(position, normalize_color(color))
138
148
  end
139
149
 
140
- unless (0..1).cover?(position)
141
- raise ArgumentError, 'position must be between 0 and 1'
142
- end
143
-
144
- GradientStop.new(position, normalize_color(color))
145
- end
146
-
147
150
  if stops.first.position != 0
148
151
  raise ArgumentError, 'The first stop must have a position of 0'
149
152
  end
@@ -159,8 +162,10 @@ module Prawn
159
162
  r1 ? :radial : :axial,
160
163
  apply_transformations,
161
164
  stops,
162
- from, to,
163
- r1, r2
165
+ from,
166
+ to,
167
+ r1,
168
+ r2
164
169
  )
165
170
  end
166
171
  # rubocop: enable Metrics/ParameterLists
@@ -195,37 +200,40 @@ module Prawn
195
200
  'for more information.'
196
201
  end
197
202
 
198
- shader_stops = gradient.stops.each_cons(2).map do |first, second|
199
- ref!(
200
- FunctionType: 2,
201
- Domain: [0.0, 1.0],
202
- C0: first.color,
203
- C1: second.color,
204
- N: 1.0
205
- )
206
- end
203
+ shader_stops =
204
+ gradient.stops.each_cons(2).map do |first, second|
205
+ ref!(
206
+ FunctionType: 2,
207
+ Domain: [0.0, 1.0],
208
+ C0: first.color,
209
+ C1: second.color,
210
+ N: 1.0
211
+ )
212
+ end
207
213
 
208
214
  # If there's only two stops, we can use the single shader.
209
215
  # Otherwise we stitch the multiple shaders together.
210
- shader = if shader_stops.length == 1
211
- shader_stops.first
212
- else
213
- ref!(
214
- FunctionType: 3, # stitching function
215
- Domain: [0.0, 1.0],
216
- Functions: shader_stops,
217
- Bounds: gradient.stops[1..-2].map(&:position),
218
- Encode: [0.0, 1.0] * shader_stops.length
219
- )
220
- end
216
+ shader =
217
+ if shader_stops.length == 1
218
+ shader_stops.first
219
+ else
220
+ ref!(
221
+ FunctionType: 3, # stitching function
222
+ Domain: [0.0, 1.0],
223
+ Functions: shader_stops,
224
+ Bounds: gradient.stops[1..-2].map(&:position),
225
+ Encode: [0.0, 1.0] * shader_stops.length
226
+ )
227
+ end
221
228
 
222
229
  x1, y1, x2, y2, transformation = gradient_coordinates(gradient)
223
230
 
224
- coords = if gradient.type == :axial
225
- [0, 0, x2 - x1, y2 - y1]
226
- else
227
- [0, 0, gradient.r1, x2 - x1, y2 - y1, gradient.r2]
228
- end
231
+ coords =
232
+ if gradient.type == :axial
233
+ [0, 0, x2 - x1, y2 - y1]
234
+ else
235
+ [0, 0, gradient.r1, x2 - x1, y2 - y1, gradient.r2]
236
+ end
229
237
 
230
238
  shading = ref!(
231
239
  ShadingType: gradient.type == :axial ? 2 : 3,
@@ -48,7 +48,7 @@ module Prawn
48
48
  if options[:origin].nil?
49
49
  transformation_matrix(cos, sin, -sin, cos, 0, 0, &block)
50
50
  else
51
- raise Prawn::Errors::BlockRequired unless block_given?
51
+ raise Prawn::Errors::BlockRequired unless block
52
52
 
53
53
  x = options[:origin][0] + bounds.absolute_left
54
54
  y = options[:origin][1] + bounds.absolute_bottom
@@ -118,7 +118,7 @@ module Prawn
118
118
  if options[:origin].nil?
119
119
  transformation_matrix(factor, 0, 0, factor, 0, 0, &block)
120
120
  else
121
- raise Prawn::Errors::BlockRequired unless block_given?
121
+ raise Prawn::Errors::BlockRequired unless block
122
122
 
123
123
  x = options[:origin][0] + bounds.absolute_left
124
124
  y = options[:origin][1] + bounds.absolute_bottom
@@ -149,11 +149,11 @@ module Prawn
149
149
  raise ArgumentError,
150
150
  'Transformation matrix must have exacty 6 elements'
151
151
  end
152
- values = matrix.map { |x| x.to_f.round(5) }.join(' ')
153
152
  save_graphics_state if block_given?
154
153
 
155
154
  add_to_transformation_stack(*matrix)
156
155
 
156
+ values = PDF::Core.real_params(matrix)
157
157
  renderer.add_content "#{values} cm"
158
158
  if block_given?
159
159
  yield
@@ -56,6 +56,7 @@ module Prawn
56
56
  # @group Experimental API
57
57
  class Grid
58
58
  attr_reader :pdf, :columns, :rows, :gutter, :row_gutter, :column_gutter
59
+
59
60
  def initialize(pdf, options = {}) # :nodoc:
60
61
  valid_options = %i[columns rows gutter row_gutter column_gutter]
61
62
  Prawn.verify_options valid_options, options
@@ -213,12 +214,14 @@ module Prawn
213
214
  # A MultiBox is specified by 2 Boxes and spans the areas between.
214
215
  #
215
216
  # @group Experimental API
216
- class MultiBox < GridBox
217
+ class MultiBox
217
218
  def initialize(pdf, box1, box2)
218
219
  @pdf = pdf
219
220
  @boxes = [box1, box2]
220
221
  end
221
222
 
223
+ attr_reader :pdf
224
+
222
225
  def name
223
226
  @boxes.map(&:name).join(':')
224
227
  end
@@ -255,6 +258,38 @@ module Prawn
255
258
  bottom_box.bottom
256
259
  end
257
260
 
261
+ def top_left
262
+ [left, top]
263
+ end
264
+
265
+ def top_right
266
+ [right, top]
267
+ end
268
+
269
+ def bottom_left
270
+ [left, bottom]
271
+ end
272
+
273
+ def bottom_right
274
+ [right, bottom]
275
+ end
276
+
277
+ def bounding_box(&blk)
278
+ pdf.bounding_box(top_left, width: width, height: height, &blk)
279
+ end
280
+
281
+ def show(grid_color = 'CCCCCC')
282
+ bounding_box do
283
+ original_stroke_color = pdf.stroke_color
284
+
285
+ pdf.stroke_color = grid_color
286
+ pdf.text name
287
+ pdf.stroke_bounds
288
+
289
+ pdf.stroke_color = original_stroke_color
290
+ end
291
+ end
292
+
258
293
  private
259
294
 
260
295
  def left_box
@@ -66,10 +66,10 @@ module Prawn
66
66
  # (See also: Prawn::Images::PNG , Prawn::Images::JPG)
67
67
  #
68
68
  def image(file, options = {})
69
- Prawn.verify_options %i[
70
- at position vposition height
71
- width scale fit
72
- ], options
69
+ Prawn.verify_options(
70
+ %i[at position vposition height width scale fit],
71
+ options
72
+ )
73
73
 
74
74
  pdf_obj, info = build_image_object(file)
75
75
  embed_image(pdf_obj, info, options)
@@ -156,29 +156,31 @@ module Prawn
156
156
  def image_position(width, height, options)
157
157
  options[:position] ||= :left
158
158
 
159
- y = case options[:vposition]
160
- when :top
161
- bounds.absolute_top
162
- when :center
163
- bounds.absolute_top - (bounds.height - height) / 2.0
164
- when :bottom
165
- bounds.absolute_bottom + height
166
- when Numeric
167
- bounds.absolute_top - options[:vposition]
168
- else
169
- determine_y_with_page_flow(height)
170
- end
171
-
172
- x = case options[:position]
173
- when :left
174
- bounds.left_side
175
- when :center
176
- bounds.left_side + (bounds.width - width) / 2.0
177
- when :right
178
- bounds.right_side - width
179
- when Numeric
180
- options[:position] + bounds.left_side
181
- end
159
+ y =
160
+ case options[:vposition]
161
+ when :top
162
+ bounds.absolute_top
163
+ when :center
164
+ bounds.absolute_top - (bounds.height - height) / 2.0
165
+ when :bottom
166
+ bounds.absolute_bottom + height
167
+ when Numeric
168
+ bounds.absolute_top - options[:vposition]
169
+ else
170
+ determine_y_with_page_flow(height)
171
+ end
172
+
173
+ x =
174
+ case options[:position]
175
+ when :left
176
+ bounds.left_side
177
+ when :center
178
+ bounds.left_side + (bounds.width - width) / 2.0
179
+ when :right
180
+ bounds.right_side - width
181
+ when Numeric
182
+ options[:position] + bounds.left_side
183
+ end
182
184
 
183
185
  [x, y]
184
186
  end
@@ -16,6 +16,8 @@ module Prawn
16
16
  # of a JPG image that we need to embed them in a PDF
17
17
  #
18
18
  class JPG < Image
19
+ class FormatError < StandardError; end
20
+
19
21
  # @group Extension API
20
22
 
21
23
  attr_reader :width, :height, :bits, :channels
@@ -35,6 +37,7 @@ module Prawn
35
37
  # <tt>:data</tt>:: A binary string of JPEG data
36
38
  #
37
39
  def initialize(data)
40
+ super()
38
41
  @data = data
39
42
  d = StringIO.new(@data)
40
43
  d.binmode
@@ -43,7 +46,7 @@ module Prawn
43
46
  d.seek(2) # Skip the first two bytes of JPEG identifier.
44
47
  loop do
45
48
  marker, code, length = d.read(4).unpack('CCn')
46
- raise 'JPEG marker not found!' if marker != c_marker
49
+ raise FormatError, 'JPEG marker not found!' if marker != c_marker
47
50
 
48
51
  if JPEG_SOF_BLOCKS.include?(code)
49
52
  @bits, @height, @width, @channels = d.read(6).unpack('CnnC')
@@ -34,6 +34,7 @@ module Prawn
34
34
  # <tt>data</tt>:: A binary string of PNG data
35
35
  #
36
36
  def initialize(data)
37
+ super()
37
38
  data = StringIO.new(data.dup)
38
39
 
39
40
  data.read(8) # Skip the default header
@@ -212,7 +213,7 @@ module Prawn
212
213
  # - An array with N elements, where N is two times the number of color
213
214
  # components.
214
215
  rgb = transparency[:rgb]
215
- obj.data[:Mask] = rgb.collect { |x| [x, x] }.flatten
216
+ obj.data[:Mask] = rgb.map { |x| [x, x] }.flatten
216
217
  end
217
218
 
218
219
  # For PNG color types 4 and 6, the transparency data is stored as