#-- # PDF::Writer for Ruby. # http://rubyforge.org/projects/ruby-pdf/ # Copyright 2003 - 2005 Austin Ziegler. # # Licensed under a MIT-style licence. See LICENCE in the main distribution # for full licensing information. # # $Id: strokestyle.rb 75 2005-06-02 21:20:35Z austin $ #++ # A class that represents a style with which lines will be drawn. class PDF::Writer::StrokeStyle LINE_CAPS = { :butt => 0, :round => 1, :square => 2 } LINE_JOINS = { :miter => 0, :round => 1, :bevel => 2 } SOLID_LINE = { :pattern => [], :phase => 0 } def initialize(width = 1, options = {}) @width = width @cap = options[:cap] @join = options[:join] @dash = options[:dash] @miter_limit = options[:miter_limit] yield self if block_given? end DEFAULT = self.new(1, :cap => :butt, :join => :miter, :dash => SOLID_LINE) # The thickness of the line in PDF units. attr_accessor :width # The type of cap to put on the line. # # :butt:: The stroke is squared off at the endpoint of the # path. There is no projection beyond the end of the # path. # :round:: A semicircular arc with a diameter equal to the # line width is drawn around the endpoint and filled # in. # :square:: The stroke continues beyond the endpoint of the # path for a distance equal to half the line width # and is squared off. # +nil+:: Keeps the current line cap. attr_accessor :cap def cap=(c) #:nodoc: if c.nil? or LINE_CAPS.include?(c) @cap = c else raise ArgumentError, "Line cap styles must be nil (none), butt, round, or square." end end # How two lines join together. # # :miter:: The outer edges of the strokes for the two # segments are extended until they meet at an angle, # as in a picture frame. If the segments meet at too # sharp an angle (as defined by the #miter_limit), a # bevel join is used instead. # :round:: An arc of a circle with a diameter equal to the # line width is drawn around the point where the two # segments meet, connecting the outer edges of the # strokes for the two segments. This pie-slice # shaped figure is filled in, producing a rounded # corner. # :bevel:: The two segments are finished with butt caps and # the the resulting notch beyond the ends of the # segments is filled with a triangle, forming a # flattened edge on the join. # +nil+:: Keeps the current line join. attr_accessor :join def join=(j) #:nodoc: if j.nil? or LINE_JOINS.include?(j) @join = j else raise ArgumentError, "Line join styles must be nil (none), miter, round, or bevel." end end # When two line segments meet and :miter joins have been # specified, the miter may extend far beyond the thickness of the line # stroking the path. #miter_limit imposes a maximum ratio miter length # to line width at which point the join will be converted from a miter # to a bevel. Adobe points out that the ratio is directly related to the # angle between the segments in user space. With [p] representing the # angle at which the segments meet: # # miter_length / line_width == 1 / (sin ([p] / 2)) # # A miter limit of 1.414 converts miters to bevels for [p] less than 90 # degrees, a limit of 2.0 converts them for [p] less than 60 degrees, # and a limit of 10.0 converts them for [p] less than approximately 11.5 # degrees. attr_accessor :miter_limit # Controls the pattern of dashes and gaps used to stroke paths. This # value must either be +nil+, or a hash with the following values: # # :pattern:: An array of numbers specifying the lengths (in PDF # userspace units) of alternating dashes and gaps. # The array is processed cyclically, so that a # :pattern of [3] represents three units # on, three units off, and a :pattern of # [2, 1] represents two units on, one unit off. # # # - represents on, _ represents off # ---___---___--- # pattern [3] # --_--_--_--_--_ # pattern [2, 1] # # :phase:: The offset in the :pattern where the # drawing of the stroke begins. Using a # :phase of 1, the :pattern [3] # will start offset by one phase, for two units on, # three units off, three units on. # # --___---___---_ # pattern [3], phase 1 # -_--_--_--_--_- # pattern [2, 1], phase 1 # # The constant SOLID_LINE may be used to restore line drawing to a solid # line; this corresponds to an empty pattern with zero phase ([] 0). # # Dashed lines wrap around curves and corners just as solid stroked # lines do, with normal cap and join handling with no consideration of # the dash pattern. A path with several subpaths treats each subpath # independently; the complete dash pattern is restarted at the beginning # of each subpath. attr_accessor :dash def render(debug = false) s = "" s << "#{width} w" if @width > 0 s << " #{LINE_CAPS[@cap]} J" if @cap s << " #{LINE_JOINS[@join]} j" if @join s << " #{@miter_limit} M" if @miter_limit if @dash s << " [" @dash[:pattern].each { |len| s << " #{len}" } s << " ] #{@dash[:phase] or 0} d" end s end end