Commit 7c70bd11 authored by Eric Domke's avatar Eric Domke
Browse files

Text on a Path & SVG Fonts

- Extraction interface for SvgRenderer
- Initial support for Text on a Path
- Initial support for Svg Fonts
- Support for symbol element
- Minor bug fixes with image pattern rendering
- Additional support for Text whitespace modes
parent 1a00f391
......@@ -98,7 +98,7 @@ namespace Svg
/// <summary>
/// Gets the <see cref="GraphicsPath"/> representing this element.
/// </summary>
public override GraphicsPath Path(SvgRenderer renderer)
public override GraphicsPath Path(ISvgRenderer renderer)
{
if (this._path == null || this.IsPathDirty)
{
......@@ -117,7 +117,7 @@ namespace Svg
/// Renders the circle to the specified <see cref="Graphics"/> object.
/// </summary>
/// <param name="graphics">The graphics object.</param>
protected override void Render(SvgRenderer renderer)
protected override void Render(ISvgRenderer renderer)
{
// Don't draw if there is no radius set
if (this.Radius.Value > 0.0f)
......
......@@ -102,7 +102,7 @@ namespace Svg
/// Gets the <see cref="GraphicsPath"/> for this element.
/// </summary>
/// <value></value>
public override GraphicsPath Path(SvgRenderer renderer)
public override GraphicsPath Path(ISvgRenderer renderer)
{
if (this._path == null || this.IsPathDirty)
{
......@@ -122,7 +122,7 @@ namespace Svg
/// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="Graphics"/> object.
/// </summary>
/// <param name="graphics">The <see cref="Graphics"/> object to render to.</param>
protected override void Render(SvgRenderer renderer)
protected override void Render(ISvgRenderer renderer)
{
if (this._radiusX.Value > 0.0f && this._radiusY.Value > 0.0f)
{
......
......@@ -94,7 +94,7 @@ namespace Svg
/// <summary>
/// Gets the <see cref="GraphicsPath"/> for this element.
/// </summary>
public override GraphicsPath Path(SvgRenderer renderer)
public override GraphicsPath Path(ISvgRenderer renderer)
{
return null;
}
......@@ -102,7 +102,7 @@ namespace Svg
/// <summary>
/// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="Graphics"/> object.
/// </summary>
protected override void Render(SvgRenderer renderer)
protected override void Render(ISvgRenderer renderer)
{
if (!Visible || !Displayable)
return;
......@@ -120,7 +120,7 @@ namespace Svg
RectangleF destRect = destClip;
this.PushTransforms(renderer);
renderer.AddClip(new Region(destClip));
renderer.SetClip(new Region(destClip), CombineMode.Intersect);
this.SetClip(renderer);
if (AspectRatio != null && AspectRatio.Align != SvgPreserveAspectRatio.none)
......@@ -223,6 +223,7 @@ namespace Svg
if (uri.LocalPath.EndsWith(".svg", StringComparison.InvariantCultureIgnoreCase))
{
var doc = SvgDocument.Open<SvgDocument>(ms);
doc.BaseUri = uri;
return doc.Draw();
}
else
......
......@@ -92,7 +92,7 @@ namespace Svg
{
}
public override System.Drawing.Drawing2D.GraphicsPath Path(SvgRenderer renderer)
public override System.Drawing.Drawing2D.GraphicsPath Path(ISvgRenderer renderer)
{
if (this._path == null || this.IsPathDirty)
{
......
......@@ -32,7 +32,7 @@ namespace Svg
get { return true; }
}
public override GraphicsPath Path(SvgRenderer renderer)
public override GraphicsPath Path(ISvgRenderer renderer)
{
if (this._path == null || this.IsPathDirty)
{
......
......@@ -14,7 +14,7 @@ namespace Svg
public class SvgPolyline : SvgPolygon
{
private GraphicsPath _Path;
public override GraphicsPath Path(SvgRenderer renderer)
public override GraphicsPath Path(ISvgRenderer renderer)
{
if (_Path == null || this.IsPathDirty)
{
......
......@@ -174,7 +174,7 @@ namespace Svg
/// <summary>
/// Gets the <see cref="GraphicsPath"/> for this element.
/// </summary>
public override GraphicsPath Path(SvgRenderer renderer)
public override GraphicsPath Path(ISvgRenderer renderer)
{
if (_path == null || IsPathDirty)
{
......@@ -261,7 +261,7 @@ namespace Svg
/// <summary>
/// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="Graphics"/> object.
/// </summary>
protected override void Render(SvgRenderer renderer)
protected override void Render(ISvgRenderer renderer)
{
if (Width.Value > 0.0f && Height.Value > 0.0f)
{
......
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Diagnostics;
namespace Svg
{
......@@ -16,7 +17,7 @@ namespace Svg
/// <summary>
/// Gets the <see cref="GraphicsPath"/> for this element.
/// </summary>
public abstract GraphicsPath Path(SvgRenderer renderer);
public abstract GraphicsPath Path(ISvgRenderer renderer);
PointF ISvgBoundable.Location
{
......@@ -104,13 +105,13 @@ namespace Svg
/// <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>
protected override void Render(SvgRenderer renderer)
/// <param name="renderer">The <see cref="ISvgRenderer"/> object to render to.</param>
protected override void Render(ISvgRenderer renderer)
{
this.Render(renderer, true);
}
private void Render(SvgRenderer renderer, bool renderFilter)
private void Render(ISvgRenderer renderer, bool renderFilter)
{
if (this.Visible && this.Displayable && this.PushTransforms(renderer) &&
(!Renderable || this.Path(renderer) != null))
......@@ -119,11 +120,20 @@ namespace Svg
if (renderFilter && this.Filter != null)
{
var filter = this.OwnerDocument.IdManager.GetElementById(this.Filter) as FilterEffects.SvgFilter;
var filterPath = this.Filter;
if (filterPath.ToString().StartsWith("url("))
{
filterPath = new Uri(filterPath.ToString().Substring(4, filterPath.ToString().Length - 5), UriKind.RelativeOrAbsolute);
}
var filter = this.OwnerDocument.IdManager.GetElementById(filterPath) as FilterEffects.SvgFilter;
if (filter != null)
{
this.PopTransforms(renderer);
try
{
filter.ApplyFilter(this, renderer, (r) => this.Render(r, false));
}
catch (Exception ex) { Debug.Print(ex.ToString()); }
renderNormal = false;
}
}
......@@ -163,10 +173,10 @@ namespace Svg
}
/// <summary>
/// Renders the fill of the <see cref="SvgVisualElement"/> to the specified <see cref="SvgRenderer"/>
/// Renders the fill of the <see cref="SvgVisualElement"/> to the specified <see cref="ISvgRenderer"/>
/// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> object to render to.</param>
protected internal virtual void RenderFill(SvgRenderer renderer)
/// <param name="renderer">The <see cref="ISvgRenderer"/> object to render to.</param>
protected internal virtual void RenderFill(ISvgRenderer renderer)
{
if (this.Fill != null)
{
......@@ -182,12 +192,12 @@ namespace Svg
}
/// <summary>
/// Renders the stroke of the <see cref="SvgVisualElement"/> to the specified <see cref="SvgRenderer"/>
/// Renders the stroke of the <see cref="SvgVisualElement"/> to the specified <see cref="ISvgRenderer"/>
/// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> object to render to.</param>
protected internal virtual void RenderStroke(SvgRenderer renderer)
/// <param name="renderer">The <see cref="ISvgRenderer"/> object to render to.</param>
protected internal virtual void RenderStroke(ISvgRenderer renderer)
{
if (this.Stroke != null)
if (this.Stroke != null && this.Stroke != SvgColourServer.None)
{
float strokeWidth = this.StrokeWidth.ToDeviceValue(renderer, UnitRenderingType.Other, this);
using (var pen = new Pen(this.Stroke.GetBrush(this, renderer, Math.Min(Math.Max(this.StrokeOpacity * this.Opacity, 0), 1)), strokeWidth))
......@@ -204,50 +214,50 @@ namespace Svg
}
/// <summary>
/// Sets the clipping region of the specified <see cref="SvgRenderer"/>.
/// Sets the clipping region of the specified <see cref="ISvgRenderer"/>.
/// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> to have its clipping region set.</param>
protected internal virtual void SetClip(SvgRenderer renderer)
/// <param name="renderer">The <see cref="ISvgRenderer"/> to have its clipping region set.</param>
protected internal virtual void SetClip(ISvgRenderer renderer)
{
if (this.ClipPath != null)
{
SvgClipPath clipPath = this.OwnerDocument.GetElementById<SvgClipPath>(this.ClipPath.ToString());
this._previousClip = renderer.Clip;
this._previousClip = renderer.GetClip();
if (clipPath != null)
{
renderer.AddClip(clipPath.GetClipRegion(this));
renderer.SetClip(clipPath.GetClipRegion(this), CombineMode.Intersect);
}
}
}
/// <summary>
/// Resets the clipping region of the specified <see cref="SvgRenderer"/> back to where it was before the <see cref="SetClip"/> method was called.
/// Resets the clipping region of the specified <see cref="ISvgRenderer"/> back to where it was before the <see cref="SetClip"/> method was called.
/// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> to have its clipping region reset.</param>
protected internal virtual void ResetClip(SvgRenderer renderer)
/// <param name="renderer">The <see cref="ISvgRenderer"/> to have its clipping region reset.</param>
protected internal virtual void ResetClip(ISvgRenderer renderer)
{
if (this._previousClip != null)
{
renderer.Clip = this._previousClip;
renderer.SetClip(this._previousClip);
this._previousClip = null;
}
}
/// <summary>
/// Sets the clipping region of the specified <see cref="SvgRenderer"/>.
/// Sets the clipping region of the specified <see cref="ISvgRenderer"/>.
/// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> to have its clipping region set.</param>
void ISvgClipable.SetClip(SvgRenderer renderer)
/// <param name="renderer">The <see cref="ISvgRenderer"/> to have its clipping region set.</param>
void ISvgClipable.SetClip(ISvgRenderer renderer)
{
this.SetClip(renderer);
}
/// <summary>
/// Resets the clipping region of the specified <see cref="SvgRenderer"/> back to where it was before the <see cref="SetClip"/> method was called.
/// Resets the clipping region of the specified <see cref="ISvgRenderer"/> back to where it was before the <see cref="SetClip"/> method was called.
/// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> to have its clipping region reset.</param>
void ISvgClipable.ResetClip(SvgRenderer renderer)
/// <param name="renderer">The <see cref="ISvgRenderer"/> to have its clipping region reset.</param>
void ISvgClipable.ResetClip(ISvgRenderer renderer)
{
this.ResetClip(renderer);
}
......
......@@ -187,7 +187,7 @@ namespace Svg
}
/// <summary>
/// Refers to the boldness of the font.
/// Refers to the style of the font.
/// </summary>
[SvgAttribute("font-style")]
public virtual SvgFontStyle FontStyle
......@@ -197,7 +197,7 @@ namespace Svg
}
/// <summary>
/// Refers to the boldness of the font.
/// Refers to the varient of the font.
/// </summary>
[SvgAttribute("font-variant")]
public virtual SvgFontVariant FontVariant
......@@ -323,7 +323,7 @@ namespace Svg
/// Get the font information based on data stored with the text object or inherited from the parent.
/// </summary>
/// <returns></returns>
internal System.Drawing.Font GetFont(SvgRenderer renderer)
internal IFontDefn GetFont(ISvgRenderer renderer)
{
// Get the font-size
float fontSize;
......@@ -337,6 +337,11 @@ namespace Svg
fontSize = fontSizeUnit.ToDeviceValue(renderer, UnitRenderingType.Vertical, this);
}
var family = ValidateFontFamily(this.FontFamily, this.OwnerDocument);
var sFaces = family as IEnumerable<SvgFontFace>;
if (sFaces == null)
{
var fontStyle = System.Drawing.FontStyle.Regular;
// Get the font-weight
......@@ -372,27 +377,41 @@ namespace Svg
break;
}
var family = ValidateFontFamily(this.FontFamily);
if (!family.IsStyleAvailable(fontStyle))
var ff = family as FontFamily;
if (!ff.IsStyleAvailable(fontStyle))
{
// Do Something
}
// Get the font-family
return new System.Drawing.Font(family, fontSize, fontStyle, System.Drawing.GraphicsUnit.Pixel);
return new GdiFontDefn(new System.Drawing.Font(ff, fontSize, fontStyle, System.Drawing.GraphicsUnit.Pixel));
}
else
{
var font = sFaces.First().Parent as SvgFont;
if (font == null)
{
var uri = sFaces.First().Descendants().OfType<SvgFontFaceUri>().First().ReferencedElement;
font = OwnerDocument.IdManager.GetElementById(uri) as SvgFont;
}
return new SvgFontDefn(font, fontSize, OwnerDocument.Ppi);
}
}
private static FontFamily ValidateFontFamily(string fontFamilyList)
public static object ValidateFontFamily(string fontFamilyList, SvgDocument doc)
{
// 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;
FontFamily family;
IEnumerable<SvgFontFace> sFaces;
// 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)
{
if (doc.FontDefns().TryGetValue(f, out sFaces)) return sFaces;
family = families.FirstOrDefault(ff => ff.Name.ToLower() == f.ToLower());
if (family != null) return family;
......
......@@ -20,14 +20,14 @@ namespace Svg
/// </summary>
SvgClipRule ClipRule { get; set; }
/// <summary>
/// Sets the clipping region of the specified <see cref="SvgRenderer"/>.
/// Sets the clipping region of the specified <see cref="ISvgRenderer"/>.
/// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> to have its clipping region set.</param>
void SetClip(SvgRenderer renderer);
/// <param name="renderer">The <see cref="ISvgRenderer"/> to have its clipping region set.</param>
void SetClip(ISvgRenderer renderer);
/// <summary>
/// Resets the clipping region of the specified <see cref="SvgRenderer"/> back to where it was before the <see cref="SetClip"/> method was called.
/// Resets the clipping region of the specified <see cref="ISvgRenderer"/> back to where it was before the <see cref="SetClip"/> method was called.
/// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> to have its clipping region reset.</param>
void ResetClip(SvgRenderer renderer);
/// <param name="renderer">The <see cref="ISvgRenderer"/> to have its clipping region reset.</param>
void ResetClip(ISvgRenderer renderer);
}
}
\ No newline at end of file
......@@ -75,7 +75,7 @@ namespace Svg
}
}
path.AddPath(childPath, false);
if (childPath.PointCount > 0) path.AddPath(childPath, false);
}
foreach (SvgElement child in element.Children)
......@@ -108,10 +108,10 @@ namespace Svg
}
/// <summary>
/// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="SvgRenderer"/> object.
/// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="ISvgRenderer"/> object.
/// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> object to render to.</param>
protected override void Render(SvgRenderer renderer)
/// <param name="renderer">The <see cref="ISvgRenderer"/> object to render to.</param>
protected override void Render(ISvgRenderer renderer)
{
// Do nothing
}
......
......@@ -23,7 +23,7 @@ namespace Svg
set { this.y = value; }
}
public PointF ToDeviceValue(SvgRenderer renderer, SvgElement owner)
public PointF ToDeviceValue(ISvgRenderer renderer, SvgElement owner)
{
return SvgUnit.GetDevicePoint(this.X, this.Y, renderer, owner);
}
......
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Svg
{
public enum SvgTextLengthAdjust
{
spacing,
spacingAndGlyphs
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Svg
{
public enum SvgTextPathMethod
{
align,
stretch
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Svg
{
public enum SvgTextPathSpacing
{
exact,
auto
}
}
......@@ -64,7 +64,7 @@ namespace Svg
/// </summary>
/// <param name="boundable">The container element used as the basis for calculations</param>
/// <returns>The representation of the current unit in a device value (usually pixels).</returns>
public float ToDeviceValue(SvgRenderer renderer, UnitRenderingType renderType, SvgElement owner)
public float ToDeviceValue(ISvgRenderer renderer, UnitRenderingType renderType, SvgElement owner)
{
// If it's already been calculated
if (this._deviceValue.HasValue)
......@@ -107,7 +107,7 @@ namespace Svg
}
float points;
Font currFont;
IFontDefn currFont;
switch (type)
{
......@@ -158,7 +158,7 @@ namespace Svg
break;
case SvgUnitType.Percentage:
// Can't calculate if there is no style owner
var boundable = (renderer == null ? (owner == null ? null : owner.OwnerDocument) : renderer.Boundable());
var boundable = (renderer == null ? (owner == null ? null : owner.OwnerDocument) : renderer.GetBoundable());
if (boundable == null)
{
_deviceValue = value;
......@@ -193,7 +193,7 @@ namespace Svg
return this._deviceValue.Value;
}
private Font GetFont(SvgRenderer renderer, SvgElement owner)
private IFontDefn GetFont(ISvgRenderer renderer, SvgElement owner)
{
if (owner == null) return null;
......@@ -335,18 +335,18 @@ namespace Svg
this._deviceValue = null;
}
public static System.Drawing.PointF GetDevicePoint(SvgUnit x, SvgUnit y, SvgRenderer renderer, SvgElement owner)
public static System.Drawing.PointF GetDevicePoint(SvgUnit x, SvgUnit y, ISvgRenderer renderer, SvgElement owner)
{
return new System.Drawing.PointF(x.ToDeviceValue(renderer, UnitRenderingType.Horizontal, owner),
y.ToDeviceValue(renderer, UnitRenderingType.Vertical, owner));
}
public static System.Drawing.PointF GetDevicePointOffset(SvgUnit x, SvgUnit y, SvgRenderer renderer, SvgElement owner)
public static System.Drawing.PointF GetDevicePointOffset(SvgUnit x, SvgUnit y, ISvgRenderer renderer, SvgElement owner)
{
return new System.Drawing.PointF(x.ToDeviceValue(renderer, UnitRenderingType.HorizontalOffset, owner),
y.ToDeviceValue(renderer, UnitRenderingType.VerticalOffset, owner));
}
public static System.Drawing.SizeF GetDeviceSize(SvgUnit width, SvgUnit height, SvgRenderer renderer, SvgElement owner)
public static System.Drawing.SizeF GetDeviceSize(SvgUnit width, SvgUnit height, ISvgRenderer renderer, SvgElement owner)
{
return new System.Drawing.SizeF(width.ToDeviceValue(renderer, UnitRenderingType.HorizontalOffset, owner),
height.ToDeviceValue(renderer, UnitRenderingType.VerticalOffset, owner));
......
......@@ -4,6 +4,7 @@ using System.ComponentModel;
using System.Collections.Generic;
using System.Text;
using System.Globalization;
using System.Drawing.Drawing2D;
namespace Svg
{
......@@ -123,6 +124,81 @@ namespace Svg
}
#endregion
public void AddViewBoxTransform(SvgAspectRatio aspectRatio, ISvgRenderer renderer, SvgFragment frag)
{
if (this.Equals(SvgViewBox.Empty)) return;
var width = (frag == null ? this.Width : frag.Width.ToDeviceValue(renderer, UnitRenderingType.Horizontal, frag));
var height = (frag == null ? this.Height : frag.Height.ToDeviceValue(renderer, UnitRenderingType.Vertical, frag));
var fScaleX = width / this.Width;
var fScaleY = height / this.Height; //(this.MinY < 0 ? -1 : 1) *
var fMinX = this.MinX;
var fMinY = this.MinY;
if (aspectRatio == null) aspectRatio = new SvgAspectRatio(SvgPreserveAspectRatio.xMidYMid, false);
if (aspectRatio.Align != SvgPreserveAspectRatio.none)
{
if (aspectRatio.Slice)
{
fScaleX = Math.Max(fScaleX, fScaleY);
fScaleY = Math.Max(fScaleX, fScaleY);
}
else
{
fScaleX = Math.Min(fScaleX, fScaleY);
fScaleY = Math.Min(fScaleX, fScaleY);
}
float fViewMidX = (this.Width / 2) * fScaleX;
float fViewMidY = (this.Height / 2) * fScaleY;
float fMidX = width / 2;
float fMidY = height / 2;
switch (aspectRatio.Align)
{
case SvgPreserveAspectRatio.xMinYMin:
break;
case SvgPreserveAspectRatio.xMidYMin:
fMinX += fMidX - fViewMidX;
break;
case SvgPreserveAspectRatio.xMaxYMin:
fMinX += width - this.Width * fScaleX;
break;
case SvgPreserveAspectRatio.xMinYMid:
fMinY += fMidY - fViewMidY;
break;
case SvgPreserveAspectRatio.xMidYMid:
fMinX += fMidX - fViewMidX;
fMinY += fMidY - fViewMidY;
break;
case SvgPreserveAspectRatio.xMaxYMid:
fMinX += width - this.Width * fScaleX;
fMinY += fMidY - fViewMidY;
break;
case SvgPreserveAspectRatio.xMinYMax:
fMinY += height - this.Height * fScaleY;
break;
case SvgPreserveAspectRatio.xMidYMax:
fMinX += fMidX - fViewMidX;
fMinY += height - this.Height * fScaleY;
break;
case SvgPreserveAspectRatio.xMaxYMax:
fMinX += width - this.Width * fScaleX;
fMinY += height - this.Height * fScaleY;
break;
default:
break;
}
}
var x = (frag == null ? 0 : frag.X.ToDeviceValue(renderer, UnitRenderingType.Horizontal, frag));
var y = (frag == null ? 0 : frag.Y.ToDeviceValue(renderer, UnitRenderingType.Vertical, frag));
renderer.SetClip(new Region(new RectangleF(x, y, width, height)), CombineMode.Intersect);
renderer.ScaleTransform(fScaleX, fScaleY, MatrixOrder.Prepend);
renderer.TranslateTransform(x, y);
renderer.TranslateTransform(-fMinX, -fMinY);
}
}
internal class SvgViewBoxConverter : TypeConverter
......
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Svg
{
public enum XmlSpaceHandling
{
@default,
inherit,
preserve
}
}
......@@ -18,10 +18,10 @@ namespace Svg
}
/// <summary>
/// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="SvgRenderer"/> object.
/// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="ISvgRenderer"/> object.
/// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> object to render to.</param>
protected override void Render(SvgRenderer renderer)
/// <param name="renderer">The <see cref="ISvgRenderer"/> object to render to.</param>
protected override void Render(ISvgRenderer renderer)
{
// Do nothing. Children should NOT be rendered.
}
......
......@@ -31,10 +31,10 @@ namespace Svg
/// <summary>
/// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="SvgRenderer"/> object.
/// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="ISvgRenderer"/> object.
/// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> object to render to.</param>
protected override void Render(SvgRenderer renderer)
/// <param name="renderer">The <see cref="ISvgRenderer"/> object to render to.</param>
protected override void Render(ISvgRenderer renderer)
{
// Do nothing. Children should NOT be rendered.
}
......
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