giga-fast-kit 0.0.1

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 (75) hide show
  1. checksums.yaml +7 -0
  2. data/giga-fast-kit.gemspec +12 -0
  3. data/prawn-2.5.0/COPYING +2 -0
  4. data/prawn-2.5.0/GPLv2 +339 -0
  5. data/prawn-2.5.0/GPLv3 +674 -0
  6. data/prawn-2.5.0/LICENSE +56 -0
  7. data/prawn-2.5.0/data/fonts/Courier-Bold.afm +342 -0
  8. data/prawn-2.5.0/data/fonts/Courier-BoldOblique.afm +342 -0
  9. data/prawn-2.5.0/data/fonts/Courier-Oblique.afm +342 -0
  10. data/prawn-2.5.0/data/fonts/Courier.afm +342 -0
  11. data/prawn-2.5.0/data/fonts/Helvetica-Bold.afm +2827 -0
  12. data/prawn-2.5.0/data/fonts/Helvetica-BoldOblique.afm +2827 -0
  13. data/prawn-2.5.0/data/fonts/Helvetica-Oblique.afm +3051 -0
  14. data/prawn-2.5.0/data/fonts/Helvetica.afm +3051 -0
  15. data/prawn-2.5.0/data/fonts/MustRead.html +19 -0
  16. data/prawn-2.5.0/data/fonts/Symbol.afm +213 -0
  17. data/prawn-2.5.0/data/fonts/Times-Bold.afm +2588 -0
  18. data/prawn-2.5.0/data/fonts/Times-BoldItalic.afm +2384 -0
  19. data/prawn-2.5.0/data/fonts/Times-Italic.afm +2667 -0
  20. data/prawn-2.5.0/data/fonts/Times-Roman.afm +2419 -0
  21. data/prawn-2.5.0/data/fonts/ZapfDingbats.afm +225 -0
  22. data/prawn-2.5.0/lib/prawn/document/bounding_box.rb +625 -0
  23. data/prawn-2.5.0/lib/prawn/document/column_box.rb +177 -0
  24. data/prawn-2.5.0/lib/prawn/document/internals.rb +76 -0
  25. data/prawn-2.5.0/lib/prawn/document/span.rb +65 -0
  26. data/prawn-2.5.0/lib/prawn/document.rb +846 -0
  27. data/prawn-2.5.0/lib/prawn/encoding.rb +85 -0
  28. data/prawn-2.5.0/lib/prawn/errors.rb +73 -0
  29. data/prawn-2.5.0/lib/prawn/font.rb +567 -0
  30. data/prawn-2.5.0/lib/prawn/font_metric_cache.rb +50 -0
  31. data/prawn-2.5.0/lib/prawn/fonts/afm.rb +332 -0
  32. data/prawn-2.5.0/lib/prawn/fonts/dfont.rb +42 -0
  33. data/prawn-2.5.0/lib/prawn/fonts/otf.rb +14 -0
  34. data/prawn-2.5.0/lib/prawn/fonts/to_unicode_cmap.rb +151 -0
  35. data/prawn-2.5.0/lib/prawn/fonts/ttc.rb +41 -0
  36. data/prawn-2.5.0/lib/prawn/fonts/ttf.rb +615 -0
  37. data/prawn-2.5.0/lib/prawn/fonts.rb +14 -0
  38. data/prawn-2.5.0/lib/prawn/graphics/blend_mode.rb +62 -0
  39. data/prawn-2.5.0/lib/prawn/graphics/cap_style.rb +44 -0
  40. data/prawn-2.5.0/lib/prawn/graphics/color.rb +257 -0
  41. data/prawn-2.5.0/lib/prawn/graphics/dash.rb +123 -0
  42. data/prawn-2.5.0/lib/prawn/graphics/join_style.rb +60 -0
  43. data/prawn-2.5.0/lib/prawn/graphics/patterns.rb +389 -0
  44. data/prawn-2.5.0/lib/prawn/graphics/transformation.rb +172 -0
  45. data/prawn-2.5.0/lib/prawn/graphics/transparency.rb +79 -0
  46. data/prawn-2.5.0/lib/prawn/graphics.rb +791 -0
  47. data/prawn-2.5.0/lib/prawn/grid.rb +449 -0
  48. data/prawn-2.5.0/lib/prawn/image_handler.rb +61 -0
  49. data/prawn-2.5.0/lib/prawn/images/image.rb +48 -0
  50. data/prawn-2.5.0/lib/prawn/images/jpg.rb +121 -0
  51. data/prawn-2.5.0/lib/prawn/images/png.rb +383 -0
  52. data/prawn-2.5.0/lib/prawn/images.rb +195 -0
  53. data/prawn-2.5.0/lib/prawn/measurement_extensions.rb +82 -0
  54. data/prawn-2.5.0/lib/prawn/measurements.rb +129 -0
  55. data/prawn-2.5.0/lib/prawn/outline.rb +316 -0
  56. data/prawn-2.5.0/lib/prawn/repeater.rb +140 -0
  57. data/prawn-2.5.0/lib/prawn/security/arcfour.rb +54 -0
  58. data/prawn-2.5.0/lib/prawn/security.rb +308 -0
  59. data/prawn-2.5.0/lib/prawn/soft_mask.rb +105 -0
  60. data/prawn-2.5.0/lib/prawn/stamp.rb +147 -0
  61. data/prawn-2.5.0/lib/prawn/text/box.rb +192 -0
  62. data/prawn-2.5.0/lib/prawn/text/formatted/arranger.rb +396 -0
  63. data/prawn-2.5.0/lib/prawn/text/formatted/box.rb +725 -0
  64. data/prawn-2.5.0/lib/prawn/text/formatted/fragment.rb +373 -0
  65. data/prawn-2.5.0/lib/prawn/text/formatted/line_wrap.rb +335 -0
  66. data/prawn-2.5.0/lib/prawn/text/formatted/parser.rb +284 -0
  67. data/prawn-2.5.0/lib/prawn/text/formatted/wrap.rb +166 -0
  68. data/prawn-2.5.0/lib/prawn/text/formatted.rb +82 -0
  69. data/prawn-2.5.0/lib/prawn/text.rb +701 -0
  70. data/prawn-2.5.0/lib/prawn/transformation_stack.rb +63 -0
  71. data/prawn-2.5.0/lib/prawn/utilities.rb +34 -0
  72. data/prawn-2.5.0/lib/prawn/version.rb +6 -0
  73. data/prawn-2.5.0/lib/prawn/view.rb +116 -0
  74. data/prawn-2.5.0/lib/prawn.rb +83 -0
  75. metadata +114 -0
