prawn 1.1.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 (309) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data/.yardopts +1 -0
  4. data/GPLv2 +20 -21
  5. data/Gemfile +3 -9
  6. data/Rakefile +9 -40
  7. data/lib/prawn/document/bounding_box.rb +54 -40
  8. data/lib/prawn/document/column_box.rb +8 -10
  9. data/lib/prawn/document/internals.rb +39 -146
  10. data/lib/prawn/document/span.rb +23 -17
  11. data/lib/prawn/document.rb +217 -182
  12. data/lib/prawn/encoding.rb +69 -101
  13. data/lib/prawn/errors.rb +47 -43
  14. data/lib/prawn/font.rb +124 -104
  15. data/lib/prawn/font_metric_cache.rb +23 -21
  16. data/lib/prawn/fonts/afm.rb +292 -0
  17. data/lib/prawn/{font → fonts}/dfont.rb +5 -12
  18. data/lib/prawn/fonts/otf.rb +11 -0
  19. data/lib/prawn/fonts/ttc.rb +36 -0
  20. data/lib/prawn/{font → fonts}/ttf.rb +140 -88
  21. data/lib/prawn/graphics/blend_mode.rb +65 -0
  22. data/lib/prawn/graphics/cap_style.rb +5 -5
  23. data/lib/prawn/graphics/color.rb +46 -44
  24. data/lib/prawn/graphics/dash.rb +27 -11
  25. data/lib/prawn/graphics/join_style.rb +11 -6
  26. data/lib/prawn/graphics/patterns.rb +220 -78
  27. data/lib/prawn/graphics/transformation.rb +20 -12
  28. data/lib/prawn/graphics/transparency.rb +20 -18
  29. data/lib/prawn/graphics.rb +153 -115
  30. data/lib/prawn/grid.rb +93 -50
  31. data/lib/prawn/image_handler.rb +4 -4
  32. data/lib/prawn/images/image.rb +3 -2
  33. data/lib/prawn/images/jpg.rb +31 -24
  34. data/lib/prawn/images/png.rb +101 -68
  35. data/lib/prawn/images.rb +64 -56
  36. data/lib/prawn/measurement_extensions.rb +10 -9
  37. data/lib/prawn/measurements.rb +20 -23
  38. data/lib/prawn/outline.rb +96 -75
  39. data/lib/prawn/repeater.rb +16 -16
  40. data/lib/prawn/security/arcfour.rb +2 -2
  41. data/lib/prawn/security.rb +100 -85
  42. data/lib/prawn/soft_mask.rb +37 -38
  43. data/lib/prawn/stamp.rb +28 -13
  44. data/lib/prawn/text/box.rb +24 -29
  45. data/lib/prawn/text/formatted/arranger.rb +108 -63
  46. data/lib/prawn/text/formatted/box.rb +187 -124
  47. data/lib/prawn/text/formatted/fragment.rb +24 -20
  48. data/lib/prawn/text/formatted/line_wrap.rb +133 -73
  49. data/lib/prawn/text/formatted/parser.rb +147 -127
  50. data/lib/prawn/text/formatted/wrap.rb +48 -35
  51. data/lib/prawn/text/formatted.rb +5 -5
  52. data/lib/prawn/text.rb +103 -68
  53. data/lib/prawn/transformation_stack.rb +44 -0
  54. data/lib/prawn/utilities.rb +10 -22
  55. data/lib/prawn/version.rb +5 -0
  56. data/lib/prawn/view.rb +101 -0
  57. data/lib/prawn.rb +39 -54
  58. data/manual/basic_concepts/adding_pages.rb +6 -7
  59. data/manual/basic_concepts/basic_concepts.rb +34 -25
  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 +8 -9
  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 +48 -0
  66. data/manual/bounding_box/bounding_box.rb +30 -28
  67. data/manual/bounding_box/bounds.rb +12 -13
  68. data/manual/bounding_box/canvas.rb +7 -8
  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 +25 -18
  72. data/manual/bounding_box/russian_boxes.rb +18 -18
  73. data/manual/bounding_box/stretchy.rb +12 -14
  74. data/manual/contents.rb +28 -22
  75. data/manual/cover.rb +33 -32
  76. data/manual/document_and_page_options/background.rb +15 -13
  77. data/manual/document_and_page_options/document_and_page_options.rb +24 -22
  78. data/manual/document_and_page_options/metadata.rb +20 -18
  79. data/manual/document_and_page_options/page_margins.rb +18 -20
  80. data/manual/document_and_page_options/page_size.rb +13 -13
  81. data/manual/document_and_page_options/print_scaling.rb +18 -15
  82. data/manual/example_helper.rb +5 -4
  83. data/manual/graphics/blend_mode.rb +52 -0
  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 +5 -6
  88. data/manual/graphics/fill_rules.rb +12 -11
  89. data/manual/graphics/gradients.rb +27 -21
  90. data/manual/graphics/graphics.rb +46 -40
  91. data/manual/graphics/helper.rb +19 -9
  92. data/manual/graphics/line_width.rb +8 -7
  93. data/manual/graphics/lines_and_curves.rb +8 -9
  94. data/manual/graphics/polygon.rb +6 -8
  95. data/manual/graphics/rectangle.rb +4 -5
  96. data/manual/graphics/rotate.rb +6 -9
  97. data/manual/graphics/scale.rb +14 -13
  98. data/manual/graphics/soft_masks.rb +4 -6
  99. data/manual/graphics/stroke_cap.rb +7 -8
  100. data/manual/graphics/stroke_dash.rb +15 -16
  101. data/manual/graphics/stroke_join.rb +6 -7
  102. data/manual/graphics/translate.rb +10 -10
  103. data/manual/graphics/transparency.rb +7 -9
  104. data/manual/how_to_read_this_manual.rb +8 -9
  105. data/manual/images/absolute_position.rb +6 -7
  106. data/manual/images/fit.rb +7 -8
  107. data/manual/images/horizontal.rb +10 -11
  108. data/manual/images/images.rb +28 -27
  109. data/manual/images/plain_image.rb +5 -6
  110. data/manual/images/scale.rb +9 -10
  111. data/manual/images/vertical.rb +16 -14
  112. data/manual/images/width_and_height.rb +10 -11
  113. data/manual/layout/boxes.rb +10 -11
  114. data/manual/layout/content.rb +9 -10
  115. data/manual/layout/layout.rb +17 -18
  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 +22 -21
  120. data/manual/outline/sections_and_pages.rb +17 -18
  121. data/manual/repeatable_content/alternate_page_numbering.rb +36 -0
  122. data/manual/repeatable_content/page_numbering.rb +17 -16
  123. data/manual/repeatable_content/repeatable_content.rb +27 -23
  124. data/manual/repeatable_content/repeater.rb +15 -16
  125. data/manual/repeatable_content/stamp.rb +14 -15
  126. data/manual/security/encryption.rb +8 -11
  127. data/manual/security/permissions.rb +20 -15
  128. data/manual/security/security.rb +18 -18
  129. data/manual/table.rb +16 -0
  130. data/manual/text/alignment.rb +16 -17
  131. data/manual/text/color.rb +12 -12
  132. data/manual/text/column_box.rb +9 -11
  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 +10 -8
  137. data/manual/text/formatted_callbacks.rb +33 -23
  138. data/manual/text/formatted_text.rb +36 -25
  139. data/manual/text/free_flowing_text.rb +22 -23
  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 +15 -10
  145. data/manual/text/positioned_text.rb +16 -17
  146. data/manual/text/registering_families.rb +27 -24
  147. data/manual/text/rendering_and_color.rb +9 -10
  148. data/manual/text/right_to_left_text.rb +30 -19
  149. data/manual/text/rotation.rb +33 -24
  150. data/manual/text/single_usage.rb +8 -9
  151. data/manual/text/text.rb +56 -54
  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 +24 -17
  155. data/manual/text/utf8.rb +12 -13
  156. data/manual/text/win_ansi_charset.rb +28 -25
  157. data/prawn.gemspec +45 -50
  158. data/spec/extensions/encoding_helpers.rb +3 -3
  159. data/spec/prawn/document/bounding_box_spec.rb +550 -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 +44 -0
  167. data/spec/prawn/document_spec.rb +805 -0
  168. data/spec/prawn/font_metric_cache_spec.rb +54 -0
  169. data/spec/prawn/font_spec.rb +544 -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 +872 -0
  173. data/spec/prawn/graphics_stroke_styles_spec.rb +229 -0
  174. data/spec/prawn/image_handler_spec.rb +53 -0
  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 +229 -0
  178. data/spec/prawn/measurements_extensions_spec.rb +24 -0
  179. data/spec/prawn/outline_spec.rb +512 -0
  180. data/spec/prawn/repeater_spec.rb +166 -0
  181. data/spec/prawn/soft_mask_spec.rb +74 -0
  182. data/spec/prawn/stamp_spec.rb +173 -0
  183. data/spec/prawn/text/box_spec.rb +1110 -0
  184. data/spec/prawn/text/formatted/arranger_spec.rb +466 -0
  185. data/spec/prawn/text/formatted/box_spec.rb +849 -0
  186. data/spec/prawn/text/formatted/fragment_spec.rb +343 -0
  187. data/spec/prawn/text/formatted/line_wrap_spec.rb +495 -0
  188. data/spec/prawn/text/formatted/parser_spec.rb +697 -0
  189. data/spec/prawn/text_draw_text_spec.rb +150 -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/prawn/transformation_stack_spec.rb +66 -0
  195. data/spec/prawn/view_spec.rb +63 -0
  196. data/spec/prawn_manual_spec.rb +35 -0
  197. data/spec/spec_helper.rb +19 -25
  198. data.tar.gz.sig +0 -0
  199. metadata +113 -276
  200. metadata.gz.sig +0 -0
  201. data/data/encodings/win_ansi.txt +0 -29
  202. data/data/images/16bit.alpha +0 -0
  203. data/data/images/16bit.color +0 -0
  204. data/data/images/16bit.png +0 -0
  205. data/data/images/arrow.png +0 -0
  206. data/data/images/arrow2.png +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/letterhead.jpg +0 -0
  215. data/data/images/license.md +0 -8
  216. data/data/images/page_white_text.alpha +0 -0
  217. data/data/images/page_white_text.color +0 -0
  218. data/data/images/page_white_text.png +0 -0
  219. data/data/images/pigs.jpg +0 -0
  220. data/data/images/prawn.png +0 -0
  221. data/data/images/ruport.png +0 -0
  222. data/data/images/ruport_data.dat +0 -0
  223. data/data/images/ruport_transparent.png +0 -0
  224. data/data/images/ruport_type0.png +0 -0
  225. data/data/images/stef.jpg +0 -0
  226. data/data/images/tru256.bmp +0 -0
  227. data/data/images/web-links.dat +0 -1
  228. data/data/images/web-links.png +0 -0
  229. data/data/pdfs/complex_template.pdf +0 -0
  230. data/data/pdfs/contains_ttf_font.pdf +0 -0
  231. data/data/pdfs/encrypted.pdf +0 -0
  232. data/data/pdfs/form.pdf +1 -819
  233. data/data/pdfs/hexagon.pdf +0 -61
  234. data/data/pdfs/indirect_reference.pdf +0 -86
  235. data/data/pdfs/multipage_template.pdf +0 -127
  236. data/data/pdfs/nested_pages.pdf +0 -118
  237. data/data/pdfs/page_without_mediabox.pdf +0 -193
  238. data/data/pdfs/resources_as_indirect_object.pdf +0 -83
  239. data/data/pdfs/two_hexagons.pdf +0 -90
  240. data/data/pdfs/version_1_6.pdf +0 -61
  241. data/data/shift_jis_text.txt +0 -1
  242. data/lib/prawn/document/graphics_state.rb +0 -73
  243. data/lib/prawn/font/afm.rb +0 -247
  244. data/lib/prawn/table/cell/image.rb +0 -69
  245. data/lib/prawn/table/cell/in_table.rb +0 -33
  246. data/lib/prawn/table/cell/span_dummy.rb +0 -93
  247. data/lib/prawn/table/cell/subtable.rb +0 -66
  248. data/lib/prawn/table/cell/text.rb +0 -154
  249. data/lib/prawn/table/cell.rb +0 -772
  250. data/lib/prawn/table/cells.rb +0 -255
  251. data/lib/prawn/table/column_width_calculator.rb +0 -182
  252. data/lib/prawn/table.rb +0 -644
  253. data/manual/table/basic_block.rb +0 -53
  254. data/manual/table/before_rendering_page.rb +0 -26
  255. data/manual/table/cell_border_lines.rb +0 -24
  256. data/manual/table/cell_borders_and_bg.rb +0 -31
  257. data/manual/table/cell_dimensions.rb +0 -30
  258. data/manual/table/cell_text.rb +0 -38
  259. data/manual/table/column_widths.rb +0 -30
  260. data/manual/table/content_and_subtables.rb +0 -39
  261. data/manual/table/creation.rb +0 -27
  262. data/manual/table/filtering.rb +0 -36
  263. data/manual/table/flow_and_header.rb +0 -17
  264. data/manual/table/image_cells.rb +0 -33
  265. data/manual/table/position.rb +0 -29
  266. data/manual/table/row_colors.rb +0 -20
  267. data/manual/table/span.rb +0 -30
  268. data/manual/table/style.rb +0 -22
  269. data/manual/table/table.rb +0 -52
  270. data/manual/table/width.rb +0 -27
  271. data/spec/acceptance/png.rb +0 -25
  272. data/spec/annotations_spec.rb +0 -74
  273. data/spec/bounding_box_spec.rb +0 -510
  274. data/spec/cell_spec.rb +0 -629
  275. data/spec/column_box_spec.rb +0 -65
  276. data/spec/destinations_spec.rb +0 -15
  277. data/spec/document_spec.rb +0 -730
  278. data/spec/extensions/mocha.rb +0 -46
  279. data/spec/font_metric_cache_spec.rb +0 -52
  280. data/spec/font_spec.rb +0 -449
  281. data/spec/formatted_text_arranger_spec.rb +0 -421
  282. data/spec/formatted_text_box_spec.rb +0 -639
  283. data/spec/formatted_text_fragment_spec.rb +0 -298
  284. data/spec/graphics_spec.rb +0 -669
  285. data/spec/grid_spec.rb +0 -96
  286. data/spec/image_handler_spec.rb +0 -54
  287. data/spec/images_spec.rb +0 -153
  288. data/spec/inline_formatted_text_parser_spec.rb +0 -564
  289. data/spec/jpg_spec.rb +0 -25
  290. data/spec/line_wrap_spec.rb +0 -344
  291. data/spec/measurement_units_spec.rb +0 -25
  292. data/spec/outline_spec.rb +0 -430
  293. data/spec/png_spec.rb +0 -237
  294. data/spec/reference_spec.rb +0 -25
  295. data/spec/repeater_spec.rb +0 -160
  296. data/spec/security_spec.rb +0 -158
  297. data/spec/soft_mask_spec.rb +0 -79
  298. data/spec/span_spec.rb +0 -44
  299. data/spec/stamp_spec.rb +0 -160
  300. data/spec/stroke_styles_spec.rb +0 -211
  301. data/spec/table/span_dummy_spec.rb +0 -17
  302. data/spec/table_spec.rb +0 -1527
  303. data/spec/text_at_spec.rb +0 -115
  304. data/spec/text_box_spec.rb +0 -1034
  305. data/spec/text_rendering_mode_spec.rb +0 -45
  306. data/spec/text_spacing_spec.rb +0 -93
  307. data/spec/text_spec.rb +0 -437
  308. data/spec/text_with_inline_formatting_spec.rb +0 -35
  309. data/spec/transparency_spec.rb +0 -91
