Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
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
ceb78a3f
Commit
ceb78a3f
authored
10 years ago
by
Eric Domke
Browse files
Options
Download
Email Patches
Plain Diff
Iteration 1 of tspan functionality
parent
f10b0336
master
netstandard2
nuget2.4.2
nuget2.4.1
nuget2.4
nuget2.3
nuget2.2.2
nuget2.2.1
No related merge requests found
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
Source/Paths/SvgClosePathSegment.cs
+1
-1
Source/Paths/SvgClosePathSegment.cs
Source/Svg.csproj
+2
-0
Source/Svg.csproj
Source/SvgContentNode.cs
+12
-0
Source/SvgContentNode.cs
Source/SvgDocument.cs
+14
-8
Source/SvgDocument.cs
Source/SvgElement.cs
+15
-4
Source/SvgElement.cs
Source/SvgRenderer.cs
+1
-0
Source/SvgRenderer.cs
Source/Text/SvgText.cs
+19
-474
Source/Text/SvgText.cs
Source/Text/SvgTextBase.cs
+601
-0
Source/Text/SvgTextBase.cs
Source/Text/SvgTextSpan.cs
+3
-64
Source/Text/SvgTextSpan.cs
with
668 additions
and
551 deletions
+668
-551
Source/Paths/SvgClosePathSegment.cs
+
1
-
1
View file @
ceb78a3f
...
@@ -9,7 +9,7 @@ namespace Svg.Pathing
...
@@ -9,7 +9,7 @@ namespace Svg.Pathing
public
override
void
AddToPath
(
System
.
Drawing
.
Drawing2D
.
GraphicsPath
graphicsPath
)
public
override
void
AddToPath
(
System
.
Drawing
.
Drawing2D
.
GraphicsPath
graphicsPath
)
{
{
// Important for custom line caps. Force the path the close with an explicit line, not just an implicit close of the figure.
// Important for custom line caps. Force the path the close with an explicit line, not just an implicit close of the figure.
if
(
graphicsPath
.
P
athPoints
.
Length
>
1
&&
!
graphicsPath
.
PathPoints
[
0
].
Equals
(
graphicsPath
.
PathPoints
[
graphicsPath
.
PathPoints
.
Length
-
1
]))
if
(
graphicsPath
.
P
ointCount
>
0
&&
!
graphicsPath
.
PathPoints
[
0
].
Equals
(
graphicsPath
.
PathPoints
[
graphicsPath
.
PathPoints
.
Length
-
1
]))
{
{
int
i
=
graphicsPath
.
PathTypes
.
Length
-
1
;
int
i
=
graphicsPath
.
PathTypes
.
Length
-
1
;
while
(
i
>=
0
&&
graphicsPath
.
PathTypes
[
i
]
>
0
)
i
--;
while
(
i
>=
0
&&
graphicsPath
.
PathTypes
[
i
]
>
0
)
i
--;
...
...
This diff is collapsed.
Click to expand it.
Source/Svg.csproj
+
2
-
0
View file @
ceb78a3f
...
@@ -136,6 +136,7 @@
...
@@ -136,6 +136,7 @@
<Compile
Include=
"Filter Effects\feGaussianBlur\SvgGaussianBlur.cs"
/>
<Compile
Include=
"Filter Effects\feGaussianBlur\SvgGaussianBlur.cs"
/>
<Compile
Include=
"Filter Effects\feMerge\SvgMerge.cs"
/>
<Compile
Include=
"Filter Effects\feMerge\SvgMerge.cs"
/>
<Compile
Include=
"Painting\EnumConverters.cs"
/>
<Compile
Include=
"Painting\EnumConverters.cs"
/>
<Compile
Include=
"SvgContentNode.cs"
/>
<Compile
Include=
"SvgDefinitionDefaults.cs"
/>
<Compile
Include=
"SvgDefinitionDefaults.cs"
/>
<Compile
Include=
"NonSvgElement.cs"
/>
<Compile
Include=
"NonSvgElement.cs"
/>
<Compile
Include=
"SvgUnknownElement.cs"
/>
<Compile
Include=
"SvgUnknownElement.cs"
/>
...
@@ -183,6 +184,7 @@
...
@@ -183,6 +184,7 @@
<Compile
Include=
"DataTypes\SvgUnitConverter.cs"
/>
<Compile
Include=
"DataTypes\SvgUnitConverter.cs"
/>
<Compile
Include=
"SvgTextReader.cs"
/>
<Compile
Include=
"SvgTextReader.cs"
/>
<Compile
Include=
"Text\SvgText.cs"
/>
<Compile
Include=
"Text\SvgText.cs"
/>
<Compile
Include=
"Text\SvgTextBase.cs"
/>
<Compile
Include=
"Text\SvgTextAnchor.cs"
/>
<Compile
Include=
"Text\SvgTextAnchor.cs"
/>
<Compile
Include=
"Text\SvgTextSpan.cs"
/>
<Compile
Include=
"Text\SvgTextSpan.cs"
/>
<Compile
Include=
"Transforms\ISvgTransformable.cs"
/>
<Compile
Include=
"Transforms\ISvgTransformable.cs"
/>
...
...
This diff is collapsed.
Click to expand it.
Source/SvgContentNode.cs
0 → 100644
+
12
-
0
View file @
ceb78a3f
using
System
;
using
System.Collections.Generic
;
using
System.Linq
;
using
System.Text
;
namespace
Svg
{
public
class
SvgContentNode
:
ISvgNode
{
public
string
Content
{
get
;
set
;
}
}
}
This diff is collapsed.
Click to expand it.
Source/SvgDocument.cs
+
14
-
8
View file @
ceb78a3f
...
@@ -8,6 +8,7 @@ using System.Drawing.Text;
...
@@ -8,6 +8,7 @@ using System.Drawing.Text;
using
System.IO
;
using
System.IO
;
using
System.Text
;
using
System.Text
;
using
System.Xml
;
using
System.Xml
;
using
System.Linq
;
namespace
Svg
namespace
Svg
{
{
...
@@ -184,7 +185,6 @@ namespace Svg
...
@@ -184,7 +185,6 @@ namespace Svg
using
(
var
reader
=
new
SvgTextReader
(
stream
,
entities
))
using
(
var
reader
=
new
SvgTextReader
(
stream
,
entities
))
{
{
var
elementStack
=
new
Stack
<
SvgElement
>();
var
elementStack
=
new
Stack
<
SvgElement
>();
var
value
=
new
StringBuilder
();
bool
elementEmpty
;
bool
elementEmpty
;
SvgElement
element
=
null
;
SvgElement
element
=
null
;
SvgElement
parent
;
SvgElement
parent
;
...
@@ -218,7 +218,10 @@ namespace Svg
...
@@ -218,7 +218,10 @@ namespace Svg
{
{
parent
=
elementStack
.
Peek
();
parent
=
elementStack
.
Peek
();
if
(
parent
!=
null
&&
element
!=
null
)
if
(
parent
!=
null
&&
element
!=
null
)
{
parent
.
Children
.
Add
(
element
);
parent
.
Children
.
Add
(
element
);
parent
.
Nodes
.
Add
(
element
);
}
}
}
// Push element into stack
// Push element into stack
...
@@ -236,21 +239,24 @@ namespace Svg
...
@@ -236,21 +239,24 @@ namespace Svg
// Pop the element out of the stack
// Pop the element out of the stack
element
=
elementStack
.
Pop
();
element
=
elementStack
.
Pop
();
if
(
value
.
Length
>
0
&&
element
!=
null
)
if
(
element
.
Nodes
.
OfType
<
SvgContentNode
>().
Any
()
)
{
{
element
.
Content
=
value
.
ToString
();
element
.
Content
=
(
from
e
in
element
.
Nodes
select
e
.
Content
).
Aggregate
((
p
,
c
)
=>
p
+
c
);
}
// Reset content value for new element
else
value
.
Length
=
0
;
{
element
.
Nodes
.
Clear
();
// No sense wasting the space where it isn't needed
}
}
break
;
break
;
case
XmlNodeType
.
CDATA
:
case
XmlNodeType
.
CDATA
:
case
XmlNodeType
.
Text
:
case
XmlNodeType
.
Text
:
value
.
Append
(
reader
.
Value
);
element
=
elementStack
.
Peek
();
element
.
Nodes
.
Add
(
new
SvgContentNode
()
{
Content
=
reader
.
Value
});
break
;
break
;
case
XmlNodeType
.
EntityReference
:
case
XmlNodeType
.
EntityReference
:
reader
.
ResolveEntity
();
reader
.
ResolveEntity
();
value
.
Append
(
reader
.
Value
);
element
=
elementStack
.
Peek
();
element
.
Nodes
.
Add
(
new
SvgContentNode
()
{
Content
=
reader
.
Value
});
break
;
break
;
}
}
}
}
...
...
This diff is collapsed.
Click to expand it.
Source/SvgElement.cs
+
15
-
4
View file @
ceb78a3f
...
@@ -15,7 +15,7 @@ namespace Svg
...
@@ -15,7 +15,7 @@ namespace Svg
/// <summary>
/// <summary>
/// The base class of which all SVG elements are derived from.
/// The base class of which all SVG elements are derived from.
/// </summary>
/// </summary>
public
abstract
class
SvgElement
:
ISvgElement
,
ISvgTransformable
,
ICloneable
public
abstract
class
SvgElement
:
ISvgElement
,
ISvgTransformable
,
ICloneable
,
ISvgNode
{
{
//optimization
//optimization
protected
class
PropertyAttributeTuple
protected
class
PropertyAttributeTuple
...
@@ -42,6 +42,7 @@ namespace Svg
...
@@ -42,6 +42,7 @@ namespace Svg
private
static
readonly
object
_loadEventKey
=
new
object
();
private
static
readonly
object
_loadEventKey
=
new
object
();
private
Matrix
_graphicsMatrix
;
private
Matrix
_graphicsMatrix
;
private
SvgCustomAttributeCollection
_customAttributes
;
private
SvgCustomAttributeCollection
_customAttributes
;
private
List
<
ISvgNode
>
_nodes
=
new
List
<
ISvgNode
>();
/// <summary>
/// <summary>
/// Gets the name of the element.
/// Gets the name of the element.
...
@@ -117,6 +118,11 @@ namespace Svg
...
@@ -117,6 +118,11 @@ namespace Svg
get
{
return
this
.
_children
;
}
get
{
return
this
.
_children
;
}
}
}
public
IList
<
ISvgNode
>
Nodes
{
get
{
return
this
.
_nodes
;
}
}
public
IEnumerable
<
SvgElement
>
Descendants
()
public
IEnumerable
<
SvgElement
>
Descendants
()
{
{
return
this
.
AsEnumerable
().
Descendants
();
return
this
.
AsEnumerable
().
Descendants
();
...
@@ -611,8 +617,8 @@ namespace Svg
...
@@ -611,8 +617,8 @@ namespace Svg
childPath
=
(
GraphicsPath
)
childPath
.
Clone
();
childPath
=
(
GraphicsPath
)
childPath
.
Clone
();
if
(
child
.
Transforms
!=
null
)
if
(
child
.
Transforms
!=
null
)
childPath
.
Transform
(
child
.
Transforms
.
GetMatrix
());
childPath
.
Transform
(
child
.
Transforms
.
GetMatrix
());
path
.
AddPath
(
childPath
,
false
);
if
(
childPath
.
PointCount
>
0
)
path
.
AddPath
(
childPath
,
false
);
}
}
}
}
}
}
...
@@ -921,7 +927,6 @@ namespace Svg
...
@@ -921,7 +927,6 @@ namespace Svg
}
}
#
endregion
graphical
EVENTS
#
endregion
graphical
EVENTS
}
}
public
class
SVGArg
:
EventArgs
public
class
SVGArg
:
EventArgs
...
@@ -1035,10 +1040,16 @@ namespace Svg
...
@@ -1035,10 +1040,16 @@ namespace Svg
public
bool
CtrlKey
;
public
bool
CtrlKey
;
}
}
public
interface
ISvgNode
{
string
Content
{
get
;
}
}
internal
interface
ISvgElement
internal
interface
ISvgElement
{
{
SvgElement
Parent
{
get
;}
SvgElement
Parent
{
get
;}
SvgElementCollection
Children
{
get
;
}
SvgElementCollection
Children
{
get
;
}
IList
<
ISvgNode
>
Nodes
{
get
;
}
void
Render
(
SvgRenderer
renderer
);
void
Render
(
SvgRenderer
renderer
);
}
}
...
...
This diff is collapsed.
Click to expand it.
Source/SvgRenderer.cs
+
1
-
0
View file @
ceb78a3f
...
@@ -157,6 +157,7 @@ namespace Svg
...
@@ -157,6 +157,7 @@ namespace Svg
StringFormat
format
=
StringFormat
.
GenericTypographic
;
StringFormat
format
=
StringFormat
.
GenericTypographic
;
format
.
SetMeasurableCharacterRanges
(
new
CharacterRange
[]{
new
CharacterRange
(
0
,
text
.
Length
)});
format
.
SetMeasurableCharacterRanges
(
new
CharacterRange
[]{
new
CharacterRange
(
0
,
text
.
Length
)});
format
.
FormatFlags
|=
StringFormatFlags
.
MeasureTrailingSpaces
;
Region
[]
r
=
this
.
_innerGraphics
.
MeasureCharacterRanges
(
text
,
font
,
new
Rectangle
(
0
,
0
,
1000
,
1000
),
format
);
Region
[]
r
=
this
.
_innerGraphics
.
MeasureCharacterRanges
(
text
,
font
,
new
Rectangle
(
0
,
0
,
1000
,
1000
),
format
);
RectangleF
rect
=
r
[
0
].
GetBounds
(
this
.
_innerGraphics
);
RectangleF
rect
=
r
[
0
].
GetBounds
(
this
.
_innerGraphics
);
...
...
This diff is collapsed.
Click to expand it.
Source/Text/SvgText.cs
+
19
-
474
View file @
ceb78a3f
using
System
;
using
System
;
using
System.Collections.Generic
;
using
System.Collections.Generic
;
using
System.Linq
;
using
System.Text
;
using
System.Text
;
using
System.Text.RegularExpressions
;
using
System.ComponentModel
;
using
System.Drawing
;
using
System.Drawing
;
using
System.Drawing.Drawing2D
;
using
System.Drawing.Drawing2D
;
using
System.Drawing.Text
;
using
Svg.DataTypes
;
using
Svg.DataTypes
;
using
System.Linq
;
namespace
Svg
namespace
Svg
{
{
...
@@ -15,43 +12,12 @@ namespace Svg
...
@@ -15,43 +12,12 @@ namespace Svg
/// The <see cref="SvgText"/> element defines a graphics element consisting of text.
/// The <see cref="SvgText"/> element defines a graphics element consisting of text.
/// </summary>
/// </summary>
[
SvgElement
(
"text"
)]
[
SvgElement
(
"text"
)]
public
class
SvgText
:
Svg
VisualElement
public
class
SvgText
:
Svg
TextBase
{
{
private
SvgUnit
_x
;
private
SvgUnit
_y
;
private
SvgUnit
_dy
;
private
SvgUnit
_dx
;
private
SvgUnit
_letterSpacing
;
private
SvgUnit
_wordSpacing
;
private
SvgUnit
_fontSize
;
private
SvgFontWeight
_fontWeight
;
private
string
_font
;
private
string
_fontFamily
;
private
GraphicsPath
_path
;
private
SvgTextAnchor
_textAnchor
=
SvgTextAnchor
.
Start
;
private
static
readonly
SvgRenderer
_stringMeasure
;
private
const
string
DefaultFontFamily
=
"Times New Roman"
;
/// <summary>
/// <summary>
/// Initializes the <see cref="SvgText"/> class.
/// Initializes the <see cref="SvgText"/> class.
/// </summary>
/// </summary>
static
SvgText
()
public
SvgText
()
:
base
()
{}
{
Bitmap
bitmap
=
new
Bitmap
(
1
,
1
);
_stringMeasure
=
SvgRenderer
.
FromImage
(
bitmap
);
_stringMeasure
.
TextRenderingHint
=
TextRenderingHint
.
AntiAlias
;
}
/// <summary>
/// Initializes a new instance of the <see cref="SvgText"/> class.
/// </summary>
public
SvgText
()
{
this
.
_fontFamily
=
DefaultFontFamily
;
this
.
_fontSize
=
new
SvgUnit
(
0.0f
);
this
.
_dy
=
new
SvgUnit
(
0.0f
);
this
.
_dx
=
new
SvgUnit
(
0.0f
);
}
/// <summary>
/// <summary>
/// Initializes a new instance of the <see cref="SvgText"/> class.
/// Initializes a new instance of the <see cref="SvgText"/> class.
...
@@ -61,446 +27,25 @@ namespace Svg
...
@@ -61,446 +27,25 @@ namespace Svg
{
{
this
.
Text
=
text
;
this
.
Text
=
text
;
}
}
/// <summary>
public
override
SvgElement
DeepCopy
()
/// Gets or sets the text to be rendered.
/// </summary>
public
virtual
string
Text
{
get
{
return
base
.
Content
;
}
set
{
base
.
Content
=
value
;
this
.
IsPathDirty
=
true
;
this
.
Content
=
value
;
}
}
/// <summary>
/// Gets or sets the text anchor.
/// </summary>
/// <value>The text anchor.</value>
[
SvgAttribute
(
"text-anchor"
)]
public
virtual
SvgTextAnchor
TextAnchor
{
get
{
return
this
.
_textAnchor
;
}
set
{
this
.
_textAnchor
=
value
;
this
.
IsPathDirty
=
true
;
}
}
/// <summary>
/// Gets or sets the X.
/// </summary>
/// <value>The X.</value>
[
SvgAttribute
(
"x"
)]
public
virtual
SvgUnit
X
{
get
{
return
this
.
_x
;
}
set
{
if
(
_x
!=
value
)
{
this
.
_x
=
value
;
this
.
IsPathDirty
=
true
;
OnAttributeChanged
(
new
AttributeEventArgs
{
Attribute
=
"x"
,
Value
=
value
});
}
}
}
/// <summary>
/// Gets or sets the dX.
/// </summary>
/// <value>The dX.</value>
[
SvgAttribute
(
"dx"
)]
public
virtual
SvgUnit
Dx
{
get
{
return
this
.
_dx
;
}
set
{
if
(
_dx
!=
value
)
{
this
.
_dx
=
value
;
this
.
IsPathDirty
=
true
;
OnAttributeChanged
(
new
AttributeEventArgs
{
Attribute
=
"dx"
,
Value
=
value
});
}
}
}
/// <summary>
/// Gets or sets the Y.
/// </summary>
/// <value>The Y.</value>
[
SvgAttribute
(
"y"
)]
public
virtual
SvgUnit
Y
{
get
{
return
this
.
_y
;
}
set
{
if
(
_y
!=
value
)
{
this
.
_y
=
value
;
this
.
IsPathDirty
=
true
;
OnAttributeChanged
(
new
AttributeEventArgs
{
Attribute
=
"y"
,
Value
=
value
});
}
}
}
/// <summary>
/// Gets or sets the dY.
/// </summary>
/// <value>The dY.</value>
[
SvgAttribute
(
"dy"
)]
public
virtual
SvgUnit
Dy
{
get
{
return
this
.
_dy
;
}
set
{
if
(
_dy
!=
value
)
{
this
.
_dy
=
value
;
this
.
IsPathDirty
=
true
;
OnAttributeChanged
(
new
AttributeEventArgs
{
Attribute
=
"dy"
,
Value
=
value
});
}
}
}
/// <summary>
/// Specifies spacing behavior between text characters.
/// </summary>
[
SvgAttribute
(
"letter-spacing"
)]
public
virtual
SvgUnit
LetterSpacing
{
get
{
return
this
.
_letterSpacing
;
}
set
{
this
.
_letterSpacing
=
value
;
this
.
IsPathDirty
=
true
;
}
}
/// <summary>
/// Specifies spacing behavior between words.
/// </summary>
[
SvgAttribute
(
"word-spacing"
)]
public
virtual
SvgUnit
WordSpacing
{
get
{
return
this
.
_wordSpacing
;
}
set
{
this
.
_wordSpacing
=
value
;
this
.
IsPathDirty
=
true
;
}
}
/// <summary>
/// Indicates which font family is to be used to render the text.
/// </summary>
[
SvgAttribute
(
"font-family"
)]
public
virtual
string
FontFamily
{
get
{
return
this
.
_fontFamily
;
}
set
{
this
.
_fontFamily
=
ValidateFontFamily
(
value
);
this
.
IsPathDirty
=
true
;
}
}
/// <summary>
/// Refers to the size of the font from baseline to baseline when multiple lines of text are set solid in a multiline layout environment.
/// </summary>
[
SvgAttribute
(
"font-size"
)]
public
virtual
SvgUnit
FontSize
{
get
{
return
this
.
_fontSize
;
}
set
{
this
.
_fontSize
=
value
;
this
.
IsPathDirty
=
true
;
}
}
/// <summary>
/// Refers to the boldness of the font.
/// </summary>
[
SvgAttribute
(
"font-weight"
)]
public
virtual
SvgFontWeight
FontWeight
{
get
{
return
this
.
_fontWeight
;
}
set
{
this
.
_fontWeight
=
value
;
this
.
IsPathDirty
=
true
;
}
}
/// <summary>
/// Set all font information.
/// </summary>
[
SvgAttribute
(
"font"
)]
public
virtual
string
Font
{
get
{
return
this
.
_font
;
}
set
{
var
parts
=
value
.
Split
(
','
);
foreach
(
var
part
in
parts
)
{
//This deals with setting font size. Looks for either <number>px or <number>pt style="font: bold 16px/normal 'trebuchet ms', verdana, sans-serif;"
Regex
rx
=
new
Regex
(
@"(\d+)+(?=pt|px)"
);
var
res
=
rx
.
Match
(
part
);
if
(
res
.
Success
)
{
int
fontSize
=
10
;
int
.
TryParse
(
res
.
Value
,
out
fontSize
);
this
.
FontSize
=
new
SvgUnit
((
float
)
fontSize
);
}
//this assumes "bold" has spaces around it. e.g.: style="font: bold 16px/normal
rx
=
new
Regex
(
@"\sbold\s"
);
res
=
rx
.
Match
(
part
);
if
(
res
.
Success
)
{
this
.
FontWeight
=
SvgFontWeight
.
bold
;
}
}
var
font
=
ValidateFontFamily
(
value
);
this
.
_fontFamily
=
font
;
this
.
_font
=
font
;
//not sure this is used?
this
.
IsPathDirty
=
true
;
}
}
/// <summary>
/// Gets or sets the fill.
/// </summary>
/// <remarks>
/// <para>Unlike other <see cref="SvgGraphicsElement"/>s, <see cref="SvgText"/> has a default fill of black rather than transparent.</para>
/// </remarks>
/// <value>The fill.</value>
public
override
SvgPaintServer
Fill
{
get
{
return
(
this
.
Attributes
[
"fill"
]
==
null
)
?
new
SvgColourServer
(
Color
.
Black
)
:
(
SvgPaintServer
)
this
.
Attributes
[
"fill"
];
}
set
{
this
.
Attributes
[
"fill"
]
=
value
;
}
}
/// <summary>
/// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
/// </summary>
/// <returns>
/// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
/// </returns>
public
override
string
ToString
()
{
return
this
.
Text
;
}
/// <summary>
/// Gets or sets a value to determine if anti-aliasing should occur when the element is being rendered.
/// </summary>
/// <value></value>
protected
override
bool
RequiresSmoothRendering
{
get
{
return
true
;
}
}
/// <summary>
/// Gets the bounds of the element.
/// </summary>
/// <value>The bounds.</value>
public
override
System
.
Drawing
.
RectangleF
Bounds
{
get
{
return
this
.
Path
.
GetBounds
();
}
}
static
private
RectangleF
MeasureString
(
SvgRenderer
renderer
,
string
text
,
Font
font
)
{
GraphicsPath
p
=
new
GraphicsPath
();
p
.
AddString
(
text
,
font
.
FontFamily
,
0
,
font
.
Size
,
new
PointF
(
0.0f
,
0.0f
),
StringFormat
.
GenericTypographic
);
p
.
Transform
(
renderer
.
Transform
);
return
p
.
GetBounds
();
}
/// <summary>
/// Gets the <see cref="GraphicsPath"/> for this element.
/// </summary>
/// <value></value>
public
override
System
.
Drawing
.
Drawing2D
.
GraphicsPath
Path
{
get
{
// Make sure the path is always null if there is no text
//if there is a TSpan inside of this text element then path should not be null (even if this text is empty!)
if
((
string
.
IsNullOrEmpty
(
this
.
Text
)
||
this
.
Text
.
Trim
().
Length
<
1
)
&&
this
.
Children
.
Where
(
x
=>
x
is
SvgTextSpan
).
Select
(
x
=>
x
as
SvgTextSpan
).
Count
()
==
0
)
return
_path
=
null
;
//NOT SURE WHAT THIS IS ABOUT - Path gets created again anyway - WTF?
// When an empty string is passed to GraphicsPath, it rises an InvalidArgumentException...
if
(
_path
==
null
||
this
.
IsPathDirty
)
{
float
fontSize
=
this
.
FontSize
.
ToDeviceValue
(
this
);
if
(
fontSize
==
0.0f
)
{
fontSize
=
1.0f
;
}
FontStyle
fontWeight
=
(
this
.
FontWeight
==
SvgFontWeight
.
bold
?
FontStyle
.
Bold
:
FontStyle
.
Regular
);
Font
font
=
new
Font
(
this
.
_fontFamily
,
fontSize
,
fontWeight
,
GraphicsUnit
.
Pixel
);
_path
=
new
GraphicsPath
();
_path
.
StartFigure
();
if
(!
string
.
IsNullOrEmpty
(
this
.
Text
))
DrawString
(
_path
,
this
.
X
,
this
.
Y
,
this
.
Dx
,
this
.
Dy
,
font
,
fontSize
,
this
.
Text
);
foreach
(
var
tspan
in
this
.
Children
.
Where
(
x
=>
x
is
SvgTextSpan
).
Select
(
x
=>
x
as
SvgTextSpan
))
{
if
(!
string
.
IsNullOrEmpty
(
tspan
.
Text
))
DrawString
(
_path
,
tspan
.
X
==
SvgUnit
.
Empty
?
this
.
X
:
tspan
.
X
,
tspan
.
Y
==
SvgUnit
.
Empty
?
this
.
Y
:
tspan
.
Y
,
tspan
.
DX
,
tspan
.
DY
,
font
,
fontSize
,
tspan
.
Text
);
}
_path
.
CloseFigure
();
this
.
IsPathDirty
=
false
;
}
return
_path
;
}
protected
set
{
_path
=
value
;
}
}
private
static
string
ValidateFontFamily
(
string
fontFamilyList
)
{
{
// Split font family list on "," and then trim start and end spaces and quotes.
return
DeepCopy
<
SvgText
>();
var
fontParts
=
fontFamilyList
.
Split
(
new
[]
{
','
}).
Select
(
fontName
=>
fontName
.
Trim
(
new
[]
{
'"'
,
' '
,
'\''
}));
var
families
=
System
.
Drawing
.
FontFamily
.
Families
;
// Find a the first font that exists in the list of installed font families.
//styles from IE get sent through as lowercase.
foreach
(
var
f
in
fontParts
.
Where
(
f
=>
families
.
Any
(
family
=>
family
.
Name
.
ToLower
()
==
f
.
ToLower
())))
{
return
f
;
}
// No valid font family found from the list requested.
return
DefaultFontFamily
;
}
}
private
void
DrawString
(
GraphicsPath
path
,
SvgUnit
x
,
SvgUnit
y
,
SvgUnit
dx
,
SvgUnit
dy
,
Font
font
,
float
fontSize
,
string
text
)
public
override
SvgElement
DeepCopy
<
T
>()
{
PointF
location
=
PointF
.
Empty
;
SizeF
stringBounds
;
lock
(
_stringMeasure
)
{
stringBounds
=
_stringMeasure
.
MeasureString
(
text
,
font
);
}
float
xToDevice
=
x
.
ToDeviceValue
(
this
)
+
dx
.
ToDeviceValue
(
this
);
float
yToDevice
=
y
.
ToDeviceValue
(
this
,
true
)
+
dy
.
ToDeviceValue
(
this
,
true
);
// Minus FontSize because the x/y coords mark the bottom left, not bottom top.
switch
(
this
.
TextAnchor
)
{
case
SvgTextAnchor
.
Start
:
location
=
new
PointF
(
xToDevice
,
yToDevice
-
stringBounds
.
Height
);
break
;
case
SvgTextAnchor
.
Middle
:
location
=
new
PointF
(
xToDevice
-
(
stringBounds
.
Width
/
2
),
yToDevice
-
stringBounds
.
Height
);
break
;
case
SvgTextAnchor
.
End
:
location
=
new
PointF
(
xToDevice
-
stringBounds
.
Width
,
yToDevice
-
stringBounds
.
Height
);
break
;
}
// No way to do letter-spacing or word-spacing, so do manually
if
(
this
.
LetterSpacing
.
Value
>
0.0f
||
this
.
WordSpacing
.
Value
>
0.0f
)
{
// Cut up into words, or just leave as required
string
[]
words
=
(
this
.
WordSpacing
.
Value
>
0.0f
)
?
text
.
Split
(
' '
)
:
new
string
[]
{
text
};
float
wordSpacing
=
this
.
WordSpacing
.
ToDeviceValue
(
this
);
float
letterSpacing
=
this
.
LetterSpacing
.
ToDeviceValue
(
this
);
float
start
=
this
.
X
.
ToDeviceValue
(
this
);
foreach
(
string
word
in
words
)
{
// Only do if there is line spacing, just write the word otherwise
if
(
this
.
LetterSpacing
.
Value
>
0.0f
)
{
char
[]
characters
=
word
.
ToCharArray
();
foreach
(
char
currentCharacter
in
characters
)
{
path
.
AddString
(
currentCharacter
.
ToString
(),
new
FontFamily
(
this
.
_fontFamily
),
(
int
)
font
.
Style
,
fontSize
,
location
,
StringFormat
.
GenericTypographic
);
location
=
new
PointF
(
path
.
GetBounds
().
Width
+
start
+
letterSpacing
,
location
.
Y
);
}
}
else
{
path
.
AddString
(
word
,
new
FontFamily
(
this
.
_fontFamily
),
(
int
)
font
.
Style
,
fontSize
,
location
,
StringFormat
.
GenericTypographic
);
}
// Move the location of the word to be written along
location
=
new
PointF
(
path
.
GetBounds
().
Width
+
start
+
wordSpacing
,
location
.
Y
);
}
}
else
{
if
(!
string
.
IsNullOrEmpty
(
text
))
{
path
.
AddString
(
text
,
new
FontFamily
(
this
.
_fontFamily
),
(
int
)
font
.
Style
,
fontSize
,
location
,
StringFormat
.
GenericTypographic
);
}
}
}
[
SvgAttribute
(
"onchange"
)]
public
event
EventHandler
<
StringArg
>
Change
;
//change
protected
void
OnChange
(
string
newString
,
string
sessionID
)
{
RaiseChange
(
this
,
new
StringArg
{
s
=
newString
,
SessionID
=
sessionID
});
}
protected
void
RaiseChange
(
object
sender
,
StringArg
s
)
{
{
var
handler
=
Change
;
var
newObj
=
base
.
DeepCopy
<
T
>()
as
SvgText
;
if
(
handler
!=
null
)
newObj
.
TextAnchor
=
this
.
TextAnchor
;
{
newObj
.
WordSpacing
=
this
.
WordSpacing
;
handler
(
sender
,
s
);
newObj
.
LetterSpacing
=
this
.
LetterSpacing
;
}
newObj
.
Font
=
this
.
Font
;
newObj
.
FontFamily
=
this
.
FontFamily
;
newObj
.
FontSize
=
this
.
FontSize
;
newObj
.
FontWeight
=
this
.
FontWeight
;
newObj
.
X
=
this
.
X
;
newObj
.
Y
=
this
.
Y
;
return
newObj
;
}
}
#if Net4
public
override
void
RegisterEvents
(
ISvgEventCaller
caller
)
{
//register basic events
base
.
RegisterEvents
(
caller
);
//add change event for text
caller
.
RegisterAction
<
string
,
string
>(
this
.
ID
+
"/onchange"
,
OnChange
);
}
public
override
void
UnregisterEvents
(
ISvgEventCaller
caller
)
{
//unregister base events
base
.
UnregisterEvents
(
caller
);
//unregister change event
caller
.
UnregisterAction
(
this
.
ID
+
"/onchange"
);
}
#endif
public
override
SvgElement
DeepCopy
()
{
return
DeepCopy
<
SvgText
>();
}
public
override
SvgElement
DeepCopy
<
T
>()
{
var
newObj
=
base
.
DeepCopy
<
T
>()
as
SvgText
;
newObj
.
TextAnchor
=
this
.
TextAnchor
;
newObj
.
WordSpacing
=
this
.
WordSpacing
;
newObj
.
LetterSpacing
=
this
.
LetterSpacing
;
newObj
.
Font
=
this
.
Font
;
newObj
.
FontFamily
=
this
.
FontFamily
;
newObj
.
FontSize
=
this
.
FontSize
;
newObj
.
FontWeight
=
this
.
FontWeight
;
newObj
.
X
=
this
.
X
;
newObj
.
Y
=
this
.
Y
;
return
newObj
;
}
}
}
}
}
This diff is collapsed.
Click to expand it.
Source/Text/SvgTextBase.cs
0 → 100644
+
601
-
0
View file @
ceb78a3f
using
System
;
using
System.Collections.Generic
;
using
System.Text
;
using
System.Text.RegularExpressions
;
using
System.ComponentModel
;
using
System.Drawing
;
using
System.Drawing.Drawing2D
;
using
System.Drawing.Text
;
using
Svg.DataTypes
;
using
System.Linq
;
namespace
Svg
{
public
enum
XmlSpaceHandling
{
@default
,
preserve
}
public
abstract
class
SvgTextBase
:
SvgVisualElement
{
private
SvgUnit
_x
;
private
SvgUnit
_y
;
private
SvgUnit
_dy
;
private
SvgUnit
_dx
;
private
SvgUnit
_letterSpacing
;
private
SvgUnit
_wordSpacing
;
private
SvgUnit
_fontSize
;
private
SvgFontWeight
_fontWeight
;
private
string
_font
;
protected
string
_fontFamily
;
private
SvgTextAnchor
_textAnchor
=
SvgTextAnchor
.
Start
;
private
static
readonly
SvgRenderer
_stringMeasure
;
private
const
string
DefaultFontFamily
=
"Times New Roman"
;
private
XmlSpaceHandling
_space
=
XmlSpaceHandling
.
@default
;
/// <summary>
/// Initializes the <see cref="SvgTextBase"/> class.
/// </summary>
static
SvgTextBase
()
{
Bitmap
bitmap
=
new
Bitmap
(
1
,
1
);
_stringMeasure
=
SvgRenderer
.
FromImage
(
bitmap
);
_stringMeasure
.
TextRenderingHint
=
TextRenderingHint
.
AntiAlias
;
}
/// <summary>
/// Initializes a new instance of the <see cref="SvgTextBase"/> class.
/// </summary>
public
SvgTextBase
()
{
this
.
_fontSize
=
new
SvgUnit
(
0.0f
);
this
.
_dy
=
new
SvgUnit
(
0.0f
);
this
.
_dx
=
new
SvgUnit
(
0.0f
);
}
/// <summary>
/// Gets or sets the text to be rendered.
/// </summary>
public
virtual
string
Text
{
get
{
return
base
.
Content
;
}
set
{
base
.
Content
=
value
;
this
.
IsPathDirty
=
true
;
this
.
Content
=
value
;
}
}
/// <summary>
/// Gets or sets the text anchor.
/// </summary>
/// <value>The text anchor.</value>
[
SvgAttribute
(
"text-anchor"
)]
public
virtual
SvgTextAnchor
TextAnchor
{
get
{
return
this
.
_textAnchor
;
}
set
{
this
.
_textAnchor
=
value
;
this
.
IsPathDirty
=
true
;
}
}
/// <summary>
/// Gets or sets the X.
/// </summary>
/// <value>The X.</value>
[
SvgAttribute
(
"x"
)]
public
virtual
SvgUnit
X
{
get
{
return
this
.
_x
;
}
set
{
if
(
_x
!=
value
)
{
this
.
_x
=
value
;
this
.
IsPathDirty
=
true
;
OnAttributeChanged
(
new
AttributeEventArgs
{
Attribute
=
"x"
,
Value
=
value
});
}
}
}
/// <summary>
/// Gets or sets the dX.
/// </summary>
/// <value>The dX.</value>
[
SvgAttribute
(
"dx"
)]
public
virtual
SvgUnit
Dx
{
get
{
return
this
.
_dx
;
}
set
{
if
(
_dx
!=
value
)
{
this
.
_dx
=
value
;
this
.
IsPathDirty
=
true
;
OnAttributeChanged
(
new
AttributeEventArgs
{
Attribute
=
"dx"
,
Value
=
value
});
}
}
}
/// <summary>
/// Gets or sets the Y.
/// </summary>
/// <value>The Y.</value>
[
SvgAttribute
(
"y"
)]
public
virtual
SvgUnit
Y
{
get
{
return
this
.
_y
;
}
set
{
if
(
_y
!=
value
)
{
this
.
_y
=
value
;
this
.
IsPathDirty
=
true
;
OnAttributeChanged
(
new
AttributeEventArgs
{
Attribute
=
"y"
,
Value
=
value
});
}
}
}
/// <summary>
/// Gets or sets the dY.
/// </summary>
/// <value>The dY.</value>
[
SvgAttribute
(
"dy"
)]
public
virtual
SvgUnit
Dy
{
get
{
return
this
.
_dy
;
}
set
{
if
(
_dy
!=
value
)
{
this
.
_dy
=
value
;
this
.
IsPathDirty
=
true
;
OnAttributeChanged
(
new
AttributeEventArgs
{
Attribute
=
"dy"
,
Value
=
value
});
}
}
}
/// <summary>
/// Specifies spacing behavior between text characters.
/// </summary>
[
SvgAttribute
(
"letter-spacing"
)]
public
virtual
SvgUnit
LetterSpacing
{
get
{
return
this
.
_letterSpacing
;
}
set
{
this
.
_letterSpacing
=
value
;
this
.
IsPathDirty
=
true
;
}
}
/// <summary>
/// Specifies spacing behavior between words.
/// </summary>
[
SvgAttribute
(
"word-spacing"
)]
public
virtual
SvgUnit
WordSpacing
{
get
{
return
this
.
_wordSpacing
;
}
set
{
this
.
_wordSpacing
=
value
;
this
.
IsPathDirty
=
true
;
}
}
/// <summary>
/// Indicates which font family is to be used to render the text.
/// </summary>
[
SvgAttribute
(
"font-family"
)]
public
virtual
string
FontFamily
{
get
{
return
this
.
_fontFamily
??
DefaultFontFamily
;
}
set
{
this
.
_fontFamily
=
ValidateFontFamily
(
value
);
this
.
IsPathDirty
=
true
;
}
}
/// <summary>
/// Refers to the size of the font from baseline to baseline when multiple lines of text are set solid in a multiline layout environment.
/// </summary>
[
SvgAttribute
(
"font-size"
)]
public
virtual
SvgUnit
FontSize
{
get
{
return
this
.
_fontSize
;
}
set
{
this
.
_fontSize
=
value
;
this
.
IsPathDirty
=
true
;
}
}
/// <summary>
/// Refers to the boldness of the font.
/// </summary>
[
SvgAttribute
(
"font-weight"
)]
public
virtual
SvgFontWeight
FontWeight
{
get
{
return
this
.
_fontWeight
;
}
set
{
this
.
_fontWeight
=
value
;
this
.
IsPathDirty
=
true
;
}
}
/// <summary>
/// Set all font information.
/// </summary>
[
SvgAttribute
(
"font"
)]
public
virtual
string
Font
{
get
{
return
this
.
_font
;
}
set
{
var
parts
=
value
.
Split
(
','
);
foreach
(
var
part
in
parts
)
{
//This deals with setting font size. Looks for either <number>px or <number>pt style="font: bold 16px/normal 'trebuchet ms', verdana, sans-serif;"
Regex
rx
=
new
Regex
(
@"(\d+)+(?=pt|px)"
);
var
res
=
rx
.
Match
(
part
);
if
(
res
.
Success
)
{
int
fontSize
=
10
;
int
.
TryParse
(
res
.
Value
,
out
fontSize
);
this
.
FontSize
=
new
SvgUnit
((
float
)
fontSize
);
}
//this assumes "bold" has spaces around it. e.g.: style="font: bold 16px/normal
rx
=
new
Regex
(
@"\sbold\s"
);
res
=
rx
.
Match
(
part
);
if
(
res
.
Success
)
{
this
.
FontWeight
=
SvgFontWeight
.
bold
;
}
}
var
font
=
ValidateFontFamily
(
value
);
this
.
_fontFamily
=
font
;
this
.
_font
=
font
;
//not sure this is used?
this
.
IsPathDirty
=
true
;
}
}
/// <summary>
/// Gets or sets the fill.
/// </summary>
/// <remarks>
/// <para>Unlike other <see cref="SvgGraphicsElement"/>s, <see cref="SvgText"/> has a default fill of black rather than transparent.</para>
/// </remarks>
/// <value>The fill.</value>
public
override
SvgPaintServer
Fill
{
get
{
return
(
this
.
Attributes
[
"fill"
]
==
null
)
?
new
SvgColourServer
(
Color
.
Black
)
:
(
SvgPaintServer
)
this
.
Attributes
[
"fill"
];
}
set
{
this
.
Attributes
[
"fill"
]
=
value
;
}
}
/// <summary>
/// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
/// </summary>
/// <returns>
/// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
/// </returns>
public
override
string
ToString
()
{
return
this
.
Text
;
}
/// <summary>
/// Gets or sets a value to determine if anti-aliasing should occur when the element is being rendered.
/// </summary>
/// <value></value>
protected
override
bool
RequiresSmoothRendering
{
get
{
return
true
;
}
}
/// <summary>
/// Gets the bounds of the element.
/// </summary>
/// <value>The bounds.</value>
public
override
System
.
Drawing
.
RectangleF
Bounds
{
get
{
return
this
.
Path
.
GetBounds
();
}
}
static
private
RectangleF
MeasureString
(
SvgRenderer
renderer
,
string
text
,
Font
font
)
{
GraphicsPath
p
=
new
GraphicsPath
();
p
.
AddString
(
text
,
font
.
FontFamily
,
0
,
font
.
Size
,
new
PointF
(
0.0f
,
0.0f
),
StringFormat
.
GenericTypographic
);
p
.
Transform
(
renderer
.
Transform
);
return
p
.
GetBounds
();
}
private
static
string
ValidateFontFamily
(
string
fontFamilyList
)
{
// Split font family list on "," and then trim start and end spaces and quotes.
var
fontParts
=
fontFamilyList
.
Split
(
new
[]
{
','
}).
Select
(
fontName
=>
fontName
.
Trim
(
new
[]
{
'"'
,
' '
,
'\''
}));
var
families
=
System
.
Drawing
.
FontFamily
.
Families
;
// Find a the first font that exists in the list of installed font families.
//styles from IE get sent through as lowercase.
foreach
(
var
f
in
fontParts
.
Where
(
f
=>
families
.
Any
(
family
=>
family
.
Name
.
ToLower
()
==
f
.
ToLower
())))
{
return
f
;
}
// No valid font family found from the list requested.
return
null
;
}
/// <summary>
/// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="Graphics"/> object.
/// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> object to render to.</param>
/// <remarks>Necessary to make sure that any internal tspan elements get rendered as well</remarks>
protected
override
void
Render
(
SvgRenderer
renderer
)
{
if
((
this
.
Path
!=
null
)
&&
this
.
Visible
&&
this
.
Displayable
)
{
this
.
PushTransforms
(
renderer
);
this
.
SetClip
(
renderer
);
// If this element needs smoothing enabled turn anti-aliasing on
if
(
this
.
RequiresSmoothRendering
)
{
renderer
.
SmoothingMode
=
SmoothingMode
.
AntiAlias
;
}
this
.
RenderFill
(
renderer
);
this
.
RenderStroke
(
renderer
);
this
.
RenderChildren
(
renderer
);
// Reset the smoothing mode
if
(
this
.
RequiresSmoothRendering
&&
renderer
.
SmoothingMode
==
SmoothingMode
.
AntiAlias
)
{
renderer
.
SmoothingMode
=
SmoothingMode
.
Default
;
}
this
.
ResetClip
(
renderer
);
this
.
PopTransforms
(
renderer
);
}
}
private
GraphicsPath
_path
;
protected
class
NodeBounds
{
public
float
xOffset
{
get
;
set
;
}
public
SizeF
Bounds
{
get
;
set
;
}
public
ISvgNode
Node
{
get
;
set
;
}
}
protected
class
BoundsData
{
private
List
<
NodeBounds
>
_nodes
=
new
List
<
NodeBounds
>();
public
IList
<
NodeBounds
>
Nodes
{
get
{
return
_nodes
;
}
}
public
SizeF
Bounds
{
get
;
set
;
}
}
protected
BoundsData
GetTextBounds
()
{
var
font
=
GetFont
();
SvgTextBase
innerText
;
SizeF
stringBounds
;
float
totalHeight
=
0
;
float
totalWidth
=
0
;
var
result
=
new
BoundsData
();
var
nodes
=
(
from
n
in
this
.
Nodes
where
(
n
is
SvgContentNode
||
n
is
SvgTextBase
)
&&
!
string
.
IsNullOrEmpty
(
n
.
Content
)
select
n
).
ToList
();
ISvgNode
node
;
for
(
var
i
=
0
;
i
<
nodes
.
Count
;
i
++)
{
node
=
nodes
[
i
];
lock
(
_stringMeasure
)
{
innerText
=
node
as
SvgTextBase
;
if
(
innerText
==
null
)
{
stringBounds
=
_stringMeasure
.
MeasureString
(
PrepareText
(
node
.
Content
,
i
>
0
&&
nodes
[
i
-
1
]
is
SvgTextBase
,
i
<
nodes
.
Count
-
1
&&
nodes
[
i
+
1
]
is
SvgTextBase
),
font
);
}
else
{
stringBounds
=
innerText
.
GetTextBounds
().
Bounds
;
}
result
.
Nodes
.
Add
(
new
NodeBounds
()
{
Bounds
=
stringBounds
,
Node
=
node
,
xOffset
=
totalWidth
});
totalHeight
=
Math
.
Max
(
totalHeight
,
stringBounds
.
Height
);
totalWidth
+=
stringBounds
.
Width
;
}
}
result
.
Bounds
=
new
SizeF
(
totalWidth
,
totalHeight
);
return
result
;
}
protected
float
_calcX
=
0
;
protected
float
_calcY
=
0
;
/// <summary>
/// Gets the <see cref="GraphicsPath"/> for this element.
/// </summary>
/// <value></value>
public
override
System
.
Drawing
.
Drawing2D
.
GraphicsPath
Path
{
get
{
// Make sure the path is always null if there is no text
//if there is a TSpan inside of this text element then path should not be null (even if this text is empty!)
if
((
string
.
IsNullOrEmpty
(
this
.
Text
)
||
this
.
Text
.
Trim
().
Length
<
1
)
&&
this
.
Children
.
Where
(
x
=>
x
is
SvgTextSpan
).
Select
(
x
=>
x
as
SvgTextSpan
).
Count
()
==
0
)
return
_path
=
null
;
//NOT SURE WHAT THIS IS ABOUT - Path gets created again anyway - WTF?
// When an empty string is passed to GraphicsPath, it rises an InvalidArgumentException...
if
(
_path
==
null
||
this
.
IsPathDirty
)
{
var
font
=
GetFont
();
SvgTextBase
innerText
;
//RectangleF bounds;
float
x
=
(
_x
==
SvgUnit
.
Empty
||
_x
==
SvgUnit
.
None
?
_calcX
:
_x
.
ToDeviceValue
(
this
))
+
_dx
.
ToDeviceValue
(
this
);
float
y
=
(
_y
==
SvgUnit
.
Empty
||
_y
==
SvgUnit
.
None
?
_calcY
:
_y
.
ToDeviceValue
(
this
,
true
))
+
_dy
.
ToDeviceValue
(
this
,
true
);
_path
=
new
GraphicsPath
();
_path
.
StartFigure
();
// Measure the overall bounds of all the text
var
boundsData
=
GetTextBounds
();
// Determine the location of the start point
switch
(
this
.
TextAnchor
)
{
case
SvgTextAnchor
.
Middle
:
x
-=
(
boundsData
.
Bounds
.
Width
/
2
);
break
;
case
SvgTextAnchor
.
End
:
x
-=
boundsData
.
Bounds
.
Width
;
break
;
}
NodeBounds
data
;
for
(
var
i
=
0
;
i
<
boundsData
.
Nodes
.
Count
;
i
++)
{
data
=
boundsData
.
Nodes
[
i
];
innerText
=
data
.
Node
as
SvgTextBase
;
if
(
innerText
==
null
)
{
// Minus FontSize because the x/y coords mark the bottom left, not bottom top.
DrawString
(
_path
,
x
+
data
.
xOffset
,
y
-
boundsData
.
Bounds
.
Height
,
font
,
PrepareText
(
data
.
Node
.
Content
,
i
>
0
&&
boundsData
.
Nodes
[
i
-
1
].
Node
is
SvgTextBase
,
i
<
boundsData
.
Nodes
.
Count
-
1
&&
boundsData
.
Nodes
[
i
+
1
].
Node
is
SvgTextBase
));
}
else
{
innerText
.
_calcX
=
x
+
data
.
xOffset
;
innerText
.
_calcY
=
y
;
}
}
_path
.
CloseFigure
();
this
.
IsPathDirty
=
false
;
}
return
_path
;
}
protected
set
{
_path
=
value
;
}
}
/// <summary>
/// Prepare the text according to the whitespace handling rules. <see href="http://www.w3.org/TR/SVG/text.html">SVG Spec</see>.
/// </summary>
/// <param name="value">Text to be prepared</param>
/// <returns>Prepared text</returns>
protected
string
PrepareText
(
string
value
,
bool
leadingSpace
,
bool
trailingSpace
)
{
if
(
_space
==
XmlSpaceHandling
.
preserve
)
{
return
(
leadingSpace
?
" "
:
""
)
+
value
.
Replace
(
'\t'
,
' '
).
Replace
(
"\r\n"
,
" "
).
Replace
(
'\r'
,
' '
).
Replace
(
'\n'
,
' '
)
+
(
trailingSpace
?
" "
:
""
);
}
else
{
return
(
leadingSpace
?
" "
:
""
)
+
value
.
Replace
(
"\r"
,
""
).
Replace
(
"\n"
,
""
).
Replace
(
'\t'
,
' '
).
Trim
().
Replace
(
" "
,
" "
)
+
(
trailingSpace
?
" "
:
""
);
}
}
/// <summary>
/// Get the font information based on data stored with the text object or inherited from the parent.
/// </summary>
/// <returns></returns>
internal
Font
GetFont
()
{
var
parent
=
this
.
Parent
as
SvgTextBase
;
Font
parentFont
=
null
;
if
(
parent
!=
null
)
parentFont
=
parent
.
GetFont
();
float
fontSize
=
this
.
FontSize
.
ToDeviceValue
(
this
);
if
(
fontSize
==
0.0f
)
{
fontSize
=
(
parentFont
==
null
?
1.0f
:
parentFont
.
Size
);
fontSize
=
(
fontSize
==
0.0f
?
1.0f
:
fontSize
);
}
var
fontWeight
=
((
_fontWeight
==
SvgFontWeight
.
inherit
&&
parentFont
!=
null
&&
parentFont
.
Bold
)
||
_fontWeight
==
SvgFontWeight
.
bold
?
FontStyle
.
Bold
:
FontStyle
.
Regular
);
var
family
=
_fontFamily
??
(
parentFont
==
null
?
DefaultFontFamily
:
parentFont
.
FontFamily
.
Name
);
return
new
Font
(
family
,
fontSize
,
fontWeight
,
GraphicsUnit
.
Pixel
);
}
/// <summary>
/// Draws a string on a path at a specified location and with a specified font.
/// </summary>
internal
void
DrawString
(
GraphicsPath
path
,
float
x
,
float
y
,
Font
font
,
string
text
)
{
PointF
location
=
new
PointF
(
x
,
y
);
// No way to do letter-spacing or word-spacing, so do manually
if
(
this
.
LetterSpacing
.
Value
>
0.0f
||
this
.
WordSpacing
.
Value
>
0.0f
)
{
// Cut up into words, or just leave as required
string
[]
words
=
(
this
.
WordSpacing
.
Value
>
0.0f
)
?
text
.
Split
(
' '
)
:
new
string
[]
{
text
};
float
wordSpacing
=
this
.
WordSpacing
.
ToDeviceValue
(
this
);
float
letterSpacing
=
this
.
LetterSpacing
.
ToDeviceValue
(
this
);
float
start
=
this
.
X
.
ToDeviceValue
(
this
);
foreach
(
string
word
in
words
)
{
// Only do if there is line spacing, just write the word otherwise
if
(
this
.
LetterSpacing
.
Value
>
0.0f
)
{
char
[]
characters
=
word
.
ToCharArray
();
foreach
(
char
currentCharacter
in
characters
)
{
path
.
AddString
(
currentCharacter
.
ToString
(),
font
.
FontFamily
,
(
int
)
font
.
Style
,
font
.
Size
,
location
,
StringFormat
.
GenericTypographic
);
location
=
new
PointF
(
path
.
GetBounds
().
Width
+
start
+
letterSpacing
,
location
.
Y
);
}
}
else
{
path
.
AddString
(
word
,
font
.
FontFamily
,
(
int
)
font
.
Style
,
font
.
Size
,
location
,
StringFormat
.
GenericTypographic
);
}
// Move the location of the word to be written along
location
=
new
PointF
(
path
.
GetBounds
().
Width
+
start
+
wordSpacing
,
location
.
Y
);
}
}
else
{
if
(!
string
.
IsNullOrEmpty
(
text
))
{
path
.
AddString
(
text
,
font
.
FontFamily
,
(
int
)
font
.
Style
,
font
.
Size
,
location
,
StringFormat
.
GenericTypographic
);
}
}
}
[
SvgAttribute
(
"onchange"
)]
public
event
EventHandler
<
StringArg
>
Change
;
//change
protected
void
OnChange
(
string
newString
,
string
sessionID
)
{
RaiseChange
(
this
,
new
StringArg
{
s
=
newString
,
SessionID
=
sessionID
});
}
protected
void
RaiseChange
(
object
sender
,
StringArg
s
)
{
var
handler
=
Change
;
if
(
handler
!=
null
)
{
handler
(
sender
,
s
);
}
}
#if Net4
public
override
void
RegisterEvents
(
ISvgEventCaller
caller
)
{
//register basic events
base
.
RegisterEvents
(
caller
);
//add change event for text
caller
.
RegisterAction
<
string
,
string
>(
this
.
ID
+
"/onchange"
,
OnChange
);
}
public
override
void
UnregisterEvents
(
ISvgEventCaller
caller
)
{
//unregister base events
base
.
UnregisterEvents
(
caller
);
//unregister change event
caller
.
UnregisterAction
(
this
.
ID
+
"/onchange"
);
}
#endif
}
}
This diff is collapsed.
Click to expand it.
Source/Text/SvgTextSpan.cs
+
3
-
64
View file @
ceb78a3f
...
@@ -8,69 +8,8 @@ using System.Text;
...
@@ -8,69 +8,8 @@ using System.Text;
namespace
Svg
namespace
Svg
{
{
[
SvgElement
(
"tspan"
)]
[
SvgElement
(
"tspan"
)]
public
class
SvgTextSpan
:
Svg
Element
public
class
SvgTextSpan
:
Svg
TextBase
{
{
private
SvgUnit
_x
;
private
SvgUnit
_y
;
private
SvgUnit
_dx
;
private
SvgUnit
_dy
;
/// <summary>
/// Gets or sets the X.
/// </summary>
/// <value>The X.</value>
[
SvgAttribute
(
"x"
)]
public
SvgUnit
X
{
get
{
return
this
.
_x
;
}
set
{
this
.
_x
=
value
;
}
}
/// <summary>
/// Gets or sets the X.
/// </summary>
/// <value>The X.</value>
[
SvgAttribute
(
"y"
)]
public
SvgUnit
Y
{
get
{
return
this
.
_y
;
}
set
{
this
.
_y
=
value
;
}
}
/// <summary>
/// Gets or sets the deltaX from the containing text.
/// </summary>
/// <value>The dX.</value>
[
SvgAttribute
(
"dx"
)]
public
SvgUnit
DX
{
get
{
return
this
.
_dx
;
}
set
{
this
.
_dx
=
value
;
}
}
/// <summary>
/// Gets or sets the deltaY from the containing text.
/// </summary>
/// <value>The dY.</value>
[
SvgAttribute
(
"dy"
)]
public
SvgUnit
DY
{
get
{
return
this
.
_dy
;
}
set
{
this
.
_dy
=
value
;
}
}
/// <summary>
/// Gets or sets the text to be rendered.
/// </summary>
public
virtual
string
Text
{
get
{
return
base
.
Content
;
}
set
{
base
.
Content
=
value
;
this
.
Content
=
value
;
}
}
public
override
SvgElement
DeepCopy
()
public
override
SvgElement
DeepCopy
()
{
{
...
@@ -82,8 +21,8 @@ namespace Svg
...
@@ -82,8 +21,8 @@ namespace Svg
var
newObj
=
base
.
DeepCopy
<
T
>()
as
SvgTextSpan
;
var
newObj
=
base
.
DeepCopy
<
T
>()
as
SvgTextSpan
;
newObj
.
X
=
this
.
X
;
newObj
.
X
=
this
.
X
;
newObj
.
Y
=
this
.
Y
;
newObj
.
Y
=
this
.
Y
;
newObj
.
D
X
=
this
.
D
X
;
newObj
.
D
x
=
this
.
D
x
;
newObj
.
D
Y
=
this
.
D
Y
;
newObj
.
D
y
=
this
.
D
y
;
newObj
.
Text
=
this
.
Text
;
newObj
.
Text
=
this
.
Text
;
return
newObj
;
return
newObj
;
...
...
This diff is collapsed.
Click to expand it.
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
Menu
Projects
Groups
Snippets
Help