prawn 2.1.0 → 2.3.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 (280) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/GPLv2 +20 -21
  5. data/Gemfile +3 -9
  6. data/Rakefile +20 -23
  7. data/lib/prawn.rb +36 -49
  8. data/lib/prawn/document.rb +180 -133
  9. data/lib/prawn/document/bounding_box.rb +41 -29
  10. data/lib/prawn/document/column_box.rb +7 -7
  11. data/lib/prawn/document/internals.rb +8 -6
  12. data/lib/prawn/document/span.rb +21 -16
  13. data/lib/prawn/encoding.rb +69 -68
  14. data/lib/prawn/errors.rb +12 -7
  15. data/lib/prawn/font.rb +102 -69
  16. data/lib/prawn/font_metric_cache.rb +14 -8
  17. data/lib/prawn/{font → fonts}/afm.rb +102 -68
  18. data/lib/prawn/{font → fonts}/dfont.rb +5 -11
  19. data/lib/prawn/fonts/otf.rb +11 -0
  20. data/lib/prawn/fonts/ttc.rb +36 -0
  21. data/lib/prawn/{font → fonts}/ttf.rb +87 -68
  22. data/lib/prawn/graphics.rb +119 -81
  23. data/lib/prawn/graphics/blend_mode.rb +9 -8
  24. data/lib/prawn/graphics/cap_style.rb +3 -3
  25. data/lib/prawn/graphics/color.rb +27 -25
  26. data/lib/prawn/graphics/dash.rb +23 -11
  27. data/lib/prawn/graphics/join_style.rb +9 -3
  28. data/lib/prawn/graphics/patterns.rb +190 -96
  29. data/lib/prawn/graphics/transformation.rb +15 -9
  30. data/lib/prawn/graphics/transparency.rb +17 -13
  31. data/lib/prawn/grid.rb +48 -47
  32. data/lib/prawn/image_handler.rb +5 -5
  33. data/lib/prawn/images.rb +39 -30
  34. data/lib/prawn/images/image.rb +2 -1
  35. data/lib/prawn/images/jpg.rb +28 -22
  36. data/lib/prawn/images/png.rb +65 -62
  37. data/lib/prawn/measurement_extensions.rb +10 -9
  38. data/lib/prawn/measurements.rb +19 -15
  39. data/lib/prawn/outline.rb +97 -77
  40. data/lib/prawn/repeater.rb +14 -10
  41. data/lib/prawn/security.rb +81 -61
  42. data/lib/prawn/security/arcfour.rb +2 -2
  43. data/lib/prawn/soft_mask.rb +26 -26
  44. data/lib/prawn/stamp.rb +20 -13
  45. data/lib/prawn/text.rb +68 -52
  46. data/lib/prawn/text/box.rb +11 -8
  47. data/lib/prawn/text/formatted.rb +5 -5
  48. data/lib/prawn/text/formatted/arranger.rb +53 -32
  49. data/lib/prawn/text/formatted/box.rb +134 -100
  50. data/lib/prawn/text/formatted/fragment.rb +11 -14
  51. data/lib/prawn/text/formatted/line_wrap.rb +122 -63
  52. data/lib/prawn/text/formatted/parser.rb +139 -117
  53. data/lib/prawn/text/formatted/wrap.rb +43 -31
  54. data/lib/prawn/transformation_stack.rb +7 -5
  55. data/lib/prawn/utilities.rb +7 -22
  56. data/lib/prawn/version.rb +2 -2
  57. data/lib/prawn/view.rb +17 -7
  58. data/manual/basic_concepts/adding_pages.rb +6 -7
  59. data/manual/basic_concepts/basic_concepts.rb +31 -22
  60. data/manual/basic_concepts/creation.rb +10 -11
  61. data/manual/basic_concepts/cursor.rb +4 -5
  62. data/manual/basic_concepts/measurement.rb +6 -7
  63. data/manual/basic_concepts/origin.rb +5 -6
  64. data/manual/basic_concepts/other_cursor_helpers.rb +11 -12
  65. data/manual/basic_concepts/view.rb +22 -16
  66. data/manual/bounding_box/bounding_box.rb +29 -24
  67. data/manual/bounding_box/bounds.rb +11 -12
  68. data/manual/bounding_box/canvas.rb +4 -5
  69. data/manual/bounding_box/creation.rb +6 -7
  70. data/manual/bounding_box/indentation.rb +14 -15
  71. data/manual/bounding_box/nesting.rb +24 -17
  72. data/manual/bounding_box/russian_boxes.rb +14 -13
  73. data/manual/bounding_box/stretchy.rb +12 -13
  74. data/manual/contents.rb +28 -22
  75. data/manual/cover.rb +33 -28
  76. data/manual/document_and_page_options/background.rb +11 -13
  77. data/manual/document_and_page_options/document_and_page_options.rb +25 -20
  78. data/manual/document_and_page_options/metadata.rb +18 -16
  79. data/manual/document_and_page_options/page_margins.rb +18 -20
  80. data/manual/document_and_page_options/page_size.rb +13 -12
  81. data/manual/document_and_page_options/print_scaling.rb +17 -15
  82. data/manual/example_helper.rb +5 -4
  83. data/manual/graphics/blend_mode.rb +12 -9
  84. data/manual/graphics/circle_and_ellipse.rb +4 -5
  85. data/manual/graphics/color.rb +7 -9
  86. data/manual/graphics/common_lines.rb +7 -8
  87. data/manual/graphics/fill_and_stroke.rb +4 -5
  88. data/manual/graphics/fill_rules.rb +9 -10
  89. data/manual/graphics/gradients.rb +27 -21
  90. data/manual/graphics/graphics.rb +48 -40
  91. data/manual/graphics/helper.rb +12 -9
  92. data/manual/graphics/line_width.rb +8 -7
  93. data/manual/graphics/lines_and_curves.rb +7 -8
  94. data/manual/graphics/polygon.rb +6 -8
  95. data/manual/graphics/rectangle.rb +4 -5
  96. data/manual/graphics/rotate.rb +6 -7
  97. data/manual/graphics/scale.rb +14 -15
  98. data/manual/graphics/soft_masks.rb +3 -4
  99. data/manual/graphics/stroke_cap.rb +6 -7
  100. data/manual/graphics/stroke_dash.rb +11 -12
  101. data/manual/graphics/stroke_join.rb +5 -6
  102. data/manual/graphics/translate.rb +9 -10
  103. data/manual/graphics/transparency.rb +7 -8
  104. data/manual/how_to_read_this_manual.rb +6 -6
  105. data/manual/images/absolute_position.rb +6 -7
  106. data/manual/images/fit.rb +7 -8
  107. data/manual/images/horizontal.rb +9 -10
  108. data/manual/images/images.rb +28 -24
  109. data/manual/images/plain_image.rb +5 -6
  110. data/manual/images/scale.rb +9 -10
  111. data/manual/images/vertical.rb +13 -14
  112. data/manual/images/width_and_height.rb +10 -11
  113. data/manual/layout/boxes.rb +5 -6
  114. data/manual/layout/content.rb +7 -8
  115. data/manual/layout/layout.rb +18 -16
  116. data/manual/layout/simple_grid.rb +6 -7
  117. data/manual/outline/add_subsection_to.rb +20 -21
  118. data/manual/outline/insert_section_after.rb +15 -16
  119. data/manual/outline/outline.rb +21 -17
  120. data/manual/outline/sections_and_pages.rb +17 -18
  121. data/manual/repeatable_content/alternate_page_numbering.rb +21 -17
  122. data/manual/repeatable_content/page_numbering.rb +17 -16
  123. data/manual/repeatable_content/repeatable_content.rb +25 -19
  124. data/manual/repeatable_content/repeater.rb +14 -15
  125. data/manual/repeatable_content/stamp.rb +14 -15
  126. data/manual/security/encryption.rb +9 -10
  127. data/manual/security/permissions.rb +19 -14
  128. data/manual/security/security.rb +19 -16
  129. data/manual/table.rb +3 -3
  130. data/manual/text/alignment.rb +16 -17
  131. data/manual/text/color.rb +12 -11
  132. data/manual/text/column_box.rb +9 -10
  133. data/manual/text/fallback_fonts.rb +25 -21
  134. data/manual/text/font.rb +11 -12
  135. data/manual/text/font_size.rb +13 -14
  136. data/manual/text/font_style.rb +7 -8
  137. data/manual/text/formatted_callbacks.rb +25 -21
  138. data/manual/text/formatted_text.rb +33 -25
  139. data/manual/text/free_flowing_text.rb +20 -21
  140. data/manual/text/inline.rb +18 -19
  141. data/manual/text/kerning_and_character_spacing.rb +14 -15
  142. data/manual/text/leading.rb +7 -8
  143. data/manual/text/line_wrapping.rb +37 -18
  144. data/manual/text/paragraph_indentation.rb +13 -14
  145. data/manual/text/positioned_text.rb +15 -16
  146. data/manual/text/registering_families.rb +20 -21
  147. data/manual/text/rendering_and_color.rb +9 -10
  148. data/manual/text/right_to_left_text.rb +26 -19
  149. data/manual/text/rotation.rb +28 -23
  150. data/manual/text/single_usage.rb +8 -9
  151. data/manual/text/text.rb +57 -52
  152. data/manual/text/text_box_excess.rb +20 -17
  153. data/manual/text/text_box_extensions.rb +18 -15
  154. data/manual/text/text_box_overflow.rb +18 -19
  155. data/manual/text/utf8.rb +11 -12
  156. data/manual/text/win_ansi_charset.rb +21 -19
  157. data/prawn.gemspec +44 -31
  158. data/spec/extensions/encoding_helpers.rb +3 -3
  159. data/spec/prawn/document/bounding_box_spec.rb +546 -0
  160. data/spec/prawn/document/column_box_spec.rb +75 -0
  161. data/spec/prawn/document/security_spec.rb +176 -0
  162. data/spec/prawn/document_annotations_spec.rb +76 -0
  163. data/spec/prawn/document_destinations_spec.rb +15 -0
  164. data/spec/prawn/document_grid_spec.rb +99 -0
  165. data/spec/prawn/document_reference_spec.rb +27 -0
  166. data/spec/prawn/document_span_spec.rb +36 -0
  167. data/spec/prawn/document_spec.rb +802 -0
  168. data/spec/prawn/font_metric_cache_spec.rb +54 -0
  169. data/spec/prawn/font_spec.rb +542 -0
  170. data/spec/prawn/graphics/blend_mode_spec.rb +63 -0
  171. data/spec/prawn/graphics/transparency_spec.rb +81 -0
  172. data/spec/prawn/graphics_spec.rb +837 -0
  173. data/spec/prawn/graphics_stroke_styles_spec.rb +229 -0
  174. data/spec/{image_handler_spec.rb → prawn/image_handler_spec.rb} +14 -14
  175. data/spec/prawn/images/jpg_spec.rb +20 -0
  176. data/spec/prawn/images/png_spec.rb +283 -0
  177. data/spec/prawn/images_spec.rb +224 -0
  178. data/spec/prawn/measurements_extensions_spec.rb +24 -0
  179. data/spec/prawn/outline_spec.rb +412 -0
  180. data/spec/prawn/repeater_spec.rb +165 -0
  181. data/spec/prawn/soft_mask_spec.rb +74 -0
  182. data/spec/prawn/stamp_spec.rb +172 -0
  183. data/spec/prawn/text/box_spec.rb +1112 -0
  184. data/spec/prawn/text/formatted/arranger_spec.rb +466 -0
  185. data/spec/prawn/text/formatted/box_spec.rb +846 -0
  186. data/spec/prawn/text/formatted/fragment_spec.rb +343 -0
  187. data/spec/prawn/text/formatted/line_wrap_spec.rb +494 -0
  188. data/spec/prawn/text/formatted/parser_spec.rb +697 -0
  189. data/spec/prawn/text_draw_text_spec.rb +149 -0
  190. data/spec/prawn/text_rendering_mode_spec.rb +48 -0
  191. data/spec/prawn/text_spacing_spec.rb +95 -0
  192. data/spec/prawn/text_spec.rb +603 -0
  193. data/spec/prawn/text_with_inline_formatting_spec.rb +35 -0
  194. data/spec/{transformation_stack_spec.rb → prawn/transformation_stack_spec.rb} +22 -19
  195. data/spec/prawn/view_spec.rb +63 -0
  196. data/spec/prawn_manual_spec.rb +35 -0
  197. data/spec/spec_helper.rb +18 -19
  198. metadata +144 -180
  199. metadata.gz.sig +4 -0
  200. data/data/images/16bit.alpha +0 -0
  201. data/data/images/16bit.color +0 -0
  202. data/data/images/16bit.png +0 -0
  203. data/data/images/arrow.png +0 -0
  204. data/data/images/arrow2.png +0 -0
  205. data/data/images/blend_modes_bottom_layer.jpg +0 -0
  206. data/data/images/blend_modes_top_layer.jpg +0 -0
  207. data/data/images/dice.alpha +0 -0
  208. data/data/images/dice.color +0 -0
  209. data/data/images/dice.png +0 -0
  210. data/data/images/dice_interlaced.png +0 -0
  211. data/data/images/fractal.jpg +0 -0
  212. data/data/images/indexed_color.dat +0 -0
  213. data/data/images/indexed_color.png +0 -0
  214. data/data/images/indexed_transparency.png +0 -0
  215. data/data/images/indexed_transparency_alpha.dat +0 -0
  216. data/data/images/indexed_transparency_color.dat +0 -0
  217. data/data/images/letterhead.jpg +0 -0
  218. data/data/images/license.md +0 -8
  219. data/data/images/page_white_text.alpha +0 -0
  220. data/data/images/page_white_text.color +0 -0
  221. data/data/images/page_white_text.png +0 -0
  222. data/data/images/pigs.jpg +0 -0
  223. data/data/images/prawn.png +0 -0
  224. data/data/images/ruport.png +0 -0
  225. data/data/images/ruport_data.dat +0 -0
  226. data/data/images/ruport_transparent.png +0 -0
  227. data/data/images/ruport_type0.png +0 -0
  228. data/data/images/stef.jpg +0 -0
  229. data/data/images/tru256.bmp +0 -0
  230. data/data/images/web-links.dat +0 -1
  231. data/data/images/web-links.png +0 -0
  232. data/data/pdfs/complex_template.pdf +0 -0
  233. data/data/pdfs/contains_ttf_font.pdf +0 -0
  234. data/data/pdfs/encrypted.pdf +0 -0
  235. data/data/pdfs/form.pdf +1 -819
  236. data/data/pdfs/hexagon.pdf +0 -61
  237. data/data/pdfs/indirect_reference.pdf +0 -86
  238. data/data/pdfs/multipage_template.pdf +0 -127
  239. data/data/pdfs/nested_pages.pdf +0 -118
  240. data/data/pdfs/page_without_mediabox.pdf +0 -193
  241. data/data/pdfs/resources_as_indirect_object.pdf +0 -83
  242. data/data/pdfs/two_hexagons.pdf +0 -90
  243. data/data/pdfs/version_1_6.pdf +0 -61
  244. data/data/shift_jis_text.txt +0 -1
  245. data/spec/acceptance/png_spec.rb +0 -35
  246. data/spec/annotations_spec.rb +0 -67
  247. data/spec/blend_mode_spec.rb +0 -71
  248. data/spec/bounding_box_spec.rb +0 -501
  249. data/spec/column_box_spec.rb +0 -59
  250. data/spec/destinations_spec.rb +0 -13
  251. data/spec/document_spec.rb +0 -738
  252. data/spec/font_metric_cache_spec.rb +0 -52
  253. data/spec/font_spec.rb +0 -475
  254. data/spec/formatted_text_arranger_spec.rb +0 -452
  255. data/spec/formatted_text_box_spec.rb +0 -716
  256. data/spec/formatted_text_fragment_spec.rb +0 -299
  257. data/spec/graphics_spec.rb +0 -705
  258. data/spec/grid_spec.rb +0 -95
  259. data/spec/images_spec.rb +0 -167
  260. data/spec/inline_formatted_text_parser_spec.rb +0 -568
  261. data/spec/jpg_spec.rb +0 -23
  262. data/spec/line_wrap_spec.rb +0 -366
  263. data/spec/measurement_units_spec.rb +0 -22
  264. data/spec/outline_spec.rb +0 -409
  265. data/spec/png_spec.rb +0 -257
  266. data/spec/reference_spec.rb +0 -25
  267. data/spec/repeater_spec.rb +0 -154
  268. data/spec/security_spec.rb +0 -151
  269. data/spec/soft_mask_spec.rb +0 -78
  270. data/spec/span_spec.rb +0 -43
  271. data/spec/stamp_spec.rb +0 -179
  272. data/spec/stroke_styles_spec.rb +0 -208
  273. data/spec/text_at_spec.rb +0 -142
  274. data/spec/text_box_spec.rb +0 -1042
  275. data/spec/text_rendering_mode_spec.rb +0 -45
  276. data/spec/text_spacing_spec.rb +0 -93
  277. data/spec/text_spec.rb +0 -543
  278. data/spec/text_with_inline_formatting_spec.rb +0 -35
  279. data/spec/transparency_spec.rb +0 -91
  280. data/spec/view_spec.rb +0 -42
@@ -1,5 +1,5 @@
1
- # encoding: utf-8
2
- #
1
+ # frozen_string_literal: true
2
+
3
3
  # blend_mode.rb : Implements blend modes
4
4
  #
5
5
  # Contributed by John Ford. October, 2015
@@ -15,9 +15,10 @@ module Prawn
15
15
  # Passing an array of blend modes is allowed. PDF viewers should
16
16
  # blend layers based on the first recognized blend mode.
17
17
  #
18
- # Valid blend modes in v1.4 of the PDF spec include :Normal, :Multiply, :Screen,
19
- # :Overlay, :Darken, :Lighten, :ColorDodge, :ColorBurn, :HardLight, :SoftLight,
20
- # :Difference, :Exclusion, :Hue, :Saturation, :Color, and :Luminosity.
18
+ # Valid blend modes in v1.4 of the PDF spec include :Normal, :Multiply,
19
+ # :Screen, :Overlay, :Darken, :Lighten, :ColorDodge, :ColorBurn, :HardLight,
20
+ # :SoftLight, :Difference, :Exclusion, :Hue, :Saturation, :Color, and
21
+ # :Luminosity.
21
22
  #
22
23
  # Example:
23
24
  # pdf.fill_color('0000ff')
@@ -52,11 +53,11 @@ module Prawn
52
53
  dictionary_name = "BM#{key}"
