rmagick 2.15.3 → 2.15.4

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rmagick might be problematic. Click here for more details.

Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.editorconfig +14 -0
  3. data/.rubocop.yml +27 -3
  4. data/.travis.yml +9 -6
  5. data/CHANGELOG.md +4 -0
  6. data/CONTRIBUTING.md +10 -0
  7. data/README.textile +4 -0
  8. data/before_install_linux.sh +4 -4
  9. data/doc/ex/gravity.rb +2 -1
  10. data/ext/RMagick/extconf.rb +60 -17
  11. data/lib/rmagick/version.rb +1 -1
  12. data/lib/rvg/clippath.rb +37 -36
  13. data/lib/rvg/container.rb +106 -107
  14. data/lib/rvg/deep_equal.rb +46 -48
  15. data/lib/rvg/describable.rb +35 -36
  16. data/lib/rvg/embellishable.rb +384 -380
  17. data/lib/rvg/misc.rb +705 -690
  18. data/lib/rvg/paint.rb +39 -40
  19. data/lib/rvg/pathdata.rb +120 -121
  20. data/lib/rvg/rvg.rb +212 -209
  21. data/lib/rvg/stretchable.rb +159 -158
  22. data/lib/rvg/stylable.rb +99 -100
  23. data/lib/rvg/text.rb +0 -1
  24. data/lib/rvg/transformable.rb +110 -110
  25. data/lib/rvg/units.rb +58 -58
  26. data/rmagick.gemspec +1 -1
  27. data/spec/rmagick/image/blue_shift_spec.rb +16 -0
  28. data/spec/rmagick/image/composite_spec.rb +140 -0
  29. data/spec/rmagick/image/constitute_spec.rb +15 -0
  30. data/spec/rmagick/image/dispatch_spec.rb +18 -0
  31. data/spec/rmagick/image/from_blob_spec.rb +14 -0
  32. data/spec/rmagick/image/ping_spec.rb +14 -0
  33. data/spec/rmagick/image/properties_spec.rb +29 -0
  34. data/spec/spec_helper.rb +3 -0
  35. data/test/Image1.rb +524 -718
  36. data/test/Image2.rb +1262 -1262
  37. data/test/Image3.rb +973 -973
  38. data/test/ImageList2.rb +341 -341
  39. data/test/Image_attributes.rb +678 -678
  40. data/test/Info.rb +336 -336
  41. data/test/Magick.rb +245 -242
  42. data/test/Pixel.rb +105 -105
  43. data/test/Preview.rb +42 -42
  44. metadata +21 -6
@@ -1,708 +1,723 @@
1
1
  # $Id: misc.rb,v 1.17 2010/03/21 01:43:01 baror Exp $
2
2
  # Copyright (C) 2009 Timothy P. Hunter
3
3
  module Magick
4
- class RVG
5
- # This is a standard deep_copy method that is used in most classes.
6
- # Thanks to Robert Klemme.
7
- module Duplicatable
8
- def deep_copy(h = {})
9
- # Prevent recursion. If we reach the
10
- # object we started with, stop copying.
11
- copy = h[__id__]
12
- unless copy
13
- h[__id__] = copy = self.class.allocate
14
- ivars = instance_variables
15
- ivars.each do |ivar|
16
- ivalue = instance_variable_get(ivar)
17
- cvalue = case
18
- when NilClass === ivalue, Symbol === ivalue, Float === ivalue,
19
- Fixnum === ivalue, FalseClass === ivalue, TrueClass === ivalue
20
- ivalue
21
- when ivalue.respond_to?(:deep_copy)
22
- ivalue.deep_copy(h)
23
- when ivalue.respond_to?(:dup)
24
- ivalue.dup
25
- else
26
- ivalue
27
- end
28
- copy.instance_variable_set(ivar, cvalue)
29
- end
30
- copy.freeze if frozen?
31
- end
32
- copy
4
+ class RVG
5
+ # This is a standard deep_copy method that is used in most classes.
6
+ # Thanks to Robert Klemme.
7
+ module Duplicatable
8
+ def deep_copy(h = {})
9
+ # Prevent recursion. If we reach the
10
+ # object we started with, stop copying.
11
+ copy = h[__id__]
12
+ unless copy
13
+ h[__id__] = copy = self.class.allocate
14
+ ivars = instance_variables
15
+ ivars.each do |ivar|
16
+ ivalue = instance_variable_get(ivar)
17
+ cvalue = case
18
+ when NilClass === ivalue, Symbol === ivalue, Float === ivalue,
19
+ Fixnum === ivalue, FalseClass === ivalue, TrueClass === ivalue
20
+ ivalue
21
+ when ivalue.respond_to?(:deep_copy)
22
+ ivalue.deep_copy(h)
23
+ when ivalue.respond_to?(:dup)
24
+ ivalue.dup
25
+ else
26
+ ivalue
27
+ end
28
+ copy.instance_variable_set(ivar, cvalue)
29
+ end
30
+ copy.freeze if frozen?
31
+ end
32
+ copy
33
+ end
34
+ end # module Duplicatable
35
+
36
+ # Convert an array of method arguments to Float objects. If any
37
+ # cannot be converted, raise ArgumentError and issue a message.
38
+ def self.fmsg(*args)
39
+ "at least one argument cannot be converted to Float (got #{args.collect {|a| a.class}.join(', ')})"
40
+ end
41
+
42
+ def self.convert_to_float(*args)
43
+ allow_nil = false
44
+ if args.last == :allow_nil
45
+ allow_nil = true
46
+ args.pop
47
+ end
48
+ begin
49
+ fargs = args.collect { |a| (allow_nil && a.nil?) ? a : Float(a) }
50
+ rescue ArgumentError, TypeError
51
+ raise ArgumentError, fmsg(*args)
52
+ end
53
+ fargs
54
+ end
55
+
56
+ def self.convert_one_to_float(arg)
57
+ begin
58
+ farg = Float(arg)
59
+ rescue ArgumentError, TypeError
60
+ raise ArgumentError, "argument cannot be converted to Float (got #{arg.class})"
61
+ end
62
+ farg
63
+ end
64
+ end # class RVG
65
+ end # module Magick
66
+
67
+ module Magick
68
+ class RVG
69
+ class Utility
70
+ class TextStrategy
71
+ def initialize(context)
72
+ @ctx = context
73
+ @ctx.shadow.affine = @ctx.text_attrs.affine
74
+ end
75
+
76
+ def enquote(text)
77
+ if text.length > 2 && /\A(?:\"[^\"]+\"|\'[^\']+\'|\{[^\}]+\})\z/.match(text)
78
+ return text
79
+ elsif !text['\'']
80
+ text = '\''+text+'\''
81
+ return text
82
+ elsif !text['"']
83
+ text = '"'+text+'"'
84
+ return text
85
+ elsif !(text['{'] || text['}'])
86
+ text = '{'+text+'}'
87
+ return text
88
+ end
89
+
90
+ # escape existing braces, surround with braces
91
+ text.gsub!(/[}]/) { |b| '\\' + b }
92
+ '{' + text + '}'
93
+ end
94
+
95
+ def glyph_metrics(glyph_orientation, glyph)
96
+ gm = @ctx.shadow.get_type_metrics('a' + glyph + 'a')
97
+ gm2 = @ctx.shadow.get_type_metrics('aa')
98
+ h = (gm.ascent - gm.descent + 0.5).to_i
99
+ w = gm.width - gm2.width
100
+ if glyph_orientation == 0 || glyph_orientation == 180
101
+ [w, h]
102
+ else
103
+ [h, w]
104
+ end
105
+ end
106
+
107
+ def text_rel_coords(text)
108
+ y_rel_coords = []
109
+ x_rel_coords = []
110
+ first_word = true
111
+ words = text.split(::Magick::RVG::WORD_SEP)
112
+ words.each do |word|
113
+ unless first_word
114
+ wx, wy = get_word_spacing
115
+ x_rel_coords << wx
116
+ y_rel_coords << wy
117
+ end
118
+ first_word = false
119
+ word.split('').each do |glyph|
120
+ wx, wy = get_letter_spacing(glyph)
121
+ x_rel_coords << wx
122
+ y_rel_coords << wy
33
123
  end
