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