#-- # $Id: stretchable.rb,v 1.1 2005/03/12 17:02:00 rmagick Exp $ # Copyright (C) 2005 Timothy P. Hunter #++ class Magick::RVG module PreserveAspectRatio #-- # Included in Stretchable module and Image class #++ # Specifies how the image within a viewport should be scaled. # [+align+] a combination of 'xMin', 'xMid', or 'xMax', followed by # 'YMin', 'YMid', or 'YMax' # [+meet_or_slice+] one of 'meet' or 'slice' def preserve_aspect_ratio(align, meet_or_slice='meet') @align = align.to_s if @align != 'none' m = /\A(xMin|xMid|xMax)(YMin|YMid|YMax)\z/.match(@align) raise(ArgumentError, "unknown alignment specifier: #{@align}") unless m end if meet_or_slice meet_or_slice = meet_or_slice.to_s.downcase if meet_or_slice == 'meet' || meet_or_slice == 'slice' @meet_or_slice = meet_or_slice else raise(ArgumentError, "specifier must be `meet' or `slice' (got #{meet_or_slice})") end end yield(self) if block_given? self end end # module PreserveAspectRatio # The methods in this module describe the user-coordinate space. # Only RVG objects are stretchable. module Stretchable private def set_viewbox_none(width, height) sx, sy = 1.0, 1.0 tx, ty = @vbx_x, @vbx_y if @vbx_width sx = width / @vbx_width end if @vbx_height sy = height / @vbx_height end return [tx, ty, sx, sy] end # Use align attribute to compute x- and y-offset from viewport's upper-left corner. def align_to_viewport(width, height, sx, sy) tx, ty = @vbx_x, @vbx_y tx += case @align when /\AxMin/ 0 when NilClass, /\AxMid/ (width - @vbx_width*sx) / 2.0 when /\AxMax/ width - @vbx_width*sx end ty += case @align when /YMin\z/ 0 when NilClass, /YMid\z/ (height - @vbx_height*sy) / 2.0 when /YMax\z/ height - @vbx_height*sy end return [tx, ty] end # Scale to smaller viewbox dimension def set_viewbox_meet(width, height) sx = sy = [width / @vbx_width, height / @vbx_height].min tx, ty = align_to_viewport(width, height, sx, sy) return [tx, ty, sx, sy] end # Scale to larger viewbox dimension def set_viewbox_slice(width, height) sx = sy = [width / @vbx_width, height / @vbx_height].max tx, ty = align_to_viewport(width, height, sx, sy) return [tx, ty, sx, sy] end # Establish the viewbox as necessary def add_viewbox_primitives(width, height, gc) @vbx_width ||= width @vbx_height ||= height @vbx_x ||= 0.0 @vbx_y ||= 0.0 if @align == 'none' tx, ty, sx, sy = set_viewbox_none(width, height) elsif @meet_or_slice == 'meet' tx, ty, sx, sy = set_viewbox_meet(width, height) else tx, ty, sx, sy = set_viewbox_slice(width, height) end # Establish clipping path around the current viewport name = __id__.to_s gc.define_clip_path(name) do gc.path("M0,0 l#{width},0 l0,#{height} l-#{width},0 l0,-#{height}z") end gc.clip_path(name) gc.translate(tx, ty) if (tx.abs > 1.0e-10 || ty.abs > 1.0e-10) gc.scale(sx, sy) if (sx != 1.0 || sy != 1.0) end def initialize(*args, &block) super() @vbx_x, @vbx_y, @vbx_width, @vbx_height = nil @meet_or_slice = 'meet' @align = nil end public include PreserveAspectRatio # Describe a user coordinate system to be imposed on the viewbox. # The arguments must be numbers and the +width+ and +height+ # arguments must be positive. def viewbox(x, y, width, height) begin @vbx_x = Float(x) @vbx_y = Float(y) @vbx_width = Float(width) @vbx_height = Float(height) rescue ArgumentError raise ArgumentError, "arguments must be convertable to float (got #{x.class}, #{y.class}, #{width.class}, #{height.class})" end raise(ArgumentError, "viewbox width must be > 0 (#{width} given)") unless width >= 0 raise(ArgumentError, "viewbox height must be > 0 (#{height} given)") unless height >= 0 yield(self) if block_given? self end end # module Stretchable end # class Magick::RVG