34
- end # module Duplicatable
124
+ end
125
+ [x_rel_coords, y_rel_coords]
126
+ end
127
+
128
+ def shift_baseline(glyph_orientation, glyph)
129
+ glyph_dimensions = @ctx.shadow.get_type_metrics(glyph)
130
+ if glyph_orientation == 0 || glyph_orientation == 180
131
+ x = glyph_dimensions.width
132
+ else
133
+ x = glyph_dimensions.ascent - glyph_dimensions.descent
134
+ end
135
+ case @ctx.text_attrs.baseline_shift
136
+ when :baseline
137
+ x = 0
138
+ when :sub
139
+
140
+ when :super
141
+ x = -x
142
+ when /[-+]?(\d+)%/
143
+ m = $1 == '-' ? -1.0 : 1.0
144
+ x = (m * x * $1.to_f / 100.0)
145
+ else
146
+ x = -@ctx.text_attrs.baseline_shift
147
+ end
148
+ x
149
+ end
150
+
151
+ def render_glyph(glyph_orientation, x, y, glyph)
152
+ if glyph_orientation == 0
153
+ @ctx.gc.text(x, y, enquote(glyph))
154
+ else
155
+ @ctx.gc.push
156
+ @ctx.gc.translate(x, y)
157
+ @ctx.gc.rotate(glyph_orientation)
158
+ @ctx.gc.translate(-x, -y)
159
+ @ctx.gc.text(x, y, enquote(glyph))
160
+ @ctx.gc.pop
161
+ end
162
+ end
163
+ end # class TextStrategy
164
+
165
+ class LRTextStrategy < TextStrategy
166
+ def get_word_spacing
167
+ @word_space ||= glyph_metrics(@ctx.text_attrs.glyph_orientation_horizontal, ' ')[0]
168
+ [@word_space + @ctx.text_attrs.word_spacing, 0]
169
+ end
35
170
 
36
- # Convert an array of method arguments to Float objects. If any
37
- # cannot be converted, raise ArgumentError and issue a message.
38
- def self.fmsg(*args)
39
- "at least one argument cannot be converted to Float (got #{args.collect {|a| a.class}.join(', ')})"
171
+ def get_letter_spacing(glyph)
172
+ gx, gy = glyph_metrics(@ctx.text_attrs.glyph_orientation_horizontal, glyph)
173
+ [gx+@ctx.text_attrs.letter_spacing, gy]
40
174
  end
41
175
 
42
- def self.convert_to_float(*args)
43
- allow_nil = false
44
- if args.last == :allow_nil
45
- allow_nil = true
46
- args.pop
176
+ def render(x, y, text)
177
+ x_rel_coords, y_rel_coords = text_rel_coords(text)
178
+ dx = x_rel_coords.inject(0) {|sum, a| sum + a}
179
+ dy = y_rel_coords.max
180
+
181
+ # We're handling the anchoring.
182
+ @ctx.gc.push
183
+ @ctx.gc.text_anchor(Magick::StartAnchor)
184
+ if @ctx.text_attrs.text_anchor == :end
185
+ x -= dx
186
+ elsif @ctx.text_attrs.text_anchor == :middle
187
+ x -= dx / 2
188
+ end
189
+
190
+ # Align the first glyph
191
+ case @ctx.text_attrs.glyph_orientation_horizontal
192
+ when 0
193
+
194
+ when 90
195
+ y -= dy
196
+ when 180
197
+ x += x_rel_coords.shift
198
+ x_rel_coords << 0
199
+ y -= dy
200
+ when 270
201
+ x += x_rel_coords[0]
202
+ end
203
+
204
+ y += shift_baseline(@ctx.text_attrs.glyph_orientation_horizontal, text[0,1])
205
+
206
+ first_word = true
207
+ text.split(::Magick::RVG::WORD_SEP).each do |word|
208
+ unless first_word
209
+ x += x_rel_coords.shift
47
210
  end
48
- begin
49
- fargs = args.collect { |a| (allow_nil && a.nil?) ? a : Float(a) }
50
- rescue ArgumentError, TypeError
51
- raise ArgumentError, fmsg(*args)
211
+ first_word = false
212
+ word.split('').each do |glyph|
213
+ render_glyph(@ctx.text_attrs.glyph_orientation_horizontal, x, y, glyph)
214
+ x += x_rel_coords.shift
52
215
  end
53
- fargs
216
+ end
217
+
218
+ @ctx.gc.pop
219
+ [dx, 0]
54
220
  end
221
+ end # class LRTextStrategy
55
222
 