@@ -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
  #
@@ -52,17 +55,31 @@ module Prawn
52
55
  # Integers or Floats may be used for length and the option values.
53
56
  # Dash units are in PDF points (1/72 inch).
54
57
  #
55
- def dash(length=nil, options={})
58
+ def dash(length = nil, options = {})
56
59
  return current_dash_state if length.nil?
57
60
 
58
- self.current_dash_state = { :dash => length,
59
- :space => length.kind_of?(Array) ? nil : options[:space] || length,
60
- :phase => options[:phase] || 0 }
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.'
69
+ end
70
+
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
+ }
61
78
 
62
79
  write_stroke_dash
63
80
  end
64
81
 
65
- alias_method :dash=, :dash
82
+ alias dash= dash
66
83
 
67
84
  # Stops dashing, restoring solid stroked lines and curves
68
85
  #
@@ -80,11 +97,11 @@ module Prawn
80
97
  private
81
98
 
82
99
  def write_stroke_dash
83
- add_content dash_setting
100
+ renderer.add_content dash_setting
84
101
  end
85
102
 
86
103
  def undashed_setting
87
- { :dash => nil, :space => nil, :phase => 0 }
104
+ { dash: nil, space: nil, phase: 0 }
88
105
  end
89
106
 
90
107
  def current_dash_state=(dash_options)
