require 'test/spec' # gem install test-spec
require 'builder'
require 'rexml/document'
require 'svgtidy'

describe "path" do
  it "should round" do
    tidy { |svg|
      svg.path :d => "M-3.7,-2.3 l7.1,8.9"
    }.elements['path/@d'].value.should == "M-4-2l7,9"
  end

  it "should truncate trailing zeros" do
    tidy(:grid=>false) { |svg|
      svg.path :d => "M-3.7,-2.0 l7.0,8.9"
    }.elements['path/@d'].value.should == "M-3.7-2l7,8.9"
  end

  it "should convert absolute segments to relative ones" do
    tidy { |svg|
      svg.path :d => "M10,20 L50,60"
    }.elements['path/@d'].value.should == "M10,20l40,40"
  end

  it "should convert absolute segments to relative ones" do
    tidy(:absolute=>true, :grid=>true) { |svg|
      svg.path :d => "M10,20 L50,60"
    }.elements['path/@d'].value.should == "M10,20L50,60"
  end

  it "should detect vertical lines" do
    tidy { |svg|
      svg.path :d => "M10,20 L10,60"
    }.elements['path/@d'].value.should == "M10,20v40"
  end

  it "should detect horizontal lines" do
    tidy { |svg|
      svg.path :d => "M10,20 L40,20"
    }.elements['path/@d'].value.should == "M10,20h30"
  end

  it "should eliminate empty segments" do
    tidy { |svg|
      svg.path :d => "M10,20 L10.2,20.2 L40,20"
    }.elements['path/@d'].value.should == "M10,20h30"
  end

  it "should eliminate last segment in a polygon" do
    tidy { |svg|
      svg.path :d => "M0,0 L0,10 L10,10 L0,0 Z"
    }.elements['path/@d'].value.should == "M0,0v10h10z"
  end

  it "should collapse straight curves" do
    tidy { |svg|
      svg.path :d => "M10,20 C20,20 30,20 40,20"
    }.elements['path/@d'].value.should == "M10,20h30"
  end

  it "should do quadratic Bezier curves" do
    tidy { |svg|
      svg.path :d => "M200,300 Q400,50 600,300 T1000,300"
    }.elements['path/@d'].value.should == "M200,300q200-250,400,0t400,0"
  end
end

describe "rect" do
  it "should scale x" do
    tidy { |svg|
      svg.g :transform => "scale(2)" do
        svg.rect :x => "10", :y => "20.8"
      end
    }.elements['g/rect/@x'].value.should == "20"
  end

  it "should scale y" do
    tidy { |svg|
      svg.g :transform => "scale(2)" do
        svg.rect :x => "10", :y => "20.8"
      end
    }.elements['g/rect/@y'].value.should == "42"
  end

  it "should scale width" do
    tidy { |svg|
      svg.g :transform => "scale(2)" do
        svg.rect :width => "10", :height => "20.8"
      end
    }.elements['g/rect/@width'].value.should == "20"
  end

  it "should scale height" do
    tidy { |svg|
      svg.g :transform => "scale(2)" do
        svg.rect :width => "10", :height => "20.8"
      end
    }.elements['g/rect/@height'].value.should == "42"
  end
end

describe "transform" do
  it "should matrix" do
    tidy { |svg|
      svg.g :transform => "matrix(0 1 1 0 1 2)" do
        svg.path :d => "M10,20"
      end
    }.elements['g/path/@d'].value.should == "M21,12"
  end

  it "should translate" do
    tidy { |svg|
      svg.g :transform => "translate(-5 +5)" do
        svg.path :d => "M10,20"
      end
    }.elements['g/path/@d'].value.should == "M5,25"
  end

  it "should scale" do
    tidy { |svg|
      svg.g :transform => "scale(2)" do
        svg.path :d => "M10,20"
      end
    }.elements['g/path/@d'].value.should == "M20,40"
  end

  it "should stretch" do
    tidy { |svg|
      svg.g :transform => "scale(2,3)" do
        svg.path :d => "M10,20"
      end
    }.elements['g/path/@d'].value.should == "M20,60"
  end

  it "should rotate" do
    tidy { |svg|
      svg.g :transform => "rotate(30)" do
        svg.path :d => "M-50,0h100"
      end
    }.elements['g/path/@d'].value.should == "M-43-25l86,50"
  end

  it "should compose" do
    tidy { |svg|
      svg.g :transform => "translate(-10,-10) scale(2)" do
        svg.path :d => "M10,20"
      end
    }.elements['g/path/@d'].value.should == "M10,30"
  end

  it "should nest" do
    tidy { |svg|
      svg.g :transform => "translate(-10,-10)" do
        svg.g :transform => "scale(2)" do
          svg.path :d => "M10,20"
        end
      end
    }.elements['g/g/path/@d'].value.should == "M10,30"
  end

  it "should curve" do
    tidy { |svg|
      svg.g :transform => "scale(2)" do
        svg.path :d => "M0,0c10,15,20,30,40,40"
      end
    }.elements['g/path/@d'].value.should == "M0,0c20,30,40,60,80,80"
  end