56
- def self.convert_one_to_float(arg)
57
- begin
58
- farg = Float(arg)
59
- rescue ArgumentError, TypeError
60
- raise ArgumentError, "argument cannot be converted to Float (got #{arg.class})"
223
+ class RLTextStrategy < TextStrategy
224
+ def render(x, y, text)
225
+ fail NotImplementedError
226
+ end
227
+ end # class RLTextStrategy
228
+
229
+ class TBTextStrategy < TextStrategy
230
+ def get_word_spacing
231
+ @word_space ||= glyph_metrics(@ctx.text_attrs.glyph_orientation_vertical, ' ')[1]
232
+ [0, @word_space + @ctx.text_attrs.word_spacing]
233
+ end
234
+
235
+ def get_letter_spacing(glyph)
236
+ gx, gy = glyph_metrics(@ctx.text_attrs.glyph_orientation_vertical, glyph)
237
+ [gx, gy+@ctx.text_attrs.letter_spacing]
238
+ end
239
+
240
+ def render(x, y, text)
241
+ x_rel_coords, y_rel_coords = text_rel_coords(text)
242
+ dx = x_rel_coords.max
243
+ dy = y_rel_coords.inject(0) {|sum, a| sum + a}
244
+
245
+ # We're handling the anchoring.
246
+ @ctx.gc.push
247
+ @ctx.gc.text_anchor(Magick::StartAnchor)
248
+ if @ctx.text_attrs.text_anchor == :end
249
+ y -= dy
250
+ elsif @ctx.text_attrs.text_anchor == :middle
251
+ y -= dy / 2
252
+ end
253
+
254
+ # Align the first glyph such that its center
255
+ # is aligned on x and its top is aligned on y.
256
+
257
+ case @ctx.text_attrs.glyph_orientation_vertical
258
+ when 0
259
+ x -= x_rel_coords.max / 2
260
+ y += y_rel_coords[0]
261
+ when 90
262
+ x -= x_rel_coords.max / 2
263
+ when 180
264
+ x += x_rel_coords.max / 2
265
+ when 270
266
+ x += x_rel_coords.max / 2
267
+ y += y_rel_coords.shift
268
+ y_rel_coords << 0 # since we used an element we need to add a dummy
269
+ end
270
+
271
+ x -= shift_baseline(@ctx.text_attrs.glyph_orientation_vertical, text[0,1])
272
+
273
+ first_word = true
274
+ text.split(::Magick::RVG::WORD_SEP).each do |word|
275
+ unless first_word
276
+ y += y_rel_coords.shift
277
+ x_rel_coords.shift
278
+ end
279
+ first_word = false
280
+ word.split('').each do |glyph|
281
+ case @ctx.text_attrs.glyph_orientation_vertical.to_i
282
+ when 0, 90, 270
283
+ x_shift = (dx - x_rel_coords.shift) / 2
284
+ when 180
285
+ x_shift = -(dx - x_rel_coords.shift) / 2
286
+ end
287
+
288
+ render_glyph(@ctx.text_attrs.glyph_orientation_vertical, x+x_shift, y, glyph)
289
+ y += y_rel_coords.shift
61
290
  end
62
- farg
291
+ end
292
+
293
+ @ctx.gc.pop
294
+ [0, dy]
295
+ end
296
+ end # class TBTextStrategy
297
+
298
+ # Handle "easy" text
299
+ class DefaultTextStrategy < TextStrategy
300
+ def render(x, y, text)
301
+ @ctx.gc.text(x, y, enquote(text))
302
+ tm = @ctx.shadow.get_type_metrics(text)
303
+ dx = case @ctx.text_attrs.text_anchor
304
+ when :start
305
+ tm.width
306
+ when :middle
307
+ tm.width / 2
308
+ when :end
309
+ 0
310
+ end
311
+ [dx, 0]
63
312
  end
64
- end # class RVG
313
+ end # class NormalTextStrategy
314
+ end # class Utility
315
+ end # class RVG
65
316
  end # module Magick
66
317
 
67
318
  module Magick
