Commit 535abaf8 authored by Tebjan Halm's avatar Tebjan Halm
Browse files

Merge pull request #90 from erdomke/master

Initial Work on W3C Test Compliance
parents 4b1ff3d4 bd05ecbc
......@@ -79,18 +79,20 @@ namespace Svg
Y2 = new SvgUnit(SvgUnitType.Percentage, 0F);
}
public override Brush GetBrush(SvgVisualElement renderingElement, float opacity)
public override Brush GetBrush(SvgVisualElement renderingElement, SvgRenderer renderer, float opacity)
{
LoadStops();
LoadStops(renderingElement);
if (IsInvalid)
{
return null;
}
var boundable = CalculateBoundable(renderingElement);
try
{
if (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox) renderer.Boundable(renderingElement);
var specifiedStart = CalculateStart(boundable);
var specifiedEnd = CalculateEnd(boundable);
var specifiedStart = CalculateStart(renderer);
var specifiedEnd = CalculateEnd(renderer);
var effectiveStart = specifiedStart;
var effectiveEnd = specifiedEnd;
......@@ -102,21 +104,26 @@ namespace Svg
effectiveEnd = expansion.EndPoint;
}
return new LinearGradientBrush(effectiveStart, effectiveEnd, Color.Transparent, Color.Transparent)
return new LinearGradientBrush(effectiveStart, effectiveEnd, System.Drawing.Color.Transparent, System.Drawing.Color.Transparent)
{
InterpolationColors = CalculateColorBlend(renderingElement, opacity, specifiedStart, effectiveStart, specifiedEnd, effectiveEnd),
InterpolationColors = CalculateColorBlend(renderer, opacity, specifiedStart, effectiveStart, specifiedEnd, effectiveEnd),
WrapMode = WrapMode.TileFlipX
};
}
finally
{
if (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox) renderer.PopBoundable();
}
}
private PointF CalculateStart(ISvgBoundable boundable)
private PointF CalculateStart(SvgRenderer renderer)
{
return TransformPoint(new PointF(this.X1.ToDeviceValue(boundable), this.Y1.ToDeviceValue(boundable, true)));
return TransformPoint(SvgUnit.GetDevicePointOffset(this.X1, this.Y1, renderer, this));
}
private PointF CalculateEnd(ISvgBoundable boundable)
private PointF CalculateEnd(SvgRenderer renderer)
{
return TransformPoint(new PointF(this.X2.ToDeviceValue(boundable), this.Y2.ToDeviceValue(boundable, true)));
return TransformPoint(SvgUnit.GetDevicePointOffset(this.X2, this.Y2, renderer, this));
}
private bool NeedToExpandGradient(ISvgBoundable boundable, PointF specifiedStart, PointF specifiedEnd)
......@@ -174,9 +181,9 @@ namespace Svg
return new GradientPoints(effectiveStart, effectiveEnd);
}
private ColorBlend CalculateColorBlend(SvgVisualElement owner, float opacity, PointF specifiedStart, PointF effectiveStart, PointF specifiedEnd, PointF effectiveEnd)
private ColorBlend CalculateColorBlend(SvgRenderer renderer, float opacity, PointF specifiedStart, PointF effectiveStart, PointF specifiedEnd, PointF effectiveEnd)
{
var colorBlend = GetColorBlend(owner, opacity, false);
var colorBlend = GetColorBlend(renderer, opacity, false);
var startDelta = CalculateDistance(specifiedStart, effectiveStart);
var endDelta = CalculateDistance(specifiedEnd, effectiveEnd);
......
......@@ -87,26 +87,19 @@ namespace Svg
Overflow = SvgOverflow.hidden;
}
public override System.Drawing.Drawing2D.GraphicsPath Path
{
get
public override System.Drawing.Drawing2D.GraphicsPath Path(SvgRenderer renderer)
{
var path = this.Children.FirstOrDefault(x => x is SvgPath);
if (path != null)
return (path as SvgPath).Path;
return (path as SvgPath).Path(renderer);
return null;
}
protected set
{
// No-op
}
}
public override System.Drawing.RectangleF Bounds
{
get
{
var path = this.Path;
var path = this.Path(null);
if (path != null)
return path.GetBounds();
return new System.Drawing.RectangleF();
......@@ -177,7 +170,7 @@ namespace Svg
/// <param name="pMarkerPoint"></param>
private void RenderPart2(float fAngle, SvgRenderer pRenderer, SvgPath pOwner, PointF pMarkerPoint)
{
Pen pRenderPen = CreatePen(pOwner);
Pen pRenderPen = CreatePen(pOwner, pRenderer);
GraphicsPath markerPath = GetClone(pOwner);
......@@ -190,10 +183,14 @@ namespace Svg
switch (MarkerUnits)
{
case SvgMarkerUnits.strokeWidth:
transMatrix.Translate(AdjustForViewBoxWidth(-RefX * pOwner.StrokeWidth), AdjustForViewBoxHeight(-RefY * pOwner.StrokeWidth));
transMatrix.Translate(AdjustForViewBoxWidth(-RefX.ToDeviceValue(pRenderer, UnitRenderingType.Horizontal, this) *
pOwner.StrokeWidth.ToDeviceValue(pRenderer, UnitRenderingType.Other, this)),
AdjustForViewBoxHeight(-RefY.ToDeviceValue(pRenderer, UnitRenderingType.Vertical, this) *
pOwner.StrokeWidth.ToDeviceValue(pRenderer, UnitRenderingType.Other, this)));
break;
case SvgMarkerUnits.userSpaceOnUse:
transMatrix.Translate(-RefX, -RefY);
transMatrix.Translate(-RefX.ToDeviceValue(pRenderer, UnitRenderingType.Horizontal, this),
-RefY.ToDeviceValue(pRenderer, UnitRenderingType.Vertical, this));
break;
}
markerPath.Transform(transMatrix);
......@@ -205,7 +202,7 @@ namespace Svg
if (pFill != null)
{
Brush pBrush = pFill.GetBrush(this, fOpacity);
Brush pBrush = pFill.GetBrush(this, pRenderer, fOpacity);
pRenderer.FillPath(pBrush, markerPath);
pBrush.Dispose();
}
......@@ -219,17 +216,18 @@ namespace Svg
/// </summary>
/// <param name="pStroke"></param>
/// <returns></returns>
private Pen CreatePen(SvgPath pPath)
private Pen CreatePen(SvgPath pPath, SvgRenderer renderer)
{
Brush pBrush = pPath.Stroke.GetBrush(this, Opacity);
Brush pBrush = pPath.Stroke.GetBrush(this, renderer, Opacity);
switch (MarkerUnits)
{
case SvgMarkerUnits.strokeWidth:
return (new Pen(pBrush, StrokeWidth * pPath.StrokeWidth));
return (new Pen(pBrush, StrokeWidth.ToDeviceValue(renderer, UnitRenderingType.Other, this) *
pPath.StrokeWidth.ToDeviceValue(renderer, UnitRenderingType.Other, this)));
case SvgMarkerUnits.userSpaceOnUse:
return (new Pen(pBrush, StrokeWidth));
return (new Pen(pBrush, StrokeWidth.ToDeviceValue(renderer, UnitRenderingType.Other, this)));
}
return (new Pen(pBrush, StrokeWidth));
return (new Pen(pBrush, StrokeWidth.ToDeviceValue(renderer, UnitRenderingType.Other, this)));
}
/// <summary>
......@@ -238,7 +236,7 @@ namespace Svg
/// <returns></returns>
private GraphicsPath GetClone(SvgPath pPath)
{
GraphicsPath pRet = Path.Clone() as GraphicsPath;
GraphicsPath pRet = Path(null).Clone() as GraphicsPath;
switch (MarkerUnits)
{
case SvgMarkerUnits.strokeWidth:
......
......@@ -39,7 +39,7 @@ namespace Svg
/// </summary>
/// <param name="styleOwner">The owner <see cref="SvgVisualElement"/>.</param>
/// <param name="opacity">The opacity of the brush.</param>
public abstract Brush GetBrush(SvgVisualElement styleOwner, float opacity);
public abstract Brush GetBrush(SvgVisualElement styleOwner, SvgRenderer renderer, float opacity);
/// <summary>
/// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
......
......@@ -27,6 +27,14 @@ namespace Svg
{
return SvgColourServer.NotSet;
}
else if (value == "inherit")
{
return SvgColourServer.Inherit;
}
else if (value == "currentColor")
{
return new SvgDeferredPaintServer(document, value);
}
else if (value.IndexOf("url(#") > -1)
{
Match match = _urlRefPattern.Match(value);
......
......@@ -13,13 +13,15 @@ namespace Svg
/// A pattern is used to fill or stroke an object using a pre-defined graphic object which can be replicated ("tiled") at fixed intervals in x and y to cover the areas to be painted.
/// </summary>
[SvgElement("pattern")]
public sealed class SvgPatternServer : SvgPaintServer, ISvgViewPort
public sealed class SvgPatternServer : SvgPaintServer, ISvgViewPort, ISvgSupportsCoordinateUnits
{
private SvgUnit _width;
private SvgUnit _height;
private SvgUnit _x;
private SvgUnit _y;
private SvgViewBox _viewBox;
private SvgCoordinateUnits _patternUnits;
private SvgCoordinateUnits _patternContentUnits;
[SvgAttribute("overflow")]
public SvgOverflow Overflow
......@@ -61,6 +63,26 @@ namespace Svg
set { this._width = value; }
}
/// <summary>
/// Gets or sets the width of the pattern.
/// </summary>
[SvgAttribute("patternUnits")]
public SvgCoordinateUnits PatternUnits
{
get { return this._patternUnits; }
set { this._patternUnits = value; }
}
/// <summary>
/// Gets or sets the width of the pattern.
/// </summary>
[SvgAttribute("patternUnits")]
public SvgCoordinateUnits PatternContentUnits
{
get { return this._patternContentUnits; }
set { this._patternContentUnits = value; }
}
/// <summary>
/// Gets or sets the height of the pattern.
/// </summary>
......@@ -107,7 +129,7 @@ namespace Svg
/// </summary>
/// <param name="renderingElement">The owner <see cref="SvgVisualElement"/>.</param>
/// <param name="opacity">The opacity of the brush.</param>
public override Brush GetBrush(SvgVisualElement renderingElement, float opacity)
public override Brush GetBrush(SvgVisualElement renderingElement, SvgRenderer renderer, float opacity)
{
// If there aren't any children, return null
if (this.Children.Count == 0)
......@@ -117,18 +139,21 @@ namespace Svg
if (this._width.Value == 0.0f || this._height.Value == 0.0f)
return null;
float width = this._width.ToDeviceValue(renderingElement);
float height = this._height.ToDeviceValue(renderingElement, true);
Bitmap image = new Bitmap((int)width, (int)height);
using (SvgRenderer renderer = SvgRenderer.FromImage(image))
try
{
Matrix patternMatrix = new Matrix();
if (this.PatternUnits == SvgCoordinateUnits.ObjectBoundingBox) renderer.Boundable(renderingElement);
float width = this._width.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this);
float height = this._height.ToDeviceValue(renderer, UnitRenderingType.Vertical, this);
Matrix patternMatrix = new Matrix();
// Apply a translate if needed
if (this._x.Value > 0.0f || this._y.Value > 0.0f)
{
patternMatrix.Translate(this._x.ToDeviceValue(renderingElement) + -1.0f, this._y.ToDeviceValue(renderingElement, true) + -1.0f);
float x = this._x.ToDeviceValue(renderer, UnitRenderingType.HorizontalOffset, this);
float y = this._y.ToDeviceValue(renderer, UnitRenderingType.VerticalOffset, this);
patternMatrix.Translate(x + -1.0f, y + -1.0f);
}
else
{
......@@ -137,27 +162,40 @@ namespace Svg
if (this.ViewBox.Height > 0 || this.ViewBox.Width > 0)
{
patternMatrix.Scale(this.Width.ToDeviceValue() / this.ViewBox.Width, this.Height.ToDeviceValue() / this.ViewBox.Height);
patternMatrix.Scale(this.Width.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this) / this.ViewBox.Width,
this.Height.ToDeviceValue(renderer, UnitRenderingType.Vertical, this) / this.ViewBox.Height);
}
renderer.Transform = patternMatrix;
renderer.CompositingQuality = CompositingQuality.HighQuality;
renderer.SmoothingMode = SmoothingMode.AntiAlias;
renderer.PixelOffsetMode = PixelOffsetMode.Half;
Bitmap image = new Bitmap((int)width, (int)height);
using (SvgRenderer iRenderer = SvgRenderer.FromImage(image))
{
iRenderer.Boundable((_patternContentUnits == SvgCoordinateUnits.ObjectBoundingBox) ? new GenericBoundable(0, 0, width, height) : renderer.Boundable());
iRenderer.Transform = patternMatrix;
iRenderer.CompositingQuality = CompositingQuality.HighQuality;
iRenderer.SmoothingMode = SmoothingMode.AntiAlias;
iRenderer.PixelOffsetMode = PixelOffsetMode.Half;
foreach (SvgElement child in this.Children)
{
child.RenderElement(renderer);
child.RenderElement(iRenderer);
}
renderer.Save();
iRenderer.Save();
}
image.Save(string.Format(@"C:\test{0:D3}.png", imgNumber++));
TextureBrush textureBrush = new TextureBrush(image);
return textureBrush;
}
finally
{
if (this.PatternUnits == SvgCoordinateUnits.ObjectBoundingBox) renderer.PopBoundable();
}
}
private static int imgNumber = 0;
......@@ -180,5 +218,10 @@ namespace Svg
return newObj;
}
public SvgCoordinateUnits GetUnits()
{
return _patternUnits;
}
}
}
\ No newline at end of file
......@@ -95,21 +95,23 @@ namespace Svg
Radius = new SvgUnit(SvgUnitType.Percentage, 50F);
}
public override Brush GetBrush(SvgVisualElement renderingElement, float opacity)
public override Brush GetBrush(SvgVisualElement renderingElement, SvgRenderer renderer, float opacity)
{
LoadStops();
var origin = CalculateOrigin(renderingElement);
LoadStops(renderingElement);
var centerPoint = CalculateCenterPoint(renderingElement, origin);
var focalPoint = CalculateFocalPoint(renderingElement, origin);
try
{
if (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox) renderer.Boundable(renderingElement);
var origin = renderer.Boundable().Location;
var centerPoint = CalculateCenterPoint(renderer, origin);
var focalPoint = CalculateFocalPoint(renderer, origin);
var specifiedRadius = CalculateRadius(renderingElement);
var specifiedRadius = CalculateRadius(renderer);
var effectiveRadius = CalculateEffectiveRadius(renderingElement, centerPoint, specifiedRadius);
var brush = new PathGradientBrush(CreateGraphicsPath(origin, centerPoint, effectiveRadius))
{
InterpolationColors = CalculateColorBlend(renderingElement, opacity, specifiedRadius, effectiveRadius),
InterpolationColors = CalculateColorBlend(renderer, opacity, specifiedRadius, effectiveRadius),
CenterPoint = focalPoint
};
......@@ -117,31 +119,31 @@ namespace Svg
return brush;
}
private PointF CalculateOrigin(SvgVisualElement renderingElement)
finally
{
return CalculateBoundable(renderingElement).Location;
if (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox) renderer.PopBoundable();
}
}
private PointF CalculateCenterPoint(ISvgBoundable boundable, PointF origin)
private PointF CalculateCenterPoint(SvgRenderer renderer, PointF origin)
{
var deviceCenterX = origin.X + CenterX.ToDeviceValue(boundable);
var deviceCenterY = origin.Y + CenterY.ToDeviceValue(boundable, true);
var deviceCenterX = origin.X + CenterX.ToDeviceValue(renderer, UnitRenderingType.HorizontalOffset, this);
var deviceCenterY = origin.Y + CenterY.ToDeviceValue(renderer, UnitRenderingType.VerticalOffset, this);
var transformedCenterPoint = TransformPoint(new PointF(deviceCenterX, deviceCenterY));
return transformedCenterPoint;
}
private PointF CalculateFocalPoint(ISvgBoundable boundable, PointF origin)
private PointF CalculateFocalPoint(SvgRenderer renderer, PointF origin)
{
var deviceFocalX = origin.X + FocalX.ToDeviceValue(boundable);
var deviceFocalY = origin.Y + FocalY.ToDeviceValue(boundable, true);
var deviceFocalX = origin.X + FocalX.ToDeviceValue(renderer, UnitRenderingType.HorizontalOffset, this);
var deviceFocalY = origin.Y + FocalY.ToDeviceValue(renderer, UnitRenderingType.VerticalOffset, this);
var transformedFocalPoint = TransformPoint(new PointF(deviceFocalX, deviceFocalY));
return transformedFocalPoint;
}
private float CalculateRadius(ISvgBoundable boundable)
private float CalculateRadius(SvgRenderer renderer)
{
var radius = Radius.ToDeviceValue(boundable);
var radius = Radius.ToDeviceValue(renderer, UnitRenderingType.Other, this);
var transformRadiusVector = TransformVector(new PointF(radius, 0));
var transformedRadius = CalculateLength(transformRadiusVector);
return transformedRadius;
......@@ -191,9 +193,9 @@ namespace Svg
return path;
}
private ColorBlend CalculateColorBlend(SvgVisualElement renderingElement, float opacity, float specifiedRadius, float effectiveRadius)
private ColorBlend CalculateColorBlend(SvgRenderer renderer, float opacity, float specifiedRadius, float effectiveRadius)
{
var colorBlend = GetColorBlend(renderingElement, opacity, true);
var colorBlend = GetColorBlend(renderer, opacity, true);
if (specifiedRadius >= effectiveRadius)
{
......
......@@ -81,9 +81,7 @@ namespace Svg
/// <summary>
/// Gets the <see cref="GraphicsPath"/> for this element.
/// </summary>
public override GraphicsPath Path
{
get
public override GraphicsPath Path(SvgRenderer renderer)
{
if (this._path == null || this.IsPathDirty)
{
......@@ -98,11 +96,6 @@ namespace Svg
}
return _path;
}
protected set
{
_path = value;
}
}
internal void OnPathUpdated()
{
......@@ -124,7 +117,7 @@ namespace Svg
/// <value>The bounds.</value>
public override System.Drawing.RectangleF Bounds
{
get { return this.Path.GetBounds(); }
get { return this.Path(null).GetBounds(); }
}
/// <summary>
......@@ -145,8 +138,8 @@ namespace Svg
{
if (this.Stroke != null)
{
float strokeWidth = this.StrokeWidth.ToDeviceValue(this);
using (Pen pen = new Pen(this.Stroke.GetBrush(this, this.StrokeOpacity), strokeWidth))
float strokeWidth = this.StrokeWidth.ToDeviceValue(renderer, UnitRenderingType.Other, this);
using (Pen pen = new Pen(this.Stroke.GetBrush(this, renderer, this.StrokeOpacity), strokeWidth))
{
if (this.StrokeDashArray != null && this.StrokeDashArray.Count > 0)
{
......@@ -154,25 +147,26 @@ namespace Svg
pen.DashPattern = this.StrokeDashArray.ConvertAll(u => u.Value / ((strokeWidth <= 0) ? 1 : strokeWidth)).ToArray();
}
renderer.DrawPath(pen, this.Path);
var path = this.Path(renderer);
renderer.DrawPath(pen, path);
if (this.MarkerStart != null)
{
SvgMarker marker = this.OwnerDocument.GetElementById<SvgMarker>(this.MarkerStart.ToString());
marker.RenderMarker(renderer, this, Path.PathPoints[0], Path.PathPoints[0], Path.PathPoints[1]);
marker.RenderMarker(renderer, this, path.PathPoints[0], path.PathPoints[0], path.PathPoints[1]);
}
if (this.MarkerMid != 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]);
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]);
}
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]);
marker.RenderMarker(renderer, this, path.PathPoints[path.PathPoints.Length - 1], path.PathPoints[path.PathPoints.Length - 2], path.PathPoints[path.PathPoints.Length - 1]);
}
}
}
......
......@@ -223,8 +223,7 @@ namespace Svg
var lastSegment = segments.Last;
// if the last element is a SvgClosePathSegment the position of the previous element should be used because the position of SvgClosePathSegment is 0,0
if (lastSegment is SvgClosePathSegment)
lastSegment = segments[segments.Count - 2];
if (lastSegment is SvgClosePathSegment) lastSegment = segments.Reverse().OfType<SvgMoveToSegment>().First();
if (isRelativeX)
{
......
......@@ -36,3 +36,5 @@ using System.Runtime.InteropServices;
//[assembly: AssemblyFileVersion("1.0.1.*")]
[assembly: CLSCompliant(true)]
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Svg.UnitTests")]
\ No newline at end of file
......@@ -60,7 +60,7 @@
<DebugSymbols>true</DebugSymbols>
<DocumentationFile>
</DocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>PdbOnly</DebugType>
......@@ -76,12 +76,13 @@
<DocumentationFile>bin\Release\Svg.XML</DocumentationFile>
</PropertyGroup>
<PropertyGroup>
<SignAssembly>True</SignAssembly>
<SignAssembly>false</SignAssembly>
</PropertyGroup>
<PropertyGroup>
<AssemblyOriginatorKeyFile>svgkey.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<ItemGroup>
<Reference Include="PresentationCore" />
<Reference Include="System" />
<Reference Include="System.Drawing" />
<Reference Include="System.Web" />
......@@ -99,6 +100,11 @@
<Compile Include="Clipping and Masking\SvgClipRule.cs" />
<Compile Include="Clipping and Masking\SvgClipPath.cs" />
<Compile Include="Clipping and Masking\SvgMask.cs" />
<Compile Include="DataTypes\ISvgSupportsCoordinateUnits.cs" />
<Compile Include="Painting\GenericBoundable.cs" />
<Compile Include="SvgNodeReader.cs" />
<Compile Include="Css\CssQuery.cs" />
<Compile Include="Css\SvgElementOps.cs" />
<Compile Include="DataTypes\SvgAspectRatioConverter.cs" />
<Compile Include="DataTypes\SvgFontStyle.cs" />
<Compile Include="DataTypes\SvgFontVariant.cs" />
......@@ -117,8 +123,92 @@
<Compile Include="Document Structure\SvgSwitch.cs" />
<Compile Include="Document Structure\SvgTitle.cs" />
<Compile Include="Document Structure\SvgDocumentMetadata.cs" />
<Compile Include="External\ExCSS\IToString.cs" />
<Compile Include="External\ExCSS\Lexer.cs" />
<Compile Include="External\ExCSS\Model\Enumerations.cs" />
<Compile Include="External\ExCSS\Model\Extensions\CharacterExtensions.cs" />
<Compile Include="External\ExCSS\Model\Extensions\StringExtensions.cs" />
<Compile Include="External\ExCSS\Model\FunctionBuffer.cs" />
<Compile Include="External\ExCSS\Model\HtmlEncoding.cs" />
<Compile Include="External\ExCSS\Model\ICssRules.cs" />
<Compile Include="External\ExCSS\Model\ICssSelector.cs" />
<Compile Include="External\ExCSS\Model\IStyleDeclaration.cs" />
<Compile Include="External\ExCSS\Model\ISupportsMedia.cs" />
<Compile Include="External\ExCSS\Model\MediaTypeList.cs" />
<Compile Include="External\ExCSS\Model\Rules\AggregateRule.cs" />
<Compile Include="External\ExCSS\Model\Rules\CharacterSetRule.cs" />
<Compile Include="External\ExCSS\Model\Rules\ConditionalRule.cs" />
<Compile Include="External\ExCSS\Model\Rules\DocumentRule.cs" />
<Compile Include="External\ExCSS\Model\Rules\FontFaceRule.cs" />
<Compile Include="External\ExCSS\Model\Rules\GenericRule.cs" />
<Compile Include="External\ExCSS\Model\Rules\ImportRule.cs" />
<Compile Include="External\ExCSS\Model\Rules\IRuleContainer.cs" />
<Compile Include="External\ExCSS\Model\Rules\KeyframeRule.cs" />
<Compile Include="External\ExCSS\Model\Rules\KeyframesRule.cs" />
<Compile Include="External\ExCSS\Model\Rules\MediaRule.cs" />
<Compile Include="External\ExCSS\Model\Rules\NamespaceRule.cs" />
<Compile Include="External\ExCSS\Model\Rules\PageRule.cs" />
<Compile Include="External\ExCSS\Model\Rules\RuleSet.cs" />
<Compile Include="External\ExCSS\Model\Rules\StyleDeclaration.cs" />
<Compile Include="External\ExCSS\Model\Rules\StyleRule.cs" />
<Compile Include="External\ExCSS\Model\Rules\SupportsRule.cs" />
<Compile Include="External\ExCSS\Model\Selector\AggregateSelectorList.cs" />
<Compile Include="External\ExCSS\Model\Selector\BaseSelector.cs" />
<Compile Include="External\ExCSS\Model\Selector\CombinatorSelector.cs" />
<Compile Include="External\ExCSS\Model\Selector\ComplexSelector.cs" />
<Compile Include="External\ExCSS\Model\Selector\FirstChildSelector.cs" />
<Compile Include="External\ExCSS\Model\Selector\LastChildSelector.cs" />
<Compile Include="External\ExCSS\Model\Selector\MultipleSelectorList.cs" />
<Compile Include="External\ExCSS\Model\Selector\NthChildSelector.cs" />
<Compile Include="External\ExCSS\Model\Selector\NthFirstChildSelector.cs" />
<Compile Include="External\ExCSS\Model\Selector\NthLastChildSelector.cs" />
<Compile Include="External\ExCSS\Model\Selector\NthLastOfTypeSelector.cs" />
<Compile Include="External\ExCSS\Model\Selector\NthOfTypeSelector.cs" />
<Compile Include="External\ExCSS\Model\Selector\SelectorFactory.cs" />
<Compile Include="External\ExCSS\Model\Selector\SelectorList.cs" />
<Compile Include="External\ExCSS\Model\Selector\SimpleSelector.cs" />
<Compile Include="External\ExCSS\Model\Specification.cs" />
<Compile Include="External\ExCSS\Model\TextBlocks\Block.cs" />
<Compile Include="External\ExCSS\Model\TextBlocks\BracketBlock.cs" />
<Compile Include="External\ExCSS\Model\TextBlocks\CharacterBlock.cs" />
<Compile Include="External\ExCSS\Model\TextBlocks\CommentBlock.cs" />
<Compile Include="External\ExCSS\Model\TextBlocks\DelimiterBlock.cs" />
<Compile Include="External\ExCSS\Model\TextBlocks\MatchBlock.cs" />
<Compile Include="External\ExCSS\Model\TextBlocks\NumericBlock.cs" />
<Compile Include="External\ExCSS\Model\TextBlocks\PipeBlock.cs" />
<Compile Include="External\ExCSS\Model\TextBlocks\RangeBlock.cs" />
<Compile Include="External\ExCSS\Model\TextBlocks\SpecialCharacter.cs" />
<Compile Include="External\ExCSS\Model\TextBlocks\StringBlock.cs" />
<Compile Include="External\ExCSS\Model\TextBlocks\SymbolBlock.cs" />
<Compile Include="External\ExCSS\Model\TextBlocks\UnitBlock.cs" />
<Compile Include="External\ExCSS\Model\Values\GenericFunction.cs" />
<Compile Include="External\ExCSS\Model\Values\HtmlColor.cs" />
<Compile Include="External\ExCSS\Model\Values\InheritTerm.cs" />
<Compile Include="External\ExCSS\Model\Values\PrimitiveTerm.cs" />
<Compile Include="External\ExCSS\Model\Values\Property.cs" />
<Compile Include="External\ExCSS\Model\Values\Term.cs" />
<Compile Include="External\ExCSS\Model\Values\TermList.cs" />
<Compile Include="External\ExCSS\Parser.Blocks.cs" />
<Compile Include="External\ExCSS\Parser.cs" />
<Compile Include="External\ExCSS\StyleSheet.cs" />
<Compile Include="External\ExCSS\StylesheetParseError.cs" />
<Compile Include="External\ExCSS\StylesheetReader.cs" />
<Compile Include="Extensibility\SvgForeignObject.cs" />
<Compile Include="Extensions.cs" />
<Compile Include="External\Fizzler\Either.cs" />
<Compile Include="External\Fizzler\HumanReadableSelectorGenerator.cs" />
<Compile Include="External\Fizzler\IElementOps.cs" />
<Compile Include="External\Fizzler\ISelectorGenerator.cs" />
<Compile Include="External\Fizzler\NamespacePrefix.cs" />
<Compile Include="External\Fizzler\Parser.cs" />
<Compile Include="External\Fizzler\Reader.cs" />
<Compile Include="External\Fizzler\Selector.cs" />
<Compile Include="External\Fizzler\SelectorGenerator.cs" />
<Compile Include="External\Fizzler\SelectorGeneratorTee.cs" />
<Compile Include="External\Fizzler\SelectorsCachingCompiler.cs" />
<Compile Include="External\Fizzler\Token.cs" />
<Compile Include="External\Fizzler\Tokener.cs" />
<Compile Include="External\Fizzler\TokenKind.cs" />
<Compile Include="Painting\ISvgBoundable.cs" />
<Compile Include="Painting\SvgDeferredPaintServer.cs" />
<Compile Include="Painting\SvgMarker.cs" />
......@@ -141,6 +231,7 @@
<Compile Include="SvgContentNode.cs" />
<Compile Include="SvgDefinitionDefaults.cs" />
<Compile Include="NonSvgElement.cs" />
<Compile Include="SvgReader.cs" />
<Compile Include="SvgUnknownElement.cs" />
<Compile Include="SvgElementAttribute.cs" />
<Compile Include="SvgExtentions.cs" />
......

Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
# SharpDevelop 4.4
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Svg", "Svg.csproj", "{886A98C5-37C0-4E8B-885E-30C1D2F98B47}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SVGViewer", "..\Samples\SVGViewer\SVGViewer.csproj", "{1B8F3C8A-CCAC-474E-B09D-522FBA93DCFD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Svg.UnitTests", "..\Tests\Svg.UnitTests\Svg.UnitTests.csproj", "{E702EB7D-D01D-438A-BADD-E72D4E49109F}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A417AF1E-BEDB-4715-B4FD-D795579217F9}"
ProjectSection(SolutionItems) = preProject
Local.testsettings = Local.testsettings
Svg.vsmdi = Svg.vsmdi
TraceAndTestImpact.testsettings = TraceAndTestImpact.testsettings
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SvgW3CTestRunner", "..\Tests\SvgW3CTestRunner\SvgW3CTestRunner.csproj", "{8ED74C39-6CFF-421E-952A-30F9E6957108}"
EndProject
Global
GlobalSection(TestCaseManagementSettings) = postSolution
CategoryFile = Svg.vsmdi
EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|Mixed Platforms = Debug|Mixed Platforms
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|Mixed Platforms = Release|Mixed Platforms
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{886A98C5-37C0-4E8B-885E-30C1D2F98B47}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{886A98C5-37C0-4E8B-885E-30C1D2F98B47}.Debug|Any CPU.Build.0 = Debug|Any CPU
{886A98C5-37C0-4E8B-885E-30C1D2F98B47}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{886A98C5-37C0-4E8B-885E-30C1D2F98B47}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{886A98C5-37C0-4E8B-885E-30C1D2F98B47}.Debug|x86.ActiveCfg = Debug|Any CPU
{886A98C5-37C0-4E8B-885E-30C1D2F98B47}.Release|Any CPU.ActiveCfg = Release|Any CPU
{886A98C5-37C0-4E8B-885E-30C1D2F98B47}.Release|Any CPU.Build.0 = Release|Any CPU
{886A98C5-37C0-4E8B-885E-30C1D2F98B47}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{886A98C5-37C0-4E8B-885E-30C1D2F98B47}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{886A98C5-37C0-4E8B-885E-30C1D2F98B47}.Release|x86.ActiveCfg = Release|Any CPU
{1B8F3C8A-CCAC-474E-B09D-522FBA93DCFD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1B8F3C8A-CCAC-474E-B09D-522FBA93DCFD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1B8F3C8A-CCAC-474E-B09D-522FBA93DCFD}.Debug|x86.Build.0 = Debug|Any CPU
{1B8F3C8A-CCAC-474E-B09D-522FBA93DCFD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1B8F3C8A-CCAC-474E-B09D-522FBA93DCFD}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
{1B8F3C8A-CCAC-474E-B09D-522FBA93DCFD}.Debug|Mixed Platforms.Build.0 = Debug|x86
{1B8F3C8A-CCAC-474E-B09D-522FBA93DCFD}.Debug|x86.ActiveCfg = Debug|x86
{1B8F3C8A-CCAC-474E-B09D-522FBA93DCFD}.Release|Any CPU.Build.0 = Release|Any CPU
{1B8F3C8A-CCAC-474E-B09D-522FBA93DCFD}.Debug|x86.Build.0 = Debug|Any CPU
{1B8F3C8A-CCAC-474E-B09D-522FBA93DCFD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1B8F3C8A-CCAC-474E-B09D-522FBA93DCFD}.Release|x86.Build.0 = Release|Any CPU
{1B8F3C8A-CCAC-474E-B09D-522FBA93DCFD}.Release|Any CPU.Build.0 = Release|Any CPU
{1B8F3C8A-CCAC-474E-B09D-522FBA93DCFD}.Release|Mixed Platforms.ActiveCfg = Release|x86
{1B8F3C8A-CCAC-474E-B09D-522FBA93DCFD}.Release|Mixed Platforms.Build.0 = Release|x86
{1B8F3C8A-CCAC-474E-B09D-522FBA93DCFD}.Release|x86.ActiveCfg = Release|Any CPU
{1B8F3C8A-CCAC-474E-B09D-522FBA93DCFD}.Release|x86.Build.0 = Release|Any CPU
{E702EB7D-D01D-438A-BADD-E72D4E49109F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E702EB7D-D01D-438A-BADD-E72D4E49109F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E702EB7D-D01D-438A-BADD-E72D4E49109F}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{E702EB7D-D01D-438A-BADD-E72D4E49109F}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{E702EB7D-D01D-438A-BADD-E72D4E49109F}.Debug|x86.ActiveCfg = Debug|Any CPU
{E702EB7D-D01D-438A-BADD-E72D4E49109F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E702EB7D-D01D-438A-BADD-E72D4E49109F}.Release|Any CPU.Build.0 = Release|Any CPU
{E702EB7D-D01D-438A-BADD-E72D4E49109F}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{E702EB7D-D01D-438A-BADD-E72D4E49109F}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{E702EB7D-D01D-438A-BADD-E72D4E49109F}.Release|x86.ActiveCfg = Release|Any CPU
{8ED74C39-6CFF-421E-952A-30F9E6957108}.Debug|Any CPU.ActiveCfg = Debug|x86
{8ED74C39-6CFF-421E-952A-30F9E6957108}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
{8ED74C39-6CFF-421E-952A-30F9E6957108}.Debug|Mixed Platforms.Build.0 = Debug|x86
{8ED74C39-6CFF-421E-952A-30F9E6957108}.Debug|x86.ActiveCfg = Debug|x86
{8ED74C39-6CFF-421E-952A-30F9E6957108}.Debug|x86.Build.0 = Debug|x86
{8ED74C39-6CFF-421E-952A-30F9E6957108}.Release|Any CPU.ActiveCfg = Release|x86
{8ED74C39-6CFF-421E-952A-30F9E6957108}.Release|Mixed Platforms.ActiveCfg = Release|x86
{8ED74C39-6CFF-421E-952A-30F9E6957108}.Release|Mixed Platforms.Build.0 = Release|x86
{8ED74C39-6CFF-421E-952A-30F9E6957108}.Release|x86.ActiveCfg = Release|x86
{8ED74C39-6CFF-421E-952A-30F9E6957108}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
......
<?xml version="1.0" encoding="UTF-8"?>
<TestLists xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
<TestList name="Lists of Tests" id="8c43106b-9dc1-4907-a29f-aa66a61bf5b6">
<RunConfiguration id="616d39c5-86e4-40f8-97e9-b8a423ce7322" name="Local" storage="local.testsettings" type="Microsoft.VisualStudio.TestTools.Common.TestRunConfiguration, Microsoft.VisualStudio.QualityTools.Common, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
</TestList>
</TestLists>
\ No newline at end of file
......@@ -63,9 +63,12 @@ namespace Svg
/// <returns>The attribute value if available; otherwise the ancestors value for the same attribute; otherwise the default value of <typeparamref name="TAttributeType"/>.</returns>
public TAttributeType GetInheritedAttribute<TAttributeType>(string attributeName)
{
if (this.ContainsKey(attributeName) /*&& base[attributeName] != null*/)
if (this.ContainsKey(attributeName) && !IsInheritValue(base[attributeName]))
{
return (TAttributeType)base[attributeName];
var result = (TAttributeType)base[attributeName];
var deferred = result as SvgDeferredPaintServer;
if (deferred != null) deferred.EnsureServer(_owner);
return result;
}
if (this._owner.Parent != null)
......@@ -79,6 +82,16 @@ namespace Svg
return default(TAttributeType);
}
private bool IsInheritValue(object value)
{
return (value == null ||
(value is SvgFontStyle && (SvgFontStyle)value == SvgFontStyle.inherit) ||
(value is SvgFontWeight && (SvgFontWeight)value == SvgFontWeight.inherit) ||
(value is SvgTextAnchor && (SvgTextAnchor)value == SvgTextAnchor.inherit) ||
(value == "inherit")
);
}
/// <summary>
/// Gets the attribute with the specified name.
/// </summary>
......
......@@ -9,6 +9,8 @@ using System.IO;
using System.Text;
using System.Xml;
using System.Linq;
using ExCSS;
using Svg.Css;
namespace Svg
{
......@@ -28,6 +30,8 @@ namespace Svg
Ppi = PointsPerInch;
}
public Uri BaseUri { get; set; }
/// <summary>
/// Gets an <see cref="SvgElementIdManager"/> for this document.
/// </summary>
......@@ -155,7 +159,9 @@ namespace Svg
throw new FileNotFoundException("The specified document cannot be found.", path);
}
return Open<T>(File.OpenRead(path), entities);
var doc = Open<T>(File.OpenRead(path), entities);
doc.BaseUri = new Uri(System.IO.Path.GetFullPath(path));
return doc;
}
/// <summary>
......@@ -167,6 +173,27 @@ namespace Svg
return Open<T>(stream, null);
}
/// <summary>
/// Attempts to create an SVG document from the specified string data.
/// </summary>
/// <param name="svg">The SVG data.</param>
public static T FromSvg<T>(string svg) where T : SvgDocument, new()
{
if (string.IsNullOrEmpty(svg))
{
throw new ArgumentNullException("svg");
}
using (var strReader = new System.IO.StringReader(svg))
{
var reader = new SvgTextReader(strReader, null);
reader.XmlResolver = new SvgDtdResolver();
reader.WhitespaceHandling = WhitespaceHandling.None;
return Open<T>(reader);
}
}
/// <summary>
/// Opens an SVG document from the specified <see cref="Stream"/> and adds the specified entities.
/// </summary>
......@@ -180,17 +207,22 @@ namespace Svg
throw new ArgumentNullException("stream");
}
//Trace.TraceInformation("Begin Read");
// Don't close the stream via a dispose: that is the client's job.
var reader = new SvgTextReader(stream, entities);
reader.XmlResolver = new SvgDtdResolver();
reader.WhitespaceHandling = WhitespaceHandling.None;
return Open<T>(reader);
}
using (var reader = new SvgTextReader(stream, entities))
private static T Open<T>(XmlReader reader) where T : SvgDocument, new()
{
var elementStack = new Stack<SvgElement>();
bool elementEmpty;
SvgElement element = null;
SvgElement parent;
T svgDocument = null;
reader.XmlResolver = new SvgDtdResolver();
reader.WhitespaceHandling = WhitespaceHandling.None;
var styles = new List<ISvgNode>();
while (reader.Read())
{
......@@ -247,6 +279,12 @@ namespace Svg
{
element.Nodes.Clear(); // No sense wasting the space where it isn't needed
}
var unknown = element as SvgUnknownElement;
if (unknown != null && unknown.ElementName == "style")
{
styles.Add(unknown);
}
break;
case XmlNodeType.CDATA:
case XmlNodeType.Text:
......@@ -266,9 +304,52 @@ namespace Svg
}
}
//Trace.TraceInformation("End Read");
if (styles.Any())
{
var cssTotal = styles.Select((s) => s.Content).Aggregate((p, c) => p + Environment.NewLine + c);
var cssParser = new Parser();
var sheet = cssParser.Parse(cssTotal);
AggregateSelectorList aggList;
IEnumerable<BaseSelector> selectors;
IEnumerable<SvgElement> elemsToStyle;
foreach (var rule in sheet.StyleRules)
{
aggList = rule.Selector as AggregateSelectorList;
if (aggList != null && aggList.Delimiter == ",")
{
selectors = aggList;
}
else
{
selectors = Enumerable.Repeat(rule.Selector, 1);
}
foreach (var selector in selectors)
{
elemsToStyle = svgDocument.QuerySelectorAll(rule.Selector.ToString());
foreach (var elem in elemsToStyle)
{
foreach (var decl in rule.Declarations)
{
elem.AddStyle(decl.Name, decl.Term.ToString(), rule.Selector.GetSpecificity());
}
}
}
}
}
if (svgDocument != null) FlushStyles(svgDocument);
return svgDocument;
}
private static void FlushStyles(SvgElement elem)
{
elem.FlushStyles();
foreach (var child in elem.Children)
{
FlushStyles(child);
}
}
/// <summary>
......@@ -283,10 +364,8 @@ namespace Svg
throw new ArgumentNullException("document");
}
using (var stream = new MemoryStream(UTF8Encoding.Default.GetBytes(document.InnerXml)))
{
return Open<SvgDocument>(stream, null);
}
var reader = new SvgNodeReader(document.DocumentElement, null);
return Open<SvgDocument>(reader);
}
public static Bitmap OpenAsBitmap(string path)
......@@ -311,6 +390,7 @@ namespace Svg
throw new ArgumentNullException("renderer");
}
renderer.Boundable(this);
this.Render(renderer);
}
......@@ -326,7 +406,9 @@ namespace Svg
throw new ArgumentNullException("graphics");
}
this.Render(SvgRenderer.FromGraphics(graphics));
var renderer = SvgRenderer.FromGraphics(graphics);
renderer.Boundable(this);
this.Render(renderer);
}
/// <summary>
......@@ -365,6 +447,7 @@ namespace Svg
{
using (var renderer = SvgRenderer.FromImage(bitmap))
{
renderer.Boundable(this);
renderer.TextRenderingHint = TextRenderingHint.AntiAlias;
renderer.TextContrast = 1;
renderer.PixelOffsetMode = PixelOffsetMode.Half;
......@@ -395,7 +478,7 @@ namespace Svg
public void Write(string path)
{
using(var fs = new FileStream(path, FileMode.Create, FileAccess.Write))
using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write))
{
this.Write(fs);
}
......
......@@ -40,10 +40,34 @@ namespace Svg
private EventHandlerList _eventHandlers;
private SvgElementCollection _children;
private static readonly object _loadEventKey = new object();
private Region _graphicsClip;
private Matrix _graphicsMatrix;
private SvgCustomAttributeCollection _customAttributes;
private List<ISvgNode> _nodes = new List<ISvgNode>();
private Dictionary<string, SortedDictionary<int, string>> _styles = new Dictionary<string, SortedDictionary<int, string>>();
public void AddStyle(string name, string value, int specificity)
{
SortedDictionary<int, string> rules;
if (!_styles.TryGetValue(name, out rules))
{
rules = new SortedDictionary<int, string>();
_styles[name] = rules;
}
while (rules.ContainsKey(specificity)) specificity++;
rules[specificity] = value;
}
public void FlushStyles()
{
foreach (var s in _styles)
{
SvgElementFactory.SetPropertyValue(this, s.Key, s.Value.Last().Value, this.OwnerDocument);
}
_styles = null;
}
/// <summary>
/// Gets the name of the element.
/// </summary>
......@@ -66,6 +90,16 @@ namespace Svg
internal set { this._elementName = value; }
}
/// <summary>
/// Gets or sets the color <see cref="SvgPaintServer"/> of this element which drives the currentColor property.
/// </summary>
[SvgAttribute("color")]
public virtual SvgPaintServer Color
{
get { return (this.Attributes["color"] == null) ? SvgColourServer.NotSet : (SvgPaintServer)this.Attributes["color"]; }
set { this.Attributes["color"] = value; }
}
/// <summary>
/// Gets or sets the content of the element.
/// </summary>
......@@ -224,15 +258,17 @@ namespace Svg
/// Applies the required transforms to <see cref="SvgRenderer"/>.
/// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> to be transformed.</param>
protected internal virtual void PushTransforms(SvgRenderer renderer)
protected internal virtual bool PushTransforms(SvgRenderer renderer)
{
_graphicsMatrix = renderer.Transform;
_graphicsClip = renderer.Clip;
// Return if there are no transforms
if (this.Transforms == null || this.Transforms.Count == 0)
{
return;
return true;
}
if (this.Transforms.Count == 1 && this.Transforms[0].Matrix.Equals(new Matrix(0, 0, 0, 0, 0, 0))) return false;
Matrix transformMatrix = renderer.Transform;
......@@ -242,6 +278,8 @@ namespace Svg
}
renderer.Transform = transformMatrix;
return true;
}
/// <summary>
......@@ -252,6 +290,8 @@ namespace Svg
{
renderer.Transform = _graphicsMatrix;
_graphicsMatrix = null;
renderer.SetClip(_graphicsClip);
_graphicsClip = null;
}
/// <summary>
......@@ -636,7 +676,7 @@ namespace Svg
{
if(!(child is SvgGroup))
{
var childPath = ((SvgVisualElement)child).Path;
var childPath = ((SvgVisualElement)child).Path(null);
if (childPath != null)
{
......@@ -649,7 +689,7 @@ namespace Svg
}
}
AddPaths(child, path);
if (!(child is SvgPaintServer)) AddPaths(child, path);
}
}
......@@ -658,7 +698,7 @@ namespace Svg
/// </summary>
/// <param name="elem"></param>
/// <param name="path"></param>
protected GraphicsPath GetPaths(SvgElement elem)
protected GraphicsPath GetPaths(SvgElement elem, SvgRenderer renderer)
{
var ret = new GraphicsPath();
......@@ -668,7 +708,7 @@ namespace Svg
{
if(!(child is SvgGroup))
{
var childPath = ((SvgVisualElement)child).Path;
var childPath = ((SvgVisualElement)child).Path(renderer);
if (childPath != null)
{
......@@ -681,7 +721,7 @@ namespace Svg
}
else
{
var childPath = GetPaths(child);
var childPath = GetPaths(child, renderer);
if(child.Transforms != null)
childPath.Transform(child.Transforms.GetMatrix());
}
......
......@@ -5,6 +5,7 @@ using System.Xml;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using ExCSS;
namespace Svg
{
......@@ -15,11 +16,12 @@ namespace Svg
{
private static List<ElementInfo> availableElements;
private const string svgNS = "http://www.w3.org/2000/svg";
private static Parser cssParser = new Parser();
/// <summary>
/// Gets a list of available types that can be used when creating an <see cref="SvgElement"/>.
/// </summary>
private static List<ElementInfo> AvailableElements
public static List<ElementInfo> AvailableElements
{
get
{
......@@ -43,7 +45,7 @@ namespace Svg
/// <param name="reader">The <see cref="XmlTextReader"/> containing the node to parse into an <see cref="SvgDocument"/>.</param>
/// <exception cref="ArgumentNullException">The <paramref name="reader"/> parameter cannot be <c>null</c>.</exception>
/// <exception cref="InvalidOperationException">The CreateDocument method can only be used to parse root &lt;svg&gt; elements.</exception>
public static T CreateDocument<T>(XmlTextReader reader) where T : SvgDocument, new()
public static T CreateDocument<T>(XmlReader reader) where T : SvgDocument, new()
{
if (reader == null)
{
......@@ -64,7 +66,7 @@ namespace Svg
/// <param name="reader">The <see cref="XmlTextReader"/> containing the node to parse into a subclass of <see cref="SvgElement"/>.</param>
/// <param name="document">The <see cref="SvgDocument"/> that the created element belongs to.</param>
/// <exception cref="ArgumentNullException">The <paramref name="reader"/> and <paramref name="document"/> parameters cannot be <c>null</c>.</exception>
public static SvgElement CreateElement(XmlTextReader reader, SvgDocument document)
public static SvgElement CreateElement(XmlReader reader, SvgDocument document)
{
if (reader == null)
{
......@@ -74,7 +76,7 @@ namespace Svg
return CreateElement<SvgDocument>(reader, false, document);
}
private static SvgElement CreateElement<T>(XmlTextReader reader, bool fragmentIsDocument, SvgDocument document) where T : SvgDocument, new()
private static SvgElement CreateElement<T>(XmlReader reader, bool fragmentIsDocument, SvgDocument document) where T : SvgDocument, new()
{
SvgElement createdElement = null;
string elementName = reader.LocalName;
......@@ -118,58 +120,114 @@ namespace Svg
return createdElement;
}
private static void SetAttributes(SvgElement element, XmlTextReader reader, SvgDocument document)
private static void SetAttributes(SvgElement element, XmlReader reader, SvgDocument document)
{
//Trace.TraceInformation("Begin SetAttributes");
string[] styles = null;
string[] style = null;
int i = 0;
//string[] styles = null;
//string[] style = null;
//int i = 0;
while (reader.MoveToNextAttribute())
{
// Special treatment for "style"
if (reader.LocalName.Equals("style") && !(element is NonSvgElement))
{
styles = reader.Value.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
for (i = 0; i < styles.Length; i++)
var inlineSheet = cssParser.Parse("#a{" + reader.Value + "}");
foreach (var rule in inlineSheet.StyleRules)
{
if (!styles[i].Contains(":"))
foreach (var decl in rule.Declarations)
{
continue;
element.AddStyle(decl.Name, decl.Term.ToString(), 1 << 16);
}
style = styles[i].Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);
SetPropertyValue(element, style[0].Trim(), style[1].Trim(), document);
}
//defaults for text can come from the document
if (element.ElementName == "text")
{
if (!styles.Contains("font-size") && document.CustomAttributes.ContainsKey("font-size") && document.CustomAttributes["font-size"] != null)
{
SetPropertyValue(element, "font-size", document.CustomAttributes["font-size"], document);
}
if (!styles.Contains("font-family") && document.CustomAttributes.ContainsKey("font-family") && document.CustomAttributes["font-family"] != null)
else if (IsStyleAttribute(reader.LocalName))
{
SetPropertyValue(element, "font-family", document.CustomAttributes["font-family"], document);
element.AddStyle(reader.LocalName, reader.Value, 2 << 16);
}
else
{
SetPropertyValue(element, reader.LocalName, reader.Value, document);
}
continue;
}
SetPropertyValue(element, reader.LocalName, reader.Value, document);
//Trace.TraceInformation("End SetAttributes");
}
//Trace.TraceInformation("End SetAttributes");
private static bool IsStyleAttribute(string name)
{
switch (name)
{
case "alignment-baseline":
case "baseline-shift":
case "clip":
case "clip-path":
case "clip-rule":
case "color":
case "color-interpolation":
case "color-interpolation-filters":
case "color-profile":
case "color-rendering":
case "cursor":
case "direction":
case "display":
case "dominant-baseline":
case "enable-background":
case "fill":
case "fill-opacity":
case "fill-rule":
case "filter":
case "flood-color":
case "flood-opacity":
case "font":
case "font-family":
case "font-size":
case "font-size-adjust":
case "font-stretch":
case "font-style":
case "font-variant":
case "font-weight":
case "glyph-orientation-horizontal":
case "glyph-orientation-vertical":
case "image-rendering":
case "kerning":
case "letter-spacing":
case "lighting-color":
case "marker":
case "marker-end":
case "marker-mid":
case "marker-start":
case "mask":
case "opacity":
case "overflow":
case "pointer-events":
case "shape-rendering":
case "stop-color":
case "stop-opacity":
case "stroke":
case "stroke-dasharray":
case "stroke-dashoffset":
case "stroke-linecap":
case "stroke-linejoin":
case "stroke-miterlimit":
case "stroke-opacity":
case "stroke-width":
case "text-anchor":
case "text-decoration":
case "text-rendering":
case "unicode-bidi":
case "visibility":
case "word-spacing":
case "writing-mode":
return true;
}
return false;
}
private static Dictionary<Type, Dictionary<string, PropertyDescriptorCollection>> _propertyDescriptors = new Dictionary<Type, Dictionary<string, PropertyDescriptorCollection>>();
private static object syncLock = new object();
private static void SetPropertyValue(SvgElement element, string attributeName, string attributeValue, SvgDocument document)
internal static void SetPropertyValue(SvgElement element, string attributeName, string attributeValue, SvgDocument document)
{
var elementType = element.GetType();
......
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.IO;
using System.Collections.Specialized;
namespace Svg
{
internal sealed class SvgNodeReader : XmlNodeReader
{
private Dictionary<string, string> _entities;
private string _value;
private bool _customValue = false;
private string _localName;
public SvgNodeReader(XmlNode node, Dictionary<string, string> entities)
: base(node)
{
this._entities = entities;
}
/// <summary>
/// Gets the text value of the current node.
/// </summary>
/// <value></value>
/// <returns>The value returned depends on the <see cref="P:System.Xml.XmlTextReader.NodeType"/> of the node. The following table lists node types that have a value to return. All other node types return String.Empty.Node Type Value AttributeThe value of the attribute. CDATAThe content of the CDATA section. CommentThe content of the comment. DocumentTypeThe internal subset. ProcessingInstructionThe entire content, excluding the target. SignificantWhitespaceThe white space within an xml:space= 'preserve' scope. TextThe content of the text node. WhitespaceThe white space between markup. XmlDeclarationThe content of the declaration. </returns>
public override string Value
{
get
{
return (this._customValue) ? this._value : base.Value;
}
}
/// <summary>
/// Gets the local name of the current node.
/// </summary>
/// <value></value>
/// <returns>The name of the current node with the prefix removed. For example, LocalName is book for the element &lt;bk:book&gt;.For node types that do not have a name (like Text, Comment, and so on), this property returns String.Empty.</returns>
public override string LocalName
{
get
{
return (this._customValue) ? this._localName : base.LocalName;
}
}
private IDictionary<string, string> Entities
{
get
{
if (this._entities == null)
{
this._entities = new Dictionary<string, string>();
}
return this._entities;
}
}
/// <summary>
/// Moves to the next attribute.
/// </summary>
/// <returns>
/// true if there is a next attribute; false if there are no more attributes.
/// </returns>
public override bool MoveToNextAttribute()
{
bool moved = base.MoveToNextAttribute();
if (moved)
{
this._localName = base.LocalName;
if (this.ReadAttributeValue())
{
if (this.NodeType == XmlNodeType.EntityReference)
{
this.ResolveEntity();
}
else
{
this._value = base.Value;
}
}
this._customValue = true;
}
return moved;
}
/// <summary>
/// Reads the next node from the stream.
/// </summary>
/// <returns>
/// true if the next node was read successfully; false if there are no more nodes to read.
/// </returns>
/// <exception cref="T:System.Xml.XmlException">An error occurred while parsing the XML. </exception>
public override bool Read()
{
this._customValue = false;
bool read = base.Read();
if (this.NodeType == XmlNodeType.DocumentType)
{
this.ParseEntities();
}
return read;
}
private void ParseEntities()
{
const string entityText = "<!ENTITY";
string[] entities = this.Value.Split(new string[]{entityText}, StringSplitOptions.None);
string[] parts = null;
string name = null;
string value = null;
foreach (string entity in entities)
{
if (string.IsNullOrEmpty(entity.Trim()))
{
continue;
}
parts = entity.Trim().Split(new char[]{' ', '\t'}, StringSplitOptions.RemoveEmptyEntries);
name = parts[0];
value = parts[1].Split(new char[] { this.QuoteChar }, StringSplitOptions.RemoveEmptyEntries)[0];
this.Entities.Add(name, value);
}
}
/// <summary>
/// Resolves the entity reference for EntityReference nodes.
/// </summary>
public override void ResolveEntity()
{
if (this.NodeType == XmlNodeType.EntityReference)
{
if (this._entities.ContainsKey(this.Name))
{
this._value = this._entities[this.Name];
}
else
{
this._value = string.Empty;
}
this._customValue = true;
}
}
}
}
\ No newline at end of file
......@@ -8,9 +8,26 @@ using System.Drawing.Text;
namespace Svg
{
/// <summary>
/// Convenience wrapper around a graphics object
/// </summary>
public sealed class SvgRenderer : IDisposable
{
private Graphics _innerGraphics;
private Stack<ISvgBoundable> _boundables = new Stack<ISvgBoundable>();
public void Boundable(ISvgBoundable boundable)
{
_boundables.Push(boundable);
}
public ISvgBoundable Boundable()
{
return _boundables.Peek();
}
public ISvgBoundable PopBoundable()
{
return _boundables.Pop();
}
/// <summary>
/// Initializes a new instance of the <see cref="SvgRenderer"/> class.
......@@ -47,6 +64,14 @@ namespace Svg
return renderer;
}
public static SvgRenderer FromNull()
{
SvgRenderer renderer = new SvgRenderer();
var img = new Bitmap(1, 1);
renderer._innerGraphics = Graphics.FromImage(img);
return renderer;
}
public void DrawImageUnscaled(Image image, Point location)
{
this._innerGraphics.DrawImageUnscaled(image, location);
......@@ -57,9 +82,13 @@ namespace Svg
_innerGraphics.DrawImage(image, destRect, srcRect, graphicsUnit);
}
public void AddClip(Region region)
{
this._innerGraphics.SetClip(region, CombineMode.Intersect);
}
public void SetClip(Region region)
{
this._innerGraphics.SetClip(region, CombineMode.Complement);
this._innerGraphics.SetClip(region, CombineMode.Replace);
}
public void FillPath(Brush brush, GraphicsPath path)
......@@ -151,9 +180,10 @@ namespace Svg
public SizeF MeasureString(string text, Font font)
{
var ff = font.FontFamily;
float lineSpace = ff.GetLineSpacing(font.Style);
//Baseline calculation to match http://bobpowell.net/formattingtext.aspx
float ascent = ff.GetCellAscent(font.Style);
float baseline = font.GetHeight(this._innerGraphics) * ascent / lineSpace;
float baselineOffset = font.SizeInPoints / ff.GetEmHeight(font.Style) * ascent;
float baselineOffsetPixels = this._innerGraphics.DpiY / 72f * baselineOffset;
StringFormat format = StringFormat.GenericTypographic;
format.SetMeasurableCharacterRanges(new CharacterRange[]{new CharacterRange(0, text.Length)});
......@@ -161,7 +191,7 @@ namespace Svg
Region[] r = this._innerGraphics.MeasureCharacterRanges(text, font, new Rectangle(0, 0, 1000, 1000), format);
RectangleF rect = r[0].GetBounds(this._innerGraphics);
return new SizeF(rect.Width, baseline);
return new SizeF(rect.Width, baselineOffsetPixels);
}
}
}
\ No newline at end of file
......@@ -18,7 +18,14 @@ namespace Svg
public SvgTextReader(Stream stream, Dictionary<string, string> entities)
: base(stream)
{
this.EntityHandling = EntityHandling.ExpandCharEntities;
this.EntityHandling = EntityHandling.ExpandEntities;
this._entities = entities;
}
public SvgTextReader(TextReader reader, Dictionary<string, string> entities)
: base(reader)
{
this.EntityHandling = EntityHandling.ExpandEntities;
this._entities = entities;
}
......@@ -119,6 +126,7 @@ namespace Svg
string[] parts = null;
string name = null;
string value = null;
int quoteIndex;
foreach (string entity in entities)
{
......@@ -127,13 +135,16 @@ namespace Svg
continue;
}
parts = entity.Trim().Split(new char[]{' ', '\t'}, StringSplitOptions.RemoveEmptyEntries);
name = parts[0];
value = parts[1].Split(new char[] { this.QuoteChar }, StringSplitOptions.RemoveEmptyEntries)[0];
name = entity.Trim();
quoteIndex = name.IndexOf(this.QuoteChar);
if (quoteIndex > 0)
{
value = name.Substring(quoteIndex + 1, name.LastIndexOf(this.QuoteChar) - quoteIndex - 1);
name = name.Substring(0, quoteIndex).Trim();
this.Entities.Add(name, value);
}
}
}
/// <summary>
/// Resolves the entity reference for EntityReference nodes.
......
......@@ -12,6 +12,7 @@ namespace Svg
[TypeConverter(typeof(SvgTextAnchorConverter))]
public enum SvgTextAnchor
{
inherit,
/// <summary>
/// The rendered characters are aligned such that the start of the text string is at the initial current text position.
/// </summary>
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment