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
4b19ecb4
Commit
4b19ecb4
authored
Jun 28, 2014
by
Brian C. Barnes
Browse files
Marker support. perserveAspectRatio Align support
parent
2f2a1e02
Changes
9
Hide whitespace changes
Inline
Side-by-side
Source/DataTypes/SvgAspectRatio.cs
View file @
4b19ecb4
using
S
ystem
;
using
S
vg.DataTypes
;
using
System.ComponentModel
;
namespace
Svg
...
...
@@ -6,9 +6,10 @@ namespace Svg
/// <summary>
/// Description of SvgAspectRatio.
/// </summary>
[
TypeConverter
(
typeof
(
SvgPreserveAspectRatioConverter
))]
public
class
SvgAspectRatio
{
public
SvgAspectRatio
()
public
SvgAspectRatio
()
:
this
(
SvgPreserveAspectRatio
.
none
)
{
}
...
...
@@ -42,11 +43,10 @@ namespace Svg
}
[
TypeConverter
(
typeof
(
SvgPreserverAspectRatioConverter
))]
public
enum
SvgPreserveAspectRatio
{
XMidYMid
,
//default
N
one
,
n
one
,
XMinYMin
,
XMidYMin
,
XMaxYMin
,
...
...
Source/DataTypes/SvgOrient.cs
View file @
4b19ecb4
using
System
;
using
System.Collections.Generic
;
using
System.Text
;
using
Svg.DataTypes
;
using
System.ComponentModel
;
using
System.Web.UI.WebControls
;
using
System.Globalization
;
namespace
Svg
{
/// <summary>
/// Represents an orientation in an Scalable Vector Graphics document.
/// </summary>
public
class
SvgOrient
[
TypeConverter
(
typeof
(
SvgOrientConverter
))]
public
class
SvgOrient
{
private
bool
_isAuto
=
true
;
private
float
_angle
;
...
...
Source/Document Structure/SvgFragment.cs
View file @
4b19ecb4
...
...
@@ -100,11 +100,11 @@ namespace Svg
/// Gets or sets the aspect of the viewport.
/// </summary>
/// <value></value>
[
SvgAttribute
(
"preserveAspectRatio"
)]
[
SvgAttribute
(
"preserveAspectRatio"
)]
public
SvgAspectRatio
AspectRatio
{
get
{
return
this
.
Attributes
.
GetAttribute
<
SvgAspectRatio
>(
"preserveAspectRatio"
);
}
set
{
this
.
Attributes
[
"preserveAspectRatio"
]
=
value
;
}
get
{
return
this
.
Attributes
.
GetAttribute
<
SvgAspectRatio
>(
"preserveAspectRatio"
);
}
set
{
this
.
Attributes
[
"preserveAspectRatio"
]
=
value
;
}
}
/// <summary>
...
...
@@ -117,10 +117,60 @@ namespace Svg
if
(!
this
.
ViewBox
.
Equals
(
SvgViewBox
.
Empty
))
{
renderer
.
TranslateTransform
(
_x
,
_y
,
MatrixOrder
.
Append
);
renderer
.
TranslateTransform
(-
this
.
ViewBox
.
MinX
,
-
this
.
ViewBox
.
MinY
,
MatrixOrder
.
Append
);
float
fScaleX
=
this
.
Width
.
ToDeviceValue
()
/
this
.
ViewBox
.
Width
;
float
fScaleY
=
this
.
Height
.
ToDeviceValue
()
/
this
.
ViewBox
.
Height
;
float
fMinX
=
-
this
.
ViewBox
.
MinX
;
float
fMinY
=
-
this
.
ViewBox
.
MinY
;
if
(
AspectRatio
.
Align
!=
SvgPreserveAspectRatio
.
none
)
{
fScaleX
=
Math
.
Min
(
fScaleX
,
fScaleY
);
fScaleY
=
Math
.
Min
(
fScaleX
,
fScaleY
);
float
fViewMidX
=
(
this
.
ViewBox
.
Width
/
2
)
*
fScaleX
;
float
fViewMidY
=
(
this
.
ViewBox
.
Height
/
2
)
*
fScaleY
;
float
fMidX
=
this
.
Width
.
ToDeviceValue
()
/
2
;
float
fMidY
=
this
.
Height
.
ToDeviceValue
()
/
2
;
renderer
.
ScaleTransform
(
this
.
Width
.
ToDeviceValue
()
/
this
.
ViewBox
.
Width
,
this
.
Height
.
ToDeviceValue
()
/
this
.
ViewBox
.
Height
,
MatrixOrder
.
Append
);
switch
(
AspectRatio
.
Align
)
{
case
SvgPreserveAspectRatio
.
XMinYMin
:
break
;
case
SvgPreserveAspectRatio
.
XMidYMin
:
fMinX
+=
(
fMidX
-
fViewMidX
)
/
fScaleX
;
break
;
case
SvgPreserveAspectRatio
.
XMaxYMin
:
fMinX
+=
this
.
ViewBox
.
Width
-
this
.
Width
.
ToDeviceValue
();
break
;
case
SvgPreserveAspectRatio
.
XMinYMid
:
fMinY
+=
(
fMidY
-
fViewMidY
)
/
fScaleY
;
break
;
case
SvgPreserveAspectRatio
.
XMidYMid
:
fMinX
+=
(
fMidX
-
fViewMidX
)
/
fScaleX
;
fMinY
+=
(
fMidY
-
fViewMidY
)
/
fScaleY
;
break
;
case
SvgPreserveAspectRatio
.
XMaxYMid
:
fMinX
+=
this
.
ViewBox
.
Width
-
this
.
Width
.
ToDeviceValue
();
fMinY
+=
(
fMidY
-
fViewMidY
)
/
fScaleY
;
break
;
case
SvgPreserveAspectRatio
.
XMinYMax
:
fMinY
+=
this
.
ViewBox
.
Height
-
this
.
Height
.
ToDeviceValue
();
break
;
case
SvgPreserveAspectRatio
.
XMidYMax
:
fMinX
+=
(
fMidX
-
fViewMidX
)
/
fScaleX
;
fMinY
+=
this
.
ViewBox
.
Height
-
this
.
Height
.
ToDeviceValue
();
break
;
case
SvgPreserveAspectRatio
.
XMaxYMax
:
fMinX
+=
this
.
ViewBox
.
Width
-
this
.
Width
.
ToDeviceValue
();
fMinY
+=
this
.
ViewBox
.
Height
-
this
.
Height
.
ToDeviceValue
();
break
;
default
:
break
;
}
}
renderer
.
TranslateTransform
(
_x
,
_y
,
MatrixOrder
.
Append
);
renderer
.
TranslateTransform
(
fMinX
,
fMinY
,
MatrixOrder
.
Append
);
renderer
.
ScaleTransform
(
fScaleX
,
fScaleY
,
MatrixOrder
.
Append
);
}
}
...
...
@@ -162,7 +212,7 @@ namespace Svg
this
.
Height
=
new
SvgUnit
(
SvgUnitType
.
Percentage
,
100.0f
);
this
.
Width
=
new
SvgUnit
(
SvgUnitType
.
Percentage
,
100.0f
);
this
.
ViewBox
=
SvgViewBox
.
Empty
;
this
.
AspectRatio
=
new
SvgAspectRatio
(
SvgPreserveAspectRatio
.
None
);
this
.
AspectRatio
=
new
SvgAspectRatio
(
SvgPreserveAspectRatio
.
XMidYMid
);
}
...
...
Source/Painting/EnumConverters.cs
View file @
4b19ecb4
using
System
;
using
Svg.DataTypes
;
using
System
;
using
System.ComponentModel
;
using
System.Globalization
;
...
...
@@ -108,11 +109,6 @@ namespace Svg
{
}
//implementaton for preserve aspect ratio
public
sealed
class
SvgPreserverAspectRatioConverter
:
EnumBaseConverter
<
SvgPreserveAspectRatio
>
{
}
public
sealed
class
SvgStrokeLineCapConverter
:
EnumBaseConverter
<
SvgStrokeLineCap
>
{
}
...
...
@@ -120,5 +116,8 @@ namespace Svg
public
sealed
class
SvgStrokeLineJoinConverter
:
EnumBaseConverter
<
SvgStrokeLineJoin
>
{
}
public
sealed
class
SvgMarkerUnitsConverter
:
EnumBaseConverter
<
SvgMarkerUnits
>
{
}
}
Source/Painting/SvgMarker.cs
View file @
4b19ecb4
...
...
@@ -6,6 +6,8 @@ using System.Web;
using
System.Xml
;
using
System.Xml.Serialization
;
using
System.Drawing.Drawing2D
;
using
System.Drawing
;
using
Svg.DataTypes
;
namespace
Svg
{
...
...
@@ -61,6 +63,35 @@ namespace Svg
}
[
SvgAttribute
(
"markerWidth"
)]
public
virtual
SvgUnit
MarkerWidth
{
get
{
return
this
.
Attributes
.
GetAttribute
<
SvgUnit
>(
"markerWidth"
);
}
set
{
this
.
Attributes
[
"markerWidth"
]
=
value
;
}
}
[
SvgAttribute
(
"markerHeight"
)]
public
virtual
SvgUnit
MarkerHeight
{
get
{
return
this
.
Attributes
.
GetAttribute
<
SvgUnit
>(
"markerHeight"
);
}
set
{
this
.
Attributes
[
"markerHeight"
]
=
value
;
}
}
[
SvgAttribute
(
"markerUnits"
)]
public
virtual
SvgMarkerUnits
MarkerUnits
{
get
{
return
this
.
Attributes
.
GetAttribute
<
SvgMarkerUnits
>(
"markerUnits"
);
}
set
{
this
.
Attributes
[
"markerUnits"
]
=
value
;
}
}
public
SvgMarker
()
{
MarkerUnits
=
SvgMarkerUnits
.
strokeWidth
;
MarkerHeight
=
3
;
MarkerWidth
=
3
;
Overflow
=
SvgOverflow
.
hidden
;
}
public
override
System
.
Drawing
.
Drawing2D
.
GraphicsPath
Path
{
get
...
...
@@ -87,19 +118,6 @@ namespace Svg
}
}
//protected internal override void RenderStroke(SvgRenderer renderer)
//{
// this.PushTransforms(renderer);
// SvgElement parent = element._parent;
// element._parent = this;
// element.RenderElement(renderer);
// element._parent = parent;
// this.PopTransforms(renderer);
//}
public
override
SvgElement
DeepCopy
()
{
return
DeepCopy
<
SvgMarker
>();
...
...
@@ -117,5 +135,148 @@ namespace Svg
return
newObj
;
}
}
/// <summary>
/// Render this marker using the slope of the given line segment
/// </summary>
/// <param name="pRenderer"></param>
/// <param name="pOwner"></param>
/// <param name="pMarkerPoint1"></param>
/// <param name="pMarkerPoint2"></param>
public
void
RenderMarker
(
SvgRenderer
pRenderer
,
SvgPath
pOwner
,
PointF
pRefPoint
,
PointF
pMarkerPoint1
,
PointF
pMarkerPoint2
)
{
float
xDiff
=
pMarkerPoint2
.
X
-
pMarkerPoint1
.
X
;
float
yDiff
=
pMarkerPoint2
.
Y
-
pMarkerPoint1
.
Y
;
float
fAngle1
=
(
float
)(
Math
.
Atan2
(
yDiff
,
xDiff
)
*
180.0
/
Math
.
PI
);
RenderPart2
(
fAngle1
,
pRenderer
,
pOwner
,
pRefPoint
);
}
/// <summary>
/// Render this marker using the average of the slopes of the two given line segments
/// </summary>
/// <param name="pRenderer"></param>
/// <param name="pOwner"></param>
/// <param name="pMarkerPoint1"></param>
/// <param name="pMarkerPoint2"></param>
/// <param name="pMarkerPoint3"></param>
public
void
RenderMarker
(
SvgRenderer
pRenderer
,
SvgPath
pOwner
,
PointF
pRefPoint
,
PointF
pMarkerPoint1
,
PointF
pMarkerPoint2
,
PointF
pMarkerPoint3
)
{
float
xDiff
=
pMarkerPoint2
.
X
-
pMarkerPoint1
.
X
;
float
yDiff
=
pMarkerPoint2
.
Y
-
pMarkerPoint1
.
Y
;
float
fAngle1
=
(
float
)(
Math
.
Atan2
(
yDiff
,
xDiff
)
*
180.0
/
Math
.
PI
);
xDiff
=
pMarkerPoint3
.
X
-
pMarkerPoint2
.
X
;
yDiff
=
pMarkerPoint3
.
Y
-
pMarkerPoint2
.
Y
;
float
fAngle2
=
(
float
)(
Math
.
Atan2
(
yDiff
,
xDiff
)
*
180.0
/
Math
.
PI
);
RenderPart2
((
fAngle1
+
fAngle2
)
/
2
,
pRenderer
,
pOwner
,
pRefPoint
);
}
/// <summary>
/// Common code for rendering a marker once the orientation angle has been calculated
/// </summary>
/// <param name="fAngle"></param>
/// <param name="pRenderer"></param>
/// <param name="pOwner"></param>
/// <param name="pMarkerPoint"></param>
private
void
RenderPart2
(
float
fAngle
,
SvgRenderer
pRenderer
,
SvgPath
pOwner
,
PointF
pMarkerPoint
)
{
Pen
pRenderPen
=
CreatePen
(
pOwner
);
GraphicsPath
markerPath
=
GetClone
(
pOwner
);
Matrix
transMatrix
=
new
Matrix
();
transMatrix
.
Translate
(
pMarkerPoint
.
X
,
pMarkerPoint
.
Y
);
if
(
Orient
.
IsAuto
)
transMatrix
.
Rotate
(
fAngle
);
else
transMatrix
.
Rotate
(
Orient
.
Angle
);
switch
(
MarkerUnits
)
{
case
SvgMarkerUnits
.
strokeWidth
:
transMatrix
.
Translate
(
AdjustForViewBoxWidth
(-
RefX
*
pOwner
.
StrokeWidth
),
AdjustForViewBoxHeight
(-
RefY
*
pOwner
.
StrokeWidth
));
break
;
case
SvgMarkerUnits
.
userSpaceOnUse
:
transMatrix
.
Translate
(-
RefX
,
-
RefY
);
break
;
}
markerPath
.
Transform
(
transMatrix
);
pRenderer
.
DrawPath
(
pRenderPen
,
markerPath
);
SvgPaintServer
pFill
=
Fill
;
SvgFillRule
pFillRule
=
FillRule
;
// TODO: What do we use the fill rule for?
float
fOpacity
=
FillOpacity
;
if
(
pFill
!=
null
)
{
Brush
pBrush
=
pFill
.
GetBrush
(
this
,
fOpacity
);
pRenderer
.
FillPath
(
pBrush
,
markerPath
);
pBrush
.
Dispose
();
}
pRenderPen
.
Dispose
();
markerPath
.
Dispose
();
transMatrix
.
Dispose
();
}
/// <summary>
/// Create a pen that can be used to render this marker
/// </summary>
/// <param name="pStroke"></param>
/// <returns></returns>
private
Pen
CreatePen
(
SvgPath
pPath
)
{
Brush
pBrush
=
pPath
.
Stroke
.
GetBrush
(
this
,
Opacity
);
switch
(
MarkerUnits
)
{
case
SvgMarkerUnits
.
strokeWidth
:
return
(
new
Pen
(
pBrush
,
StrokeWidth
*
pPath
.
StrokeWidth
));
case
SvgMarkerUnits
.
userSpaceOnUse
:
return
(
new
Pen
(
pBrush
,
StrokeWidth
));
}
return
(
new
Pen
(
pBrush
,
StrokeWidth
));
}
/// <summary>
/// Get a clone of the current path, scaled for the stroke with
/// </summary>
/// <returns></returns>
private
GraphicsPath
GetClone
(
SvgPath
pPath
)
{
GraphicsPath
pRet
=
Path
.
Clone
()
as
GraphicsPath
;
switch
(
MarkerUnits
)
{
case
SvgMarkerUnits
.
strokeWidth
:
Matrix
transMatrix
=
new
Matrix
();
transMatrix
.
Scale
(
AdjustForViewBoxWidth
(
pPath
.
StrokeWidth
),
AdjustForViewBoxHeight
(
pPath
.
StrokeWidth
));
pRet
.
Transform
(
transMatrix
);
break
;
case
SvgMarkerUnits
.
userSpaceOnUse
:
break
;
}
return
(
pRet
);
}
/// <summary>
/// Adjust the given value to account for the width of the viewbox in the viewport
/// </summary>
/// <param name="fWidth"></param>
/// <returns></returns>
private
float
AdjustForViewBoxWidth
(
float
fWidth
)
{
// TODO: We know this isn't correct
return
(
fWidth
/
ViewBox
.
Width
);
}
/// <summary>
/// Adjust the given value to account for the height of the viewbox in the viewport
/// </summary>
/// <param name="fWidth"></param>
/// <returns></returns>
private
float
AdjustForViewBoxHeight
(
float
fHeight
)
{
// TODO: We know this isn't correct
return
(
fHeight
/
ViewBox
.
Height
);
}
}
}
\ No newline at end of file
Source/Paths/SvgPath.cs
View file @
4b19ecb4
...
...
@@ -56,6 +56,17 @@ namespace Svg
}
/// <summary>
/// Gets or sets the marker (start cap) of the path.
/// </summary>
[
SvgAttribute
(
"marker-mid"
)]
public
Uri
MarkerMid
{
get
{
return
this
.
Attributes
.
GetAttribute
<
Uri
>(
"marker-mid"
);
}
set
{
this
.
Attributes
[
"marker-mid"
]
=
value
;
}
}
/// <summary>
/// Gets or sets the marker (start cap) of the path.
/// </summary>
...
...
@@ -135,7 +146,7 @@ namespace Svg
if
(
this
.
Stroke
!=
null
)
{
float
strokeWidth
=
this
.
StrokeWidth
.
ToDeviceValue
(
this
);
using
(
var
pen
=
new
Pen
(
this
.
Stroke
.
GetBrush
(
this
,
this
.
StrokeOpacity
),
strokeWidth
))
using
(
Pen
pen
=
new
Pen
(
this
.
Stroke
.
GetBrush
(
this
,
this
.
StrokeOpacity
),
strokeWidth
))
{
if
(
this
.
StrokeDashArray
!=
null
&&
this
.
StrokeDashArray
.
Count
>
0
)
{
...
...
@@ -143,33 +154,30 @@ namespace Svg
pen
.
DashPattern
=
this
.
StrokeDashArray
.
ConvertAll
(
u
=>
u
.
Value
/
((
strokeWidth
<=
0
)
?
1
:
strokeWidth
)).
ToArray
();
}
//hardcoded transformation matrix. I am not sure why this is not in proportion or rotated correctly (something to do with how the endcaps are determined in GDI)
var
transMatrix
=
new
Matrix
();
transMatrix
.
Rotate
(-
90f
);
transMatrix
.
Scale
(.
6f
,
.
6f
);
renderer
.
DrawPath
(
pen
,
this
.
Path
);
if
(
this
.
MarkerStart
!=
null
)
{
var
marker
=
this
.
OwnerDocument
.
GetElementById
<
SvgMarker
>(
this
.
MarkerStart
.
ToString
());
var
markerPath
=
marker
.
Path
.
Clone
()
as
GraphicsPath
;
markerPath
.
Transform
(
transMatrix
);
pen
.
CustomStartCap
=
new
CustomLineCap
(
markerPath
,
null
);
SvgMarker
marker
=
this
.
OwnerDocument
.
GetElementById
<
SvgMarker
>(
this
.
MarkerStart
.
ToString
());
marker
.
RenderMarker
(
renderer
,
this
,
Path
.
PathPoints
[
0
],
Path
.
PathPoints
[
0
],
Path
.
PathPoints
[
1
]);
}
if
(
this
.
Marker
En
d
!=
null
)
if
(
this
.
Marker
Mi
d
!=
null
)
{
var
marker
=
this
.
OwnerDocument
.
GetElementById
<
SvgMarker
>(
this
.
MarkerEnd
.
ToString
());
var
markerPath
=
marker
.
Path
.
Clone
()
as
GraphicsPath
;
markerPath
.
Transform
(
transMatrix
);
pen
.
CustomEndCap
=
new
CustomLineCap
(
markerPath
,
null
);
SvgMarker
marker
=
this
.
OwnerDocument
.
GetElementById
<
SvgMarker
>(
this
.
MarkerMid
.
ToString
());
for
(
int
i
=
1
;
i
<=
Path
.
PathPoints
.
Length
-
2
;
i
++)
marker
.
RenderMarker
(
renderer
,
this
,
Path
.
PathPoints
[
i
],
Path
.
PathPoints
[
i
-
1
],
Path
.
PathPoints
[
i
],
Path
.
PathPoints
[
i
+
1
]);
}
renderer
.
DrawPath
(
pen
,
this
.
Path
);
if
(
this
.
MarkerEnd
!=
null
)
{
SvgMarker
marker
=
this
.
OwnerDocument
.
GetElementById
<
SvgMarker
>(
this
.
MarkerEnd
.
ToString
());
marker
.
RenderMarker
(
renderer
,
this
,
Path
.
PathPoints
[
Path
.
PathPoints
.
Length
-
1
],
Path
.
PathPoints
[
Path
.
PathPoints
.
Length
-
2
],
Path
.
PathPoints
[
Path
.
PathPoints
.
Length
-
1
]);
}
}
}
}
public
override
SvgElement
DeepCopy
()
{
return
DeepCopy
<
SvgPath
>();
...
...
@@ -186,9 +194,5 @@ namespace Svg
return
newObj
;
}
}
}
\ No newline at end of file
Source/Svg.csproj
View file @
4b19ecb4
...
...
@@ -52,7 +52,7 @@
<PropertyGroup
Condition=
" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "
>
<DebugType>
Full
</DebugType>
<Optimize>
false
</Optimize>
<OutputPath>
..\..\vvvv\public\common\src\thirdparty\
</OutputPath>
<OutputPath>
..\..\vvvv\public\common\src\thirdparty\
</OutputPath>
<DefineConstants>
TRACE;DEBUG;REFLECTION
</DefineConstants>
<ErrorReport>
prompt
</ErrorReport>
<WarningLevel>
4
</WarningLevel>
...
...
@@ -99,6 +99,8 @@
<Compile
Include=
"Clipping and Masking\SvgClipRule.cs"
/>
<Compile
Include=
"Clipping and Masking\SvgClipPath.cs"
/>
<Compile
Include=
"Clipping and Masking\SvgMask.cs"
/>
<Compile
Include=
"DataTypes\SvgAspectRatioConverter.cs"
/>
<Compile
Include=
"DataTypes\SvgMarkerUnits.cs"
/>
<Compile
Include=
"DataTypes\SvgOrient.cs"
/>
<Compile
Include=
"DataTypes\ISvgViewPort.cs"
/>
<Compile
Include=
"DataTypes\SvgAspectRatio.cs"
/>
...
...
@@ -106,6 +108,7 @@
<Compile
Include=
"DataTypes\SvgElementStyle.cs"
/>
<Compile
Include=
"DataTypes\SvgCoordinateUnits.cs"
/>
<Compile
Include=
"DataTypes\SvgFontWeight.cs"
/>
<Compile
Include=
"DataTypes\SvgOrientConverter.cs"
/>
<Compile
Include=
"DataTypes\SvgOverflow.cs"
/>
<Compile
Include=
"DataTypes\SvgUnitCollection.cs"
/>
<Compile
Include=
"DataTypes\SvgViewBox.cs"
/>
...
...
Source/SvgDocument.cs
View file @
4b19ecb4
...
...
@@ -26,7 +26,7 @@ namespace Svg
/// </summary>
public
SvgDocument
()
{
Ppi
=
96
;
Ppi
=
PointsPerInch
;
}
/// <summary>
...
...
Source/SvgRenderer.cs
View file @
4b19ecb4
...
...
@@ -72,6 +72,16 @@ namespace Svg
this
.
_innerGraphics
.
DrawPath
(
pen
,
path
);
}
public
void
RotateTransform
(
float
fAngle
,
MatrixOrder
order
)
{
this
.
_innerGraphics
.
RotateTransform
(
fAngle
,
order
);
}
public
void
RotateTransform
(
float
fAngle
)
{
this
.
RotateTransform
(
fAngle
,
MatrixOrder
.
Append
);
}
public
void
TranslateTransform
(
float
dx
,
float
dy
,
MatrixOrder
order
)
{
this
.
_innerGraphics
.
TranslateTransform
(
dx
,
dy
,
order
);
...
...
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