Skip to content

Commit

Permalink
Bringing into line with SVG spec to represent initial commands as abs…
Browse files Browse the repository at this point in the history
…olute if given as relative
  • Loading branch information
awebneck committed Aug 2, 2012
1 parent 3f560f2 commit 890610f
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 11 deletions.
18 changes: 10 additions & 8 deletions lib/savage/parser.rb
Expand Up @@ -7,8 +7,8 @@ def parse(parsable)
raise TypeError if (subpaths.empty?)
path = Path.new
path.subpaths = []
subpaths.each do |subpath|
path.subpaths << parse_subpath(subpath)
subpaths.each_with_index do |subpath, i|
path.subpaths << parse_subpath(subpath, i == 0)
end
path
end
Expand All @@ -27,29 +27,31 @@ def extract_subpaths(parsable)
subpaths
end

def parse_subpath(parsable)
def parse_subpath(parsable, force_absolute=false)
subpath = SubPath.new
subpath.directions = extract_directions parsable
subpath.directions = extract_directions parsable, force_absolute
subpath
end

def extract_directions(parsable)
def extract_directions(parsable, force_absolute=false)
directions = []
i = 0
parsable.scan /[MmLlHhVvQqCcTtSsAaZz](?:\d|[eE.,+-]|\W)*/m do |match_group|
direction = build_direction $&
direction = build_direction $&, force_absolute && i == 0
if direction.kind_of?(Array)
directions.concat direction
else
directions << direction
end
i += 1
end
directions
end

def build_direction(parsable)
def build_direction(parsable, force_absolute=false)
directions = []
coordinates = extract_coordinates parsable
absolute = (parsable[0,1] == parsable[0,1].upcase) ? true : false
absolute = (force_absolute || parsable[0,1] == parsable[0,1].upcase) ? true : false
recurse_code = parsable[0,1]

# we need to handle this separately, since ClosePath doesn't take any coordinates
Expand Down
6 changes: 3 additions & 3 deletions savage.gemspec
Expand Up @@ -5,11 +5,11 @@

Gem::Specification.new do |s|
s.name = "savage"
s.version = "1.1.5"
s.version = "1.1.6"

s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Jeremy Holland"]
s.date = "2011-12-28"
s.date = "2012-08-02"
s.description = "A little gem for extracting and manipulating SVG vector path data."
s.email = "jeremy@jeremypholland.com"
s.extra_rdoc_files = [
Expand Down Expand Up @@ -62,7 +62,7 @@ Gem::Specification.new do |s|
]
s.homepage = "http://github.com/awebneck/savage"
s.require_paths = ["lib"]
s.rubygems_version = "1.8.10"
s.rubygems_version = "1.8.15"
s.summary = "A little library to manipulate SVG path data"

if s.respond_to? :specification_version then
Expand Down
22 changes: 22 additions & 0 deletions spec/savage/parser_spec.rb
Expand Up @@ -12,6 +12,7 @@
lambda { Parser.parse("M100 200") }.should_not raise_error
lambda { Parser.parse(2) }.should raise_error
end

it 'should return a path object with one subpath containing one move_to when the string is only a move_to command' do
path = Parser.parse("M100 200")
path.class.should == Path
Expand All @@ -20,6 +21,15 @@
path.subpaths.last.directions.last.class.should == Directions::MoveTo
end

it 'should return a path object with one subpath containing an absolute move_to even when the string starts with a relative move_to command' do
path = Parser.parse("m100 200")
path.class.should == Path
path.subpaths.length.should == 1
path.subpaths.last.directions.length.should == 1
path.subpaths.last.directions.last.class.should == Directions::MoveTo
path.subpaths.last.directions.last.should be_absolute
end

it 'should handle comma separated coordinates' do
path = Parser.parse("M100,200")
path.class.should == Path
Expand All @@ -37,6 +47,7 @@
path.subpaths.last.directions[1].should_not be_absolute
path.subpaths.last.directions[1].class.should == Directions::LineTo
end

it 'should return a path object with one subpath containing a move_to and a horizontal_to when the string is a move_to command followed by a horizontal_to command' do
path = Parser.parse("M100 200H-342.65")
path.class.should == Path
Expand All @@ -45,6 +56,7 @@
path.subpaths.last.directions[0].class.should == Directions::MoveTo
path.subpaths.last.directions[1].class.should == Directions::HorizontalTo
end