@@ -98,7 +115,6 @@ module Prawn
98
115
  def dash_setting
99
116
  graphic_state.dash_setting
100
117
  end
101
-
102
118
  end
103
119
  end
104
120
  end
@@ -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
 
@@ -20,15 +20,21 @@ module Prawn
20
20
  # NOTE: if this method is never called, :miter will be used for join style
21
21
  # throughout the document
22
22
  #
23
- def join_style(style=nil)
23
+ def join_style(style = nil)
24
24
  return current_join_style || :miter if style.nil?
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
 
@@ -40,9 +46,8 @@ module Prawn
40
46
  graphic_state.join_style = style
41
47
  end
42
48
 
43
-
44
49
  def write_stroke_join_style
45
- add_content "#{JOIN_STYLES[current_join_style]} j"
50
+ renderer.add_content "#{JOIN_STYLES[current_join_style]} j"
46
51
  end
47
52
  end
48
53
  end
@@ -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,116 +12,256 @@
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
18
- # or from, r1, to, r2, color1, color2
19
- def fill_gradient(*args)
20
- set_gradient(:fill, *args)
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' }
46
+ #
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
62
+ # gradient's co-ordinate space so it matches the current co-ordinate
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)
21
71
  end
22
72
 
23
- # Sets the stroke gradient from color1 to color2.
24
- # old arguments: point, width, height, color1, color2, options = {}
25
- # new arguments: from, to, color1, color2
26
- # or from, r1, to, r2, color1, color2
27
- def stroke_gradient(*args)
28
- 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)
29
77
  end
30
78
 
31
79
  private
32
80
 
33
- def set_gradient(type, *grad)
81
+ def set_gradient(type, *grad, **kwargs)
82
+ gradient = parse_gradient_arguments(*grad, **kwargs)
83
+
34
84
  patterns = page.resources[:Pattern] ||= {}
35
85
 
36
- registry_key = gradient_registry_key grad
86
+ registry_key = gradient_registry_key gradient
37
87
 
38
- if patterns["SP#{registry_key}"]
39
- shading = patterns["SP#{registry_key}"]
40
- else
41
- unless shading = gradient_registry[registry_key]
42
- shading = gradient(*grad)
88
+ unless patterns.key? "SP#{registry_key}"
89
+ shading = gradient_registry[registry_key]
90
+ unless shading
91
+ shading = create_gradient_pattern(gradient)
43
92
  gradient_registry[registry_key] = shading
44
93
  end
45
94
 
46
95
  patterns["SP#{registry_key}"] = shading
47
96
  end
48
97
 
49
- operator = case type
50
- when :fill
51
- 'scn'
52
- when :stroke
53
- 'SCN'
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
107
+
108
+ set_color_space type, :Pattern
109
+ renderer.add_content "/SP#{registry_key} #{operator}"
110
+ end
111
+
112
+ # rubocop: disable Metrics/ParameterLists
113
+ def parse_gradient_arguments(
114
+ *arguments, from: nil, to: nil, r1: nil, r2: nil, stops: nil,
115
+ apply_transformations: nil
116
+ )
117
+
118
+ case arguments.length
119
+ when 0
120
+ apply_transformations = true if apply_transformations.nil?
121
+ when 4
122
+ from, to, *stops = arguments
123
+ when 6
124
+ from, r1, to, r2, *stops = arguments
54
125
  else