68
- class RVG
69
- class Utility
70
- class TextStrategy
71
- def initialize(context)
72
- @ctx = context
73
- @ctx.shadow.affine = @ctx.text_attrs.affine
74
- end
75
-
76
- def enquote(text)
77
- if text.length > 2 && /\A(?:\"[^\"]+\"|\'[^\']+\'|\{[^\}]+\})\z/.match(text)
78
- return text
79
- elsif !text['\'']
80
- text = '\''+text+'\''
81
- return text
82
- elsif !text['"']
83
- text = '"'+text+'"'
84
- return text
85
- elsif !(text['{'] || text['}'])
86
- text = '{'+text+'}'
87
- return text
88
- end
89
-
90
- # escape existing braces, surround with braces
91
- text.gsub!(/[}]/) { |b| '\\' + b }
92
- '{' + text + '}'
93
- end
94
-
95
- def glyph_metrics(glyph_orientation, glyph)
96
- gm = @ctx.shadow.get_type_metrics('a' + glyph + 'a')
97
- gm2 = @ctx.shadow.get_type_metrics('aa')
98
- h = (gm.ascent - gm.descent + 0.5).to_i
99
- w = gm.width - gm2.width
100
- if glyph_orientation == 0 || glyph_orientation == 180
101
- [w, h]
102
- else
103
- [h, w]
104
- end
105
- end
106
-
107
- def text_rel_coords(text)
108
- y_rel_coords = []
109
- x_rel_coords = []
110
- first_word = true
111
- words = text.split(::Magick::RVG::WORD_SEP)
112
- words.each do |word|
113
- unless first_word
114
- wx, wy = get_word_spacing
115
- x_rel_coords << wx
116
- y_rel_coords << wy
117
- end
118
- first_word = false
119
- word.split('').each do |glyph|
120
- wx, wy = get_letter_spacing(glyph)
121
- x_rel_coords << wx
122
- y_rel_coords << wy
123
- end
124
- end
125
- [x_rel_coords, y_rel_coords]
126
- end
127
-
128
- def shift_baseline(glyph_orientation, glyph)
129
- glyph_dimensions = @ctx.shadow.get_type_metrics(glyph)
130
- if glyph_orientation == 0 || glyph_orientation == 180
131
- x = glyph_dimensions.width
132
- else
133
- x = glyph_dimensions.ascent - glyph_dimensions.descent
134
- end
135
- case @ctx.text_attrs.baseline_shift
136
- when :baseline
137
- x = 0
138
- when :sub
139
-
140
- when :super
141
- x = -x
142
- when /[-+]?(\d+)%/
143
- m = $1 == '-' ? -1.0 : 1.0
144
- x = (m * x * $1.to_f / 100.0)
145
- else
146
- x = -@ctx.text_attrs.baseline_shift
147
- end
148
- x
149
- end
150
-
151
- def render_glyph(glyph_orientation, x, y, glyph)
152
- if glyph_orientation == 0
153
- @ctx.gc.text(x, y, enquote(glyph))
154
- else
155
- @ctx.gc.push
156
- @ctx.gc.translate(x, y)
157
- @ctx.gc.rotate(glyph_orientation)
158
- @ctx.gc.translate(-x, -y)
159
- @ctx.gc.text(x, y, enquote(glyph))
160
- @ctx.gc.pop
161
- end
162
- end
163
- end # class TextStrategy
164
-
165
- class LRTextStrategy < TextStrategy
166
- def get_word_spacing
167
- @word_space ||= glyph_metrics(@ctx.text_attrs.glyph_orientation_horizontal, ' ')[0]
168
- [@word_space + @ctx.text_attrs.word_spacing, 0]
169
- end
170
-
171
- def get_letter_spacing(glyph)
172
- gx, gy = glyph_metrics(@ctx.text_attrs.glyph_orientation_horizontal, glyph)
173
- [gx+@ctx.text_attrs.letter_spacing, gy]
174
- end
175
-
176
- def render(x, y, text)
177
- x_rel_coords, y_rel_coords = text_rel_coords(text)
178
- dx = x_rel_coords.inject(0) {|sum, a| sum + a}
179
- dy = y_rel_coords.max
180
-
181
- # We're handling the anchoring.
182
- @ctx.gc.push
183
- @ctx.gc.text_anchor(Magick::StartAnchor)
184
- if @ctx.text_attrs.text_anchor == :end
185
- x -= dx
186
- elsif @ctx.text_attrs.text_anchor == :middle
187
- x -= dx / 2
188
- end
189
-
190
- # Align the first glyph
191
- case @ctx.text_attrs.glyph_orientation_horizontal
192
- when 0
193
-
194
- when 90
195
- y -= dy
196
- when 180
197
- x += x_rel_coords.shift
198
- x_rel_coords << 0
199
- y -= dy
200
- when 270
201
- x += x_rel_coords[0]
202
- end
203
-
204
- y += shift_baseline(@ctx.text_attrs.glyph_orientation_horizontal, text[0,1])
205
-
206
- first_word = true
207
- text.split(::Magick::RVG::WORD_SEP).each do |word|
208
- unless first_word
209
- x += x_rel_coords.shift
210
- end
211
- first_word = false
212
- word.split('').each do |glyph|
213
- render_glyph(@ctx.text_attrs.glyph_orientation_horizontal, x, y, glyph)
214
- x += x_rel_coords.shift
215
- end
216
- end
217
-
218
- @ctx.gc.pop
219
- [dx, 0]
220
- end
221
- end # class LRTextStrategy
222
-
223
- class RLTextStrategy < TextStrategy
224
- def render(x, y, text)
225
- fail NotImplementedError
226
- end
227
- end # class RLTextStrategy
228
-
229
- class TBTextStrategy < TextStrategy
230
- def get_word_spacing
231
- @word_space ||= glyph_metrics(@ctx.text_attrs.glyph_orientation_vertical, ' ')[1]
232
- [0, @word_space + @ctx.text_attrs.word_spacing]
233
- end
234
-
235
- def get_letter_spacing(glyph)
236
- gx, gy = glyph_metrics(@ctx.text_attrs.glyph_orientation_vertical, glyph)
237
- [gx, gy+@ctx.text_attrs.letter_spacing]
238
- end
239
-
240
- def render(x, y, text)
241
- x_rel_coords, y_rel_coords = text_rel_coords(text)
242
- dx = x_rel_coords.max
243
- dy = y_rel_coords.inject(0) {|sum, a| sum + a}
244
-
245
- # We're handling the anchoring.
246
- @ctx.gc.push
247
- @ctx.gc.text_anchor(Magick::StartAnchor)
248
- if @ctx.text_attrs.text_anchor == :end
249
- y -= dy
250
- elsif @ctx.text_attrs.text_anchor == :middle
251
- y -= dy / 2
252
- end
253
-
254
- # Align the first glyph such that its center
255
- # is aligned on x and its top is aligned on y.
256
-
257
- case @ctx.text_attrs.glyph_orientation_vertical
258
- when 0
259
- x -= x_rel_coords.max / 2
260
- y += y_rel_coords[0]
261
- when 90
262
- x -= x_rel_coords.max / 2
263
- when 180
264
- x += x_rel_coords.max / 2
265
- when 270
266
- x += x_rel_coords.max / 2
267
- y += y_rel_coords.shift
268
- y_rel_coords << 0 # since we used an element we need to add a dummy
269
- end
270
-
271
- x -= shift_baseline(@ctx.text_attrs.glyph_orientation_vertical, text[0,1])
272
-
273
- first_word = true
274
- text.split(::Magick::RVG::WORD_SEP).each do |word|
275
- unless first_word
276
- y += y_rel_coords.shift
277
- x_rel_coords.shift
278
- end
279
- first_word = false
280
- word.split('').each do |glyph|
281
- case @ctx.text_attrs.glyph_orientation_vertical.to_i
282
- when 0, 90, 270
283
- x_shift = (dx - x_rel_coords.shift) / 2
284
- when 180
285
- x_shift = -(dx - x_rel_coords.shift) / 2
286
- end
287
-
288
- render_glyph(@ctx.text_attrs.glyph_orientation_vertical, x+x_shift, y, glyph)
289
- y += y_rel_coords.shift
290
- end
291
- end
292
-
293
- @ctx.gc.pop
294
- [0, dy]
295
- end
296
- end # class TBTextStrategy
297
-
298
- # Handle "easy" text
299
- class DefaultTextStrategy < TextStrategy
300
- def render(x, y, text)
301
- @ctx.gc.text(x, y, enquote(text))
302
- tm = @ctx.shadow.get_type_metrics(text)
303
- dx = case @ctx.text_attrs.text_anchor
304
- when :start
305
- tm.width
306
- when :middle
307
- tm.width / 2
308
- when :end
309
- 0
310
- end
311
- [dx, 0]
312
- end
313
- end # class NormalTextStrategy
314
- end # class Utility
315
- end # class RVG
316
- end # module Magick
319
+ class RVG
320
+ class Utility
321
+ class TextAttributes
322
+ public
323
+
324
+ WRITING_MODE = %w{lr-tb lr rl-tb rl tb-rl tb}
325
+
326
+ def initialize
327
+ @affine = []
328
+ @affine << Magick::AffineMatrix.new(1, 0, 0, 1, 0, 0)
329
+ @baseline_shift = []
330
+ @baseline_shift << :baseline
331
+ @glyph_orientation_horizontal = []
332
+ @glyph_orientation_horizontal << 0
333
+ @glyph_orientation_vertical = []
334
+ @glyph_orientation_vertical << 90
335
+ @letter_spacing = []
336
+ @letter_spacing << 0
337
+ @text_anchor = []
338
+ @text_anchor << :start
339
+ @word_spacing = []
340
+ @word_spacing << 0
341
+ @writing_mode = []
342
+ @writing_mode << 'lr-tb'
343
+ end
317
344
 