it 'should return a path object with one subpath containing a move_to and a vertical_to when the string is a move_to command followed by a vertical_to command' do
path = Parser.parse("M100 200V-342.65")
path.class.should == Path
Expand All @@ -53,6 +65,7 @@
path.subpaths.last.directions[0].class.should == Directions::MoveTo
path.subpaths.last.directions[1].class.should == Directions::VerticalTo
end

it 'should return a path object with one subpath containing a move_to and a full cubic_curve_to when the string is a move_to command followed by a full cubic_curve_to command' do
path = Parser.parse("M100 200C-342.65-32 1.233-34 255 12")
path.class.should == Path
Expand All @@ -62,6 +75,7 @@
path.subpaths.last.directions[1].class.should == Directions::CubicCurveTo
path.subpaths.last.directions[1].command_code.should == 'C'
end

it 'should return a path object with one subpath containing a move_to and a short cubic_curve_to when the string is a move_to command followed by a short cubic_curve_to command' do
path = Parser.parse("M100 200S1.233-34 255 12")
path.class.should == Path
Expand All @@ -71,6 +85,7 @@
path.subpaths.last.directions[1].class.should == Directions::CubicCurveTo
path.subpaths.last.directions[1].command_code.should == 'S'
end

it 'should return a path object with one subpath containing a move_to and a full quadratic_curve_to when the string is a move_to command followed by a full quadratic_curve_to command' do
path = Parser.parse("M100 200Q1.233-34 255 12")
path.class.should == Path
Expand All @@ -80,6 +95,7 @@
path.subpaths.last.directions[1].class.should == Directions::QuadraticCurveTo
path.subpaths.last.directions[1].command_code.should == 'Q'
end

it 'should return a path object with one subpath containing a move_to and a short quadratic_curve_to when the string is a move_to command followed by a short quadratic_curve_to command' do
path = Parser.parse("M100 200T255 12")
path.class.should == Path
Expand All @@ -98,6 +114,7 @@
path.subpaths.last.directions[1].class.should == Directions::ArcTo
path.subpaths.last.directions[1].command_code.should == 'A'
end

it 'should return a path object with one subpath containing a move_to, a line_to, and a close_path command when the string is a move_to command followed by a line_to followed by a close_path command' do
path = Parser.parse("M100 200l-342.65 21Z")
path.class.should == Path
Expand All @@ -121,6 +138,7 @@
path.subpaths.last.directions[1].target.x.should == 300
path.subpaths.last.directions[1].target.y.should == 400
end

it 'should return a path object with one subpath containing two line_to directions when the string is a line_to command followed by implicit coordinates' do
path = Parser.parse("L100 200 300 400")
path.class.should == Path
Expand All @@ -133,6 +151,7 @@
path.subpaths.last.directions[1].target.x.should == 300
path.subpaths.last.directions[1].target.y.should == 400
end

it 'should return a path object with one subpath containing a move_to and a line_to direction when the string is a move_to command followed by implicit coordinates' do
path = Parser.parse("M100 200 300 400")
path.class.should == Path
Expand All @@ -145,6 +164,7 @@
path.subpaths.last.directions[1].target.x.should == 300
path.subpaths.last.directions[1].target.y.should == 400
end

it 'should return a path object with one subpath containing a move_to and two line_to directions when the string is a move_to command followed by more than one set of implicit coordinates' do
path = Parser.parse("M100 200 300 400 500 600 ")
path.class.should == Path
Expand All @@ -160,6 +180,7 @@
path.subpaths.last.directions[2].target.x.should == 500
path.subpaths.last.directions[2].target.y.should == 600
end

it 'should return a path object with two subpaths containing one line_to directions each when the string is two move_to commands each followed by a line_to command' do
path = Parser.parse("M100 200 332 -12.3M594 230-423 11.1")
path.class.should == Path
Expand All @@ -178,6 +199,7 @@
path.subpaths[1].directions[1].target.x.should == -423
path.subpaths[1].directions[1].target.y.should == 11.1
end

it 'should generate the same string given to it (assuming float values are used), if not changed in the interim' do
path_string = "M100.0 200.0A255.0 12.0-123.0 1 0 23.0-93.4L100.0 200.0 300.0 400.0Q1.233-34.0 255.0 12.0T255.0 12.0H-342.65Z"
path = Parser.parse(path_string)
Expand Down

0 comments on commit 890610f

Please sign in to comment.