53
54
 
54
55
  dictionary = blend_mode_dictionary_registry[dictionary_name] ||= ref!(
55
- :Type => :ExtGState,
56
- :BM => blend_mode
56
+ Type: :ExtGState,
57
+ BM: blend_mode
57
58
  )
58
59
 
59
- page.ext_gstates.merge!(dictionary_name => dictionary)
60
+ page.ext_gstates[dictionary_name] = dictionary
60
61
  dictionary_name
61
62
  end
62
63
  end
@@ -1,4 +1,4 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  # cap_style.rb : Implements stroke cap styling
4
4
  #
@@ -11,7 +11,7 @@ module Prawn
11
11
  module CapStyle
12
12
  # @group Stable API
13
13
 
14
- CAP_STYLES = { :butt => 0, :round => 1, :projecting_square => 2 }
14
+ CAP_STYLES = { butt: 0, round: 1, projecting_square: 2 }.freeze
15
15
 
16
16
  # Sets the cap style for stroked lines and curves
17
17
  #
@@ -27,7 +27,7 @@ module Prawn
27
27
  write_stroke_cap_style
28
28
  end
29
29
 
30
- alias_method :cap_style=, :cap_style
30
+ alias cap_style= cap_style
31
31
 
32
32
  private
33
33
 
@@ -1,4 +1,4 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  # color.rb : Implements color handling
4
4
  #
@@ -27,11 +27,12 @@ module Prawn
27
27
  #
28
28
  def fill_color(*color)
29
29
  return current_fill_color if color.empty?
30
+
30
31
  self.current_fill_color = process_color(*color)
31
32
  set_fill_color
32
33
  end
33
34
 
34
- alias_method :fill_color=, :fill_color
35
+ alias fill_color= fill_color
35
36
 
36
37
  # Sets or returns the line stroking color.
37
38
  #
@@ -49,12 +50,13 @@ module Prawn
49
50
  #
50
51
  def stroke_color(*color)
51
52
  return current_stroke_color if color.empty?
53
+
52
54
  color = process_color(*color)
53
55
  self.current_stroke_color = color
54
56
  set_stroke_color(color)
55
57
  end
56
58
 
57
- alias_method :stroke_color=, :stroke_color
59
+ alias stroke_color= stroke_color
58
60
 
59
61
  module_function
60
62
 
@@ -65,7 +67,7 @@ module Prawn
65
67
  # => "ff7808"
66
68
  #
67
69
  def rgb2hex(rgb)
68
- rgb.map { |e| "%02x" % e }.join
70
+ rgb.map { |e| format '%<value>02x', value: e }.join
69
71
  end
70
72
 
71
73
  # Converts hex string into RGB value array:
@@ -74,27 +76,33 @@ module Prawn
74
76
  # => [255, 120, 8]
75
77
  #
76
78
  def hex2rgb(hex)
77
- r, g, b = hex[0..1], hex[2..3], hex[4..5]
79
+ r = hex[0..1]
80
+ g = hex[2..3]
81
+ b = hex[4..5]
78
82
  [r, g, b].map { |e| e.to_i(16) }
79
83
  end
80
84
 
81
85
  private
82
86
 
83
87
  def process_color(*color)
84
- case(color.size)
88
+ case color.size
85
89
  when 1
86
90
  color[0]
87
91
  when 4
88
92
  color
89
93
  else
90
- fail ArgumentError, 'wrong number of arguments supplied'
94
+ raise ArgumentError, 'wrong number of arguments supplied'
91
95
  end
92
96
  end
93
97
 
94
98
  def color_type(color)
95
99
  case color
96
100
  when String
97
- :RGB
101
+ if /\A\h{6}\z/.match?(color)
102
+ :RGB
103
+ else
104
+ raise ArgumentError, "Unknown type of color: #{color.inspect}"
105
+ end
98
106
  when Array
99
107
  case color.length
100
108
  when 3
@@ -102,7 +110,7 @@ module Prawn
102
110
  when 4
103
111
  :CMYK
104
112
  else
105
- fail ArgumentError, "Unknown type of color: #{color.inspect}"
113
+ raise ArgumentError, "Unknown type of color: #{color.inspect}"
106
114
  end
107
115
  end
108
116
  end
@@ -119,7 +127,7 @@ module Prawn
119
127
  end
120
128
 
121
129
  def color_to_s(color)
122
- normalize_color(color).map { |c| '%.3f' % c }.join(' ')
130
+ PDF::Core.real_params normalize_color(color)
123
131
  end
124
132
 
125
133
  def color_space(color)
@@ -131,15 +139,19 @@ module Prawn
131
139
  end
132
140
  end
133
141
 
134
- COLOR_SPACES = [:DeviceRGB, :DeviceCMYK, :Pattern]
142
+ COLOR_SPACES = %i[DeviceRGB DeviceCMYK Pattern].freeze
135
143
 
136
144
  def set_color_space(type, color_space)
137
145
  # don't set the same color space again
138
- return if current_color_space(type) == color_space && !state.page.in_stamp_stream?
146
+ if current_color_space(type) == color_space &&
147
+ !state.page.in_stamp_stream?
148
+ return
149
+ end
150
+
139
151
  set_current_color_space(color_space, type)
140
152
 
141
153
  unless COLOR_SPACES.include?(color_space)
142
- fail ArgumentError, "unknown color space: '#{color_space}'"
154
+ raise ArgumentError, "unknown color space: '#{color_space}'"
143
155
  end
144
156
 
145
157
  operator = case type
@@ -148,7 +160,7 @@ module Prawn
148
160
  when :stroke
149
161
  'CS'
150
162
  else
151
- fail ArgumentError, "unknown type '#{type}'"
163
+ raise ArgumentError, "unknown type '#{type}'"
152
164
  end
153
165
 
154
166
  renderer.add_content "/#{color_space} #{operator}"
@@ -161,7 +173,7 @@ module Prawn
161
173
  when :stroke
162
174
  'SCN'
163
175
  else
164
- fail ArgumentError, "unknown type '#{type}'"
176
+ raise ArgumentError, "unknown type '#{type}'"
165
177
  end
166
178
 
167
179
  if options[:pattern]
@@ -187,8 +199,6 @@ module Prawn
187
199
  set_stroke_color
188
200
  end
189
201
 
190
- private
191
-
192
202
  def current_color_space(type)
193
203
  graphic_state.color_space[type]
194
204
  end
@@ -214,14 +224,6 @@ module Prawn
214
224
  graphic_state.stroke_color = color
215
225
  end
216
226
 
217
- def write_fill_color
218
- write_color(current_fill_color, 'scn')
219
- end
220
-
221
- def write_stroke_color
222
- write_color(current_fill_color, 'SCN')
223
- end
224
-
225
227
  def write_color(color, operator)
226
228
  renderer.add_content "#{color} #{operator}"
227
229
  end
@@ -1,4 +1,4 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  # dash.rb : Implements stroke dashing
4
4
  #
@@ -28,8 +28,9 @@ module Prawn
28
28
  # 3 on, 2 off, 3 on, 2 off, ...
29
29
  #
30
30
  # * If the parameter +length+ is an array, it specifies the
31
- # lengths of alternating dashes and gaps. The :space option is
32
- # ignored in this case.
31
+ # lengths of alternating dashes and gaps. The numbers must be
32
+ # non-negative and not all zero. The :space option is ignored
33
+ # in this case.
33
34
  #
34
35
  # Examples:
35
36
  #
@@ -37,6 +38,8 @@ module Prawn
37
38
  # 2 on, 1 off, 2 on, 1 off, ...
38
39
  # length = [3, 1, 2, 3]
39
40
  # 3 on, 1 off, 2 on, 3 off, 3 on, 1 off, ...
