rmagick4j 0.3.1-java → 0.3.2-java

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