55
- raise ArgumentError, "unknown type '#{type}'"
126
+ raise ArgumentError, "Unknown type of gradient: #{arguments.inspect}"
56
127
  end
57
128
 
58
- set_color_space type, :Pattern
59
- add_content "/SP#{registry_key} #{operator}"
129
+ if stops.length < 2
130
+ raise ArgumentError, 'At least two stops must be specified'
131
+ end
132
+
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))
148
+ end
149
+
150
+ if stops.first.position != 0
151
+ raise ArgumentError, 'The first stop must have a position of 0'
152
+ end
153
+ if stops.last.position != 1
154
+ raise ArgumentError, 'The last stop must have a position of 1'
155
+ end
156
+
157
+ if stops.map { |stop| color_type(stop.color) }.uniq.length > 1
158
+ raise ArgumentError, 'All colors must be of the same color space'
159
+ end
160
+
161
+ Gradient.new(
162
+ r1 ? :radial : :axial,
163
+ apply_transformations,
164
+ stops,
165
+ from,
166
+ to,
167
+ r1,
168
+ r2
169
+ )
60
170
  end
171
+ # rubocop: enable Metrics/ParameterLists
61
172
 
62
173
  def gradient_registry_key(gradient)
63
- if gradient[1].is_a?(Array) # axial
64
- [
65
- map_to_absolute(gradient[0]),
66
- map_to_absolute(gradient[1]),
67
- gradient[2], gradient[3]
68
- ]
69
- else # radial
70
- [
71
- map_to_absolute(gradient[0]),
72
- gradient[1],
73
- map_to_absolute(gradient[2]),
74
- gradient[3],
75
- gradient[4], gradient[5]
76
- ]
77
- end.hash
174
+ _x1, _y1, x2, y2, transformation = gradient_coordinates(gradient)
175
+
176
+ key = [
177
+ gradient.type.to_s,
178
+ transformation,
179
+ x2, y2,
180
+ gradient.r1 || -1, gradient.r2 || -1,
181
+ gradient.stops.length,
182
+ gradient.stops.map { |s| [s.position, s.color] }
183
+ ].flatten
184
+ Digest::SHA1.hexdigest(key.join(','))
78
185
  end