41
+ # length = [3, 0, 1]
42
+ # 3 on, 0 off, 1 on, 3 off, 0 on, 1 off, ...
40
43
  #
41
44
  # Options may contain the keys :space and :phase
42
45
  #
@@ -55,19 +58,28 @@ module Prawn
55
58
  def dash(length = nil, options = {})
56
59
  return current_dash_state if length.nil?
57
60
 
58
- if length == 0 || length.kind_of?(Array) && length.any? { |e| e == 0 }
59
- fail ArgumentError,
60
- "Zero length dashes are invalid. Call #undash to disable dashes."
61
+ length = Array(length)
62
+
63
+ if length.all?(&:zero?)
64
+ raise ArgumentError,
65
+ 'Zero length dashes are invalid. Call #undash to disable dashes.'
66
+ elsif length.any?(&:negative?)
67
+ raise ArgumentError,
68
+ 'Negative numbers are not allowed for dash lengths.'
61
69
  end
62
70
 
63
- self.current_dash_state = { :dash => length,
64
- :space => length.kind_of?(Array) ? nil : options[:space] || length,
65
- :phase => options[:phase] || 0 }
71
+ length = length.first if length.length == 1
72
+
73
+ self.current_dash_state = {
74
+ dash: length,
75
+ space: length.is_a?(Array) ? nil : options[:space] || length,
76
+ phase: options[:phase] || 0
77
+ }
66
78
 
67
79
  write_stroke_dash
68
80
  end
69
81
 
70
- alias_method :dash=, :dash
82
+ alias dash= dash
71
83
 
72
84
  # Stops dashing, restoring solid stroked lines and curves
73
85
  #
@@ -89,7 +101,7 @@ module Prawn
89
101
  end
90
102
 
91
103
  def undashed_setting
92
- { :dash => nil, :space => nil, :phase => 0 }
104
+ { dash: nil, space: nil, phase: 0 }
93
105
  end
94
106
 
95
107
  def current_dash_state=(dash_options)
@@ -1,4 +1,4 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  # join_style.rb : Implements stroke join styling
4
4
  #
@@ -9,7 +9,7 @@
9
9
  module Prawn
10
10
  module Graphics
11
11
  module JoinStyle
12
- JOIN_STYLES = { :miter => 0, :round => 1, :bevel => 2 }
12
+ JOIN_STYLES = { miter: 0, round: 1, bevel: 2 }.freeze
13
13
 
14
14
  # @group Stable API
15
15
 
@@ -25,10 +25,16 @@ module Prawn
25
25
 
26
26
  self.current_join_style = style
27
27
 
28
+ unless JOIN_STYLES.key?(current_join_style)
29
+ raise Prawn::Errors::InvalidJoinStyle,
30
+ "#{style} is not a recognized join style. Valid styles are " +
31
+ JOIN_STYLES.keys.join(', ')
32
+ end
33
+
28
34
  write_stroke_join_style
29
35
  end
30
36
 
31
- alias_method :join_style=, :join_style
37
+ alias join_style= join_style
32
38
 
33
39
  private
34
40
 
@@ -1,4 +1,6 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
+
3
+ require 'digest/sha1'
2
4
 
3
5
  # patterns.rb : Implements axial & radial gradients
4
6
  #
@@ -10,52 +12,83 @@
10
12
  module Prawn
11
13
  module Graphics
12
14
  module Patterns
15
+ GradientStop = Struct.new(:position, :color)
16
+ Gradient = Struct.new(
17
+ :type, :apply_transformations, :stops, :from, :to, :r1, :r2
18
+ )
19
+
13
20
  # @group Stable API
14
21
 
15
- # Sets the fill gradient from color1 to color2.
16
- # old arguments: point, width, height, color1, color2, options = {}
17
- # new arguments: from, to, color1, color1, options = {}
18
- # or from, r1, to, r2, color1, color2, options = {}
22
+ # Sets the fill gradient.
23
+ # old arguments:
24
+ # from, to, color1, color2
25
+ # or
26
+ # from, r1, to, r2, color1, color2
27
+ # new arguments:
28
+ # from: [x, y]
29
+ # to: [x, y]
30
+ # r1: radius
31
+ # r2: radius
32
+ # stops: [color, color, ...] or
33
+ # { position => color, position => color, ... }
34
+ # apply_transformations: true
35
+ #
36
+ # Examples:
37
+ #
38
+ # # draws a horizontal axial gradient that starts at red on the left
39
+ # # and ends at blue on the right
40
+ # fill_gradient from: [0, 0], to: [100, 0], stops: ['red', 'blue']
41
+ #
42
+ # # draws a horizontal radial gradient that starts at red, is green
43
+ # # 80% of the way through, and finishes blue
44
+ # fill_gradient from: [0, 0], r1: 0, to: [100, 0], r2: 180,
45
+ # stops: { 0 => 'red', 0.8 => 'green', 1 => 'blue' }
19
46
  #
20
- # Option :apply_transformations, if set true, will transform the
47
+ # <tt>from</tt> and <tt>to</tt> specify the axis of where the gradient
48
+ # should be painted.
49
+ #
50
+ # <tt>r1</tt> and <tt>r2</tt>, if specified, make a radial gradient with
51
+ # the starting circle of radius <tt>r1</tt> centered at <tt>from</tt>
52
+ # and ending at a circle of radius <tt>r2</tt> centered at <tt>to</tt>.
53
+ # If <tt>r1</tt> is not specified, a axial gradient will be drawn.
54
+ #
55
+ # <tt>stops</tt> is an array or hash of stops. Each stop is either just a
56
+ # string indicating the color, in which case the stops will be evenly
57
+ # distributed across the gradient, or a hash where the key is
58
+ # a position between 0 and 1 indicating what distance through the
59
+ # gradient the color should change, and the value is a color string.
60
+ #
61
+ # Option <tt>apply_transformations</tt>, if set true, will transform the
21
62
  # gradient's co-ordinate space so it matches the current co-ordinate
22
- # space of the document. This option will be the default from Prawn v3.
23
- # The current default, false, will mean if you (for example) scale your
24
- # document by 2 and put a gradient inside, you will have to manually
25
- # multiply your co-ordinates by 2 so the gradient is correctly positioned.
26
- def fill_gradient(*args)
27
- set_gradient(:fill, *args)
63
+ # space of the document. This option will be the default from Prawn v3,
64
+ # and is default true if you use the new arguments format.
65
+ # The default for the old arguments format, false, will mean if you
66
+ # (for example) scale your document by 2 and put a gradient inside, you
67
+ # will have to manually multiply your co-ordinates by 2 so the gradient
68
+ # is correctly positioned.
69
+ def fill_gradient(*args, **kwargs)
70
+ set_gradient(:fill, *args, **kwargs)
28
71
  end
29
72
 
30
- # Sets the stroke gradient from color1 to color2.
31
- # old arguments: point, width, height, color1, color2, options = {}
32
- # new arguments: from, to, color1, color2, options = {}
33
- # or from, r1, to, r2, color1, color2, options = {}
34
- #
35
- # Option :apply_transformations, if set true, will transform the
36
- # gradient's co-ordinate space so it matches the current co-ordinate
37
- # space of the document. This option will be the default from Prawn v3.
38
- # The current default, false, will mean if you (for example) scale your
39
- # document by 2 and put a gradient inside, you will have to manually
40
- # multiply your co-ordinates by 2 so the gradient is correctly positioned.
41
- def stroke_gradient(*args)
42
- set_gradient(:stroke, *args)
73
+ # Sets the stroke gradient.
74
+ # See fill_gradient for a description of the arguments to this method.
75
+ def stroke_gradient(*args, **kwargs)
76
+ set_gradient(:stroke, *args, **kwargs)
43
77
  end
44
78
 
45
79
  private
46
80
 
47
- def set_gradient(type, *grad)
48
- opts = grad.last.is_a?(Hash) ? grad.pop : {}
81
+ def set_gradient(type, *grad, **kwargs)
82
+ gradient = parse_gradient_arguments(*grad, **kwargs)
49
83
 
50
84
  patterns = page.resources[:Pattern] ||= {}
51
85
 
52
- registry_key = gradient_registry_key grad, opts
86
+ registry_key = gradient_registry_key gradient
53
87
 
54
- if patterns["SP#{registry_key}"]
55
- shading = patterns["SP#{registry_key}"]
56
- else
57
- unless shading = gradient_registry[registry_key]
58
- shading = gradient(grad, opts)
88
+ unless patterns.key? "SP#{registry_key}"
89
+ shading = gradient_registry[registry_key]
90
+ unless shading
91
+ shading = create_gradient_pattern(gradient)
59
92
  gradient_registry[registry_key] = shading
60
93
  end
61
94
 
@@ -68,96 +101,157 @@ module Prawn
68
101
  when :stroke
69
102
  'SCN'
70
103
  else
71
- fail ArgumentError, "unknown type '#{type}'"
104
+ raise ArgumentError, "unknown type '#{type}'"
72
105
  end
73
106
 
74
107
  set_color_space type, :Pattern
75
108
  renderer.add_content "/SP#{registry_key} #{operator}"
76
109
  end
77
110
 
78
- def gradient_registry_key(gradient, opts)
79
- _x1, _y1, x2, y2, transformation = gradient_coordinates(gradient, opts)
80
-
81
- if gradient[1].is_a?(Array) # axial
82
- [
83
- transformation,
84
- x2, y2,
85
- gradient[2], gradient[3]
86
- ]
87
- else # radial
88
- [
89
- transformation,
90
- x2, y2,
91
- gradient[1],
92
- gradient[3],
93
- gradient[4], gradient[5]
94
- ]
95
- end.hash
96
- end
97
-
98
- def gradient_registry
99
- @gradient_registry ||= {}
100
- end
101
-
102
- def gradient(args, opts)
103
- if args.length != 4 && args.length != 6
104
- fail ArgumentError, "Unknown type of gradient: #{args.inspect}"
111
+ # rubocop: disable Metrics/ParameterLists
112
+ def parse_gradient_arguments(
113
+ *arguments, from: nil, to: nil, r1: nil, r2: nil, stops: nil,
114
+ apply_transformations: nil
115
+ )
116
+ case arguments.length
117
+ when 0
118
+ apply_transformations = true if apply_transformations.nil?
119
+ when 4
120
+ from, to, *stops = arguments
121
+ when 6
122
+ from, r1, to, r2, *stops = arguments
123
+ else
124
+ raise ArgumentError, "Unknown type of gradient: #{arguments.inspect}"
105
125
  end
106
126
 