@@ -0,0 +1,389 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'digest/sha1'
4
+
5
+ module Prawn
6
+ module Graphics
7
+ # Implements axial & radial gradients.
8
+ module Patterns
9
+ # Gradient color stop.
10
+ # @private
11
+ GradientStop = Struct.new(:position, :color)
12
+
13
+ # @private
14
+ Gradient = Struct.new(
15
+ :type, :apply_transformations, :stops, :from, :to, :r1, :r2,
16
+ )
17
+
18
+ # @group Stable API
19
+
20
+ # Sets the fill gradient.
21
+ #
22
+ # @overload fill_gradient(from, to, color1, color2, apply_margin_options: false)
23
+ # Set an axial (linear) fill gradient.
24
+ #
25
+ # @param from [Array(Number, Number)]
26
+ # Starting point of the gradient.
27
+ # @param to [Array(Number, Number)] ending point of the gradient.
28
+ # @param color1 [Color] starting color of the gradient.
29
+ # @param color2 [Color] ending color of the gradient.
30
+ # @param apply_transformations [Boolean] (false)
31
+ # If set `true`, will transform the gradient's co-ordinate space so it
32
+ # matches the current co-ordinate space of the document. This option
33
+ # will be the default from Prawn v3, and is default `true` if you use
34
+ # the all-keyword version of this method. The default for the
35
+ # positional arguments version (this one), `false`, will mean if you
36
+ # (for example) scale your document by 2 and put a gradient inside,
37
+ # you will have to manually multiply your co-ordinates by 2 so the
38
+ # gradient is correctly positioned.
39
+ # @return [void]
40
+ #
41
+ # @overload fill_gradient(from, r1, to, r2, color1, color2, apply_margin_options: false)
42
+ # Set a radial fill gradient.
43
+ #
44
+ # @param from [Array(Number, Number)]
45
+ # Starting point of the gradient.
46
+ # @param r1 [Number]
47
+ # Radius of the starting circle of a radial gradient. The circle is
48
+ # centered at `from`.
49
+ # @param to [Array(Number, Number)]
50
+ # Ending point of the gradient.
51
+ # @param r2 [Number]
52
+ # Radius of the ending circle of a radial gradient. The circle is
53
+ # centered at `to`.
54
+ # @param color1 [Color]
55
+ # Starting color.
56
+ # @param color2 [Color]
57
+ # Ending color.
58
+ # @param apply_transformations [Boolean] (false)
59
+ # If set `true`, will transform the gradient's co-ordinate space so it
60
+ # matches the current co-ordinate space of the document. This option
61
+ # will be the default from Prawn v3, and is default `true` if you use
62
+ # the all-keyword version of this method. The default for the
63
+ # positional arguments version (this one), `false`, will mean if you
64
+ # (for example) scale your document by 2 and put a gradient inside,
65
+ # you will have to manually multiply your co-ordinates by 2 so the
66
+ # gradient is correctly positioned.
67
+ # @return [void]
68
+ #
69
+ # @overload fill_gradient(from:, to:, r1: nil, r2: nil, stops:, apply_margin_options: true)
70
+ # Set the fill gradient.
71
+ #
72
+ # @example Draw a horizontal axial gradient that starts at red on the left and ends at blue on the right
73
+ # fill_gradient from: [0, 0], to: [100, 0], stops: ['ff0000', '0000ff']
74
+ #
75
+ # @example Draw a horizontal radial gradient that starts at red, is green 80% through, and finishes blue
76
+ # fill_gradient from: [0, 0], r1: 0, to: [100, 0], r2: 180,
77
+ # stops: { 0 => 'ff0000', 0.8 => '00ff00', 1 => '0000ff' }
78
+ #
79
+ # @param from [Array(Number, Number)]
80
+ # Starting point of the gradient.
81
+ # @param r1 [Number, nil]
82
+ # Radius of the starting circle of a radial gradient. The circle is
83
+ # centered at `from`. If omitted a linear gradient will be produced.
84
+ # @param to [Array(Number, Number)]
85
+ # Ending point of the gradient.
86
+ # @param r2 [Number, nil]
87
+ # Radius of the ending circle of a radial gradient. The circle is
88
+ # centered at `to`.
89
+ # @param stops [Array<Color>, Hash{Number => Color}]
90
+ # Color stops. Each stop is either just a color, in which case the
91
+ # stops will be evenly distributed across the gradient, or a hash
92
+ # where the key is a position between 0 and 1 indicating what distance
93
+ # through the gradient the color should change, and the value is
94
+ # a color.
95
+ # @param apply_transformations [Boolean] (true)
96
+ # If set `true`, will transform the gradient's co-ordinate space so it
97
+ # matches the current co-ordinate space of the document. This option
98
+ # will be the default from Prawn v3, and is default `true` if you use
99
+ # the all-keyword version of this method (this one). The default for
100
+ # the old arguments format, `false`, will mean if you (for example)
101
+ # scale your document by 2 and put a gradient inside, you will have to
102
+ # manually multiply your co-ordinates by 2 so the gradient is
103
+ # correctly positioned.
104
+ # @return [void]
105
+ def fill_gradient(*args, **kwargs)
106
+ set_gradient(:fill, *args, **kwargs)
107
+ end
108
+
109
+ # Sets the stroke gradient.
110
+ #
111
+ # @overload fill_gradient(from, to, color1, color2, apply_margin_options: false)
112
+ # Set an axial (linear) stroke gradient.
113
+ #
114
+ # @param from [Array(Number, Number)]
115
+ # Starting point of the gradient.
116
+ # @param to [Array(Number, Number)] ending point of the gradient.
117
+ # @param color1 [Color] starting color of the gradient.
118
+ # @param color2 [Color] ending color of the gradient.
119
+ # @param apply_transformations [Boolean] (false)
120
+ # If set `true`, will transform the gradient's co-ordinate space so it
121
+ # matches the current co-ordinate space of the document. This option
122
+ # will be the default from Prawn v3, and is default `true` if you use
123
+ # the all-keyword version of this method. The default for the
124
+ # positional arguments version (this one), `false`, will mean if you
125
+ # (for example) scale your document by 2 and put a gradient inside,
126
+ # you will have to manually multiply your co-ordinates by 2 so the
127
+ # gradient is correctly positioned.
128
+ # @return [void]
129
+ #
130
+ # @overload fill_gradient(from, r1, to, r2, color1, color2, apply_margin_options: false)
131
+ # Set a radial stroke gradient.
132
+ #
133
+ # @param from [Array(Number, Number)]
134
+ # Starting point of the gradient.
135
+ # @param r1 [Number]
136
+ # Radius of the starting circle of a radial gradient. The circle is
137
+ # centered at `from`.
138
+ # @param to [Array(Number, Number)]
139
+ # Ending point of the gradient.
140
+ # @param r2 [Number]
141
+ # Radius of the ending circle of a radial gradient. The circle is
142
+ # centered at `to`.
143
+ # @param color1 [Color]
144
+ # Starting color.
145
+ # @param color2 [Color]
146
+ # Ending color.
147
+ # @param apply_transformations [Boolean] (false)
148
+ # If set `true`, will transform the gradient's co-ordinate space so it
149
+ # matches the current co-ordinate space of the document. This option
150
+ # will be the default from Prawn v3, and is default `true` if you use
151
+ # the all-keyword version of this method. The default for the
152
+ # positional arguments version (this one), `false`, will mean if you
153
+ # (for example) scale your document by 2 and put a gradient inside,
154
+ # you will have to manually multiply your co-ordinates by 2 so the
155
+ # gradient is correctly positioned.
156
+ # @return [void]
157
+ #
158
+ # @overload fill_gradient(from:, to:, r1: nil, r2: nil, stops:, apply_margin_options: true)
159
+ # Set the stroke gradient.
160
+ #
161
+ # @example Draw a horizontal axial gradient that starts at red on the left and ends at blue on the right
162
+ # stroke_gradient from: [0, 0], to: [100, 0], stops: ['ff0000', '0000ff']
163
+ #
164
+ # @example Draw a horizontal radial gradient that starts at red, is green 80% through, and finishes blue
165
+ # stroke_gradient from: [0, 0], r1: 0, to: [100, 0], r2: 180,
166
+ # stops: { 0 => 'ff0000', 0.8 => '00ff00', 1 => '0000ff' }
167
+ #
168
+ # @param from [Array(Number, Number)]
169
+ # Starting point of the gradient.
170
+ # @param r1 [Number, nil]
171
+ # Radius of the starting circle of a radial gradient. The circle is
172
+ # centered at `from`. If omitted a linear gradient will be produced.
173
+ # @param to [Array(Number, Number)]
174
+ # Ending point of the gradient.
175
+ # @param r2 [Number, nil]
176
+ # Radius of the ending circle of a radial gradient. The circle is
177
+ # centered at `to`.
178
+ # @param stops [Array<Color>, Hash{Number => Color}]
179
+ # Color stops. Each stop is either just a color, in which case the
180
+ # stops will be evenly distributed across the gradient, or a hash
181
+ # where the key is a position between 0 and 1 indicating what distance
182
+ # through the gradient the color should change, and the value is
183
+ # a color.
184
+ # @param apply_transformations [Boolean] (true)
185
+ # If set `true`, will transform the gradient's co-ordinate space so it
186
+ # matches the current co-ordinate space of the document. This option
187
+ # will be the default from Prawn v3, and is default `true` if you use
188
+ # the all-keyword version of this method (this one). The default for
189
+ # the old arguments format, `false`, will mean if you (for example)
190
+ # scale your document by 2 and put a gradient inside, you will have to
191
+ # manually multiply your co-ordinates by 2 so the gradient is
192
+ # correctly positioned.
193
+ # @return [void]
194
+ def stroke_gradient(*args, **kwargs)
195
+ set_gradient(:stroke, *args, **kwargs)
196
+ end
197
+
198
+ private
199
+
200
+ def set_gradient(type, *grad, **kwargs)
201
+ gradient = parse_gradient_arguments(*grad, **kwargs)
202
+
203
+ patterns = page.resources[:Pattern] ||= {}
204
+
205
+ registry_key = gradient_registry_key(gradient)
206
+
207
+ unless patterns.key?("SP#{registry_key}")
208
+ shading = gradient_registry[registry_key]
209
+ unless shading
210
+ shading = create_gradient_pattern(gradient)
211
+ gradient_registry[registry_key] = shading
212
+ end
213
+
214
+ patterns["SP#{registry_key}"] = shading
215
+ end
216
+
217
+ operator =
218
+ case type
219
+ when :fill
220
+ 'scn'
221
+ when :stroke
222
+ 'SCN'
223
+ else
224
+ raise ArgumentError, "unknown type '#{type}'"
225
+ end
226
+
227
+ set_color_space(type, :Pattern)
228
+ renderer.add_content("/SP#{registry_key} #{operator}")
229
+ end
230
+
231
+ # rubocop: disable Metrics/ParameterLists
232
+ def parse_gradient_arguments(
233
+ *arguments, from: nil, to: nil, r1: nil, r2: nil, stops: nil,
234
+ apply_transformations: nil
235
+ )
236
+
237
+ case arguments.length
238
+ when 0
239
+ apply_transformations = true if apply_transformations.nil?
240
+ when 4
241
+ from, to, *stops = arguments
242
+ when 6
243
+ from, r1, to, r2, *stops = arguments
244
+ else
245
+ raise ArgumentError, "Unknown type of gradient: #{arguments.inspect}"
246
+ end
247
+
248
+ if stops.length < 2
249
+ raise ArgumentError, 'At least two stops must be specified'
250
+ end
251
+
252
+ stops =
253
+ stops.map.with_index { |stop, index|
254
+ case stop
255
+ when Array, Hash
256
+ position, color = stop
257
+ else
258
+ position = index / (Float(stops.length) - 1)
259
+ color = stop
260
+ end
261
+
262
+ unless (0..1).cover?(position)
263
+ raise ArgumentError, 'position must be between 0 and 1'
264
+ end
265
+
266
+ GradientStop.new(position, normalize_color(color))
267
+ }
268
+
269
+ if stops.first.position != 0
270
+ raise ArgumentError, 'The first stop must have a position of 0'
271
+ end
272
+ if stops.last.position != 1
273
+ raise ArgumentError, 'The last stop must have a position of 1'
274
+ end
275
+
276
+ if stops.map { |stop| color_type(stop.color) }.uniq.length > 1
277
+ raise ArgumentError, 'All colors must be of the same color space'
278
+ end
279
+
280
+ Gradient.new(
281
+ r1 ? :radial : :axial,
282
+ apply_transformations,
283
+ stops,
284
+ from,
285
+ to,
286
+ r1,
287
+ r2,
288
+ )
289
+ end
290
+ # rubocop: enable Metrics/ParameterLists
291
+
292
+ def gradient_registry_key(gradient)
293
+ _x1, _y1, x2, y2, transformation = gradient_coordinates(gradient)
294
+
295
+ key = [
296
+ gradient.type.to_s,
297
+ transformation,
298
+ x2, y2,
299
+ gradient.r1 || -1, gradient.r2 || -1,
300
+ gradient.stops.length,
301
+ gradient.stops.map { |s| [s.position, s.color] },
302
+ ].flatten
303
+ Digest::SHA1.hexdigest(key.join(','))
304
+ end
305
+
306
+ def gradient_registry
307
+ @gradient_registry ||= {}
308
+ end
309
+
310
+ def create_gradient_pattern(gradient)
311
+ if gradient.apply_transformations.nil? &&
312
+ current_transformation_matrix_with_translation(0, 0) !=
313
+ [1, 0, 0, 1, 0, 0]
314
+ warn(
315
+ 'Gradients in Prawn 2.x and lower are not correctly positioned ' \
316
+ 'when a transformation has been made to the document. ' \
317
+ "Pass 'apply_transformations: true' to correctly transform the " \
318
+ 'gradient, or see ' \
319
+ 'https://github.com/prawnpdf/prawn/wiki/Gradient-Transformations ' \
320
+ 'for more information.',
321
+ )
322
+ end
323
+
324
+ shader_stops =
325
+ gradient.stops.each_cons(2).map { |first, second|
326
+ ref!(
327
+ FunctionType: 2,
328
+ Domain: [0.0, 1.0],
329
+ C0: first.color,
330
+ C1: second.color,
331
+ N: 1.0,
332
+ )
333
+ }
334
+
335
+ # If there's only two stops, we can use the single shader.
336
+ # Otherwise we stitch the multiple shaders together.
337
+ shader =
338
+ if shader_stops.length == 1
339
+ shader_stops.first
340
+ else
341
+ ref!(
342
+ FunctionType: 3, # stitching function
343
+ Domain: [0.0, 1.0],
344
+ Functions: shader_stops,
345
+ Bounds: gradient.stops[1..-2].map(&:position),
346
+ Encode: [0.0, 1.0] * shader_stops.length,
347
+ )
348
+ end
349
+
350
+ x1, y1, x2, y2, transformation = gradient_coordinates(gradient)
351
+
352
+ coords =
353
+ if gradient.type == :axial
354
+ [0, 0, x2 - x1, y2 - y1]
355
+ else
356
+ [0, 0, gradient.r1, x2 - x1, y2 - y1, gradient.r2]
357
+ end
358
+
359
+ shading = ref!(
360
+ ShadingType: gradient.type == :axial ? 2 : 3,
361
+ ColorSpace: color_space(gradient.stops.first.color),
362
+ Coords: coords,
363
+ Function: shader,
364
+ Extend: [true, true],
365
+ )
366
+
367
+ ref!(
368
+ PatternType: 2, # shading pattern
369
+ Shading: shading,
370
+ Matrix: transformation,
371
+ )
372
+ end
373
+
374
+ def gradient_coordinates(gradient)
375
+ x1, y1 = map_to_absolute(gradient.from)
376
+ x2, y2 = map_to_absolute(gradient.to)
377
+
378
+ transformation =
379
+ if gradient.apply_transformations
380
+ current_transformation_matrix_with_translation(x1, y1)
381
+ else
382
+ [1, 0, 0, 1, x1, y1]
383
+ end
384
+
385
+ [x1, y1, x2, y2, transformation]
386
+ end
387
+ end
388
+ end
389
+ end
@@ -0,0 +1,172 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Prawn
4
+ module Graphics
5
+ # Implements user-space coordinate transformation.
6
+ module Transformation
7
+ # @group Stable API
8
+
9
+ # Rotate the user space. If a block is not provided, then you must save
10
+ # and restore the graphics state yourself.
11
+ #
12
+ # @example
13
+ # save_graphics_state
14
+ # rotate 30
15
+ # text "rotated text"
16
+ # restore_graphics_state
17
+ #
18
+ # @example Rotating a rectangle around its upper-left corner
19
+ # x = 300
20
+ # y = 300
21
+ # width = 150
22
+ # height = 200
23
+ # angle = 30
24
+ # pdf.rotate(angle, :origin => [x, y]) do
25
+ # pdf.stroke_rectangle([x, y], width, height)
26
+ # end
27
+ #
28
+ # @param angle [Number] Angle in degrees.
29
+ # @param options [Hash{Symbol => any}]
30
+ # @option options :origin [Array(Number, Number)] Rotation origin point.
31
+ # A block must be provided if specified.
32
+ # @yield
33
+ # @raise [Prawn::Errors::BlockRequired] if an `:origin` option is
34
+ # provided, but no block is given.
35
+ # @return [void]
36
+ def rotate(angle, options = {}, &block)
37
+ Prawn.verify_options(:origin, options)
38
+ rad = degree_to_rad(angle)
39
+ cos = Math.cos(rad)
40
+ sin = Math.sin(rad)
41
+ if options[:origin].nil?
42
+ transformation_matrix(cos, sin, -sin, cos, 0, 0, &block)
43
+ else
44
+ raise Prawn::Errors::BlockRequired unless block
45
+
46
+ x = options[:origin][0] + bounds.absolute_left
47
+ y = options[:origin][1] + bounds.absolute_bottom
48
+ x_prime = (x * cos) - (y * sin)
49
+ y_prime = (x * sin) + (y * cos)
50
+ translate(x - x_prime, y - y_prime) do
51
+ transformation_matrix(cos, sin, -sin, cos, 0, 0, &block)
52
+ end
53
+ end
54
+ end
55
+
56
+ # Translate the user space. If a block is not provided, then you must
57
+ # save and restore the graphics state yourself.
58
+ #
59
+ # @example Move the text up and over 10
60
+ # save_graphics_state
61
+ # translate(10, 10)
62
+ # text "scaled text"
63
+ # restore_graphics_state
64
+ #
65
+ # @example draw a rectangle with its upper-left corner at x + 10, y + 10
66
+ # x = 300
67
+ # y = 300
68
+ # width = 150
69
+ # height = 200
70
+ # pdf.translate(10, 10) do
71
+ # pdf.stroke_rectangle([x, y], width, height)
72
+ # end
73
+ #
74
+ # @param x [Number]
75
+ # @param y [Number]
76
+ # @yield
77
+ # @return [void]
78
+ def translate(x, y, &block)
79
+ transformation_matrix(1, 0, 0, 1, x, y, &block)
80
+ end
81
+
82
+ # Scale the user space. If a block is not provided, then you must save
83
+ # and restore the graphics state yourself.
84
+ #
85
+ # @example
86
+ # save_graphics_state
87
+ # scale 1.5
88
+ # text "scaled text"
89
+ # restore_graphics_state
90
+ #
91
+ # @example Scale a rectangle from its upper-left corner
92
+ # x = 300
93
+ # y = 300
94
+ # width = 150
95
+ # height = 200
96
+ # factor = 1.5
97
+ # pdf.scale(angle, :origin => [x, y]) do
98
+ # pdf.stroke_rectangle([x, y], width, height)
99
+ # end
100
+ #
101
+ # @param factor [Number] Scale factor.
102
+ # @param options [Hash{Symbol => any}]
103
+ # @option options :origin [Array(Number, Number)] The point from which to
104
+ # scale. A block must be provided if specified.
105
+ # @yield
106
+ # @raise [Prawn::Errors::BlockRequired] If an `:origin` option is
107
+ # provided, but no block is given.
108
+ # @return [void]
109
+ def scale(factor, options = {}, &block)
110
+ Prawn.verify_options(:origin, options)
111
+ if options[:origin].nil?
112
+ transformation_matrix(factor, 0, 0, factor, 0, 0, &block)
113
+ else
114
+ raise Prawn::Errors::BlockRequired unless block
115
+
116
+ x = options[:origin][0] + bounds.absolute_left
117
+ y = options[:origin][1] + bounds.absolute_bottom
118
+ x_prime = factor * x
119
+ y_prime = factor * y
120
+ translate(x - x_prime, y - y_prime) do
121
+ transformation_matrix(factor, 0, 0, factor, 0, 0, &block)
122
+ end
123
+ end
124
+ end
125
+
126
+ # The following definition of skew would only work in a clearly
127
+ # predicatable manner when if the document had no margin. don't provide
128
+ # this shortcut until it behaves in a clearly understood manner
129
+ #
130
+ # def skew(a, b, &block)
131
+ # transformation_matrix(1,
132
+ # Math.tan(degree_to_rad(a)),
133
+ # Math.tan(degree_to_rad(b)),
134
+ # 1, 0, 0, &block)
135
+ # end
136
+
137
+ # Transform the user space (see notes for rotate regarding graphics state)
138
+ # Generally, one would use the {rotate}, {scale}, and {translate}
139
+ # convenience methods instead of calling transformation_matrix directly
140
+ #
141
+ # @param matrix [Array(Number, Number, Number, Number, Number, Number)]
142
+ # Transformation matrix.
143
+ #
144
+ # The six elements correspond to the following elements of the
145
+ # transformation matrix:
146
+ #
147
+ # ```plain
148
+ # a b 0
149
+ # c d 0
150
+ # e f 0
151
+ # ```
152
+ # @yield
153
+ # @return [void]
154
+ def transformation_matrix(*matrix)
155
+ if matrix.length != 6
156
+ raise ArgumentError,
157
+ 'Transformation matrix must have exacty 6 elements'
158
+ end
159
+ save_graphics_state if block_given?
160
+
161
+ add_to_transformation_stack(*matrix)
162
+
163
+ values = PDF::Core.real_params(matrix)
164
+ renderer.add_content("#{values} cm")
165
+ if block_given?
166
+ yield
167
+ restore_graphics_state
168
+ end
169
+ end
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Prawn
4
+ module Graphics
5
+ # This module is used to place transparent content on the page. It has the
6
+ # capacity for separate transparency values for stroked content and all
7
+ # other content.
8
+ module Transparency
9
+ # @group Stable API
10
+
11
+ # Set opacity.
12
+ #
13
+ # @example Both the fill and stroke will be at 50% opacity.
14
+ # pdf.transparent(0.5) do
15
+ # pdf.text("hello world")
16
+ # pdf.fill_and_stroke_circle([x, y], 25)
17
+ # end
18
+ #
19
+ # @example The fill will be at 50% opacity, but the stroke will be at 75% opacity.
20
+ # pdf.transparent(0.5, 0.75) do
21
+ # pdf.text("hello world")
22
+ # pdf.fill_and_stroke_circle([x, y], 25)
23
+ # end
24
+ #
25
+ # @param opacity [Number] Fill opacity. Clipped to the 0.0 to 1.0 range.
26
+ # @param stroke_opacity [Number] Stroke opacity. Clipped to the
27
+ # 0.0 to 1.0 range.
28
+ # @yield
29
+ # @return [void]
30
+ def transparent(opacity, stroke_opacity = opacity)
31
+ renderer.min_version(1.4)
32
+
33
+ opacity = opacity.clamp(0.0, 1.0)
34
+ stroke_opacity = stroke_opacity.clamp(0.0, 1.0)
35
+
36
+ save_graphics_state
37
+ renderer.add_content(
38
+ "/#{opacity_dictionary_name(opacity, stroke_opacity)} gs",
39
+ )
40
+ yield
41
+ restore_graphics_state
42
+ end
43
+
44
+ private
45
+
46
+ def opacity_dictionary_registry
47
+ @opacity_dictionary_registry ||= {}
48
+ end
49
+
50
+ def next_opacity_dictionary_id
51
+ opacity_dictionary_registry.length + 1
52
+ end
53
+
54
+ def opacity_dictionary_name(opacity, stroke_opacity)
55
+ key = "#{opacity}_#{stroke_opacity}"
56
+
57
+ if opacity_dictionary_registry[key]
58
+ dictionary = opacity_dictionary_registry[key][:obj]
59
+ dictionary_name = opacity_dictionary_registry[key][:name]
60
+ else
61
+ dictionary = ref!(
62
+ Type: :ExtGState,
63
+ CA: stroke_opacity,
64
+ ca: opacity,
65
+ )
66
+
67
+ dictionary_name = "Tr#{next_opacity_dictionary_id}"
68
+ opacity_dictionary_registry[key] = {
69
+ name: dictionary_name,
70
+ obj: dictionary,
71
+ }
72
+ end
73
+
74
+ page.ext_gstates[dictionary_name] = dictionary
75
+ dictionary_name
76
+ end
77
+ end
78
+ end
79
+ end