318
- module Magick
319
- class RVG
320
- class Utility
321
- class TextAttributes
322
- public
323
-
324
- WRITING_MODE = %w{lr-tb lr rl-tb rl tb-rl tb}
325
-
326
- def initialize
327
- @affine = []
328
- @affine << Magick::AffineMatrix.new(1, 0, 0, 1, 0, 0)
329
- @baseline_shift = []
330
- @baseline_shift << :baseline
331
- @glyph_orientation_horizontal = []
332
- @glyph_orientation_horizontal << 0
333
- @glyph_orientation_vertical = []
334
- @glyph_orientation_vertical << 90
335
- @letter_spacing = []
336
- @letter_spacing << 0
337
- @text_anchor = []
338
- @text_anchor << :start
339
- @word_spacing = []
340
- @word_spacing << 0
341
- @writing_mode = []
342
- @writing_mode << 'lr-tb'
343
- end
344
-
345
- def push
346
- @affine.push(@affine.last.dup)
347
- @baseline_shift.push(@baseline_shift.last)
348
- @text_anchor.push(@text_anchor.last)
349
- @writing_mode.push(@writing_mode.last.dup)
350
- @glyph_orientation_vertical.push(@glyph_orientation_vertical.last)
351
- @glyph_orientation_horizontal.push(@glyph_orientation_horizontal.last)
352
- @letter_spacing.push(@letter_spacing.last)
353
- @word_spacing.push(@word_spacing.last)
354
- end
355
-
356
- def pop
357
- @affine.pop
358
- @baseline_shift.pop
359
- @text_anchor.pop
360
- @writing_mode.pop
361
- @glyph_orientation_vertical.pop
362
- @glyph_orientation_horizontal.pop
363
- @letter_spacing.pop
364
- @word_spacing.pop
365
- end
366
-
367
- def set_affine(sx, rx, ry, sy, tx, ty)
368
- @affine[-1].sx = sx
369
- @affine[-1].rx = rx
370
- @affine[-1].ry = ry
371
- @affine[-1].sy = sy
372
- @affine[-1].tx = tx
373
- @affine[-1].ty = ty
374
- end
375
-
376
- def affine
377
- @affine[-1]
378
- end
379
-
380
- def baseline_shift
381
- @baseline_shift[-1]
382
- end
383
-
384
- def baseline_shift=(value)
385
- @baseline_shift[-1] = value
386
- end
387
-
388
- def text_anchor
389
- @text_anchor[-1]
390
- end
391
-
392
- def text_anchor=(anchor)
393
- @text_anchor[-1] = anchor
394
- end
395
-
396
- def glyph_orientation_vertical
397
- @glyph_orientation_vertical[-1]
398
- end
399
-
400
- def glyph_orientation_vertical=(angle)
401
- @glyph_orientation_vertical[-1] = angle
402
- end
403
-
404
- def glyph_orientation_horizontal
405
- @glyph_orientation_horizontal[-1]
406
- end
407
-
408
- def glyph_orientation_horizontal=(angle)
409
- @glyph_orientation_horizontal[-1] = angle
410
- end
411
-
412
- def letter_spacing
413
- @letter_spacing[-1]
414
- end
415
-
416
- def letter_spacing=(value)
417
- @letter_spacing[-1] = value
418
- end
419
-
420
- def non_default?
421
- @baseline_shift[-1] != :baseline || @letter_spacing[-1] != 0 ||
422
- @word_spacing[-1] != 0 || @writing_mode[-1][/\Alr/].nil? ||
423
- @glyph_orientation_horizontal[-1] != 0
424
- end
425
-
426
- def word_spacing
427
- @word_spacing[-1]
428
- end
429
-
430
- def word_spacing=(value)
431
- @word_spacing[-1] = value
432
- end
433
-
434
- def writing_mode
435
- @writing_mode[-1]
436
- end
437
-
438
- def writing_mode=(mode)
439
- @writing_mode[-1] = WRITING_MODE.include?(mode) ? mode : 'lr-tb'
440
- end
441
- end # class TextAttributes
442
-
443
- class GraphicContext
444
- FONT_STRETCH = {:normal => Magick::NormalStretch,
445
- :ultra_condensed => Magick::UltraCondensedStretch,
446
- :extra_condensed => Magick::ExtraCondensedStretch,
447
- :condensed => Magick::CondensedStretch,
448
- :semi_condensed => Magick::SemiCondensedStretch,
449
- :semi_expanded => Magick::SemiExpandedStretch,
450
- :expanded => Magick::ExpandedStretch,
451
- :extra_expanded => Magick::ExtraExpandedStretch,
452
- :ultra_expanded => Magick::UltraExpandedStretch}
453
-
454
- FONT_STYLE = {:normal => Magick::NormalStyle,
455
- :italic => Magick::ItalicStyle,
456
- :oblique => Magick::ObliqueStyle}
457
-
458
- FONT_WEIGHT = {'normal' => Magick::NormalWeight,
459
- 'bold' => Magick::BoldWeight,
460
- 'bolder' => Magick::BolderWeight,
461
- 'lighter' => Magick::LighterWeight}
462
-
463
- TEXT_ANCHOR = {:start => Magick::StartAnchor,
464
- :middle => Magick::MiddleAnchor,
465
- :end => Magick::EndAnchor}
466
-
467
- ANCHOR_TO_ALIGN = {:start => Magick::LeftAlign,
468
- :middle => Magick::CenterAlign,
469
- :end => Magick::RightAlign}
470
-
471
- TEXT_DECORATION = {:none => Magick::NoDecoration,
472
- :underline => Magick::UnderlineDecoration,
473
- :overline => Magick::OverlineDecoration,
474
- :line_through => Magick::LineThroughDecoration}
475
-
476
- TEXT_STRATEGIES = {'lr-tb'=>LRTextStrategy, 'lr'=>LRTextStrategy,
477
- 'rt-tb'=>RLTextStrategy, 'rl'=>RLTextStrategy,
478
- 'tb-rl'=>TBTextStrategy, 'tb'=>TBTextStrategy}
479
-
480
- def self.degrees_to_radians(deg)
481
- Math::PI * (deg % 360.0) / 180.0
482
- end
483
-
484
- private
485
-
486
- def init_matrix
487
- @rx = @ry = 0
488
- @sx = @sy = 1
489
- @tx = @ty = 0
490
- end
491
-
492
- def concat_matrix
493
- curr = @text_attrs.affine
494
- sx = curr.sx * @sx + curr.ry * @rx
495
- rx = curr.rx * @sx + curr.sy * @rx
496
- ry = curr.sx * @ry + curr.ry * @sy
497
- sy = curr.rx * @ry + curr.sy * @sy
498
- tx = curr.sx * @tx + curr.ry * @ty + curr.tx
499
- ty = curr.rx * @tx + curr.sy * @ty + curr.ty
500
- @text_attrs.set_affine(sx, rx, ry, sy, tx, ty)
501
- init_matrix
502
- end
503
-
504
- public
505
-
506
- attr_reader :gc, :text_attrs
507
-
508
- def initialize
509
- @gc = Magick::Draw.new
510
- @shadow = []
511
- @shadow << Magick::Draw.new
512
- @text_attrs = TextAttributes.new
513
- init_matrix
514
- end
515
-
516
- def method_missing(methID, *args, &block)
517
- @gc.__send__(methID, *args, &block)
518
- end
519
-
520
- def affine(sx, rx, ry, sy, tx, ty)
521
- sx, rx, ry, sy, tx, ty = Magick::RVG.convert_to_float(sx, rx, ry, sy, tx, ty)
522
- @gc.affine(sx, rx, ry, sy, tx, ty)
523
- @text_attrs.set_affine(sx, rx, ry, sy, tx, ty)
524
- nil
525
- end
526
-
527
- def baseline_shift(value)
528
- @text_attrs.baseline_shift = case value
529
- when 'baseline', 'sub', 'super'
530
- value.intern
531
- when /[-+]?\d+%/, Numeric
532
- value
533
- else
534
- :baseline
535
- end
536
- nil
537
- end
538
-
539
- def font(name)
540
- @gc.font(name)
541
- @shadow[-1].font = name
542
- nil
543
- end
544
-
545
- def font_family(name)
546
- @gc.font_family(name)
547
- @shadow[-1].font_family = name
548
- nil
549
- end
550
-
551
- def font_size(points)
552
- @gc.font_size(points)
553
- @shadow[-1].pointsize = points
554
- nil
555
- end
556
-
557
- def font_stretch(stretch)
558
- stretch = FONT_STRETCH.fetch(stretch.intern, Magick::NormalStretch)
559
- @gc.font_stretch(stretch)
560
- @shadow[-1].font_stretch = stretch
561
- nil
562
- end
563
-
564
- def font_style(style)
565
- style = FONT_STYLE.fetch(style.intern, Magick::NormalStyle)
566
- @gc.font_style(style)
567
- @shadow[-1].font_style = style
568
- nil
569
- end
570
-
571
- def font_weight(weight)
572
- # If the arg is not in the hash use it directly. Handles numeric values.
573
- weight = FONT_WEIGHT.fetch(weight) {|key| key}
574
- @gc.font_weight(weight)
575
- @shadow[-1].font_weight = weight
576
- nil
577
- end
578
-
579
- def glyph_orientation_horizontal(deg)
580
- deg = Magick::RVG.convert_one_to_float(deg)
581
- @text_attrs.glyph_orientation_horizontal = (deg % 360) / 90 * 90
582
- nil
583
- end
584
-
585
- def glyph_orientation_vertical(deg)
586
- deg = Magick::RVG.convert_one_to_float(deg)
587
- @text_attrs.glyph_orientation_vertical = (deg % 360) / 90 * 90
588
- nil
589
- end
590
-
591
- def inspect
592
- @gc.inspect
593
- end
594
-
595
- def letter_spacing(value)
596
- @text_attrs.letter_spacing = Magick::RVG.convert_one_to_float(value)
597
- nil
598
- end
599
-
600
- def push
601
- @gc.push
602
- @shadow.push(@shadow.last.dup)
603
- @text_attrs.push
604
- nil
605
- end
606
-
607
- def pop
608
- @gc.pop
609
- @shadow.pop
610
- @text_attrs.pop
611
- nil
612
- end
613
-
614
- def rotate(degrees)
615
- degrees = Magick::RVG.convert_one_to_float(degrees)
616
- @gc.rotate(degrees)
617
- @sx = Math.cos(GraphicContext.degrees_to_radians(degrees))
618
- @rx = Math.sin(GraphicContext.degrees_to_radians(degrees))
619
- @ry = -Math.sin(GraphicContext.degrees_to_radians(degrees))
620
- @sy = Math.cos(GraphicContext.degrees_to_radians(degrees))
621
- concat_matrix
622
- nil
623
- end
624
-
625
- def scale(sx, sy)
626
- sx, sy = Magick::RVG.convert_to_float(sx, sy)
627
- @gc.scale(sx, sy)
628
- @sx, @sy = sx, sy
629
- concat_matrix
630
- nil
631
- end
632
-
633
- def shadow
634
- @shadow.last
635
- end
636
-
637
- def skewX(degrees)
638
- degrees = Magick::RVG.convert_one_to_float(degrees)
639
- @gc.skewX(degrees)
640
- @ry = Math.tan(GraphicContext.degrees_to_radians(degrees))
641
- concat_matrix
642
- nil
643
- end
644
-
645
- def skewY(degrees)
646
- degrees = Magick::RVG.convert_one_to_float(degrees)
647
- @gc.skewY(degrees)
648
- @rx = Math.tan(GraphicContext.degrees_to_radians(degrees))
649
- concat_matrix
650
- nil
651
- end
652
-
653
- def stroke_width(width)
654
- width = Magick::RVG.convert_one_to_float(width)
655
- @gc.stroke_width(width)
656
- @shadow[-1].stroke_width = width
657
- nil
658
- end
659
-
660
- def text(x, y, text)
661
- return if text.length == 0
662
- if @text_attrs.non_default?
663
- text_renderer = TEXT_STRATEGIES[@text_attrs.writing_mode].new(self)
664
- else
665
- text_renderer = DefaultTextStrategy.new(self)
666
- end
667
-
668
- text_renderer.render(x, y, text)
669
- end
670
-
671
- def text_anchor(anchor)
672
- anchor = anchor.intern
673
- anchor_enum = TEXT_ANCHOR.fetch(anchor, Magick::StartAnchor)
674
- @gc.text_anchor(anchor_enum)
675
- align = ANCHOR_TO_ALIGN.fetch(anchor, Magick::LeftAlign)
676
- @shadow[-1].align = align
677
- @text_attrs.text_anchor = anchor
678
- nil
679
- end
680
-
681
- def text_decoration(decoration)
682
- decoration = TEXT_DECORATION.fetch(decoration.intern, Magick::NoDecoration)
683
- @gc.decorate(decoration)
684
- @shadow[-1].decorate = decoration
685
- nil
686
- end
687
-
688
- def translate(tx, ty)
689
- tx, ty = Magick::RVG.convert_to_float(tx, ty)
690
- @gc.translate(tx, ty)
691
- @tx, @ty = tx, ty
692
- concat_matrix
693
- nil
694
- end
695
-
696
- def word_spacing(value)
697
- @text_attrs.word_spacing = Magick::RVG.convert_one_to_float(value)
698
- nil
699
- end
700
-
701
- def writing_mode(mode)
702
- @text_attrs.writing_mode = mode
703
- nil
704
- end
705
- end # class GraphicContext
706
- end # class Utility
707
- end # class RVG
345
+ def push
346
+ @affine.push(@affine.last.dup)
347
+ @baseline_shift.push(@baseline_shift.last)
348
+ @text_anchor.push(@text_anchor.last)
349
+ @writing_mode.push(@writing_mode.last.dup)
350
+ @glyph_orientation_vertical.push(@glyph_orientation_vertical.last)
351
+ @glyph_orientation_horizontal.push(@glyph_orientation_horizontal.last)
352
+ @letter_spacing.push(@letter_spacing.last)
353
+ @word_spacing.push(@word_spacing.last)
354
+ end
355
+
356
+ def pop
357
+ @affine.pop
358
+ @baseline_shift.pop
359
+ @text_anchor.pop
360
+ @writing_mode.pop
361
+ @glyph_orientation_vertical.pop
362
+ @glyph_orientation_horizontal.pop
363
+ @letter_spacing.pop
364
+ @word_spacing.pop
365
+ end
366
+
367
+ def set_affine(sx, rx, ry, sy, tx, ty)
368
+ @affine[-1].sx = sx
369
+ @affine[-1].rx = rx
370
+ @affine[-1].ry = ry
371
+ @affine[-1].sy = sy
372
+ @affine[-1].tx = tx
373
+ @affine[-1].ty = ty
374
+ end
375
+
376
+ def affine
377
+ @affine[-1]
378
+ end
379
+
380
+ def baseline_shift
381
+ @baseline_shift[-1]
382
+ end
383
+
384
+ def baseline_shift=(value)
385
+ @baseline_shift[-1] = value
386
+ end
387
+
388
+ def text_anchor
389
+ @text_anchor[-1]
390
+ end
391
+
392
+ def text_anchor=(anchor)
393
+ @text_anchor[-1] = anchor
394
+ end
395
+
396
+ def glyph_orientation_vertical
397
+ @glyph_orientation_vertical[-1]
398
+ end
399
+
400
+ def glyph_orientation_vertical=(angle)
401
+ @glyph_orientation_vertical[-1] = angle
402
+ end
403
+
404
+ def glyph_orientation_horizontal
405
+ @glyph_orientation_horizontal[-1]
406
+ end
407
+
408
+ def glyph_orientation_horizontal=(angle)
409
+ @glyph_orientation_horizontal[-1] = angle
410
+ end
411
+
412
+ def letter_spacing
413
+ @letter_spacing[-1]
414
+ end
415
+
416
+ def letter_spacing=(value)
417
+ @letter_spacing[-1] = value
418
+ end
419
+
420
+ def non_default?
421
+ @baseline_shift[-1] != :baseline || @letter_spacing[-1] != 0 ||
422
+ @word_spacing[-1] != 0 || @writing_mode[-1][/\Alr/].nil? ||
423
+ @glyph_orientation_horizontal[-1] != 0
424
+ end
425
+
426
+ def word_spacing
427
+ @word_spacing[-1]
428
+ end
429
+
430
+ def word_spacing=(value)
431
+ @word_spacing[-1] = value
432
+ end
433
+
434
+ def writing_mode
435
+ @writing_mode[-1]
436
+ end
437
+
438
+ def writing_mode=(mode)
439
+ @writing_mode[-1] = WRITING_MODE.include?(mode) ? mode : 'lr-tb'
440
+ end
441
+ end # class TextAttributes
442
+
443
+ class GraphicContext
444
+ FONT_STRETCH = {
445
+ :normal => Magick::NormalStretch,
446
+ :ultra_condensed => Magick::UltraCondensedStretch,
447
+ :extra_condensed => Magick::ExtraCondensedStretch,
448
+ :condensed => Magick::CondensedStretch,
449
+ :semi_condensed => Magick::SemiCondensedStretch,
450
+ :semi_expanded => Magick::SemiExpandedStretch,
451
+ :expanded => Magick::ExpandedStretch,
452
+ :extra_expanded => Magick::ExtraExpandedStretch,
453
+ :ultra_expanded => Magick::UltraExpandedStretch
454
+ }
455
+
456
+ FONT_STYLE = {
457
+ :normal => Magick::NormalStyle,
458
+ :italic => Magick::ItalicStyle,
459
+ :oblique => Magick::ObliqueStyle
460
+ }
461
+
462
+ FONT_WEIGHT = {
463
+ 'normal' => Magick::NormalWeight,
464
+ 'bold' => Magick::BoldWeight,
465
+ 'bolder' => Magick::BolderWeight,
466
+ 'lighter' => Magick::LighterWeight
467
+ }
468
+
469
+ TEXT_ANCHOR = {
470
+ :start => Magick::StartAnchor,
471
+ :middle => Magick::MiddleAnchor,
472
+ :end => Magick::EndAnchor
473
+ }
474
+
475
+ ANCHOR_TO_ALIGN = {
476
+ :start => Magick::LeftAlign,
477
+ :middle => Magick::CenterAlign,
478
+ :end => Magick::RightAlign
479
+ }
480
+
481
+ TEXT_DECORATION = {
482
+ :none => Magick::NoDecoration,
483
+ :underline => Magick::UnderlineDecoration,
484
+ :overline => Magick::OverlineDecoration,
485
+ :line_through => Magick::LineThroughDecoration}
486
+
487
+ TEXT_STRATEGIES = {
488
+ 'lr-tb' => LRTextStrategy, 'lr' => LRTextStrategy,
489
+ 'rt-tb' => RLTextStrategy, 'rl' => RLTextStrategy,
490
+ 'tb-rl' => TBTextStrategy, 'tb' => TBTextStrategy
491
+ }
492
+
493
+ def self.degrees_to_radians(deg)
494
+ Math::PI * (deg % 360.0) / 180.0
495
+ end
496
+
497
+ private
498
+
499
+ def init_matrix
500
+ @rx = @ry = 0
501
+ @sx = @sy = 1
502
+ @tx = @ty = 0
503
+ end
504
+
505
+ def concat_matrix
506
+ curr = @text_attrs.affine
507
+ sx = curr.sx * @sx + curr.ry * @rx
508
+ rx = curr.rx * @sx + curr.sy * @rx
509
+ ry = curr.sx * @ry + curr.ry * @sy
510
+ sy = curr.rx * @ry + curr.sy * @sy
511
+ tx = curr.sx * @tx + curr.ry * @ty + curr.tx
512
+ ty = curr.rx * @tx + curr.sy * @ty + curr.ty
513
+ @text_attrs.set_affine(sx, rx, ry, sy, tx, ty)
514
+ init_matrix
515
+ end
516
+
517
+ public
518
+
519
+ attr_reader :gc, :text_attrs
520
+
521
+ def initialize
522
+ @gc = Magick::Draw.new
523
+ @shadow = []
524
+ @shadow << Magick::Draw.new
525
+ @text_attrs = TextAttributes.new
526
+ init_matrix
527
+ end
528
+
529
+ def method_missing(methID, *args, &block)
530
+ @gc.__send__(methID, *args, &block)
531
+ end
532
+
533
+ def affine(sx, rx, ry, sy, tx, ty)
534
+ sx, rx, ry, sy, tx, ty = Magick::RVG.convert_to_float(sx, rx, ry, sy, tx, ty)
535
+ @gc.affine(sx, rx, ry, sy, tx, ty)
536
+ @text_attrs.set_affine(sx, rx, ry, sy, tx, ty)
537
+ nil
538
+ end
539
+
540
+ def baseline_shift(value)
541
+ @text_attrs.baseline_shift = case value
542
+ when 'baseline', 'sub', 'super'
543
+ value.intern
544
+ when /[-+]?\d+%/, Numeric
545
+ value
546
+ else
547
+ :baseline
548
+ end
549
+ nil
550
+ end
551
+
552
+ def font(name)
553
+ @gc.font(name)
554
+ @shadow[-1].font = name
555
+ nil
556
+ end
557
+
558
+ def font_family(name)
559
+ @gc.font_family(name)
560
+ @shadow[-1].font_family = name
561
+ nil
562
+ end
563
+
564
+ def font_size(points)
565
+ @gc.font_size(points)
566
+ @shadow[-1].pointsize = points
567
+ nil
568
+ end
569
+
570
+ def font_stretch(stretch)
571
+ stretch = FONT_STRETCH.fetch(stretch.intern, Magick::NormalStretch)
572
+ @gc.font_stretch(stretch)
573
+ @shadow[-1].font_stretch = stretch
574
+ nil
575
+ end
576
+
577
+ def font_style(style)
578
+ style = FONT_STYLE.fetch(style.intern, Magick::NormalStyle)
579
+ @gc.font_style(style)
580
+ @shadow[-1].font_style = style
581
+ nil
582
+ end
583
+
584
+ def font_weight(weight)
585
+ # If the arg is not in the hash use it directly. Handles numeric values.
586
+ weight = FONT_WEIGHT.fetch(weight) {|key| key}
587
+ @gc.font_weight(weight)
588
+ @shadow[-1].font_weight = weight
589
+ nil
590
+ end
591
+
592
+ def glyph_orientation_horizontal(deg)
593
+ deg = Magick::RVG.convert_one_to_float(deg)
594
+ @text_attrs.glyph_orientation_horizontal = (deg % 360) / 90 * 90
595
+ nil
596
+ end
597
+
598
+ def glyph_orientation_vertical(deg)
599
+ deg = Magick::RVG.convert_one_to_float(deg)
600
+ @text_attrs.glyph_orientation_vertical = (deg % 360) / 90 * 90
601
+ nil
602
+ end
603
+
604
+ def inspect
605
+ @gc.inspect
606
+ end
607
+
608
+ def letter_spacing(value)
609
+ @text_attrs.letter_spacing = Magick::RVG.convert_one_to_float(value)
610
+ nil
611
+ end
612
+
613
+ def push
614
+ @gc.push
615
+ @shadow.push(@shadow.last.dup)
616
+ @text_attrs.push
617
+ nil
618
+ end
619
+
620
+ def pop
621
+ @gc.pop
622
+ @shadow.pop
623
+ @text_attrs.pop
624
+ nil
625
+ end
626
+
627
+ def rotate(degrees)
628
+ degrees = Magick::RVG.convert_one_to_float(degrees)
629
+ @gc.rotate(degrees)
630
+ @sx = Math.cos(GraphicContext.degrees_to_radians(degrees))
631
+ @rx = Math.sin(GraphicContext.degrees_to_radians(degrees))
632
+ @ry = -Math.sin(GraphicContext.degrees_to_radians(degrees))
633
+ @sy = Math.cos(GraphicContext.degrees_to_radians(degrees))
634
+ concat_matrix
635
+ nil
636
+ end
637
+
638
+ def scale(sx, sy)
639
+ sx, sy = Magick::RVG.convert_to_float(sx, sy)
640
+ @gc.scale(sx, sy)
641
+ @sx = sx
642
+ @sy = sy
643
+ concat_matrix
644
+ nil
645
+ end
646
+
647
+ def shadow
648
+ @shadow.last
649
+ end
650
+
651
+ def skewX(degrees)
652
+ degrees = Magick::RVG.convert_one_to_float(degrees)
653
+ @gc.skewX(degrees)
654
+ @ry = Math.tan(GraphicContext.degrees_to_radians(degrees))
655
+ concat_matrix
656
+ nil
657
+ end
658
+
659
+ def skewY(degrees)
660
+ degrees = Magick::RVG.convert_one_to_float(degrees)
661
+ @gc.skewY(degrees)
662
+ @rx = Math.tan(GraphicContext.degrees_to_radians(degrees))
663
+ concat_matrix
664
+ nil
665
+ end
666
+
667
+ def stroke_width(width)
668
+ width = Magick::RVG.convert_one_to_float(width)
669
+ @gc.stroke_width(width)
670
+ @shadow[-1].stroke_width = width
671
+ nil
672
+ end
673
+
674
+ def text(x, y, text)
675
+ return if text.length == 0
676
+ if @text_attrs.non_default?
677
+ text_renderer = TEXT_STRATEGIES[@text_attrs.writing_mode].new(self)
678
+ else
679
+ text_renderer = DefaultTextStrategy.new(self)
680
+ end
681
+
682
+ text_renderer.render(x, y, text)
683
+ end
684
+
685
+ def text_anchor(anchor)
686
+ anchor = anchor.intern
687
+ anchor_enum = TEXT_ANCHOR.fetch(anchor, Magick::StartAnchor)
688
+ @gc.text_anchor(anchor_enum)
689
+ align = ANCHOR_TO_ALIGN.fetch(anchor, Magick::LeftAlign)
690
+ @shadow[-1].align = align
691
+ @text_attrs.text_anchor = anchor
692
+ nil
693
+ end
694
+
695
+ def text_decoration(decoration)
696
+ decoration = TEXT_DECORATION.fetch(decoration.intern, Magick::NoDecoration)
697
+ @gc.decorate(decoration)
698
+ @shadow[-1].decorate = decoration
699
+ nil
700
+ end
701
+
702
+ def translate(tx, ty)
703
+ tx, ty = Magick::RVG.convert_to_float(tx, ty)
704
+ @gc.translate(tx, ty)
705
+ @tx = tx
706
+ @ty = ty
707
+ concat_matrix
708
+ nil
709
+ end
710
+
711
+ def word_spacing(value)
712
+ @text_attrs.word_spacing = Magick::RVG.convert_one_to_float(value)
713
+ nil
714
+ end
715
+
716
+ def writing_mode(mode)
717
+ @text_attrs.writing_mode = mode
718
+ nil
719
+ end
720
+ end # class GraphicContext
721
+ end # class Utility
722
+ end # class RVG
708
723
  end # module Magick