lib/rvg/misc.rb in rmagick-1.14.1 vs lib/rvg/misc.rb in rmagick-1.15.0
- old
+ new
@@ -1,729 +1,739 @@
-# $Id: misc.rb,v 1.9 2005/12/31 14:41:04 rmagick Exp $
-# Copyright (C) 2006 Timothy P. Hunter
-class Magick::RVG
+# $Id: misc.rb,v 1.10 2007/01/20 17:39:49 rmagick Exp $
+# Copyright (C) 2007 Timothy P. Hunter
+module Magick
+ class RVG
- # This is a standard deep_copy method that is used in most classes.
- # Thanks to Robert Klemme.
- module Duplicatable
+ # This is a standard deep_copy method that is used in most classes.
+ # Thanks to Robert Klemme.
+ module Duplicatable
- def deep_copy(h = {})
- # Prevent recursion. If we reach the
- # object we started with, stop copying.
- copy = h[__id__]
- unless copy
- h[__id__] = copy = self.class.allocate
- ivars = instance_variables
- ivars.each do |ivar|
- ivalue = instance_variable_get(ivar)
- cvalue = case
- when NilClass === ivalue, Symbol === ivalue, Float === ivalue,
- Fixnum === ivalue, FalseClass === ivalue, TrueClass === ivalue
- ivalue
- when ivalue.respond_to?(:deep_copy)
- ivalue.deep_copy(h)
- when ivalue.respond_to?(:dup)
- ivalue.dup
- else
- ivalue
- end
- copy.instance_variable_set(ivar, cvalue)
+ def deep_copy(h = {})
+ # Prevent recursion. If we reach the
+ # object we started with, stop copying.
+ copy = h[__id__]
+ unless copy
+ h[__id__] = copy = self.class.allocate
+ ivars = instance_variables
+ ivars.each do |ivar|
+ ivalue = instance_variable_get(ivar)
+ cvalue = case
+ when NilClass === ivalue, Symbol === ivalue, Float === ivalue,
+ Fixnum === ivalue, FalseClass === ivalue, TrueClass === ivalue
+ ivalue
+ when ivalue.respond_to?(:deep_copy)
+ ivalue.deep_copy(h)
+ when ivalue.respond_to?(:dup)
+ ivalue.dup
+ else
+ ivalue
+ end
+ copy.instance_variable_set(ivar, cvalue)
+ end
+ copy.freeze if frozen?
end
- copy.freeze if frozen?
+ return copy
end
- return copy
- end
- end # module Magick::RVG::Duplicatable
+ end # module Duplicatable
- # Convert an array of method arguments to Float objects. If any
- # cannot be converted, raise ArgumentError and issue a message.
- def self.fmsg(*args)
- "at least one argument cannot be converted to Float (got #{args.collect {|a| a.class}.join(', ')})"
- end
+ # Convert an array of method arguments to Float objects. If any
+ # cannot be converted, raise ArgumentError and issue a message.
+ def self.fmsg(*args)
+ "at least one argument cannot be converted to Float (got #{args.collect {|a| a.class}.join(', ')})"
+ end
- def self.convert_to_float(*args)
- allow_nil = false
- if args.last == :allow_nil
- allow_nil = true
- args.pop
+ def self.convert_to_float(*args)
+ allow_nil = false
+ if args.last == :allow_nil
+ allow_nil = true
+ args.pop
+ end
+ begin
+ fargs = args.collect { |a| (allow_nil && a.nil?) ? a : Float(a) }
+ rescue ArgumentError, TypeError
+ raise ArgumentError, self.fmsg(*args)
+ end
+ return fargs
end
- begin
- fargs = args.collect { |a| (allow_nil && a.nil?) ? a : Float(a) }
- rescue ArgumentError, TypeError
- raise ArgumentError, self.fmsg(*args)
- end
- return fargs
- end
- def self.convert_one_to_float(arg)
- begin
- farg = Float(arg)
- rescue ArgumentError, TypeError
- raise ArgumentError, "argument cannot be converted to Float (got #{arg.class})"
+ def self.convert_one_to_float(arg)
+ begin
+ farg = Float(arg)
+ rescue ArgumentError, TypeError
+ raise ArgumentError, "argument cannot be converted to Float (got #{arg.class})"
+ end
+ return farg
end
- return farg
- end
-end # class Magick::RVG
+ end # class RVG
+end # module Magick
-module Magick::RVG::Utility
+module Magick
+ class RVG
+ class Utility
- class TextStrategy
+ class TextStrategy
- def initialize(context)
- @ctx = context
- @ctx.shadow.affine = @ctx.text_attrs.affine
- end
+ def initialize(context)
+ @ctx = context
+ @ctx.shadow.affine = @ctx.text_attrs.affine
+ end
- def enquote(text)
- if text.length > 2 && /\A(?:\"[^\"]+\"|\'[^\']+\'|\{[^\}]+\})\z/.match(text)
- return text
- elsif !text['\'']
- text = '\''+text+'\''
- return text
- elsif !text['"']
- text = '"'+text+'"'
- return text
- elsif !(text['{'] || text['}'])
- text = '{'+text+'}'
- return text
- end
+ def enquote(text)
+ if text.length > 2 && /\A(?:\"[^\"]+\"|\'[^\']+\'|\{[^\}]+\})\z/.match(text)
+ return text
+ elsif !text['\'']
+ text = '\''+text+'\''
+ return text
+ elsif !text['"']
+ text = '"'+text+'"'
+ return text
+ elsif !(text['{'] || text['}'])
+ text = '{'+text+'}'
+ return text
+ end
- # escape existing braces, surround with braces
- text.gsub!(/[}]/) { |b| '\\' + b }
- return '{' + text + '}'
- end
+ # escape existing braces, surround with braces
+ text.gsub!(/[}]/) { |b| '\\' + b }
+ return '{' + text + '}'
+ end
- def glyph_metrics(glyph_orientation, glyph)
- glyph_metrics = @ctx.shadow.get_type_metrics(glyph)
- h = glyph_metrics.ascent - glyph_metrics.descent
- w = glyph_metrics.width
- if glyph_orientation == 0 || glyph_orientation == 180
- [w, h]
- else
- [h, w]
- end
- end
+ def glyph_metrics(glyph_orientation, glyph)
+ glyph_metrics = @ctx.shadow.get_type_metrics(glyph)
+ h = glyph_metrics.ascent - glyph_metrics.descent
+ w = glyph_metrics.width
+ if glyph_orientation == 0 || glyph_orientation == 180
+ [w, h]
+ else
+ [h, w]
+ end
+ end
- def text_rel_coords(text)
- y_rel_coords = []
- x_rel_coords = []
- first_word = true
- words = text.split(::Magick::RVG::WORD_SEP)
- words.each do |word|
- unless first_word
- wx, wy = get_word_spacing()
- x_rel_coords << wx
- y_rel_coords << wy
+ def text_rel_coords(text)
+ y_rel_coords = []
+ x_rel_coords = []
+ first_word = true
+ words = text.split(::Magick::RVG::WORD_SEP)
+ words.each do |word|
+ unless first_word
+ wx, wy = get_word_spacing()
+ x_rel_coords << wx
+ y_rel_coords << wy
+ end
+ first_word = false
+ word.split('').each do |glyph|
+ wx, wy = get_letter_spacing(glyph)
+ x_rel_coords << wx
+ y_rel_coords << wy
+ end
+ end
+ [x_rel_coords, y_rel_coords]
end
- first_word = false
- word.split('').each do |glyph|
- wx, wy = get_letter_spacing(glyph)
- x_rel_coords << wx
- y_rel_coords << wy
+
+ def shift_baseline(glyph_orientation, glyph)
+ glyph_dimensions = @ctx.shadow.get_type_metrics(glyph)
+ if glyph_orientation == 0 || glyph_orientation == 180
+ x = glyph_dimensions.width
+ else
+ x = glyph_dimensions.ascent - glyph_dimensions.descent
+ end
+ case @ctx.text_attrs.baseline_shift
+ when :baseline
+ x = 0
+ when :sub
+ ;
+ when :super
+ x = -x
+ when /[-+]?(\d+)%/
+ m = $1 == '-' ? -1.0 : 1.0
+ x = (m * x * $1.to_f / 100.0)
+ else
+ x = -@ctx.text_attrs.baseline_shift
+ end
+ return x
end
- end
- [x_rel_coords, y_rel_coords]
- end
- def shift_baseline(glyph_orientation, glyph)
- glyph_dimensions = @ctx.shadow.get_type_metrics(glyph)
- if glyph_orientation == 0 || glyph_orientation == 180
- x = glyph_dimensions.width
- else
- x = glyph_dimensions.ascent - glyph_dimensions.descent
- end
- case @ctx.text_attrs.baseline_shift
- when :baseline
- x = 0
- when :sub
- ;
- when :super
- x = -x
- when /[-+]?(\d+)%/
- m = $1 == '-' ? -1.0 : 1.0
- x = (m * x * $1.to_f / 100.0)
- else
- x = -@ctx.text_attrs.baseline_shift
- end
- return x
- end
+ def render_glyph(glyph_orientation, x, y, glyph)
+ if glyph_orientation == 0
+ @ctx.gc.text(x, y, enquote(glyph))
+ else
+ @ctx.gc.push
+ @ctx.gc.translate(x, y)
+ @ctx.gc.rotate(glyph_orientation)
+ @ctx.gc.translate(-x, -y)
+ @ctx.gc.text(x, y, enquote(glyph))
+ @ctx.gc.pop
+ end
+ end
- def render_glyph(glyph_orientation, x, y, glyph)
- if glyph_orientation == 0
- @ctx.gc.text(x, y, enquote(glyph))
- else
- @ctx.gc.push
- @ctx.gc.translate(x, y)
- @ctx.gc.rotate(glyph_orientation)
- @ctx.gc.translate(-x, -y)
- @ctx.gc.text(x, y, enquote(glyph))
- @ctx.gc.pop
- end
- end
+ end # class TextStrategy
- end # class TextStrategy
+ class LRTextStrategy < TextStrategy
- class LRTextStrategy < TextStrategy
+ def get_word_spacing()
+ @word_space ||= glyph_metrics(@ctx.text_attrs.glyph_orientation_horizontal, ' ')[0]
+ [@word_space + @ctx.text_attrs.word_spacing, 0]
+ end
- def get_word_spacing()
- @word_space ||= glyph_metrics(@ctx.text_attrs.glyph_orientation_horizontal, ' ')[0]
- [@word_space + @ctx.text_attrs.word_spacing, 0]
- end
+ def get_letter_spacing(glyph)
+ gx, gy = glyph_metrics(@ctx.text_attrs.glyph_orientation_horizontal, glyph)
+ [gx+@ctx.text_attrs.letter_spacing, gy]
+ end
- def get_letter_spacing(glyph)
- gx, gy = glyph_metrics(@ctx.text_attrs.glyph_orientation_horizontal, glyph)
- [gx+@ctx.text_attrs.letter_spacing, gy]
- end
+ def render(x, y, text)
+ x_rel_coords, y_rel_coords = text_rel_coords(text)
+ dx = x_rel_coords.inject(0) {|sum, a| sum + a}
+ dy = y_rel_coords.max
- def render(x, y, text)
- x_rel_coords, y_rel_coords = text_rel_coords(text)
- dx = x_rel_coords.inject(0) {|sum, a| sum + a}
- dy = y_rel_coords.max
+ # We're handling the anchoring.
+ @ctx.gc.push()
+ @ctx.gc.text_anchor(Magick::StartAnchor)
+ if @ctx.text_attrs.text_anchor == :end
+ x -= dx
+ elsif @ctx.text_attrs.text_anchor == :middle
+ x -= dx / 2
+ end
- # We're handling the anchoring.
- @ctx.gc.push()
- @ctx.gc.text_anchor(Magick::StartAnchor)
- if @ctx.text_attrs.text_anchor == :end
- x -= dx
- elsif @ctx.text_attrs.text_anchor == :middle
- x -= dx / 2
- end
+ # Align the first glyph
+ case @ctx.text_attrs.glyph_orientation_horizontal
+ when 0
+ ;
+ when 90
+ y -= dy
+ when 180
+ x += x_rel_coords.shift
+ x_rel_coords << 0
+ y -= dy
+ when 270
+ x += x_rel_coords[0]
+ end
- # Align the first glyph
- case @ctx.text_attrs.glyph_orientation_horizontal
- when 0
- ;
- when 90
- y -= dy
- when 180
- x += x_rel_coords.shift
- x_rel_coords << 0
- y -= dy
- when 270
- x += x_rel_coords[0]
- end
+ y += shift_baseline(@ctx.text_attrs.glyph_orientation_horizontal, text[0,1])
- y += shift_baseline(@ctx.text_attrs.glyph_orientation_horizontal, text[0,1])
+ first_word = true
+ text.split(::Magick::RVG::WORD_SEP).each do |word|
+ unless first_word
+ x += x_rel_coords.shift
+ end
+ first_word = false
+ word.split('').each do |glyph|
+ render_glyph(@ctx.text_attrs.glyph_orientation_horizontal, x, y, glyph)
+ x += x_rel_coords.shift
+ end
+ end
- first_word = true
- text.split(::Magick::RVG::WORD_SEP).each do |word|
- unless first_word
- x += x_rel_coords.shift
+ @ctx.gc.pop()
+ [dx, 0]
end
- first_word = false
- word.split('').each do |glyph|
- render_glyph(@ctx.text_attrs.glyph_orientation_horizontal, x, y, glyph)
- x += x_rel_coords.shift
- end
- end
- @ctx.gc.pop()
- [dx, 0]
- end
+ end # class LRTextStrategy
- end # class LRTextStrategy
+ class RLTextStrategy < TextStrategy
- class RLTextStrategy < TextStrategy
+ def render(x, y, text)
+ raise NotImplementedError
+ end
- def render(x, y, text)
- raise NotImplementedError
- end
+ end # class RLTextStrategy
- end # class RLTextStrategy
+ class TBTextStrategy < TextStrategy
- class TBTextStrategy < TextStrategy
+ def get_word_spacing()
+ @word_space ||= glyph_metrics(@ctx.text_attrs.glyph_orientation_vertical, ' ')[1]
+ [0, @word_space + @ctx.text_attrs.word_spacing]
+ end
- def get_word_spacing()
- @word_space ||= glyph_metrics(@ctx.text_attrs.glyph_orientation_vertical, ' ')[1]
- [0, @word_space + @ctx.text_attrs.word_spacing]
- end
+ def get_letter_spacing(glyph)
+ gx, gy = glyph_metrics(@ctx.text_attrs.glyph_orientation_vertical, glyph)
+ [gx, gy+@ctx.text_attrs.letter_spacing]
+ end
- def get_letter_spacing(glyph)
- gx, gy = glyph_metrics(@ctx.text_attrs.glyph_orientation_vertical, glyph)
- [gx, gy+@ctx.text_attrs.letter_spacing]
- end
+ def render(x, y, text)
+ x_rel_coords, y_rel_coords = text_rel_coords(text)
+ dx = x_rel_coords.max
+ dy = y_rel_coords.inject(0) {|sum, a| sum + a}
- def render(x, y, text)
- x_rel_coords, y_rel_coords = text_rel_coords(text)
- dx = x_rel_coords.max
- dy = y_rel_coords.inject(0) {|sum, a| sum + a}
+ # We're handling the anchoring.
+ @ctx.gc.push()
+ @ctx.gc.text_anchor(Magick::StartAnchor)
+ if @ctx.text_attrs.text_anchor == :end
+ y -= dy
+ elsif @ctx.text_attrs.text_anchor == :middle
+ y -= dy / 2
+ end
- # We're handling the anchoring.
- @ctx.gc.push()
- @ctx.gc.text_anchor(Magick::StartAnchor)
- if @ctx.text_attrs.text_anchor == :end
- y -= dy
- elsif @ctx.text_attrs.text_anchor == :middle
- y -= dy / 2
- end
+ # Align the first glyph such that its center
+ # is aligned on x and its top is aligned on y.
- # Align the first glyph such that its center
- # is aligned on x and its top is aligned on y.
+ case @ctx.text_attrs.glyph_orientation_vertical
+ when 0
+ x -= x_rel_coords.max / 2
+ y += y_rel_coords[0]
+ when 90
+ x -= x_rel_coords.max / 2
+ when 180
+ x += x_rel_coords.max / 2
+ when 270
+ x += x_rel_coords.max / 2
+ y += y_rel_coords.shift
+ y_rel_coords << 0 # since we used an element we need to add a dummy
+ end
- case @ctx.text_attrs.glyph_orientation_vertical
- when 0
- x -= x_rel_coords.max / 2
- y += y_rel_coords[0]
- when 90
- x -= x_rel_coords.max / 2
- when 180
- x += x_rel_coords.max / 2
- when 270
- x += x_rel_coords.max / 2
- y += y_rel_coords.shift
- y_rel_coords << 0 # since we used an element we need to add a dummy
- end
+ x -= shift_baseline(@ctx.text_attrs.glyph_orientation_vertical, text[0,1])
- x -= shift_baseline(@ctx.text_attrs.glyph_orientation_vertical, text[0,1])
+ first_word = true
+ text.split(::Magick::RVG::WORD_SEP).each do |word|
+ unless first_word
+ y += y_rel_coords.shift
+ x_rel_coords.shift
+ end
+ first_word = false
+ word.split('').each do |glyph|
+ case @ctx.text_attrs.glyph_orientation_vertical
+ when 0, 90, 270
+ x_shift = (dx - x_rel_coords.shift) / 2
+ when 180
+ x_shift = -(dx - x_rel_coords.shift) / 2
+ end
- first_word = true
- text.split(::Magick::RVG::WORD_SEP).each do |word|
- unless first_word
- y += y_rel_coords.shift
- x_rel_coords.shift
- end
- first_word = false
- word.split('').each do |glyph|
- case @ctx.text_attrs.glyph_orientation_vertical
- when 0, 90, 270
- x_shift = (dx - x_rel_coords.shift) / 2
- when 180
- x_shift = -(dx - x_rel_coords.shift) / 2
+ render_glyph(@ctx.text_attrs.glyph_orientation_vertical, x+x_shift, y, glyph)
+ y += y_rel_coords.shift
+ end
end
- render_glyph(@ctx.text_attrs.glyph_orientation_vertical, x+x_shift, y, glyph)
- y += y_rel_coords.shift
+ @ctx.gc.pop()
+ [0, dy]
end
- end
- @ctx.gc.pop()
- [0, dy]
- end
+ end # class TBTextStrategy
- end # class TBTextStrategy
+ # Handle "easy" text
+ class DefaultTextStrategy < TextStrategy
- # Handle "easy" text
- class DefaultTextStrategy < TextStrategy
+ def render(x, y, text)
+ @ctx.gc.text(x, y, enquote(text))
+ tm = @ctx.shadow.get_type_metrics(text)
+ dx = case @ctx.text_attrs.text_anchor
+ when :start
+ tm.width
+ when :middle
+ tm.width / 2
+ when :end
+ 0
+ end
+ [dx, 0]
+ end
- def render(x, y, text)
- @ctx.gc.text(x, y, enquote(text))
- tm = @ctx.shadow.get_type_metrics(text)
- dx = case @ctx.text_attrs.text_anchor
- when :start
- tm.width
- when :middle
- tm.width / 2
- when :end
- 0
- end
- [dx, 0]
- end
+ end # class NormalTextStrategy
- end # class NormalTextStrategy
+ end # class Utility
+ end # class RVG
+end # module Magick
-end # module Magick::RVG::Utility
+module Magick
+ class RVG
+ class Utility
-module Magick::RVG::Utility
+ class TextAttributes
- class TextAttributes
+ public
- public
+ WRITING_MODE = %w{lr-tb lr rl-tb rl tb-rl tb}
- WRITING_MODE = %w{lr-tb lr rl-tb rl tb-rl tb}
+ def initialize()
+ @affine = Array.new
+ @affine << Magick::AffineMatrix.new(1, 0, 0, 1, 0, 0)
+ @baseline_shift = Array.new
+ @baseline_shift << :baseline
+ @glyph_orientation_horizontal = Array.new
+ @glyph_orientation_horizontal << 0
+ @glyph_orientation_vertical = Array.new
+ @glyph_orientation_vertical << 90
+ @letter_spacing = Array.new
+ @letter_spacing << 0
+ @text_anchor = Array.new
+ @text_anchor << :start
+ @word_spacing = Array.new
+ @word_spacing << 0
+ @writing_mode = Array.new
+ @writing_mode << 'lr-tb'
+ end
- def initialize()
- @affine = Array.new
- @affine << Magick::AffineMatrix.new(1, 0, 0, 1, 0, 0)
- @baseline_shift = Array.new
- @baseline_shift << :baseline
- @glyph_orientation_horizontal = Array.new
- @glyph_orientation_horizontal << 0
- @glyph_orientation_vertical = Array.new
- @glyph_orientation_vertical << 90
- @letter_spacing = Array.new
- @letter_spacing << 0
- @text_anchor = Array.new
- @text_anchor << :start
- @word_spacing = Array.new
- @word_spacing << 0
- @writing_mode = Array.new
- @writing_mode << 'lr-tb'
- end
+ def push()
+ @affine.push(@affine.last.dup)
+ @baseline_shift.push(@baseline_shift.last)
+ @text_anchor.push(@text_anchor.last)
+ @writing_mode.push(@writing_mode.last.dup)
+ @glyph_orientation_vertical.push(@glyph_orientation_vertical.last)
+ @glyph_orientation_horizontal.push(@glyph_orientation_horizontal.last)
+ @letter_spacing.push(@letter_spacing.last)
+ @word_spacing.push(@word_spacing.last)
+ end
- def push()
- @affine.push(@affine.last.dup)
- @baseline_shift.push(@baseline_shift.last)
- @text_anchor.push(@text_anchor.last)
- @writing_mode.push(@writing_mode.last.dup)
- @glyph_orientation_vertical.push(@glyph_orientation_vertical.last)
- @glyph_orientation_horizontal.push(@glyph_orientation_horizontal.last)
- @letter_spacing.push(@letter_spacing.last)
- @word_spacing.push(@word_spacing.last)
- end
+ def pop()
+ @affine.pop
+ @baseline_shift.pop
+ @text_anchor.pop
+ @writing_mode.pop
+ @glyph_orientation_vertical.pop
+ @glyph_orientation_horizontal.pop
+ @letter_spacing.pop
+ @word_spacing.pop
+ end
- def pop()
- @affine.pop
- @baseline_shift.pop
- @text_anchor.pop
- @writing_mode.pop
- @glyph_orientation_vertical.pop
- @glyph_orientation_horizontal.pop
- @letter_spacing.pop
- @word_spacing.pop
- end
+ def set_affine(sx, rx, ry, sy, tx, ty)
+ @affine[-1].sx = sx
+ @affine[-1].rx = rx
+ @affine[-1].ry = ry
+ @affine[-1].sy = sy
+ @affine[-1].tx = tx
+ @affine[-1].ty = ty
+ end
- def set_affine(sx, rx, ry, sy, tx, ty)
- @affine[-1].sx = sx
- @affine[-1].rx = rx
- @affine[-1].ry = ry
- @affine[-1].sy = sy
- @affine[-1].tx = tx
- @affine[-1].ty = ty
- end
+ def affine()
+ @affine[-1]
+ end
- def affine()
- @affine[-1]
- end
+ def baseline_shift()
+ @baseline_shift[-1]
+ end
- def baseline_shift()
- @baseline_shift[-1]
- end
+ def baseline_shift=(value)
+ @baseline_shift[-1] = value
+ end
- def baseline_shift=(value)
- @baseline_shift[-1] = value
- end
+ def text_anchor()
+ @text_anchor[-1]
+ end
- def text_anchor()
- @text_anchor[-1]
- end
+ def text_anchor=(anchor)
+ @text_anchor[-1] = anchor
+ end
- def text_anchor=(anchor)
- @text_anchor[-1] = anchor
- end
+ def glyph_orientation_vertical()
+ @glyph_orientation_vertical[-1]
+ end
- def glyph_orientation_vertical()
- @glyph_orientation_vertical[-1]
- end
+ def glyph_orientation_vertical=(angle)
+ @glyph_orientation_vertical[-1] = angle
+ end
- def glyph_orientation_vertical=(angle)
- @glyph_orientation_vertical[-1] = angle
- end
+ def glyph_orientation_horizontal()
+ @glyph_orientation_horizontal[-1]
+ end
- def glyph_orientation_horizontal()
- @glyph_orientation_horizontal[-1]
- end
+ def glyph_orientation_horizontal=(angle)
+ @glyph_orientation_horizontal[-1] = angle
+ end
- def glyph_orientation_horizontal=(angle)
- @glyph_orientation_horizontal[-1] = angle
- end
+ def letter_spacing()
+ @letter_spacing[-1]
+ end
- def letter_spacing()
- @letter_spacing[-1]
- end
+ def letter_spacing=(value)
+ @letter_spacing[-1] = value
+ end
- def letter_spacing=(value)
- @letter_spacing[-1] = value
- end
+ def non_default?
+ @baseline_shift[-1] != :baseline || @letter_spacing[-1] != 0 ||
+ @word_spacing[-1] != 0 || @writing_mode[-1][/\Alr/].nil? ||
+ @glyph_orientation_horizontal[-1] != 0
+ end
- def non_default?
- @baseline_shift[-1] != :baseline || @letter_spacing[-1] != 0 ||
- @word_spacing[-1] != 0 || @writing_mode[-1][/\Alr/].nil? ||
- @glyph_orientation_horizontal[-1] != 0
- end
+ def word_spacing()
+ @word_spacing[-1]
+ end
- def word_spacing()
- @word_spacing[-1]
- end
+ def word_spacing=(value)
+ @word_spacing[-1] = value
+ end
- def word_spacing=(value)
- @word_spacing[-1] = value
- end
+ def writing_mode()
+ @writing_mode[-1]
+ end
- def writing_mode()
- @writing_mode[-1]
- end
+ def writing_mode=(mode)
+ @writing_mode[-1] = WRITING_MODE.include?(mode) ? mode : 'lr-tb'
+ end
- def writing_mode=(mode)
- @writing_mode[-1] = WRITING_MODE.include?(mode) ? mode : 'lr-tb'
- end
+ end # class TextAttributes
- end # class TextAttributes
+ class GraphicContext
- class GraphicContext
+ FONT_STRETCH = {:normal => Magick::NormalStretch,
+ :ultra_condensed => Magick::UltraCondensedStretch,
+ :extra_condensed => Magick::ExtraCondensedStretch,
+ :condensed => Magick::CondensedStretch,
+ :semi_condensed => Magick::SemiCondensedStretch,
+ :semi_expanded => Magick::SemiExpandedStretch,
+ :expanded => Magick::ExpandedStretch,
+ :extra_expanded => Magick::ExtraExpandedStretch,
+ :ultra_expanded => Magick::UltraExpandedStretch}
- FONT_STRETCH = {:normal => Magick::NormalStretch,
- :ultra_condensed => Magick::UltraCondensedStretch,
- :extra_condensed => Magick::ExtraCondensedStretch,
- :condensed => Magick::CondensedStretch,
- :semi_condensed => Magick::SemiCondensedStretch,
- :semi_expanded => Magick::SemiExpandedStretch,
- :expanded => Magick::ExpandedStretch,
- :extra_expanded => Magick::ExtraExpandedStretch,
- :ultra_expanded => Magick::UltraExpandedStretch}
+ FONT_STYLE = {:normal => Magick::NormalStyle,
+ :italic => Magick::ItalicStyle,
+ :oblique => Magick::ObliqueStyle}
- FONT_STYLE = {:normal => Magick::NormalStyle,
- :italic => Magick::ItalicStyle,
- :oblique => Magick::ObliqueStyle}
+ FONT_WEIGHT = {'normal' => Magick::NormalWeight,
+ 'bold' => Magick::BoldWeight,
+ 'bolder' => Magick::BolderWeight,
+ 'lighter' => Magick::LighterWeight}
- FONT_WEIGHT = {'normal' => Magick::NormalWeight,
- 'bold' => Magick::BoldWeight,
- 'bolder' => Magick::BolderWeight,
- 'lighter' => Magick::LighterWeight}
+ TEXT_ANCHOR = {:start => Magick::StartAnchor,
+ :middle => Magick::MiddleAnchor,
+ :end => Magick::EndAnchor}
- TEXT_ANCHOR = {:start => Magick::StartAnchor,
- :middle => Magick::MiddleAnchor,
- :end => Magick::EndAnchor}
+ ANCHOR_TO_ALIGN = {:start => Magick::LeftAlign,
+ :middle => Magick::CenterAlign,
+ :end => Magick::RightAlign}
- ANCHOR_TO_ALIGN = {:start => Magick::LeftAlign,
- :middle => Magick::CenterAlign,
- :end => Magick::RightAlign}
+ TEXT_DECORATION = {:none => Magick::NoDecoration,
+ :underline => Magick::UnderlineDecoration,
+ :overline => Magick::OverlineDecoration,
+ :line_through => Magick::LineThroughDecoration}
- TEXT_DECORATION = {:none => Magick::NoDecoration,
- :underline => Magick::UnderlineDecoration,
- :overline => Magick::OverlineDecoration,
- :line_through => Magick::LineThroughDecoration}
+ TEXT_STRATEGIES = {'lr-tb'=>LRTextStrategy, 'lr'=>LRTextStrategy,
+ 'rt-tb'=>RLTextStrategy, 'rl'=>RLTextStrategy,
+ 'tb-rl'=>TBTextStrategy, 'tb'=>TBTextStrategy}
- TEXT_STRATEGIES = {'lr-tb'=>LRTextStrategy, 'lr'=>LRTextStrategy,
- 'rt-tb'=>RLTextStrategy, 'rl'=>RLTextStrategy,
- 'tb-rl'=>TBTextStrategy, 'tb'=>TBTextStrategy}
+ def GraphicContext.degrees_to_radians(deg)
+ Math::PI * (deg % 360.0) / 180.0
+ end
- def GraphicContext.degrees_to_radians(deg)
- Math::PI * (deg % 360.0) / 180.0
- end
+ private
- private
+ def init_matrix()
+ @rx = @ry = 0
+ @sx = @sy = 1
+ @tx = @ty = 0
+ end
- def init_matrix()
- @rx = @ry = 0
- @sx = @sy = 1
- @tx = @ty = 0
- end
+ def concat_matrix()
+ curr = @text_attrs.affine
+ sx = curr.sx * @sx + curr.ry * @rx
+ rx = curr.rx * @sx + curr.sy * @rx
+ ry = curr.sx * @ry + curr.ry * @sy
+ sy = curr.rx * @ry + curr.sy * @sy
+ tx = curr.sx * @tx + curr.ry * @ty + curr.tx
+ ty = curr.rx * @tx + curr.sy * @ty + curr.ty
+ @text_attrs.set_affine(sx, rx, ry, sy, tx, ty)
+ init_matrix()
+ end
- def concat_matrix()
- curr = @text_attrs.affine
- sx = curr.sx * @sx + curr.ry * @rx
- rx = curr.rx * @sx + curr.sy * @rx
- ry = curr.sx * @ry + curr.ry * @sy
- sy = curr.rx * @ry + curr.sy * @sy
- tx = curr.sx * @tx + curr.ry * @ty + curr.tx
- ty = curr.rx * @tx + curr.sy * @ty + curr.ty
- @text_attrs.set_affine(sx, rx, ry, sy, tx, ty)
- init_matrix()
- end
+ public
- public
+ attr_reader :gc, :text_attrs
- attr_reader :gc, :text_attrs
+ def initialize()
+ @gc = Magick::Draw.new
+ @shadow = Array.new
+ @shadow << Magick::Draw.new
+ @text_attrs = TextAttributes.new
+ init_matrix()
+ end
- def initialize()
- @gc = Magick::Draw.new
- @shadow = Array.new
- @shadow << Magick::Draw.new
- @text_attrs = TextAttributes.new
- init_matrix()
- end
+ def method_missing(methID, *args, &block)
+ @gc.__send__(methID, *args, &block)
+ end
- def method_missing(methID, *args, &block)
- @gc.__send__(methID, *args, &block)
- end
+ def affine(sx, rx, ry, sy, tx, ty)
+ sx, rx, ry, sy, tx, ty = Magick::RVG.convert_to_float(sx, rx, ry, sy, tx, ty)
+ @gc.affine(sx, rx, ry, sy, tx, ty)
+ @text_attrs.set_affine(sx, rx, ry, sy, tx, ty)
+ nil
+ end
- def affine(sx, rx, ry, sy, tx, ty)
- sx, rx, ry, sy, tx, ty = Magick::RVG.convert_to_float(sx, rx, ry, sy, tx, ty)
- @gc.affine(sx, rx, ry, sy, tx, ty)
- @text_attrs.set_affine(sx, rx, ry, sy, tx, ty)
- nil
- end
+ def baseline_shift(value)
+ @text_attrs.baseline_shift = case value
+ when 'baseline', 'sub', 'super'
+ value.intern
+ when /[-+]?\d+%/, Numeric
+ value
+ else
+ :baseline
+ end
+ nil
+ end
- def baseline_shift(value)
- @text_attrs.baseline_shift = case value
- when 'baseline', 'sub', 'super'
- value.intern
- when /[-+]?\d+%/, Numeric
- value
- else
- :baseline
+ def font(name)
+ @gc.font(name)
+ @shadow[-1].font = name
+ nil
end
- nil
- end
- def font(name)
- @gc.font(name)
- @shadow[-1].font = name
- nil
- end
+ def font_family(name)
+ @gc.font_family(name)
+ @shadow[-1].font_family = name
+ nil
+ end
- def font_family(name)
- @gc.font_family(name)
- @shadow[-1].font_family = name
- nil
- end
+ def font_size(points)
+ @gc.font_size(points)
+ @shadow[-1].pointsize = points
+ nil
+ end
- def font_size(points)
- @gc.font_size(points)
- @shadow[-1].pointsize = points
- nil
- end
+ def font_stretch(stretch)
+ stretch = FONT_STRETCH.fetch(stretch.intern, Magick::NormalStretch)
+ @gc.font_stretch(stretch)
+ @shadow[-1].font_stretch = stretch
+ nil
+ end
- def font_stretch(stretch)
- stretch = FONT_STRETCH.fetch(stretch.intern, Magick::NormalStretch)
- @gc.font_stretch(stretch)
- @shadow[-1].font_stretch = stretch
- nil
- end
+ def font_style(style)
+ style = FONT_STYLE.fetch(style.intern, Magick::NormalStyle)
+ @gc.font_style(style)
+ @shadow[-1].font_style = style
+ nil
+ end
- def font_style(style)
- style = FONT_STYLE.fetch(style.intern, Magick::NormalStyle)
- @gc.font_style(style)
- @shadow[-1].font_style = style
- nil
- end
+ def font_weight(weight)
+ # If the arg is not in the hash use it directly. Handles numeric values.
+ weight = FONT_WEIGHT.fetch(weight) {|key| key}
+ @gc.font_weight(weight)
+ @shadow[-1].font_weight = weight
+ nil
+ end
- def font_weight(weight)
- # If the arg is not in the hash use it directly. Handles numeric values.
- weight = FONT_WEIGHT.fetch(weight) {|key| key}
- @gc.font_weight(weight)
- @shadow[-1].font_weight = weight
- nil
- end
+ def glyph_orientation_horizontal(deg)
+ deg = Magick::RVG.convert_one_to_float(deg)
+ @text_attrs.glyph_orientation_horizontal = (deg % 360) / 90 * 90
+ nil
+ end
- def glyph_orientation_horizontal(deg)
- deg = Magick::RVG.convert_one_to_float(deg)
- @text_attrs.glyph_orientation_horizontal = (deg % 360) / 90 * 90
- nil
- end
+ def glyph_orientation_vertical(deg)
+ deg = Magick::RVG.convert_one_to_float(deg)
+ @text_attrs.glyph_orientation_vertical = (deg % 360) / 90 * 90
+ nil
+ end
- def glyph_orientation_vertical(deg)
- deg = Magick::RVG.convert_one_to_float(deg)
- @text_attrs.glyph_orientation_vertical = (deg % 360) / 90 * 90
- nil
- end
+ def inspect()
+ @gc.inspect
+ end
- def inspect()
- @gc.inspect
- end
+ def letter_spacing(value)
+ @text_attrs.letter_spacing = Magick::RVG.convert_one_to_float(value)
+ nil
+ end
- def letter_spacing(value)
- @text_attrs.letter_spacing = Magick::RVG.convert_one_to_float(value)
- nil
- end
+ def push()
+ @gc.push
+ @shadow.push(@shadow.last.dup)
+ @text_attrs.push
+ nil
+ end
- def push()
- @gc.push
- @shadow.push(@shadow.last.dup)
- @text_attrs.push
- nil
- end
+ def pop()
+ @gc.pop
+ @shadow.pop
+ @text_attrs.pop
+ nil
+ end
- def pop()
- @gc.pop
- @shadow.pop
- @text_attrs.pop
- nil
- end
+ def rotate(degrees)
+ degrees = Magick::RVG.convert_one_to_float(degrees)
+ @gc.rotate(degrees)
+ @sx = Math.cos(GraphicContext.degrees_to_radians(degrees))
+ @rx = Math.sin(GraphicContext.degrees_to_radians(degrees))
+ @ry = -Math.sin(GraphicContext.degrees_to_radians(degrees))
+ @sy = Math.cos(GraphicContext.degrees_to_radians(degrees))
+ concat_matrix()
+ nil
+ end
- def rotate(degrees)
- degrees = Magick::RVG.convert_one_to_float(degrees)
- @gc.rotate(degrees)
- @sx = Math.cos(GraphicContext.degrees_to_radians(degrees))
- @rx = Math.sin(GraphicContext.degrees_to_radians(degrees))
- @ry = -Math.sin(GraphicContext.degrees_to_radians(degrees))
- @sy = Math.cos(GraphicContext.degrees_to_radians(degrees))
- concat_matrix()
- nil
- end
+ def scale(sx, sy)
+ sx, sy = Magick::RVG.convert_to_float(sx, sy)
+ @gc.scale(sx, sy)
+ @sx, @sy = sx, sy
+ concat_matrix()
+ nil
+ end
- def scale(sx, sy)
- sx, sy = Magick::RVG.convert_to_float(sx, sy)
- @gc.scale(sx, sy)
- @sx, @sy = sx, sy
- concat_matrix()
- nil
- end
+ def shadow()
+ @shadow.last
+ end
- def shadow()
- @shadow.last
- end
+ def skewX(degrees)
+ degrees = Magick::RVG.convert_one_to_float(degrees)
+ @gc.skewX(degrees)
+ @ry = Math.tan(GraphicContext.degrees_to_radians(degrees))
+ concat_matrix()
+ nil
+ end
- def skewX(degrees)
- degrees = Magick::RVG.convert_one_to_float(degrees)
- @gc.skewX(degrees)
- @ry = Math.tan(GraphicContext.degrees_to_radians(degrees))
- concat_matrix()
- nil
- end
+ def skewY(degrees)
+ degrees = Magick::RVG.convert_one_to_float(degrees)
+ @gc.skewY(degrees)
+ @rx = Math.tan(GraphicContext.degrees_to_radians(degrees))
+ concat_matrix()
+ nil
+ end
- def skewY(degrees)
- degrees = Magick::RVG.convert_one_to_float(degrees)
- @gc.skewY(degrees)
- @rx = Math.tan(GraphicContext.degrees_to_radians(degrees))
- concat_matrix()
- nil
- end
+ def stroke_width(width)
+ width = Magick::RVG.convert_one_to_float(width)
+ @gc.stroke_width(width)
+ @shadow[-1].stroke_width = width
+ nil
+ end
- def stroke_width(width)
- width = Magick::RVG.convert_one_to_float(width)
- @gc.stroke_width(width)
- @shadow[-1].stroke_width = width
- nil
- end
+ def text(x, y, text)
+ return if text.length == 0
+ if @text_attrs.non_default?
+ text_renderer = TEXT_STRATEGIES[@text_attrs.writing_mode].new(self)
+ else
+ text_renderer = DefaultTextStrategy.new(self)
+ end
- def text(x, y, text)
- return if text.length == 0
- if @text_attrs.non_default?
- text_renderer = TEXT_STRATEGIES[@text_attrs.writing_mode].new(self)
- else
- text_renderer = DefaultTextStrategy.new(self)
- end
+ return text_renderer.render(x, y, text)
+ end
- return text_renderer.render(x, y, text)
- end
+ def text_anchor(anchor)
+ anchor = anchor.intern
+ anchor_enum = TEXT_ANCHOR.fetch(anchor, Magick::StartAnchor)
+ @gc.text_anchor(anchor_enum)
+ align = ANCHOR_TO_ALIGN.fetch(anchor, Magick::LeftAlign)
+ @shadow[-1].align = align
+ @text_attrs.text_anchor = anchor
+ nil
+ end
- def text_anchor(anchor)
- anchor = anchor.intern
- anchor_enum = TEXT_ANCHOR.fetch(anchor, Magick::StartAnchor)
- @gc.text_anchor(anchor_enum)
- align = ANCHOR_TO_ALIGN.fetch(anchor, Magick::LeftAlign)
- @shadow[-1].align = align
- @text_attrs.text_anchor = anchor
- nil
- end
+ def text_decoration(decoration)
+ decoration = TEXT_DECORATION.fetch(decoration.intern, Magick::NoDecoration)
+ @gc.decorate(decoration)
+ @shadow[-1].decorate = decoration
+ nil
+ end
- def text_decoration(decoration)
- decoration = TEXT_DECORATION.fetch(decoration.intern, Magick::NoDecoration)
- @gc.decorate(decoration)
- @shadow[-1].decorate = decoration
- nil
- end
+ def translate(tx, ty)
+ tx, ty = Magick::RVG.convert_to_float(tx, ty)
+ @gc.translate(tx, ty)
+ @tx, @ty = tx, ty
+ concat_matrix()
+ nil
+ end
- def translate(tx, ty)
- tx, ty = Magick::RVG.convert_to_float(tx, ty)
- @gc.translate(tx, ty)
- @tx, @ty = tx, ty
- concat_matrix()
- nil
- end
+ def word_spacing(value)
+ @text_attrs.word_spacing = Magick::RVG.convert_one_to_float(value)
+ nil
+ end
- def word_spacing(value)
- @text_attrs.word_spacing = Magick::RVG.convert_one_to_float(value)
- nil
- end
+ def writing_mode(mode)
+ @text_attrs.writing_mode = mode
+ nil
+ end
- def writing_mode(mode)
- @text_attrs.writing_mode = mode
- nil
- end
+ end # class GraphicContext
- end # class GraphicContext
-
-end # module Magick::RVG::Utility
+ end # class Utility
+ end # class RVG
+end # module Magick