79
186
 
80
187
  def gradient_registry
81
188
  @gradient_registry ||= {}
82
189
  end
83
190
 
84
- def gradient(*args)
85
- if args.length != 4 && args.length != 6
86
- raise ArgumentError, "Unknown type of gradient: #{args.inspect}"
191
+ def create_gradient_pattern(gradient)
192
+ if gradient.apply_transformations.nil? &&
193
+ current_transformation_matrix_with_translation(0, 0) !=
194
+ [1, 0, 0, 1, 0, 0]
195
+ warn 'Gradients in Prawn 2.x and lower are not correctly positioned '\
196
+ 'when a transformation has been made to the document. ' \
197
+ "Pass 'apply_transformations: true' to correctly transform the " \
198
+ 'gradient, or see ' \
199
+ 'https://github.com/prawnpdf/prawn/wiki/Gradient-Transformations ' \
200
+ 'for more information.'
87
201
  end
88
202
 
89
- color1 = normalize_color(args[-2]).dup.freeze
90
- color2 = normalize_color(args[-1]).dup.freeze
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
91
213
 
92
- if color_type(color1) != color_type(color2)
93
- raise ArgumentError, "Both colors must be of the same color space: #{color1.inspect} and #{color2.inspect}"
94
- end
214
+ # If there's only two stops, we can use the single shader.
215
+ # Otherwise we stitch the multiple shaders together.
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
228
+
229
+ x1, y1, x2, y2, transformation = gradient_coordinates(gradient)
230
+
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
237
+
238
+ shading = ref!(
239
+ ShadingType: gradient.type == :axial ? 2 : 3,
240
+ ColorSpace: color_space(gradient.stops.first.color),
241
+ Coords: coords,
242
+ Function: shader,
243
+ Extend: [true, true]
244
+ )
245
+
246
+ ref!(
247
+ PatternType: 2, # shading pattern
248
+ Shading: shading,
249
+ Matrix: transformation
250
+ )
251
+ end
252
+
253
+ def gradient_coordinates(gradient)
254
+ x1, y1 = map_to_absolute(gradient.from)
255
+ x2, y2 = map_to_absolute(gradient.to)
256
+
257
+ transformation =
258
+ if gradient.apply_transformations
259
+ current_transformation_matrix_with_translation(x1, y1)
260
+ else
261
+ [1, 0, 0, 1, x1, y1]
262
+ end
95
263
 
96
- process_color color1
97
- process_color color2
98
-
99
- shader = ref!({
100
- :FunctionType => 2,
101
- :Domain => [0.0, 1.0],
102
- :C0 => color1,
103
- :C1 => color2,
104
- :N => 1.0,
105
- })
106
-
107
- shading = ref!({
108
- :ShadingType => args.length == 4 ? 2 : 3, # axial : radial shading
109
- :ColorSpace => color_space(color1),
110
- :Coords => args.length == 4 ?
111
- [0, 0, args[1].first - args[0].first, args[1].last - args[0].last] :
112
- [0, 0, args[1], args[2].first - args[0].first, args[2].last - args[0].last, args[3]],
113
- :Function => shader,
114
- :Extend => [true, true],
115
- })
116
-
117
- ref!({
118
- :PatternType => 2, # shading pattern
119
- :Shading => shading,
120
- :Matrix => [1, 0,
121
- 0, 1] + map_to_absolute(args[0]),
122
- })
264
+ [x1, y1, x2, y2, transformation]
123
265
  end
124
266
  end
125
267
  end
@@ -1,5 +1,5 @@
1
- # encoding: utf-8
2
- #
1
+ # frozen_string_literal: true
2
+
3
3
  # transformation.rb: Implements rotate, translate, skew, scale and a generic
4
4
  # transformation_matrix
5
5
  #
@@ -40,7 +40,7 @@ module Prawn
40
40
  # pdf.stroke_rectangle([x, y], width, height)
41
41
  # end
42
42
  #
43
- def rotate(angle, options={}, &block)
43
+ def rotate(angle, options = {}, &block)
44
44
  Prawn.verify_options(:origin, options)
45
45
  rad = degree_to_rad(angle)
46
46
  cos = Math.cos(rad)
@@ -48,7 +48,8 @@ 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
  x = options[:origin][0] + bounds.absolute_left
53
54
  y = options[:origin][1] + bounds.absolute_bottom
54
55
  x_prime = x * cos - y * sin
@@ -59,8 +60,8 @@ module Prawn
59
60
  end
60
61
  end
61
62
 
62
- # Translate the user space. If a block is not provided, then you must save
63
- # and restore the graphics state yourself.
63
+ # Translate the user space. If a block is not provided, then you must
64
+ # save and restore the graphics state yourself.
64
65
  #
65
66
  # Example without a block: move the text up and over 10
66
67
  #
@@ -112,12 +113,13 @@ module Prawn
112
113
  # pdf.stroke_rectangle([x, y], width, height)
113
114
  # end
114
115
  #
115
- def scale(factor, options={}, &block)
116
+ def scale(factor, options = {}, &block)
116
117
  Prawn.verify_options(:origin, options)
117
118
  if options[:origin].nil?
118
119
  transformation_matrix(factor, 0, 0, factor, 0, 0, &block)
119
120
  else
120
- raise Prawn::Errors::BlockRequired unless block_given?
121
+ raise Prawn::Errors::BlockRequired unless block
122
+
121
123
  x = options[:origin][0] + bounds.absolute_left
122
124
  y = options[:origin][1] + bounds.absolute_bottom
123
125
  x_prime = factor * x
@@ -142,16 +144,22 @@ module Prawn
142
144
  # Transform the user space (see notes for rotate regarding graphics state)
143
145
  # Generally, one would use the rotate, scale, translate, and skew
