Skip to content
GitLab
Menu
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
ImportedProjects
SVG
Commits
ad06b3ad
"vscode:/vscode.git/clone" did not exist on "9643cc6974e3a53ad8ee6e31220f2ac33f224493"
Commit
ad06b3ad
authored
Apr 28, 2008
by
Axyonych
Browse files
Fixed H,h,V,v path elements support (#6071).
parent
4a6fe6ff
Changes
1
Hide whitespace changes
Inline
Side-by-side
Paths/SvgPathBuilder.cs
View file @
ad06b3ad
using
System
;
using
System
;
using
System.ComponentModel
;
using
System.Collections.Generic
;
using
System.Collections.Generic
;
using
System.Text
;
using
System.ComponentModel
;
using
System.Drawing
;
using
System.Drawing.Drawing2D
;
using
System.Xml
;
using
System.Text.RegularExpressions
;
using
System.Diagnostics
;
using
System.Diagnostics
;
using
System.Drawing
;
using
System.Globalization
;
using
System.Globalization
;
using
Svg.Pathing
;
using
Svg.Pathing
;
...
@@ -18,34 +14,30 @@ namespace Svg
...
@@ -18,34 +14,30 @@ namespace Svg
public
static
SvgPathSegmentList
Parse
(
string
path
)
public
static
SvgPathSegmentList
Parse
(
string
path
)
{
{
if
(
string
.
IsNullOrEmpty
(
path
))
if
(
string
.
IsNullOrEmpty
(
path
))
{
throw
new
ArgumentNullException
(
"path"
);
throw
new
ArgumentNullException
(
"path"
);
}
SvgPathSegmentList
segments
=
new
SvgPathSegmentList
();
var
segments
=
new
SvgPathSegmentList
();
try
try
{
{
IEnumerable
<
PointF
>
coords
;
foreach
(
var
commandSet
in
SplitCommands
(
path
.
TrimEnd
(
null
)))
char
command
;
PointF
controlPoint
;
SvgQuadraticCurveSegment
lastQuadCurve
;
SvgCubicCurveSegment
lastCubicCurve
;
List
<
PointF
>
pointCache
=
new
List
<
PointF
>();
foreach
(
string
commandSet
in
SvgPathBuilder
.
SplitCommands
(
path
.
TrimEnd
(
null
)))
{
{
coords
=
SvgPathBuilder
.
ParseCoordinates
(
commandSet
,
segments
);
var
coords
=
new
List
<
float
>(
ParseCoordinates
(
commandSet
));
command
=
commandSet
[
0
];
var
command
=
commandSet
[
0
];
var
isRelative
=
char
.
IsLower
(
command
);
// http://www.w3.org/TR/SVG11/paths.html#PathDataGeneralInformation
// http://www.w3.org/TR/SVG11/paths.html#PathDataGeneralInformation
switch
(
command
)
switch
(
command
)
{
{
case
'm'
:
// relative moveto
case
'm'
:
// relative moveto
case
'M'
:
// moveto
case
'M'
:
// moveto
foreach
(
PointF
point
in
coords
)
segments
.
Add
(
new
SvgMoveToSegment
(
ToAbsolute
(
coords
[
0
],
coords
[
1
],
segments
,
isRelative
)));
for
(
var
i
=
2
;
i
<
coords
.
Count
;
i
+=
2
)
{
{
segments
.
Add
(
new
SvgMoveToSegment
(
point
));
segments
.
Add
(
new
SvgLineSegment
(
segments
.
Last
.
End
,
ToAbsolute
(
coords
[
i
],
coords
[
i
+
1
],
segments
,
isRelative
)));
}
}
break
;
break
;
case
'a'
:
case
'a'
:
...
@@ -53,78 +45,71 @@ namespace Svg
...
@@ -53,78 +45,71 @@ namespace Svg
throw
new
NotImplementedException
(
"Arc segments are not yet implemented"
);
throw
new
NotImplementedException
(
"Arc segments are not yet implemented"
);
case
'l'
:
// relative lineto
case
'l'
:
// relative lineto
case
'L'
:
// lineto
case
'L'
:
// lineto
for
each
(
PointF
point
in
coords
)
for
(
var
i
=
0
;
i
<
coords
.
Count
;
i
+=
2
)
{
{
segments
.
Add
(
new
SvgLineSegment
(
segments
.
Last
.
End
,
point
));
segments
.
Add
(
new
SvgLineSegment
(
segments
.
Last
.
End
,
ToAbsolute
(
coords
[
i
],
coords
[
i
+
1
],
segments
,
isRelative
)));
}
}
break
;
break
;
case
'H'
:
// horizontal lineto
case
'H'
:
// horizontal lineto
case
'h'
:
// relative horizontal lineto
case
'h'
:
// relative horizontal lineto
foreach
(
PointF
point
in
coords
)
foreach
(
var
value
in
coords
)
{
segments
.
Add
(
new
SvgLineSegment
(
segments
.
Last
.
End
,
segments
.
Add
(
new
SvgLineSegment
(
segments
.
Last
.
End
,
new
PointF
(
segments
.
Last
.
End
.
X
,
point
.
Y
)));
ToAbsolute
(
value
,
segments
.
Last
.
End
.
Y
,
segments
,
isRelative
,
false
)));
}
break
;
break
;
case
'V'
:
// vertical lineto
case
'V'
:
// vertical lineto
case
'v'
:
// relative vertical lineto
case
'v'
:
// relative vertical lineto
foreach
(
PointF
point
in
coords
)
foreach
(
var
value
in
coords
)
{
segments
.
Add
(
new
SvgLineSegment
(
segments
.
Last
.
End
,
segments
.
Add
(
new
SvgLineSegment
(
segments
.
Last
.
End
,
new
PointF
(
point
.
X
,
segments
.
Last
.
End
.
Y
)));
ToAbsolute
(
segments
.
Last
.
End
.
X
,
value
,
segments
,
false
,
isRelative
)));
}
break
;
break
;
case
'Q'
:
// curveto
case
'Q'
:
// curveto
case
'q'
:
// relative curveto
case
'q'
:
// relative curveto
pointCache
.
Clear
();
for
(
var
i
=
0
;
i
<
coords
.
Count
;
i
+=
4
)
foreach
(
PointF
point
in
coords
)
{
pointCache
.
Add
(
point
);
}
for
(
int
i
=
0
;
i
<
pointCache
.
Count
;
i
+=
2
)
{
{
segments
.
Add
(
new
SvgQuadraticCurveSegment
(
segments
.
Last
.
End
,
pointCache
[
i
],
pointCache
[
i
+
1
]));
segments
.
Add
(
new
SvgQuadraticCurveSegment
(
segments
.
Last
.
End
,
ToAbsolute
(
coords
[
i
],
coords
[
i
+
1
],
segments
,
isRelative
),
ToAbsolute
(
coords
[
i
+
2
],
coords
[
i
+
3
],
segments
,
isRelative
)));
}
}
break
;
break
;
case
'T'
:
// shorthand/smooth curveto
case
'T'
:
// shorthand/smooth curveto
case
't'
:
// relative shorthand/smooth curveto
case
't'
:
// relative shorthand/smooth curveto
for
each
(
PointF
point
in
coords
)
for
(
var
i
=
0
;
i
<
coords
.
Count
;
i
+=
2
)
{
{
lastQuadCurve
=
segments
.
Last
as
SvgQuadraticCurveSegment
;
var
lastQuadCurve
=
segments
.
Last
as
SvgQuadraticCurveSegment
;
if
(
lastQuadCurve
!=
null
)
var
controlPoint
=
lastQuadCurve
!=
null
controlPoint
=
Reflect
(
lastQuadCurve
.
ControlPoint
,
segments
.
Last
.
End
);
?
Reflect
(
lastQuadCurve
.
ControlPoint
,
segments
.
Last
.
End
)
else
:
segments
.
Last
.
End
;
controlPoint
=
segments
.
Last
.
End
;
segments
.
Add
(
new
SvgQuadraticCurveSegment
(
segments
.
Last
.
End
,
controlPoint
,
point
));
segments
.
Add
(
new
SvgQuadraticCurveSegment
(
segments
.
Last
.
End
,
controlPoint
,
ToAbsolute
(
coords
[
i
],
coords
[
i
+
1
],
segments
,
isRelative
)));
}
}
break
;
break
;
case
'C'
:
// curveto
case
'C'
:
// curveto
case
'c'
:
// relative curveto
case
'c'
:
// relative curveto
pointCache
.
Clear
();
for
(
var
i
=
0
;
i
<
coords
.
Count
;
i
+=
6
)
foreach
(
PointF
point
in
coords
)
{
pointCache
.
Add
(
point
);
}
for
(
int
i
=
0
;
i
<
pointCache
.
Count
;
i
+=
3
)
{
{
segments
.
Add
(
new
SvgCubicCurveSegment
(
segments
.
Last
.
End
,
pointCache
[
i
],
pointCache
[
i
+
1
],
pointCache
[
i
+
2
]));
segments
.
Add
(
new
SvgCubicCurveSegment
(
segments
.
Last
.
End
,
ToAbsolute
(
coords
[
i
],
coords
[
i
+
1
],
segments
,
isRelative
),
ToAbsolute
(
coords
[
i
+
2
],
coords
[
i
+
3
],
segments
,
isRelative
),
ToAbsolute
(
coords
[
i
+
4
],
coords
[
i
+
5
],
segments
,
isRelative
)));
}
}
break
;
break
;
case
'S'
:
// shorthand/smooth curveto
case
'S'
:
// shorthand/smooth curveto
case
's'
:
// relative shorthand/smooth curveto
case
's'
:
// relative shorthand/smooth curveto
pointCache
.
Clear
();
foreach
(
PointF
point
in
coords
)
{
pointCache
.
Add
(
point
);
}
for
(
int
i
=
0
;
i
<
pointCache
.
Count
;
i
+=
2
)
for
(
var
i
=
0
;
i
<
coords
.
Count
;
i
+=
4
)
{
{
lastCubicCurve
=
segments
.
Last
as
SvgCubicCurveSegment
;
var
lastCubicCurve
=
segments
.
Last
as
SvgCubicCurveSegment
;
if
(
lastCubicCurve
!=
null
)
var
controlPoint
=
lastCubicCurve
!=
null
{
?
Reflect
(
lastCubicCurve
.
SecondControlPoint
,
segments
.
Last
.
End
)
controlPoint
=
Reflect
(
lastCubicCurve
.
SecondControlPoint
,
segments
.
Last
.
End
);
:
segments
.
Last
.
End
;
}
else
segments
.
Add
(
new
SvgCubicCurveSegment
(
segments
.
Last
.
End
,
controlPoint
,
{
ToAbsolute
(
coords
[
i
],
coords
[
i
+
1
],
segments
,
isRelative
),
controlPoint
=
segments
.
Last
.
End
;
ToAbsolute
(
coords
[
i
+
2
],
coords
[
i
+
3
],
segments
,
isRelative
)));
}
segments
.
Add
(
new
SvgCubicCurveSegment
(
segments
.
Last
.
End
,
controlPoint
,
pointCache
[
i
],
pointCache
[
i
+
1
]));
}
}
break
;
break
;
case
'Z'
:
// closepath
case
'Z'
:
// closepath
...
@@ -145,82 +130,94 @@ namespace Svg
...
@@ -145,82 +130,94 @@ namespace Svg
private
static
PointF
Reflect
(
PointF
point
,
PointF
mirror
)
private
static
PointF
Reflect
(
PointF
point
,
PointF
mirror
)
{
{
// TODO: Only works left to right???
// TODO: Only works left to right???
float
x
=
mirror
.
X
+
(
mirror
.
X
-
point
.
X
);
var
x
=
mirror
.
X
+
(
mirror
.
X
-
point
.
X
);
float
y
=
mirror
.
Y
+
(
mirror
.
Y
-
point
.
Y
);
var
y
=
mirror
.
Y
+
(
mirror
.
Y
-
point
.
Y
);
return
new
PointF
(
Math
.
Abs
(
x
),
Math
.
Abs
(
y
));
return
new
PointF
(
Math
.
Abs
(
x
),
Math
.
Abs
(
y
));
}
}
private
static
PointF
ToAbsolute
(
PointF
point
,
SvgPathSegmentList
segments
)
/// <summary>
/// Creates point with absolute coorindates.
/// </summary>
/// <param name="x">Raw X-coordinate value.</param>
/// <param name="y">Raw Y-coordinate value.</param>
/// <param name="segments">Current path segments.</param>
/// <param name="isRelativeBoth"><b>true</b> if <paramref name="x"/> and <paramref name="y"/> contains relative coordinate values, otherwise <b>false</b>.</param>
/// <returns><see cref="PointF"/> that contains absolute coordinates.</returns>
private
static
PointF
ToAbsolute
(
float
x
,
float
y
,
SvgPathSegmentList
segments
,
bool
isRelativeBoth
)
{
{
PointF
lastPoint
=
segments
.
Last
.
End
;
return
ToAbsolute
(
x
,
y
,
segments
,
isRelativeBoth
,
isRelativeBoth
);
return
new
PointF
(
lastPoint
.
X
+
point
.
X
,
lastPoint
.
Y
+
point
.
Y
);
}
/// <summary>
/// Creates point with absolute coorindates.
/// </summary>
/// <param name="x">Raw X-coordinate value.</param>
/// <param name="y">Raw Y-coordinate value.</param>
/// <param name="segments">Current path segments.</param>
/// <param name="isRelativeX"><b>true</b> if <paramref name="x"/> contains relative coordinate value, otherwise <b>false</b>.</param>
/// <param name="isRelativeY"><b>true</b> if <paramref name="y"/> contains relative coordinate value, otherwise <b>false</b>.</param>
/// <returns><see cref="PointF"/> that contains absolute coordinates.</returns>
private
static
PointF
ToAbsolute
(
float
x
,
float
y
,
SvgPathSegmentList
segments
,
bool
isRelativeX
,
bool
isRelativeY
)
{
var
point
=
new
PointF
(
x
,
y
);
if
((
isRelativeX
||
isRelativeY
)
&&
segments
.
Count
>
0
)
{
var
lastSegment
=
segments
.
Last
;
if
(
isRelativeX
)
point
.
X
+=
lastSegment
.
End
.
X
;
if
(
isRelativeY
)
point
.
Y
+=
lastSegment
.
End
.
Y
;
}
return
point
;
}
}
private
static
IEnumerable
<
string
>
SplitCommands
(
string
path
)
private
static
IEnumerable
<
string
>
SplitCommands
(
string
path
)
{
{
int
commandStart
=
0
;
var
commandStart
=
0
;
string
command
=
null
;
for
(
int
i
=
0
;
i
<
path
.
Length
;
i
++)
for
(
var
i
=
0
;
i
<
path
.
Length
;
i
++)
{
{
string
command
;
if
(
char
.
IsLetter
(
path
[
i
]))
if
(
char
.
IsLetter
(
path
[
i
]))
{
{
command
=
path
.
Substring
(
commandStart
,
i
-
commandStart
).
Trim
();
command
=
path
.
Substring
(
commandStart
,
i
-
commandStart
).
Trim
();
commandStart
=
i
;
commandStart
=
i
;
if
(!
string
.
IsNullOrEmpty
(
command
))
if
(!
string
.
IsNullOrEmpty
(
command
))
{
yield
return
command
;
yield
return
command
;
}
if
(
path
.
Length
==
i
+
1
)
if
(
path
.
Length
==
i
+
1
)
{
yield
return
path
[
i
].
ToString
();
yield
return
path
[
i
].
ToString
();
}
}
}
else
if
(
path
.
Length
==
i
+
1
)
else
if
(
path
.
Length
==
i
+
1
)
{
{
command
=
path
.
Substring
(
commandStart
,
i
-
commandStart
+
1
).
Trim
();
command
=
path
.
Substring
(
commandStart
,
i
-
commandStart
+
1
).
Trim
();
if
(!
string
.
IsNullOrEmpty
(
command
))
if
(!
string
.
IsNullOrEmpty
(
command
))
{
yield
return
command
;
yield
return
command
;
}
}
}
}
}
}
}
private
static
IEnumerable
<
PointF
>
ParseCoordinates
(
string
coords
,
SvgPathSegmentList
segments
)
private
static
IEnumerable
<
float
>
ParseCoordinates
(
string
coords
)
{
{
// TODO: Handle "1-1" (new PointF(1, -1);
// TODO: Handle "1-1" (new PointF(1, -1);
string
[]
parts
=
coords
.
Remove
(
0
,
1
).
Replace
(
"-"
,
" -"
).
Split
(
new
char
[]
{
','
,
' '
},
StringSplitOptions
.
RemoveEmptyEntries
);
var
parts
=
coords
.
Remove
(
0
,
1
).
Replace
(
"-"
,
" -"
).
Split
(
new
[]
{
','
,
' '
},
float
x
;
StringSplitOptions
.
RemoveEmptyEntries
);
float
y
;
PointF
point
;
bool
relative
=
char
.
IsLower
(
coords
[
0
]);
for
(
int
i
=
0
;
i
<
parts
.
Length
;
i
+=
2
)
{
x
=
float
.
Parse
(
parts
[
i
],
NumberStyles
.
Float
,
CultureInfo
.
InvariantCulture
);
y
=
float
.
Parse
(
parts
[
i
+
1
],
NumberStyles
.
Float
,
CultureInfo
.
InvariantCulture
);
point
=
new
PointF
(
x
,
y
);
if
(
relative
)
for
(
var
i
=
0
;
i
<
parts
.
Length
;
i
++)
{
yield
return
float
.
Parse
(
parts
[
i
],
NumberStyles
.
Float
,
CultureInfo
.
InvariantCulture
);
point
=
SvgPathBuilder
.
ToAbsolute
(
point
,
segments
);
}
yield
return
point
;
}
}
}
public
override
object
ConvertFrom
(
ITypeDescriptorContext
context
,
System
.
Globalization
.
CultureInfo
culture
,
object
value
)
public
override
object
ConvertFrom
(
ITypeDescriptorContext
context
,
CultureInfo
culture
,
object
value
)
{
{
if
(
value
is
string
)
if
(
value
is
string
)
{
return
Parse
((
string
)
value
);
return
SvgPathBuilder
.
Parse
((
string
)
value
);
}
return
base
.
ConvertFrom
(
context
,
culture
,
value
);
return
base
.
ConvertFrom
(
context
,
culture
,
value
);
}
}
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment