lib/prawn/svg/element.rb in prawn-svg-0.12.0.3 vs lib/prawn/svg/element.rb in prawn-svg-0.12.0.4

- old
+ new

@@ -1,52 +1,56 @@ class Prawn::Svg::Element attr_reader :document, :element, :parent_calls, :base_calls, :state, :attributes attr_accessor :calls - + def initialize(document, element, parent_calls, state) @document = document @element = element @parent_calls = parent_calls @state = state @base_calls = @calls = [] - + combine_attributes_and_style_declarations apply_styles - + if id = @attributes["id"] - state[:ids][id] = @base_calls + document.elements_by_id[id] = self end end - + def name @name ||= element.name end - + def each_child_element element.elements.each do |e| yield self.class.new(@document, e, @calls, @state.dup) end end - + def warnings @document.warnings end - + def add_call(name, *arguments) @calls << [name.to_s, arguments, []] end - + def add_call_and_enter(name, *arguments) @calls << [name.to_s, arguments, []] @calls = @calls.last.last - end - + end + def append_calls_to_parent @parent_calls.concat(@base_calls) end - - + + def add_calls_from_element(other) + @calls.concat other.base_calls + end + + protected def apply_styles # Transform if transform = @attributes['transform'] parse_css_method_calls(transform).each do |name, arguments| @@ -85,19 +89,34 @@ # We can't do nested opacities quite like the SVG requires, but this is close enough. fill_opacity = stroke_opacity = clamp(@attributes['opacity'].to_f, 0, 1) if @attributes['opacity'] fill_opacity = clamp(@attributes['fill-opacity'].to_f, 0, 1) if @attributes['fill-opacity'] stroke_opacity = clamp(@attributes['stroke-opacity'].to_f, 0, 1) if @attributes['stroke-opacity'] - if fill_opacity || stroke_opacity + if fill_opacity || stroke_opacity state[:fill_opacity] = (state[:fill_opacity] || 1) * (fill_opacity || 1) state[:stroke_opacity] = (state[:stroke_opacity] || 1) * (stroke_opacity || 1) add_call_and_enter 'transparent', state[:fill_opacity], state[:stroke_opacity] end + # Clip path + if clip_path = @attributes['clip-path'] + if (matches = clip_path.strip.match(/\Aurl\(#(.*)\)\z/)).nil? + document.warnings << "Only clip-path attributes with the form 'url(#xxx)' are supported" + elsif (clip_path_element = @document.elements_by_id[matches[1]]).nil? + document.warnings << "clip-path ID '#{matches[1]}' not defined" + elsif clip_path_element.element.name != "clipPath" + document.warnings << "clip-path ID '#{matches[1]}' does not point to a clipPath tag" + else + add_call_and_enter 'save_graphics_state' + add_calls_from_element clip_path_element + add_call "clip" + end + end + # Fill and stroke - draw_types = [] + draw_types = [] [:fill, :stroke].each do |type| dec = @attributes[type.to_s] if dec == "none" state[type] = false elsif dec @@ -107,15 +126,15 @@ end end draw_types << type.to_s if state[type] end - + # Stroke width - add_call('line_width', @document.distance(@attributes['stroke-width'])) if @attributes['stroke-width'] + add_call('line_width', @document.distance(@attributes['stroke-width'])) if @attributes['stroke-width'] - # Fonts + # Fonts if size = @attributes['font-size'] @state[:font_size] = size.to_f * @document.scale end if weight = @attributes['font-weight'] font_updated = true @@ -123,24 +142,24 @@ end if (family = @attributes['font-family']) && family.strip != "" font_updated = true @state[:font_family] = family end - + if @state[:font_family] && font_updated if pdf_font = Prawn::Svg::Font.map_font_family_to_pdf_font(@state[:font_family], @state[:font_style]) add_call_and_enter 'font', pdf_font else @document.warnings << "Font family '#{@state[:font_family]}' style '#{@state[:font_style] || 'normal'}' is not a known font." end - end - + end + # Call fill, stroke, or both draw_type = draw_types.join("_and_") - if draw_type != "" && !Prawn::Svg::Parser::CONTAINER_TAGS.include?(element.name) - add_call_and_enter draw_type - end + if draw_type != "" && !@state[:disable_drawing] && !Prawn::Svg::Parser::CONTAINER_TAGS.include?(element.name) + add_call_and_enter(draw_type) + end end def parse_css_method_calls(string) string.scan(/\s*(\w+)\(([^)]+)\)\s*/).collect do |call| name, argument_string = call @@ -148,11 +167,11 @@ [name, arguments] end end # TODO : use http://www.w3.org/TR/SVG11/types.html#ColorKeywords - HTML_COLORS = { + HTML_COLORS = { 'black' => "000000", 'green' => "008000", 'silver' => "c0c0c0", 'lime' => "00ff00", 'gray' => "808080", 'olive' => "808000", 'white' => "ffffff", 'yellow' => "ffff00", 'maroon' => "800000", 'navy' => "000080", 'red' => "ff0000", 'blue' => "0000ff", 'purple' => "800080", 'teal' => "008080", 'fuchsia' => "ff00ff", 'aqua' => "00ffff" }.freeze @@ -171,30 +190,30 @@ elsif m = color.match(RGB_REGEXP) break (1..3).collect do |n| value = m[n].to_f value *= 2.55 if m[n][-1..-1] == '%' "%02x" % clamp(value.round, 0, 255) - end.join - end + end.join + end end end def clamp(value, min_value, max_value) [[value, min_value].max, max_value].min - end - + end + def combine_attributes_and_style_declarations if @document && @document.css_parser tag_style = @document.css_parser.find_by_selector(element.name) id_style = @document.css_parser.find_by_selector("##{element.attributes["id"]}") if element.attributes["id"] - + if classes = element.attributes["class"] class_styles = classes.strip.split(/\s+/).collect do |class_name| @document.css_parser.find_by_selector(".#{class_name}") end end - + element_style = element.attributes['style'] style = [tag_style, class_styles, id_style, element_style].flatten.collect do |s| s.nil? || s.strip == "" ? "" : "#{s}#{";" unless s.match(/;\s*\z/)}" end.join @@ -202,10 +221,10 @@ style = element.attributes['style'] || "" end @attributes = parse_css_declarations(style) element.attributes.each {|n,v| @attributes[n] = v unless @attributes[n]} - end + end def parse_css_declarations(declarations) # copied from css_parser declarations.gsub!(/(^[\s]*)|([\s]*$)/, '')