144
146
  # convenience methods instead of calling transformation_matrix directly
145
- def transformation_matrix(a, b, c, d, e, f)
146
- values = [a, b, c, d, e, f].map { |x| "%.5f" % x }.join(" ")
147
+ def transformation_matrix(*matrix)
148
+ if matrix.length != 6
149
+ raise ArgumentError,
150
+ 'Transformation matrix must have exacty 6 elements'
151
+ end
147
152
  save_graphics_state if block_given?
148
- add_content "#{values} cm"
153
+
154
+ add_to_transformation_stack(*matrix)
155
+
156
+ values = PDF::Core.real_params(matrix)
157
+ renderer.add_content "#{values} cm"
149
158
  if block_given?
150
159
  yield
151
160
  restore_graphics_state
152
161
  end
153
162
  end
154
-
155
163
  end
156
164
  end
157
165
  end
@@ -1,5 +1,5 @@
1
- # encoding: utf-8
2
- #
1
+ # frozen_string_literal: true
2
+
3
3
  # transparency.rb : Implements transparency
4
4
  #
5
5
  # Copyright October 2009, Daniel Nelson. All Rights Reserved.
@@ -9,7 +9,6 @@
9
9
 
10
10
  module Prawn
11
11
  module Graphics
12
-
13
12
  # The Prawn::Transparency module is used to place transparent
14
13
  # content on the page. It has the capacity for separate
15
14
  # transparency values for stroked content and all other content.
@@ -29,7 +28,6 @@ module Prawn
29
28
  # end
30
29
  #
31
30
  module Transparency
32
-
33
31
  # @group Stable API
34
32
 
35
33
  # Sets the <tt>opacity</tt> and <tt>stroke_opacity</tt> for all
@@ -53,14 +51,16 @@ module Prawn
53
51
  # pdf.fill_and_stroke_circle([x, y], 25)
54
52
  # end
55
53
  #
56
- def transparent(opacity, stroke_opacity=opacity, &block)
57
- min_version(1.4)
54
+ def transparent(opacity, stroke_opacity = opacity)
55
+ renderer.min_version(1.4)
58
56
 
59
- opacity = [[opacity, 0.0].max, 1.0].min
57
+ opacity = [[opacity, 0.0].max, 1.0].min
60
58
  stroke_opacity = [[stroke_opacity, 0.0].max, 1.0].min
61
59
 
62
60
  save_graphics_state
63
- add_content "/#{opacity_dictionary_name(opacity, stroke_opacity)} gs"
61
+ renderer.add_content(
62
+ "/#{opacity_dictionary_name(opacity, stroke_opacity)} gs"
63
+ )
64
64
  yield
65
65
  restore_graphics_state
66
66
  end
@@ -79,23 +79,25 @@ module Prawn
79
79
  key = "#{opacity}_#{stroke_opacity}"
80
80
 
81
81
  if opacity_dictionary_registry[key]
82
- dictionary = opacity_dictionary_registry[key][:obj]
83
- dictionary_name = opacity_dictionary_registry[key][:name]
82
+ dictionary = opacity_dictionary_registry[key][:obj]
83
+ dictionary_name = opacity_dictionary_registry[key][:name]
84
84
  else
85
- dictionary = ref!(:Type => :ExtGState,
86
- :CA => stroke_opacity,
87
- :ca => opacity
88
- )
85
+ dictionary = ref!(
86
+ Type: :ExtGState,
87
+ CA: stroke_opacity,
88
+ ca: opacity
89
+ )
89
90
 
90
91
  dictionary_name = "Tr#{next_opacity_dictionary_id}"
91
- opacity_dictionary_registry[key] = { :name => dictionary_name,
92
- :obj => dictionary }
92
+ opacity_dictionary_registry[key] = {
93
+ name: dictionary_name,
94
+ obj: dictionary
95
+ }
93
96
  end
94
97
 
95
- page.ext_gstates.merge!(dictionary_name => dictionary)
98
+ page.ext_gstates[dictionary_name] = dictionary
96
99
  dictionary_name
97
100
  end
98
-
99
101
  end
100
102
  end
101
103
  end