end

describe "redundancy elimination" do
  it "should remove stop-opacity:1" do
    tidy { |svg|
      svg.path 'stop-opacity' => '1'
    }.elements['path/@stop-opacity'].should == nil
  end

  it "should remove stroke-* when stroke='none'" do
    tidy { |svg|
      svg.path 'stroke' => 'none', 'stroke-linejoin' => 'miter'
    }.elements['path/@stroke-linejoin'].should == nil
  end
end

describe "viewBox" do
  it "should resize" do
    tidy(:size=>100) { |svg|
      svg.svg :viewBox => '0 0 200 800'
    }.elements['@viewBox'].value.should == "0 0 50 200"
  end

  it "should translate" do
    tidy(:size=>100, :grid=>true) { |svg|
      svg.svg :viewBox => '-50 -50 100 100' do
        svg.path :d => "M10,20"
      end
    }.elements['path/@d'].value.should == "M60,70"
  end

  it "should scale" do
    tidy(:size=>100, :grid=>true) { |svg|
      svg.svg :viewBox => '0 0 50 50' do
        svg.path :d => "M10,20"
      end
    }.elements['path/@d'].value.should == "M20,40"
  end

  it "should treat height, width as a viewBox" do
    tidy(:size=>100) { |svg|
      svg.svg :width => '200', :height => '800'
    }.elements['@viewBox'].value.should == "0 0 50 200"
  end

end

describe "polygon" do
  it "should become path" do
    tidy { |svg|
      svg.polygon :points => "10,20 20,30 20,40 10,50"
    }.elements['path/@d'].value.should == "M10,20l10,10v10l-10,10z"
  end
end

describe "style" do
  it "should convert known attributes to attributes" do
    tidy { |svg|
      svg.path :style => "color:#F00"
    }.elements['path/@color'].value.should == "#F00"
  end

  it "should leave unknown attributes as style properties" do
    tidy { |svg|
      svg.path :style => "color:#F00;position:absolute"
    }.elements['path/@style'].value.should == "position:absolute"
  end

  it "should delete style attribute if all properties are known" do
    tidy { |svg|
      svg.path :style => "color:#F00"
    }.elements['path/@style'].should == nil
  end
end

describe "strip inkscape" do
  it "should remove inkscape namespace" do
    tidy(:strip_inkscape => true) { |svg|
      svg.svg "xmlns:inkscape" => "http://www.inkscape.org/namespaces/inkscape"
    }.attributes['xmlns:inkscape'].should == nil
  end

  it "should remove sodipodi namespace" do
    tidy(:strip_inkscape => true) { |svg|
      svg.svg "xmlns:sodipodi" => "http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
    }.attributes['xmlns:sodipodi'].should == nil
  end

  it "should remove inkscape elements" do
    tidy(:strip_inkscape => true) { |svg|
      svg.svg "xmlns:sodipodi" => "http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" do
        svg.sodipodi :namedview
      end
    }.children.length.should == 0
  end

  it "should remove inkscape attributes" do
    tidy(:strip_inkscape => true) { |svg|
      svg.svg "xmlns:inkscape" => "http://www.inkscape.org/namespaces/inkscape" do
        svg.path 'inkscape:current-layer' =>'layer1'
      end
    }.elements['path'].attributes['inkscape:current-layer'].should == nil
  end
end

def tidy opts={:grid => true}
  # parse block
  svg = REXML::Document.new(yield(Builder::XmlMarkup.new))

  # wrap in an svg element, if necessary
  if svg.root.name != 'svg'
    svg << REXML::Element.new('svg').add_element(svg.root).parent
  end

  # add the svg namespace
  svg.add_namespace 'http://www.w3.org/2001/svg'

  # invoke tidy
  Svg.tidy(svg, opts).root
end
