Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Update copyright date

  • Loading branch information...
commit 738116ed508eec4769be634be08aa526d1555120 1 parent ea64d46
rmagick authored
View
74 lib/rvg/clippath.rb
@@ -1,46 +1,48 @@
#--
-# $Id: clippath.rb,v 1.2 2005/12/31 14:41:04 rmagick Exp $
-# Copyright (C) 2006 Timothy P. Hunter
+# $Id: clippath.rb,v 1.3 2007/01/20 17:39:48 rmagick Exp $
+# Copyright (C) 2007 Timothy P. Hunter
#++
-class Magick::RVG
+module Magick
+ class RVG
- class ClipPath
- include ShapeConstructors
- include UseConstructors
- include TextConstructors
- include Describable
- include Stylable
- include Duplicatable
+ class ClipPath
+ include ShapeConstructors
+ include UseConstructors
+ include TextConstructors
+ include Describable
+ include Stylable
+ include Duplicatable
- # Create a clipping path. Within the block create an outline
- # from one or more paths, basic shapes, text objects, or +use+.
- # Everything drawn within the outline will be displayed.
- # Anything drawn outside the outline will not.
- #
- # If the clipping path contains a +use+, it
- # must directly reference path, basic shape, or text objects.
- #
- # Attach the clipping path to an object with the :clip_path style.
- def initialize(clip_path_units='userSpaceOnUse')
- super()
- if ! ['userSpaceOnUse', 'objectBoundingBox'].include?(clip_path_units)
- raise ArgumentError, "undefined value for clip path units: #{clip_path_units}"
+ # Create a clipping path. Within the block create an outline
+ # from one or more paths, basic shapes, text objects, or +use+.
+ # Everything drawn within the outline will be displayed.
+ # Anything drawn outside the outline will not.
+ #
+ # If the clipping path contains a +use+, it
+ # must directly reference path, basic shape, or text objects.
+ #
+ # Attach the clipping path to an object with the :clip_path style.
+ def initialize(clip_path_units='userSpaceOnUse')
+ super()
+ if ! ['userSpaceOnUse', 'objectBoundingBox'].include?(clip_path_units)
+ raise ArgumentError, "undefined value for clip path units: #{clip_path_units}"
+ end
+ @clip_path_units = clip_path_units
+ @content = Content.new
+ yield(self) if block_given?
end
- @clip_path_units = clip_path_units
- @content = Content.new
- yield(self) if block_given?
- end
- def add_primitives(gc, style) #:nodoc:
- name = __id__.to_s
- gc.define_clip_path(name) do
- gc.clip_units(@clip_path_units)
- @content.each { |element| element.add_primitives(gc) }
+ def add_primitives(gc, style) #:nodoc:
+ name = __id__.to_s
+ gc.define_clip_path(name) do
+ gc.clip_units(@clip_path_units)
+ @content.each { |element| element.add_primitives(gc) }
+ end
+ gc.clip_path(name)
end
- gc.clip_path(name)
- end
- end # class ClipPath
+ end # class ClipPath
-end # class Magick::RVG
+ end # class RVG
+end # module Magick
View
238 lib/rvg/container.rb
@@ -1,129 +1,131 @@
#--
-# $Id: container.rb,v 1.2 2005/12/31 14:41:04 rmagick Exp $
-# Copyright (C) 2006 Timothy P. Hunter
+# $Id: container.rb,v 1.3 2007/01/20 17:39:49 rmagick Exp $
+# Copyright (C) 2007 Timothy P. Hunter
#++
-class Magick::RVG
-
- # Content is simply an Array with a deep_copy method.
- # When unit-testing, it also has a deep_equal method.
- class Content < Array #:nodoc:
-
- def deep_copy(h = {})
- me = self.__id__
- copy = h[me]
- unless copy
- copy = self.class.new
- each do |c|
- copy << case
- when c.nil?
- nil
- when c.respond_to?(:deep_copy)
- c.deep_copy(h)
- when c.respond_to?(:dup)
- c.dup rescue c
- else
- c
- end
+module Magick
+ class RVG
+
+ # Content is simply an Array with a deep_copy method.
+ # When unit-testing, it also has a deep_equal method.
+ class Content < Array #:nodoc:
+
+ def deep_copy(h = {})
+ me = self.__id__
+ copy = h[me]
+ unless copy
+ copy = self.class.new
+ each do |c|
+ copy << case
+ when c.nil?
+ nil
+ when c.respond_to?(:deep_copy)
+ c.deep_copy(h)
+ when c.respond_to?(:dup)
+ c.dup rescue c
+ else
+ c
+ end
+ end
+ copy.freeze if frozen?
+ h[me] = copy
end
- copy.freeze if frozen?
- h[me] = copy
+ return copy
end
- return copy
- end
-
- end # class Content
-
- # Define a collection of shapes, text, etc. that can be reused.
- # Group objects are _containers_. That is, styles and transforms defined
- # on the group are used by contained objects such as shapes, text, and
- # nested groups unless overridden by a nested container or the object itself.
- # Groups can be reused with the RVG::UseConstructors#use method.
- # Create groups within
- # containers with the RVG::StructureConstructors#g method.
- #
- # Example:
- # # All elements in the group will be translated by 50 in the
- # # x-direction and 10 in the y-direction.
- # rvg.g.translate(50, 10).styles(:stroke=>'red',:fill=>'none') do |grp|
- # # The line will be red.
- # grp.line(10,10, 20,20)
- # # The circle will be blue.
- # grp.circle(10, 20, 20).styles(:stroke=>'blue')
- # end
- class Group
- include Stylable
- include Transformable
- include Embellishable
- include Describable
- include Duplicatable
-
- def initialize
- super
- @content = Content.new
- yield(self) if block_given?
- end
-
- def add_primitives(gc) #:nodoc:
- gc.push
- add_transform_primitives(gc)
- add_style_primitives(gc)
- @content.each { |element| element.add_primitives(gc) }
- gc.pop
- end
-
- # Translate container according to #use arguments
- def ref(x, y, width, height) #:nodoc:
- translate(x, y) if (x != 0 || y != 0)
- end
-
- # Append an arbitrary object to the group's content. Called
- # by #use to insert a non-container object into a group.
- def <<(obj) #:nodoc:
- @content << obj
- end
-
- end # class Group
-
-
- # A Use object allows the re-use of RVG and RVG::Group
- # objects within a container. Create a Use object with the
- # RVG::UseConstructors#use method.
- class Use
- include Stylable
- include Transformable
- include Duplicatable
-
- # In a container, Use objects are created indirectly via the
+
+ end # class Content
+
+ # Define a collection of shapes, text, etc. that can be reused.
+ # Group objects are _containers_. That is, styles and transforms defined
+ # on the group are used by contained objects such as shapes, text, and
+ # nested groups unless overridden by a nested container or the object itself.
+ # Groups can be reused with the RVG::UseConstructors#use method.
+ # Create groups within
+ # containers with the RVG::StructureConstructors#g method.
+ #
+ # Example:
+ # # All elements in the group will be translated by 50 in the
+ # # x-direction and 10 in the y-direction.
+ # rvg.g.translate(50, 10).styles(:stroke=>'red',:fill=>'none') do |grp|
+ # # The line will be red.
+ # grp.line(10,10, 20,20)
+ # # The circle will be blue.
+ # grp.circle(10, 20, 20).styles(:stroke=>'blue')
+ # end
+ class Group
+ include Stylable
+ include Transformable
+ include Embellishable
+ include Describable
+ include Duplicatable
+
+ def initialize
+ super
+ @content = Content.new
+ yield(self) if block_given?
+ end
+
+ def add_primitives(gc) #:nodoc:
+ gc.push
+ add_transform_primitives(gc)
+ add_style_primitives(gc)
+ @content.each { |element| element.add_primitives(gc) }
+ gc.pop
+ end
+
+ # Translate container according to #use arguments
+ def ref(x, y, width, height) #:nodoc:
+ translate(x, y) if (x != 0 || y != 0)
+ end
+
+ # Append an arbitrary object to the group's content. Called
+ # by #use to insert a non-container object into a group.
+ def <<(obj) #:nodoc:
+ @content << obj
+ end
+
+ end # class Group
+
+
+ # A Use object allows the re-use of RVG and RVG::Group
+ # objects within a container. Create a Use object with the
# RVG::UseConstructors#use method.
- # The +x+ and +y+ arguments
- # can be used to specify an additional translation for
- # the group. The +width+ and +height+ arguments specify
- # a width and height for referenced RVG objects.
- def initialize(element, x=0, y=0, width=nil, height=nil)
- super()
-
- # If the element is not a group, defs, symbol, or rvg,
- # wrap a group around it so it can get a transform and
- # possibly a new viewport.
- if ! element.respond_to?(:ref)
- @element = Group.new
- @element << element.deep_copy
- else
- @element = element.deep_copy
+ class Use
+ include Stylable
+ include Transformable
+ include Duplicatable
+
+ # In a container, Use objects are created indirectly via the
+ # RVG::UseConstructors#use method.
+ # The +x+ and +y+ arguments
+ # can be used to specify an additional translation for
+ # the group. The +width+ and +height+ arguments specify
+ # a width and height for referenced RVG objects.
+ def initialize(element, x=0, y=0, width=nil, height=nil)
+ super()
+
+ # If the element is not a group, defs, symbol, or rvg,
+ # wrap a group around it so it can get a transform and
+ # possibly a new viewport.
+ if ! element.respond_to?(:ref)
+ @element = Group.new
+ @element << element.deep_copy
+ else
+ @element = element.deep_copy
+ end
+ @element.ref(x, y, width, height)
end
- @element.ref(x, y, width, height)
- end
- def add_primitives(gc) #:nodoc:
- gc.push
- add_transform_primitives(gc)
- add_style_primitives(gc)
- @element.add_primitives(gc)
- gc.pop
- end
+ def add_primitives(gc) #:nodoc:
+ gc.push
+ add_transform_primitives(gc)
+ add_style_primitives(gc)
+ @element.add_primitives(gc)
+ gc.pop
+ end
- end # class Use
+ end # class Use
-end # class Magick::RVG
+ end # class RVG
+end # module Magick
View
86 lib/rvg/deep_equal.rb
@@ -1,54 +1,56 @@
-class Magick::RVG
+module Magick
+ class RVG
- [PathData, Styles, Transforms].each do |c|
- c.class_eval do
- def deep_equal(other)
- if self != other
- puts "#{c.inspect} not equal.\nself:#{self} != other:#{other}"
- return false
+ [PathData, Styles, Transforms].each do |c|
+ c.class_eval do
+ def deep_equal(other)
+ if self != other
+ puts "#{c.inspect} not equal.\nself:#{self} != other:#{other}"
+ return false
+ end
+ return true
end
- return true
end
end
- end
- [Shape, TextBase, Image, Group, Content, Use, ClipPath, Pattern, self].each do |c|
- c.class_eval do
- def deep_equal(other)
- ivs = self.instance_variables
+ [Shape, TextBase, Image, Group, Content, Use, ClipPath, Pattern, self].each do |c|
+ c.class_eval do
+ def deep_equal(other)
+ ivs = self.instance_variables
- ivs.each do |iv|
- itv = self.instance_variable_get(iv)
- otv = other.instance_variable_get(iv)
- if itv.respond_to?(:deep_equal)
- if itv.equal?(otv)
- puts "#{iv} has deep_equal but self.#{iv} and other.#{iv} are the same object."
- return false
- end
- if !itv.deep_equal(otv)
- puts "Not equal.\nself.#{iv}=#{itv.inspect}\nother.#{iv}=#{otv.inspect}"
- return false
- end
- else
- case itv
- when Float, Symbol, TrueClass, FalseClass, Fixnum, NilClass
- return false if itv != otv
- else
- if itv.equal?(otv)
- puts "#{iv} is dup-able but self.#{iv} and other.#{iv} are the same object."
- return false
- end
- if itv != otv
- puts "Not equal.\nself.#{iv}=#{itv.inspect}\nother.#{iv}=#{otv.inspect}"
- return false
- end
+ ivs.each do |iv|
+ itv = self.instance_variable_get(iv)
+ otv = other.instance_variable_get(iv)
+ if itv.respond_to?(:deep_equal)
+ if itv.equal?(otv)
+ puts "#{iv} has deep_equal but self.#{iv} and other.#{iv} are the same object."
+ return false
+ end
+ if !itv.deep_equal(otv)
+ puts "Not equal.\nself.#{iv}=#{itv.inspect}\nother.#{iv}=#{otv.inspect}"
+ return false
+ end
+ else
+ case itv
+ when Float, Symbol, TrueClass, FalseClass, Fixnum, NilClass
+ return false if itv != otv
+ else
+ if itv.equal?(otv)
+ puts "#{iv} is dup-able but self.#{iv} and other.#{iv} are the same object."
+ return false
+ end
+ if itv != otv
+ puts "Not equal.\nself.#{iv}=#{itv.inspect}\nother.#{iv}=#{otv.inspect}"
+ return false
+ end
+ end
end
end
- end
- return true
+ return true
+ end
end
end
- end
-end # class Magick::RVG
+ end # class RVG
+end # module Magick
View
96 lib/rvg/describable.rb
@@ -1,51 +1,53 @@
#--
-# $Id: describable.rb,v 1.2 2005/12/31 14:41:04 rmagick Exp $
-# Copyright (C) 2006 Timothy P. Hunter
+# $Id: describable.rb,v 1.3 2007/01/20 17:39:49 rmagick Exp $
+# Copyright (C) 2007 Timothy P. Hunter
#++
-class Magick::RVG
-
- #--
- # Corresponds to SVG's Description.class
- #++
- # This module defines a number of metadata attributes.
- module Describable
-
- private
-
- def initialize(*args, &block) #:nodoc:
- super
- @title, @desc, @metadata = nil
- end
-
- public
-
- # Sets the object description
- attr_writer :desc
- # Sets the object title
- attr_writer :title
- # Sets the object metadata
- attr_writer :metadata
-
- # Returns the title of this object. The RVG object title is stored as
- # the 'title' property on the image
- def title
- @title.to_s
- end
-
- # Returns the description of this object. The RVG object description is
- # stored as the 'desc' property on the image
- def desc
- @desc.to_s
- end
-
- # Returns additional metadata of this object. The RVG object metadata
- # are stored as the 'metadata' property on the image
- def metadata
- @metadata.to_s
- end
-
- end # module Describable
-
-end # class Magick::RVG
+module Magick
+ class RVG
+
+ #--
+ # Corresponds to SVG's Description.class
+ #++
+ # This module defines a number of metadata attributes.
+ module Describable
+
+ private
+
+ def initialize(*args, &block) #:nodoc:
+ super
+ @title, @desc, @metadata = nil
+ end
+
+ public
+
+ # Sets the object description
+ attr_writer :desc
+ # Sets the object title
+ attr_writer :title
+ # Sets the object metadata
+ attr_writer :metadata
+
+ # Returns the title of this object. The RVG object title is stored as
+ # the 'title' property on the image
+ def title
+ @title.to_s
+ end
+
+ # Returns the description of this object. The RVG object description is
+ # stored as the 'desc' property on the image
+ def desc
+ @desc.to_s
+ end
+
+ # Returns additional metadata of this object. The RVG object metadata
+ # are stored as the 'metadata' property on the image
+ def metadata
+ @metadata.to_s
+ end
+
+ end # module Describable
+
+ end # class RVG
+end # module Magick
View
796 lib/rvg/embellishable.rb
@@ -1,415 +1,417 @@
#--
-# $Id: embellishable.rb,v 1.6 2005/12/31 14:41:04 rmagick Exp $
-# Copyright (C) 2006 Timothy P. Hunter
+# $Id: embellishable.rb,v 1.7 2007/01/20 17:39:49 rmagick Exp $
+# Copyright (C) 2007 Timothy P. Hunter
#++
-class Magick::RVG
-
- # Parent class of Circle, Ellipse, Text, etc.
- class Shape #:nodoc:
- include Stylable
- include Transformable
- include Duplicatable
-
- # Each shape can have its own set of transforms and styles.
- def add_primitives(gc)
- gc.push
- add_transform_primitives(gc)
- add_style_primitives(gc)
- gc.__send__(@primitive, *@args)
- gc.pop
- end
-
- end # class Shape
-
- class Circle < Shape
-
- # Define a circle with radius +r+ and centered at [<tt>cx</tt>, <tt>cy</tt>].
- # Use the RVG::ShapeConstructors#circle method to create Circle objects in a container.
- def initialize(r, cx=0, cy=0)
- super()
- r, cx, cy = Magick::RVG.convert_to_float(r, cx, cy)
- if r < 0
- raise ArgumentError, "radius must be >= 0 (#{r} given)"
+module Magick
+ class RVG
+
+ # Parent class of Circle, Ellipse, Text, etc.
+ class Shape #:nodoc:
+ include Stylable
+ include Transformable
+ include Duplicatable
+
+ # Each shape can have its own set of transforms and styles.
+ def add_primitives(gc)
+ gc.push
+ add_transform_primitives(gc)
+ add_style_primitives(gc)
+ gc.__send__(@primitive, *@args)
+ gc.pop
end
- @primitive = :circle
- @args = [cx, cy, cx+r, cy]
- self
- end
-
- end # class Circle
-
- class Ellipse < Shape
-
- # Define an ellipse with a center at [<tt>cx</tt>, <tt>cy</tt>], a horizontal radius +rx+
- # and a vertical radius +ry+.
- # Use the RVG::ShapeConstructors#ellipse method to create Ellipse objects in a container.
- def initialize(rx, ry, cx=0, cy=0)
- super()
- rx, ry, cx, cy = Magick::RVG.convert_to_float(rx, ry, cx, cy)
- if rx < 0 || ry < 0
- raise ArgumentError, "radii must be >= 0 (#{rx}, #{ry} given)"
+
+ end # class Shape
+
+ class Circle < Shape
+
+ # Define a circle with radius +r+ and centered at [<tt>cx</tt>, <tt>cy</tt>].
+ # Use the RVG::ShapeConstructors#circle method to create Circle objects in a container.
+ def initialize(r, cx=0, cy=0)
+ super()
+ r, cx, cy = Magick::RVG.convert_to_float(r, cx, cy)
+ if r < 0
+ raise ArgumentError, "radius must be >= 0 (#{r} given)"
+ end
+ @primitive = :circle
+ @args = [cx, cy, cx+r, cy]
+ self
+ end
+
+ end # class Circle
+
+ class Ellipse < Shape
+
+ # Define an ellipse with a center at [<tt>cx</tt>, <tt>cy</tt>], a horizontal radius +rx+
+ # and a vertical radius +ry+.
+ # Use the RVG::ShapeConstructors#ellipse method to create Ellipse objects in a container.
+ def initialize(rx, ry, cx=0, cy=0)
+ super()
+ rx, ry, cx, cy = Magick::RVG.convert_to_float(rx, ry, cx, cy)
+ if rx < 0 || ry < 0
+ raise ArgumentError, "radii must be >= 0 (#{rx}, #{ry} given)"
+ end
+ @primitive = :ellipse
+ # Ellipses are always complete.
+ @args = [cx, cy, rx, ry, 0, 360]
end
- @primitive = :ellipse
- # Ellipses are always complete.
- @args = [cx, cy, rx, ry, 0, 360]
- end
-
- end # class Ellipse
-
- class Line < Shape
-
- # Define a line from [<tt>x1</tt>, <tt>y1</tt>] to [<tt>x2</tt>, <tt>y2</tt>].
- # Use the RVG::ShapeConstructors#line method to create Line objects in a container.
- def initialize(x1=0, y1=0, x2=0, y2=0)
- super()
- @primitive = :line
- @args = [x1, y1, x2, y2]
- end
-
- end # class Line
-
- class Path < Shape
-
- # Define an SVG path. The argument can be either a path string
- # or a PathData object.
- # Use the RVG::ShapeConstructors#path method to create Path objects in a container.
- def initialize(path)
- super()
- @primitive = :path
- @args = [path.to_s]
- end
-
- end # class Path
-
- class Rect < Shape
-
- # Define a width x height rectangle. The upper-left corner is at [<tt>x</tt>, <tt>y</tt>].
- # If either <tt>width</tt> or <tt>height</tt> is 0, the rectangle is not rendered.
- # Use the RVG::ShapeConstructors#rect method to create Rect objects in a container.
- def initialize(width, height, x=0, y=0)
- super()
- width, height, x, y = Magick::RVG.convert_to_float(width, height, x, y)
- if width < 0 || height < 0
- raise ArgumentError, "width, height must be >= 0 (#{width}, #{height} given)"
+
+ end # class Ellipse
+
+ class Line < Shape
+
+ # Define a line from [<tt>x1</tt>, <tt>y1</tt>] to [<tt>x2</tt>, <tt>y2</tt>].
+ # Use the RVG::ShapeConstructors#line method to create Line objects in a container.
+ def initialize(x1=0, y1=0, x2=0, y2=0)
+ super()
+ @primitive = :line
+ @args = [x1, y1, x2, y2]
end
- @args = [x, y, x+width, y+height]
- @primitive = :rectangle
- end
-
- # Specify optional rounded corners for a rectangle. The arguments
- # are the x- and y-axis radii. If y is omitted it defaults to x.
- def round(rx, ry=nil)
- rx, ry = Magick::RVG.convert_to_float(rx, ry || rx)
- if rx < 0 || ry < 0
- raise ArgumentError, "rx, ry must be >= 0 (#{rx}, #{ry} given)"
+
+ end # class Line
+
+ class Path < Shape
+
+ # Define an SVG path. The argument can be either a path string
+ # or a PathData object.
+ # Use the RVG::ShapeConstructors#path method to create Path objects in a container.
+ def initialize(path)
+ super()
+ @primitive = :path
+ @args = [path.to_s]
end
- @args << rx << ry
- @primitive = :roundrectangle
- self
- end
-
- end # class Rect
-
- class PolyShape < Shape
-
- def polypoints(points)
- case points.length
- when 1
- points = Array(points[0])
- when 2
- x_coords = Array(points[0])
- y_coords = Array(points[1])
- unless x_coords.length > 0 && y_coords.length > 0
- raise ArgumentError, "array arguments must contain at least one point"
- end
- n = x_coords.length - y_coords.length
- short = n > 0 ? y_coords : x_coords
- olen = short.length
- n.abs.times {|x| short << short[x % olen]}
- points = x_coords.zip(y_coords).flatten
+
+ end # class Path
+
+ class Rect < Shape
+
+ # Define a width x height rectangle. The upper-left corner is at [<tt>x</tt>, <tt>y</tt>].
+ # If either <tt>width</tt> or <tt>height</tt> is 0, the rectangle is not rendered.
+ # Use the RVG::ShapeConstructors#rect method to create Rect objects in a container.
+ def initialize(width, height, x=0, y=0)
+ super()
+ width, height, x, y = Magick::RVG.convert_to_float(width, height, x, y)
+ if width < 0 || height < 0
+ raise ArgumentError, "width, height must be >= 0 (#{width}, #{height} given)"
+ end
+ @args = [x, y, x+width, y+height]
+ @primitive = :rectangle
+ end
+
+ # Specify optional rounded corners for a rectangle. The arguments
+ # are the x- and y-axis radii. If y is omitted it defaults to x.
+ def round(rx, ry=nil)
+ rx, ry = Magick::RVG.convert_to_float(rx, ry || rx)
+ if rx < 0 || ry < 0
+ raise ArgumentError, "rx, ry must be >= 0 (#{rx}, #{ry} given)"
+ end
+ @args << rx << ry
+ @primitive = :roundrectangle
+ self
+ end
+
+ end # class Rect
+
+ class PolyShape < Shape
+
+ def polypoints(points)
+ case points.length
+ when 1
+ points = Array(points[0])
+ when 2
+ x_coords = Array(points[0])
+ y_coords = Array(points[1])
+ unless x_coords.length > 0 && y_coords.length > 0
+ raise ArgumentError, "array arguments must contain at least one point"
+ end
+ n = x_coords.length - y_coords.length
+ short = n > 0 ? y_coords : x_coords
+ olen = short.length
+ n.abs.times {|x| short << short[x % olen]}
+ points = x_coords.zip(y_coords).flatten
+ end
+ n = points.length
+ if n < 4 || n % 2 != 0
+ raise ArgumentError, "insufficient/odd number of points specified: #{n}"
+ end
+ return Magick::RVG.convert_to_float(*points)
end
- n = points.length
- if n < 4 || n % 2 != 0
- raise ArgumentError, "insufficient/odd number of points specified: #{n}"
+
+ end # class PolyShape
+
+ class Polygon < PolyShape
+
+ # Draws a polygon. The arguments are [<tt>x</tt>, <tt>y</tt>] pairs that
+ # define the points that make up the polygon. At least two
+ # points must be specified. If the last point is not the
+ # same as the first, adds an additional point to close
+ # the polygon.
+ # Use the RVG::ShapeConstructors#polygon method to create Polygon objects in a container.
+ def initialize(*points)
+ super()
+ @primitive = :polygon
+ @args = polypoints(points)
+ end
+
+ end # class Polygon
+
+ class Polyline < PolyShape
+
+ # Draws a polyline. The arguments are [<tt>x</tt>, <tt>y</tt>] pairs that
+ # define the points that make up the polyline. At least two
+ # points must be specified.
+ # Use the RVG::ShapeConstructors#polyline method to create Polyline objects in a container.
+ def initialize(*points)
+ super()
+ points = polypoints(points)
+ @primitive = :polyline
+ @args = Magick::RVG.convert_to_float(*points)
+ end
+
+ end # class Polyline
+
+
+ class Image
+ include Stylable
+ include Transformable
+ include Describable
+ include PreserveAspectRatio
+ include Duplicatable
+
+ private
+ def align_to_viewport(scale)
+ tx = case @align
+ when 'none', /\AxMin/
+ 0
+ when NilClass, /\AxMid/
+ (@width - @image.columns*scale) / 2.0
+ when /\AxMax/
+ @width - @image.columns*scale
+ end
+
+ ty = case @align
+ when 'none', /YMin\z/
+ 0
+ when NilClass, /YMid\z/
+ (@height - @image.rows*scale) / 2.0
+ when /YMax\z/
+ @height - @image.rows*scale
+ end
+ return [tx, ty]
end
- return Magick::RVG.convert_to_float(*points)
- end
-
- end # class PolyShape
-
- class Polygon < PolyShape
-
- # Draws a polygon. The arguments are [<tt>x</tt>, <tt>y</tt>] pairs that
- # define the points that make up the polygon. At least two
- # points must be specified. If the last point is not the
- # same as the first, adds an additional point to close
- # the polygon.
- # Use the RVG::ShapeConstructors#polygon method to create Polygon objects in a container.
- def initialize(*points)
- super()
- @primitive = :polygon
- @args = polypoints(points)
- end
-
- end # class Polygon
-
- class Polyline < PolyShape
-
- # Draws a polyline. The arguments are [<tt>x</tt>, <tt>y</tt>] pairs that
- # define the points that make up the polyline. At least two
- # points must be specified.
- # Use the RVG::ShapeConstructors#polyline method to create Polyline objects in a container.
- def initialize(*points)
- super()
- points = polypoints(points)
- @primitive = :polyline
- @args = Magick::RVG.convert_to_float(*points)
- end
-
- end # class Polyline
-
-
- class Image
- include Stylable
- include Transformable
- include Describable
- include PreserveAspectRatio
- include Duplicatable
-
- private
- def align_to_viewport(scale)
- tx = case @align
- when 'none', /\AxMin/
- 0
- when NilClass, /\AxMid/
- (@width - @image.columns*scale) / 2.0
- when /\AxMax/
- @width - @image.columns*scale
+
+ def add_composite_primitive(gc)
+ if @align == 'none'
+ # Let RMagick do the scaling
+ scale = 1.0
+ width, height = @width, @height
+ elsif @meet_or_slice == 'meet'
+ scale = [@width/@image.columns, @height/@image.rows].min
+ width, height = @image.columns, @image.rows
+ else
+ # Establish clipping path around the current viewport
+ name = __id__.to_s
+ gc.define_clip_path(name) do
+ gc.path("M#{@x},#{@y} l#{@width},0 l0,#{@height} l-#{@width},0 l0,-#{@height}z")
+ end
+
+ gc.clip_path(name)
+ scale = [@width/@image.columns, @height/@image.rows].max
+ width, height = @image.columns, @image.rows
+ end
+ tx, ty = align_to_viewport(scale)
+ gc.composite(@x+tx, @y+ty, width*scale, height*scale, @image)
end
- ty = case @align
- when 'none', /YMin\z/
- 0
- when NilClass, /YMid\z/
- (@height - @image.rows*scale) / 2.0
- when /YMax\z/
- @height - @image.rows*scale
+ def init_viewbox()
+ @align = nil
+ @vbx_width, @vbx_height = @image.columns, @image.rows
+ @meet_or_slice = 'meet'
end
- return [tx, ty]
- end
-
- def add_composite_primitive(gc)
- if @align == 'none'
- # Let RMagick do the scaling
- scale = 1.0
- width, height = @width, @height
- elsif @meet_or_slice == 'meet'
- scale = [@width/@image.columns, @height/@image.rows].min
- width, height = @image.columns, @image.rows
- else
- # Establish clipping path around the current viewport
- name = __id__.to_s
- gc.define_clip_path(name) do
- gc.path("M#{@x},#{@y} l#{@width},0 l0,#{@height} l-#{@width},0 l0,-#{@height}z")
+
+ public
+
+ # Composite a raster image in the viewport defined by [x,y] and
+ # +width+ and +height+.
+ # Use the RVG::ImageConstructors#image method to create Text objects in a container.
+ def initialize(image, width=nil, height=nil, x=0, y=0)
+ super() # run module initializers
+ @image = image.copy # use a copy of the image in case app. re-uses the argument
+ @x, @y, @width, @height = Magick::RVG.convert_to_float(x, y, width || @image.columns, height || @image.rows)
+ if @width < 0 || @height < 0
+ raise ArgumentError, "width, height must be >= 0"
end
+ init_viewbox()
+ end
+
+ def add_primitives(gc) #:nodoc:
+ # Do not render if width or height is 0
+ return if @width == 0 || @height == 0
+ gc.push
+ add_transform_primitives(gc)
+ add_style_primitives(gc)
+ add_composite_primitive(gc)
+ gc.pop
+ end
+
+ end # class Image
+
+
+ # Methods that construct basic shapes within a container
+ module ShapeConstructors
+
+ # Draws a circle whose center is [<tt>cx</tt>, <tt>cy</tt>] and radius is +r+.
+ def circle(r, cx=0, cy=0)
+ circle = Circle.new(r, cx, cy)
+ @content << circle
+ return circle
+ end
+
+ # Draws an ellipse whose center is [<tt>cx</tt>, <tt>cy</tt>] and having
+ # a horizontal radius +rx+ and vertical radius +ry+.
+ def ellipse(rx, ry, cx=0, cy=0)
+ ellipse = Ellipse.new(rx, ry, cx, cy)
+ @content << ellipse
+ return ellipse
+ end
- gc.clip_path(name)
- scale = [@width/@image.columns, @height/@image.rows].max
- width, height = @image.columns, @image.rows
+ # Draws a line from [<tt>x1</tt>, <tt>y1</tt>] to [<tt>x2</tt>, <tt>y2</tt>].
+ def line(x1=0, y1=0, x2=0, y2=0)
+ line = Line.new(x1, y1, x2, y2)
+ @content << line
+ return line
end
- tx, ty = align_to_viewport(scale)
- gc.composite(@x+tx, @y+ty, width*scale, height*scale, @image)
- end
-
- def init_viewbox()
- @align = nil
- @vbx_width, @vbx_height = @image.columns, @image.rows
- @meet_or_slice = 'meet'
- end
-
- public
-
- # Composite a raster image in the viewport defined by [x,y] and
- # +width+ and +height+.
- # Use the RVG::ImageConstructors#image method to create Text objects in a container.
- def initialize(image, width=nil, height=nil, x=0, y=0)
- super() # run module initializers
- @image = image.copy # use a copy of the image in case app. re-uses the argument
- @x, @y, @width, @height = Magick::RVG.convert_to_float(x, y, width || @image.columns, height || @image.rows)
- if @width < 0 || @height < 0
- raise ArgumentError, "width, height must be >= 0"
+
+ # Draws a path defined by an SVG path string or a PathData
+ # object.
+ def path(path)
+ path = Path.new(path)
+ @content << path
+ return path
+ end
+
+ # Draws a rectangle whose upper-left corner is [<tt>x</tt>, <tt>y</tt>] and
+ # with the specified +width+ and +height+. Unless otherwise
+ # specified the rectangle has square corners. Returns a
+ # Rectangle object.
+ #
+ # Draw a rectangle with rounded corners by calling the #round
+ # method on the Rectangle object. <tt>rx</tt> and <tt>ry</tt> are
+ # the corner radii in the x- and y-directions. For example:
+ # canvas.rect(width, height, x, y).round(8, 6)
+ # If <tt>ry</tt> is omitted it defaults to <tt>rx</tt>.
+ def rect(width, height, x=0, y=0)
+ rect = Rect.new(width, height, x, y)
+ @content << rect
+ return rect
+ end
+
+ # Draws a polygon. The arguments are [<tt>x</tt>, <tt>y</tt>] pairs that
+ # define the points that make up the polygon. At least two
+ # points must be specified. If the last point is not the
+ # same as the first, adds an additional point to close
+ # the polygon.
+ def polygon(*points)
+ polygon = Polygon.new(*points)
+ @content << polygon
+ return polygon
+ end
+
+ # Draws a polyline. The arguments are [<tt>x</tt>, <tt>y</tt>] pairs that
+ # define the points that make up the polyline. At least two
+ # points must be specified.
+ def polyline(*points)
+ polyline = Polyline.new(*points)
+ @content << polyline
+ return polyline
+ end
+
+ end # module ShapeContent
+
+ # Methods that reference ("use") other drawable objects within a container
+ module UseConstructors
+
+ # Reference an object to be inserted into the container's
+ # content. [<tt>x</tt>,<tt>y</tt>] is the offset from the upper-left
+ # corner. If the argument is an RVG or Image object and +width+ and +height+
+ # are specified, these values will override the +width+ and +height+
+ # attributes on the argument.
+ def use(obj, x=0, y=0, width=nil, height=nil)
+ use = Use.new(obj, x, y, width, height)
+ @content << use
+ return use
end
- init_viewbox()
- end
-
- def add_primitives(gc) #:nodoc:
- # Do not render if width or height is 0
- return if @width == 0 || @height == 0
- gc.push
- add_transform_primitives(gc)
- add_style_primitives(gc)
- add_composite_primitive(gc)
- gc.pop
- end
-
- end # class Image
-
-
- # Methods that construct basic shapes within a container
- module ShapeConstructors
-
- # Draws a circle whose center is [<tt>cx</tt>, <tt>cy</tt>] and radius is +r+.
- def circle(r, cx=0, cy=0)
- circle = Circle.new(r, cx, cy)
- @content << circle
- return circle
- end
-
- # Draws an ellipse whose center is [<tt>cx</tt>, <tt>cy</tt>] and having
- # a horizontal radius +rx+ and vertical radius +ry+.
- def ellipse(rx, ry, cx=0, cy=0)
- ellipse = Ellipse.new(rx, ry, cx, cy)
- @content << ellipse
- return ellipse
- end
-
- # Draws a line from [<tt>x1</tt>, <tt>y1</tt>] to [<tt>x2</tt>, <tt>y2</tt>].
- def line(x1=0, y1=0, x2=0, y2=0)
- line = Line.new(x1, y1, x2, y2)
- @content << line
- return line
- end
-
- # Draws a path defined by an SVG path string or a PathData
- # object.
- def path(path)
- path = Path.new(path)
- @content << path
- return path
- end
-
- # Draws a rectangle whose upper-left corner is [<tt>x</tt>, <tt>y</tt>] and
- # with the specified +width+ and +height+. Unless otherwise
- # specified the rectangle has square corners. Returns a
- # Rectangle object.
- #
- # Draw a rectangle with rounded corners by calling the #round
- # method on the Rectangle object. <tt>rx</tt> and <tt>ry</tt> are
- # the corner radii in the x- and y-directions. For example:
- # canvas.rect(width, height, x, y).round(8, 6)
- # If <tt>ry</tt> is omitted it defaults to <tt>rx</tt>.
- def rect(width, height, x=0, y=0)
- rect = Rect.new(width, height, x, y)
- @content << rect
- return rect
- end
-
- # Draws a polygon. The arguments are [<tt>x</tt>, <tt>y</tt>] pairs that
- # define the points that make up the polygon. At least two
- # points must be specified. If the last point is not the
- # same as the first, adds an additional point to close
- # the polygon.
- def polygon(*points)
- polygon = Polygon.new(*points)
- @content << polygon
- return polygon
- end
-
- # Draws a polyline. The arguments are [<tt>x</tt>, <tt>y</tt>] pairs that
- # define the points that make up the polyline. At least two
- # points must be specified.
- def polyline(*points)
- polyline = Polyline.new(*points)
- @content << polyline
- return polyline
- end
-
- end # module ShapeContent
-
- # Methods that reference ("use") other drawable objects within a container
- module UseConstructors
-
- # Reference an object to be inserted into the container's
- # content. [<tt>x</tt>,<tt>y</tt>] is the offset from the upper-left
- # corner. If the argument is an RVG or Image object and +width+ and +height+
- # are specified, these values will override the +width+ and +height+
- # attributes on the argument.
- def use(obj, x=0, y=0, width=nil, height=nil)
- use = Use.new(obj, x, y, width, height)
- @content << use
- return use
- end
-
- end # module UseConstructors
-
- # Methods that construct container objects within a container
- module StructureConstructors
-
- # Establishes a new viewport. [<tt>x</tt>, <tt>y</tt>] is the coordinate of the
- # upper-left corner within the containing viewport. This is a
- # _container_ method. Styles and
- # transforms specified on this object will be used by objects
- # contained within, unless overridden by an inner container or
- # the contained object itself.
- def rvg(cols, rows, x=0, y=0, &block)
- rvg = Magick::RVG.new(cols, rows, &block)
- begin
- x, y = Float(x), Float(y)
- rescue ArgumentError
- args = [cols, rows, x, y]
- raise ArgumentError, "at least one argument is not convertable to Float (got #{args.collect {|a| a.class}.join(', ')})"
+
+ end # module UseConstructors
+
+ # Methods that construct container objects within a container
+ module StructureConstructors
+
+ # Establishes a new viewport. [<tt>x</tt>, <tt>y</tt>] is the coordinate of the
+ # upper-left corner within the containing viewport. This is a
+ # _container_ method. Styles and
+ # transforms specified on this object will be used by objects
+ # contained within, unless overridden by an inner container or
+ # the contained object itself.
+ def rvg(cols, rows, x=0, y=0, &block)
+ rvg = Magick::RVG.new(cols, rows, &block)
+ begin
+ x, y = Float(x), Float(y)
+ rescue ArgumentError
+ args = [cols, rows, x, y]
+ raise ArgumentError, "at least one argument is not convertable to Float (got #{args.collect {|a| a.class}.join(', ')})"
+ end
+ rvg.corner(x, y)
+ @content << rvg
+ return rvg
end
- rvg.corner(x, y)
- @content << rvg
- return rvg
- end
-
- # Defines a group.
- #
- # This method constructs a new
- # Group _container_ object. The styles and
- # transforms specified on this object will be used by objects
- # contained within, unless overridden by an inner container or
- # the contained object itself.
- # Define grouped elements by calling RVG::Embellishable
- # methods within the associated block.
- def g(&block)
- group = Group.new(&block)
- @content << group
- return group
- end
-
- end # module StructureConstructors
-
- # Methods that construct raster image objects within a container
- module ImageConstructors
-
- # Composite a raster image at [<tt>x</tt>,<tt>y</tt>]
- # in a viewport of the specified <tt>width</tt> and <tt>height</tt>.
- # If not specified, the width and height are the width and height
- # of the image. Use the RVG::PreserveAspectRatio#preserve_aspect_ratio method to
- # control the placement and scaling of the image within the
- # viewport. By default, the image is scaled to fit inside the
- # viewport and centered within the viewport.
- def image(image, width=nil, height=nil, x=0, y=0)
- img = Image.new(image, width, height, x, y)
- @content << img
- return img
- end
-
- end # module ImageConstructors
-
- # Methods that create shapes, text, and other drawable objects
- # within container objects such as ::Magick::RVG and
- # ::Magick::RVG::Group
- module Embellishable
- include StructureConstructors
- include ShapeConstructors
- include TextConstructors
- include UseConstructors
- include ImageConstructors
- end # module Embellishable
-
-end # class Magick::RVG
+
+ # Defines a group.
+ #
+ # This method constructs a new
+ # Group _container_ object. The styles and
+ # transforms specified on this object will be used by objects
+ # contained within, unless overridden by an inner container or
+ # the contained object itself.
+ # Define grouped elements by calling RVG::Embellishable
+ # methods within the associated block.
+ def g(&block)
+ group = Group.new(&block)
+ @content << group
+ return group
+ end
+
+ end # module StructureConstructors
+
+ # Methods that construct raster image objects within a container
+ module ImageConstructors
+
+ # Composite a raster image at [<tt>x</tt>,<tt>y</tt>]
+ # in a viewport of the specified <tt>width</tt> and <tt>height</tt>.
+ # If not specified, the width and height are the width and height
+ # of the image. Use the RVG::PreserveAspectRatio#preserve_aspect_ratio method to
+ # control the placement and scaling of the image within the
+ # viewport. By default, the image is scaled to fit inside the
+ # viewport and centered within the viewport.
+ def image(image, width=nil, height=nil, x=0, y=0)
+ img = Image.new(image, width, height, x, y)
+ @content << img
+ return img
+ end
+
+ end # module ImageConstructors
+
+ # Methods that create shapes, text, and other drawable objects
+ # within container objects such as ::Magick::RVG and
+ # ::Magick::RVG::Group
+ module Embellishable
+ include StructureConstructors
+ include ShapeConstructors
+ include TextConstructors
+ include UseConstructors
+ include ImageConstructors
+ end # module Embellishable
+
+ end # class RVG
+end # module Magick
View
1,216 lib/rvg/misc.rb
@@ -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
-
- # 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)
+# $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
+
+ 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
- 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)
+ # 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
- 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_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
- return farg
- end
-end # class Magick::RVG
+ 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
+ end # class RVG
+end # module Magick
-module Magick::RVG::Utility
- class TextStrategy
+module Magick
+ class RVG
+ class Utility
- def initialize(context)
- @ctx = context
- @ctx.shadow.affine = @ctx.text_attrs.affine
- end
+ class TextStrategy
- 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 initialize(context)
+ @ctx = context
+ @ctx.shadow.affine = @ctx.text_attrs.affine
+ end
- # escape existing braces, surround with braces
- text.gsub!(/[}]/) { |b| '\\' + b }
- 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
- 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
+ # escape existing braces, surround with braces
+ text.gsub!(/[}]/) { |b| '\\' + b }
+ return '{' + text + '}'
+ 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
- end
- first_word = false
- word.split('').each do |glyph|
- wx, wy = get_letter_spacing(glyph)
- x_rel_coords << wx
- y_rel_coords << wy
+ 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
- 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 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
- 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 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 # class TextStrategy
+ 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
- class LRTextStrategy < TextStrategy
+ end # class 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
+ class LRTextStrategy < TextStrategy
- 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_word_spacing()
+ @word_space ||= glyph_metrics(@ctx.text_attrs.glyph_orientation_horizontal, ' ')[0]
+ [@word_space + @ctx.text_attrs.word_spacing, 0]
+ 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
-
- # 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
+ def get_letter_spacing(glyph)
+ gx, gy = glyph_metrics(@ctx.text_attrs.glyph_orientation_horizontal, glyph)
+ [gx+@ctx.text_attrs.letter_spacing, gy]
+ 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
+ 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
- y += shift_baseline(@ctx.text_attrs.glyph_orientation_horizontal, text[0,1])
+ # 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
- 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
+ y += shift_baseline(@ctx.text_attrs.glyph_orientation_horizontal, text[0,1])
- @ctx.gc.pop()
- [dx, 0]
- end
+ 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
- end # class LRTextStrategy
+ @ctx.gc.pop()
+ [dx, 0]
+ end
- class RLTextStrategy < TextStrategy
+ end # class LRTextStrategy
- def render(x, y, text)
- raise NotImplementedError
- end
+ class RLTextStrategy < TextStrategy
- end # class RLTextStrategy
+ def render(x, y, text)
+ raise NotImplementedError
+ end
+ end # class RLTextStrategy
- 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
+ class TBTextStrategy < TextStrategy
- 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_word_spacing()
+ @word_space ||= glyph_metrics(@ctx.text_attrs.glyph_orientation_vertical, ' ')[1]
+ [0, @word_space + @ctx.text_attrs.word_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}
-
- # 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
+ def get_letter_spacing(glyph)
+ gx, gy = glyph_metrics(@ctx.text_attrs.glyph_orientation_vertical, glyph)
+ [gx, gy+@ctx.text_attrs.letter_spacing]
+ end
- # 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
+ 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
- x -= shift_baseline(@ctx.text_attrs.glyph_orientation_vertical, text[0,1])
+ # Align the first glyph such that its center
+ # is aligned on x and its top is aligned on y.
- 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 0
+ x -= x_rel_coords.max / 2
+ y += y_rel_coords[0]
+ when 90
+ x -= x_rel_coords.max / 2
when 180
- x_shift = -(dx - x_rel_coords.shift) / 2
+ 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
- render_glyph(@ctx.text_attrs.glyph_orientation_vertical, x+x_shift, y, glyph)
- y += y_rel_coords.shift
- end
- end
+ x -= shift_baseline(@ctx.text_attrs.glyph_orientation_vertical, text[0,1])
- @ctx.gc.pop()
- [0, dy]
- 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
+ end
+
+ render_glyph(@ctx.text_attrs.glyph_orientation_vertical, x+x_shift, y, glyph)
+ y += y_rel_coords.shift
+ end
+ end
- end # class TBTextStrategy
-
- # 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
+ @ctx.gc.pop()
+ [0, dy]
+ end
- end # class NormalTextStrategy
+ end # class TBTextStrategy
+
+ # 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
-end # module Magick::RVG::Utility
+ end # class NormalTextStrategy
+ end # class Utility
+ end # class RVG
+end # module Magick
-module Magick::RVG::Utility
- class TextAttributes
+module Magick
+ class RVG
+ class Utility
- public
+ class TextAttributes
- WRITING_MODE = %w{lr-tb lr rl-tb rl tb-rl tb}
+ public
- 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
+ WRITING_MODE = %w{lr-tb lr rl-tb rl tb-rl tb}
- 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 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 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 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 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 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 affine()
- @affine[-1]
- 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 baseline_shift()
- @baseline_shift[-1]
- end
+ def affine()
+ @affine[-1]
+ end
- def baseline_shift=(value)
- @baseline_shift[-1] = value
- end
+ def baseline_shift()
+ @baseline_shift[-1]
+ end
- def text_anchor()
- @text_anchor[-1]
- end
+ def baseline_shift=(value)
+ @baseline_shift[-1] = value
+ end
- def text_anchor=(anchor)
- @text_anchor[-1] = anchor
- end
+ def text_anchor()
+ @text_anchor[-1]
+ end
- def glyph_orientation_vertical()
- @glyph_orientation_vertical[-1]
- end
+ def text_anchor=(anchor)
+ @text_anchor[-1] = anchor
+ end
- def glyph_orientation_vertical=(angle)
- @glyph_orientation_vertical[-1] = angle
- end
+ def glyph_orientation_vertical()
+ @glyph_orientation_vertical[-1]
+ end
- def glyph_orientation_horizontal()
- @glyph_orientation_horizontal[-1]
- end
+ def glyph_orientation_vertical=(angle)
+ @glyph_orientation_vertical[-1] = angle
+ end
- def glyph_orientation_horizontal=(angle)
- @glyph_orientation_horizontal[-1] = angle
- end
+ def glyph_orientation_horizontal()
+ @glyph_orientation_horizontal[-1]
+ end
- def letter_spacing()
- @letter_spacing[-1]
- end
+ def glyph_orientation_horizontal=(angle)
+ @glyph_orientation_horizontal[-1] = angle
+ end
- def letter_spacing=(value)
- @letter_spacing[-1] = value
- end
+ def letter_spacing()
+ @letter_spacing[-1]
+ 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 letter_spacing=(value)
+ @letter_spacing[-1] = value
+ end
- def word_spacing()
- @word_spacing[-1]
- 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=(value)
- @word_spacing[-1] = value
- end
+ def word_spacing()
+ @word_spacing[-1]
+ end
- def writing_mode()
- @writing_mode[-1]
- end
+ def word_spacing=(value)
+ @word_spacing[-1] = value
+ end
- def writing_mode=(mode)
- @writing_mode[-1] = WRITING_MODE.include?(mode) ? mode : 'lr-tb'
- end
+ def writing_mode()
+ @writing_mode[-1]
+ end
- end # class TextAttributes
+ def writing_mode=(mode)
+ @writing_mode[-1] = WRITING_MODE.include?(mode) ? mode : 'lr-tb'
+ end
- class GraphicContext
+ end # class TextAttributes
- 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}
+ class GraphicContext
- FONT_STYLE = {:normal => Magick::NormalStyle,
- :italic => Magick::ItalicStyle,
- :oblique => Magick::ObliqueStyle}
+ 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_WEIGHT = {'normal' => Magick::NormalWeight,
- 'bold' => Magick::BoldWeight,
- 'bolder' => Magick::BolderWeight,
- 'lighter' => Magick::LighterWeight}
+ FONT_STYLE = {:normal => Magick::NormalStyle,
+ :italic => Magick::ItalicStyle,
+ :oblique => Magick::ObliqueStyle}
- TEXT_ANCHOR = {:start => Magick::StartAnchor,
- :middle => Magick::MiddleAnchor,
- :end => Magick::EndAnchor}
+ FONT_WEIGHT = {'normal' => Magick::NormalWeight,
+ 'bold' => Magick::BoldWeight,
+ 'bolder' => Magick::BolderWeight,
+ 'lighter' => Magick::LighterWeight}
- ANCHOR_TO_ALIGN = {:start => Magick::LeftAlign,
- :middle => Magick::CenterAlign,
- :end => Magick::RightAlign}
+ TEXT_ANCHOR = {:start => Magick::StartAnchor,
+ :middle => Magick::MiddleAnchor,
+ :end => Magick::EndAnchor}
- TEXT_DECORATION = {:none => Magick::NoDecoration,
- :underline => Magick::UnderlineDecoration,
- :overline => Magick::OverlineDecoration,
- :line_through => Magick::LineThroughDecoration}
+ ANCHOR_TO_ALIGN = {:start => Magick::LeftAlign,
+ :middle => Magick::CenterAlign,
+ :end => Magick::RightAlign}
- TEXT_STRATEGIES = {'lr-tb'=>LRTextStrategy, 'lr'=>LRTextStrategy,
- 'rt-tb'=>RLTextStrategy, 'rl'=>RLTextStrategy,
- 'tb-rl'=>TBTextStrategy, 'tb'=>TBTextStrategy}
+ TEXT_DECORATION = {:none => Magick::NoDecoration,
+ :underline => Magick::UnderlineDecoration,
+ :overline => Magick::OverlineDecoration,
+ :line_through => Magick::LineThroughDecoration}
- def GraphicContext.degrees_to_radians(deg)
- Math::PI * (deg % 360.0) / 180.0
- end
+ TEXT_STRATEGIES = {'lr-tb'=>LRTextStrategy, 'lr'=>LRTextStrategy,
+ 'rt-tb'=>RLTextStrategy, 'rl'=>RLTextStrategy,
+ 'tb-rl'=>TBTextStrategy, 'tb'=>TBTextStrategy}
- private
+ def GraphicContext.degrees_to_radians(deg)
+ Math::PI * (deg % 360.0) / 180.0
+ end
- def init_matrix()
- @rx = @ry = 0
- @sx = @sy = 1
- @tx = @ty = 0
- end
+ private
- 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 init_matrix()
+ @rx = @ry = 0
+ @sx = @sy = 1
+ @tx = @ty = 0
+ end
- public
+ 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()