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

- old
+ new

@@ -10,16 +10,16 @@ # This class is not passed the prawn object, so knows nothing about # prawn specifically - this might be useful if you want to take this code and use it to convert # SVG to another format. # class Prawn::Svg::Parser - CONTAINER_TAGS = %w(g svg symbol defs) - + CONTAINER_TAGS = %w(g svg symbol defs clipPath) + # - # Construct a Parser object. + # Construct a Parser object. # - # The +data+ argument is SVG data. + # The +data+ argument is SVG data. # # +bounds+ is a tuple [width, height] that specifies the bounds of the drawing space in points. # # +options+ can optionally contain # the key :width or :height. If both are specified, only :width will be used. @@ -38,50 +38,51 @@ # ] # ] # def parse @document.warnings.clear - + calls = [['fill_color', '000000', []]] root_element = Prawn::Svg::Element.new(@document, @document.root, calls, :ids => {}, :fill => true) - + parse_element(root_element) calls end - private + private REQUIRED_ATTRIBUTES = { "polyline" => %w(points), "polygon" => %w(points), "circle" => %w(r), "ellipse" => %w(rx ry), "rect" => %w(width height), "path" => %w(d), - "image" => %w(width height) + "image" => %w(width height) } - + USE_NEW_CIRCLE_CALL = Prawn::Document.new.respond_to?(:circle) USE_NEW_ELLIPSE_CALL = Prawn::Document.new.respond_to?(:ellipse) def parse_element(element) attrs = element.attributes if required_attributes = REQUIRED_ATTRIBUTES[element.name] return unless check_attrs_present(element, required_attributes) end - + case element.name when *CONTAINER_TAGS + do_not_append_calls = %w(symbol defs clipPath).include?(element.name) + element.state[:disable_drawing] = true if element.name == "clipPath" + element.each_child_element do |child| element.add_call "save" parse_element(child) element.add_call "restore" end - - do_not_append_calls = %w(symbol defs).include?(element.name) - + when 'style' load_css_styles(element) when 'text' @svg_text ||= Text.new @@ -105,60 +106,60 @@ points = attrs['points'].split(/\s+/).collect do |point| x, y = point.split(",") [x(x), y(y)] end element.add_call "polygon", *points - + when 'circle' if USE_NEW_CIRCLE_CALL - element.add_call "circle", + element.add_call "circle", [x(attrs['cx'] || "0"), y(attrs['cy'] || "0")], distance(attrs['r']) else - element.add_call "circle_at", + element.add_call "circle_at", [x(attrs['cx'] || "0"), y(attrs['cy'] || "0")], :radius => distance(attrs['r']) end - + when 'ellipse' - element.add_call USE_NEW_ELLIPSE_CALL ? "ellipse" : "ellipse_at", + element.add_call USE_NEW_ELLIPSE_CALL ? "ellipse" : "ellipse_at", [x(attrs['cx'] || "0"), y(attrs['cy'] || "0")], distance(attrs['rx']), distance(attrs['ry']) - + when 'rect' radius = distance(attrs['rx'] || attrs['ry']) args = [[x(attrs['x'] || '0'), y(attrs['y'] || '0')], distance(attrs['width']), distance(attrs['height'])] if radius # n.b. does not support both rx and ry being specified with different values element.add_call "rounded_rectangle", *(args + [radius]) else element.add_call "rectangle", *args end - + when 'path' parse_path(element) - + when 'use' parse_use(element) when 'title', 'desc', 'metadata' # ignore do_not_append_calls = true - - when 'font-face', 'clipPath' + + when 'font-face' # not supported do_not_append_calls = true when 'image' @svg_image ||= Image.new(@document) @svg_image.parse(element) else @document.warnings << "Unknown tag '#{element.name}'; ignoring" end - + element.append_calls_to_parent unless do_not_append_calls end - + def parse_path(element) @svg_path ||= Path.new begin commands = @svg_path.parse(element.attributes['d']) @@ -177,35 +178,34 @@ end element.add_call command, point_to, opts else element.add_call command end - end + end end - + def parse_use(element) if href = element.attributes['xlink:href'] if href[0..0] == '#' id = href[1..-1] - if id_calls = element.state[:ids][id] + if definition_element = @document.elements_by_id[id] x = element.attributes['x'] y = element.attributes['y'] if x || y element.add_call_and_enter "translate", distance(x || 0), -distance(y || 0) end - - element.calls.concat(id_calls) + element.add_calls_from_element definition_element else @document.warnings << "no tag with ID '#{id}' was found, referenced by use tag" end else @document.warnings << "use tag has an href that is not a reference to an id; this is not supported" end else @document.warnings << "no xlink:href specified on use tag" end - end + end #################################################################################################################### def load_css_styles(element) if @document.css_parser @@ -213,21 +213,21 @@ element.element.cdatas.collect {|d| d.to_s}.join else element.element.text end - @document.css_parser.add_block!(data) - end + @document.css_parser.add_block!(data) + end end def check_attrs_present(element, attrs) missing_attrs = attrs - element.attributes.keys if missing_attrs.any? @document.warnings << "Must have attributes #{missing_attrs.join(", ")} on tag #{element.name}; skipping tag" end missing_attrs.empty? end - + %w(x y distance).each do |method| define_method(method) {|*a| @document.send(method, *a)} end end