107
- if opts[:apply_transformations].nil? && current_transformation_matrix_with_translation(0, 0) != [1, 0, 0, 1, 0, 0]
108
- warn "Gradients in Prawn 2.x and lower are not correctly positioned when a transformation has been made to the document. Pass 'apply_transformations: true' to correctly transform the gradient, or see https://github.com/prawnpdf/prawn/wiki/Gradient-Transformations for more information."
127
+ if stops.length < 2
128
+ raise ArgumentError, 'At least two stops must be specified'
109
129
  end
110
130
 
111
- color1 = normalize_color(args[-2]).dup.freeze
112
- color2 = normalize_color(args[-1]).dup.freeze
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
138
+ end
113
139
 
114
- if color_type(color1) != color_type(color2)
115
- fail ArgumentError, "Both colors must be of the same color space: #{color1.inspect} and #{color2.inspect}"
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))
116
145
  end
117
146
 
118
- process_color color1
119
- process_color color2
147
+ if stops.first.position != 0
148
+ raise ArgumentError, 'The first stop must have a position of 0'
149
+ end
150
+ if stops.last.position != 1
151
+ raise ArgumentError, 'The last stop must have a position of 1'
152
+ end
153
+
154
+ if stops.map { |stop| color_type(stop.color) }.uniq.length > 1
155
+ raise ArgumentError, 'All colors must be of the same color space'
156
+ end
120
157
 
121
- shader = ref!(
122
- :FunctionType => 2,
123
- :Domain => [0.0, 1.0],
124
- :C0 => color1,
125
- :C1 => color2,
126
- :N => 1.0
158
+ Gradient.new(
159
+ r1 ? :radial : :axial,
160
+ apply_transformations,
161
+ stops,
162
+ from, to,
163
+ r1, r2
127
164
  )
165
+ end
166
+ # rubocop: enable Metrics/ParameterLists
128
167
 
129
- x1, y1, x2, y2, transformation = gradient_coordinates(args, opts)
168
+ def gradient_registry_key(gradient)
169
+ _x1, _y1, x2, y2, transformation = gradient_coordinates(gradient)
130
170
 
131
- if args.length == 4
132
- coords = [0, 0, x2 - x1, y2 - y1]
133
- else
134
- coords = [0, 0, args[1], x2 - x1, y2 - y1, args[3]]
171
+ key = [
172
+ gradient.type.to_s,
173
+ transformation,
174
+ x2, y2,
175
+ gradient.r1 || -1, gradient.r2 || -1,
176
+ gradient.stops.length,
177
+ gradient.stops.map { |s| [s.position, s.color] }
178
+ ].flatten
179
+ Digest::SHA1.hexdigest(key.join(','))
180
+ end
181
+
182
+ def gradient_registry
183
+ @gradient_registry ||= {}
184
+ end
185
+
186
+ def create_gradient_pattern(gradient)
187
+ if gradient.apply_transformations.nil? &&
188
+ current_transformation_matrix_with_translation(0, 0) !=
189
+ [1, 0, 0, 1, 0, 0]
190
+ warn 'Gradients in Prawn 2.x and lower are not correctly positioned '\
191
+ 'when a transformation has been made to the document. ' \
192
+ "Pass 'apply_transformations: true' to correctly transform the " \
193
+ 'gradient, or see ' \
194
+ 'https://github.com/prawnpdf/prawn/wiki/Gradient-Transformations ' \
195
+ 'for more information.'
196
+ end
197
+
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
+ )
135
206
  end
136
207
 
208
+ # If there's only two stops, we can use the single shader.
209
+ # 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
221
+
222
+ x1, y1, x2, y2, transformation = gradient_coordinates(gradient)
223
+
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
229
+
137
230
  shading = ref!(
138
- :ShadingType => args.length == 4 ? 2 : 3, # axial : radial shading
139
- :ColorSpace => color_space(color1),
140
- :Coords => coords,
141
- :Function => shader,
142
- :Extend => [true, true]
231
+ ShadingType: gradient.type == :axial ? 2 : 3,
232
+ ColorSpace: color_space(gradient.stops.first.color),
233
+ Coords: coords,
234
+ Function: shader,
235
+ Extend: [true, true]
143
236
  )
144
237
 
145
238
  ref!(
146
- :PatternType => 2, # shading pattern
147
- :Shading => shading,
148
- :Matrix => transformation
239
+ PatternType: 2, # shading pattern
240
+ Shading: shading,
241
+ Matrix: transformation
149
242
  )
150
243
  end
151
244
 
152
- def gradient_coordinates(args, opts)
153
- x1, y1 = map_to_absolute(args[0])
154
- x2, y2 = map_to_absolute(args[args.length == 4 ? 1 : 2])
245
+ def gradient_coordinates(gradient)
246
+ x1, y1 = map_to_absolute(gradient.from)
247
+ x2, y2 = map_to_absolute(gradient.to)
155
248
 
156
- transformation = if opts[:apply_transformations]
157
- current_transformation_matrix_with_translation(x1, y1)
158
- else
159
- [1, 0, 0, 1, x1, y1]
160
- end
249
+ transformation =
250
+ if gradient.apply_transformations
251
+ current_transformation_matrix_with_translation(x1, y1)
252
+ else
253
+ [1, 0, 0, 1, x1, y1]
254
+ end
161
255
 
162
256
  [x1, y1, x2, y2, transformation]
163
257
  end