Commit 2a58f88d authored by Tebjan Halm's avatar Tebjan Halm
Browse files

Merge pull request #213 from HeinrichAD/master

CurrentCulture Fix; SVG Viewer NullReferenceException fix; Little Maker rendering fix; Some optimization; Add "shape-rendering" support; Add "customer/ private fonts" support
parents a935f4ee 0f1186d9
......@@ -73,9 +73,10 @@ namespace Svg
if (this._owner.Parent != null)
{
if (this._owner.Parent.Attributes[attributeName] != null)
var parentAttribute = this._owner.Parent.Attributes[attributeName];
if (parentAttribute != null)
{
return (TAttributeType)this._owner.Parent.Attributes[attributeName];
return (TAttributeType)parentAttribute;
}
}
......@@ -91,8 +92,11 @@ namespace Svg
(value is SvgTextDecoration && (SvgTextDecoration)value == SvgTextDecoration.Inherit) ||
(value is XmlSpaceHandling && (XmlSpaceHandling)value == XmlSpaceHandling.inherit) ||
(value is SvgOverflow && (SvgOverflow)value == SvgOverflow.Inherit) ||
(value == SvgColourServer.Inherit) ||
(value is string && (string)value == "inherit")
(value is SvgColourServer && (SvgColourServer)value == SvgColourServer.Inherit) ||
(value is SvgShapeRendering && (SvgShapeRendering)value == SvgShapeRendering.Inherit) ||
(value is SvgTextRendering && (SvgTextRendering)value == SvgTextRendering.Inherit) ||
(value is SvgImageRendering && (SvgImageRendering)value == SvgImageRendering.Inherit) ||
(value is string && ((string)value).ToLower() == "inherit")
);
}
......
......@@ -485,7 +485,6 @@ namespace Svg
//EO, 2014-12-05: Requested to ensure proper zooming (draw the svg in the bitmap size, ==> proper scaling)
//EO, 2015-01-09, Added GetDimensions to use its returned size instead of this.Width and this.Height (request of Icarrere).
//BBN, 2015-07-29, it is unnecassary to call again GetDimensions and transform to 1x1
//JA, 2015-12-18, this is actually necessary to correctly render the Draw(rasterHeight, rasterWidth) overload, otherwise the rendered graphic doesn't scale correctly
SizeF size = this.GetDimensions();
......@@ -564,13 +563,17 @@ namespace Svg
{
//Save previous culture and switch to invariant for writing
var previousCulture = Thread.CurrentThread.CurrentCulture;
try {
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
base.Write(writer);
}
finally
{
// Make sure to set back the old culture even an error occurred.
//Switch culture back
Thread.CurrentThread.CurrentCulture = previousCulture;
}
}
public void Write(Stream stream)
{
......
......@@ -26,7 +26,7 @@ namespace Svg
set { this._dirty = value; }
}
private static float FixOpacityValue(float value)
protected static float FixOpacityValue(float value)
{
const float max = 1.0f;
const float min = 0.0f;
......@@ -39,7 +39,7 @@ namespace Svg
[SvgAttribute("fill", true)]
public virtual SvgPaintServer Fill
{
get { return (this.Attributes["fill"] == null) ? SvgColourServer.NotSet : (SvgPaintServer)this.Attributes["fill"]; }
get { return ((SvgPaintServer)this.Attributes["fill"] ?? SvgColourServer.NotSet); }
set { this.Attributes["fill"] = value; }
}
......@@ -49,14 +49,14 @@ namespace Svg
[SvgAttribute("stroke", true)]
public virtual SvgPaintServer Stroke
{
get { return (this.Attributes["stroke"] == null) ? null : (SvgPaintServer)this.Attributes["stroke"]; }
get { return (SvgPaintServer)this.Attributes["stroke"]; }
set { this.Attributes["stroke"] = value; }
}
[SvgAttribute("fill-rule", true)]
public virtual SvgFillRule FillRule
{
get { return (this.Attributes["fill-rule"] == null) ? SvgFillRule.NonZero : (SvgFillRule)this.Attributes["fill-rule"]; }
get { return (SvgFillRule)(this.Attributes["fill-rule"] ?? SvgFillRule.NonZero); }
set { this.Attributes["fill-rule"] = value; }
}
......@@ -66,7 +66,7 @@ namespace Svg
[SvgAttribute("fill-opacity", true)]
public virtual float FillOpacity
{
get { return (this.Attributes["fill-opacity"] == null) ? this.Opacity : (float)this.Attributes["fill-opacity"]; }
get { return (float)(this.Attributes["fill-opacity"] ?? this.Opacity); }
set { this.Attributes["fill-opacity"] = FixOpacityValue(value); }
}
......@@ -76,28 +76,28 @@ namespace Svg
[SvgAttribute("stroke-width", true)]
public virtual SvgUnit StrokeWidth
{
get { return (this.Attributes["stroke-width"] == null) ? new SvgUnit(1.0f) : (SvgUnit)this.Attributes["stroke-width"]; }
get { return (SvgUnit)(this.Attributes["stroke-width"] ?? new SvgUnit(1.0f)); }
set { this.Attributes["stroke-width"] = value; }
}
[SvgAttribute("stroke-linecap", true)]
public virtual SvgStrokeLineCap StrokeLineCap
{
get { return (this.Attributes["stroke-linecap"] == null) ? SvgStrokeLineCap.Butt : (SvgStrokeLineCap)this.Attributes["stroke-linecap"]; }
get { return (SvgStrokeLineCap)(this.Attributes["stroke-linecap"] ?? SvgStrokeLineCap.Butt); }
set { this.Attributes["stroke-linecap"] = value; }
}
[SvgAttribute("stroke-linejoin", true)]
public virtual SvgStrokeLineJoin StrokeLineJoin
{
get { return (this.Attributes["stroke-linejoin"] == null) ? SvgStrokeLineJoin.Miter : (SvgStrokeLineJoin)this.Attributes["stroke-linejoin"]; }
get { return (SvgStrokeLineJoin)(this.Attributes["stroke-linejoin"] ?? SvgStrokeLineJoin.Miter); }
set { this.Attributes["stroke-linejoin"] = value; }
}
[SvgAttribute("stroke-miterlimit", true)]
public virtual float StrokeMiterLimit
{
get { return (this.Attributes["stroke-miterlimit"] == null) ? 4f : (float)this.Attributes["stroke-miterlimit"]; }
get { return (float)(this.Attributes["stroke-miterlimit"] ?? 4f); }
set { this.Attributes["stroke-miterlimit"] = value; }
}
......@@ -111,7 +111,7 @@ namespace Svg
[SvgAttribute("stroke-dashoffset", true)]
public virtual SvgUnit StrokeDashOffset
{
get { return (this.Attributes["stroke-dashoffset"] == null) ? SvgUnit.Empty : (SvgUnit)this.Attributes["stroke-dashoffset"]; }
get { return (SvgUnit)(this.Attributes["stroke-dashoffset"] ?? SvgUnit.Empty); }
set { this.Attributes["stroke-dashoffset"] = value; }
}
......@@ -121,7 +121,7 @@ namespace Svg
[SvgAttribute("stroke-opacity", true)]
public virtual float StrokeOpacity
{
get { return (this.Attributes["stroke-opacity"] == null) ? this.Opacity : (float)this.Attributes["stroke-opacity"]; }
get { return (float)(this.Attributes["stroke-opacity"] ?? this.Opacity); }
set { this.Attributes["stroke-opacity"] = FixOpacityValue(value); }
}
......@@ -131,7 +131,7 @@ namespace Svg
/// <remarks>Apparently this can be set on non-sensical elements. Don't ask; just check the tests.</remarks>
[SvgAttribute("stop-color", true)]
[TypeConverter(typeof(SvgPaintServerFactory))]
public SvgPaintServer StopColor
public virtual SvgPaintServer StopColor
{
get { return this.Attributes["stop-color"] as SvgPaintServer; }
set { this.Attributes["stop-color"] = value; }
......@@ -143,10 +143,20 @@ namespace Svg
[SvgAttribute("opacity", true)]
public virtual float Opacity
{
get { return (this.Attributes["opacity"] == null) ? 1.0f : (float)this.Attributes["opacity"]; }
get { return (float)(this.Attributes["opacity"] ?? 1.0f); }
set { this.Attributes["opacity"] = FixOpacityValue(value); }
}
/// <summary>
/// Refers to the AnitAlias rendering of shapes.
/// </summary>
[SvgAttribute("shape-rendering")]
public virtual SvgShapeRendering ShapeRendering
{
get { return this.Attributes.GetInheritedAttribute<SvgShapeRendering>("shape-rendering"); }
set { this.Attributes["shape-rendering"] = value; }
}
/// <summary>
/// Indicates which font family is to be used to render the text.
/// </summary>
......@@ -163,7 +173,7 @@ namespace Svg
[SvgAttribute("font-size", true)]
public virtual SvgUnit FontSize
{
get { return (this.Attributes["font-size"] == null) ? SvgUnit.Empty : (SvgUnit)this.Attributes["font-size"]; }
get { return (SvgUnit)(this.Attributes["font-size"] ?? SvgUnit.Empty); }
set { this.Attributes["font-size"] = value; this.IsPathDirty = true; }
}
......@@ -173,7 +183,7 @@ namespace Svg
[SvgAttribute("font-style", true)]
public virtual SvgFontStyle FontStyle
{
get { return (this.Attributes["font-style"] == null) ? SvgFontStyle.All : (SvgFontStyle)this.Attributes["font-style"]; }
get { return (SvgFontStyle)(this.Attributes["font-style"] ?? SvgFontStyle.All); }
set { this.Attributes["font-style"] = value; this.IsPathDirty = true; }
}
......@@ -183,7 +193,7 @@ namespace Svg
[SvgAttribute("font-variant", true)]
public virtual SvgFontVariant FontVariant
{
get { return (this.Attributes["font-variant"] == null) ? SvgFontVariant.Inherit : (SvgFontVariant)this.Attributes["font-variant"]; }
get { return (SvgFontVariant)(this.Attributes["font-variant"] ?? SvgFontVariant.Inherit); }
set { this.Attributes["font-variant"] = value; this.IsPathDirty = true; }
}
......@@ -193,7 +203,7 @@ namespace Svg
[SvgAttribute("text-decoration", true)]
public virtual SvgTextDecoration TextDecoration
{
get { return (this.Attributes["text-decoration"] == null) ? SvgTextDecoration.Inherit : (SvgTextDecoration)this.Attributes["text-decoration"]; }
get { return (SvgTextDecoration)(this.Attributes["text-decoration"] ?? SvgTextDecoration.Inherit); }
set { this.Attributes["text-decoration"] = value; this.IsPathDirty = true; }
}
......@@ -203,7 +213,7 @@ namespace Svg
[SvgAttribute("font-weight", true)]
public virtual SvgFontWeight FontWeight
{
get { return (this.Attributes["font-weight"] == null) ? SvgFontWeight.Inherit : (SvgFontWeight)this.Attributes["font-weight"]; }
get { return (SvgFontWeight)(this.Attributes["font-weight"] ?? SvgFontWeight.Inherit); }
set { this.Attributes["font-weight"] = value; this.IsPathDirty = true; }
}
......@@ -223,7 +233,7 @@ namespace Svg
[SvgAttribute("font", true)]
public virtual string Font
{
get { return (this.Attributes["font"] == null ? "" : this.Attributes["font"] as string); }
get { return ((this.Attributes["font"] ?? string.Empty) as string); }
set
{
var state = FontParseState.fontStyle;
......@@ -298,8 +308,6 @@ namespace Svg
}
}
private const string DefaultFontFamily = "Times New Roman";
/// <summary>
/// Get the font information based on data stored with the text object or inherited from the parent.
/// </summary>
......@@ -379,11 +387,13 @@ namespace Svg
}
}
public static System.Drawing.Text.PrivateFontCollection PrivateFonts = new System.Drawing.Text.PrivateFontCollection();
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 fontParts = (fontFamilyList ?? string.Empty).Split(new[] { ',' }).Select(fontName => fontName.Trim(new[] { '"', ' ', '\'' }));
var families = System.Drawing.FontFamily.Families;
Func<FontFamily, bool> getFamily;
FontFamily family;
IEnumerable<SvgFontFace> sFaces;
......@@ -393,10 +403,13 @@ namespace Svg
{
if (doc.FontDefns().TryGetValue(f, out sFaces)) return sFaces;
family = families.FirstOrDefault(ff => ff.Name.ToLower() == f.ToLower());
getFamily = new Func<FontFamily, bool>(ff => string.Equals(ff.Name, f, StringComparison.OrdinalIgnoreCase));
family = families.FirstOrDefault(getFamily);
if (family != null) return family;
family = PrivateFonts.Families.FirstOrDefault(getFamily);
if (family != null) return family;
switch (f)
switch (f.ToLower())
{
case "serif":
return System.Drawing.FontFamily.GenericSerif;
......
......@@ -49,6 +49,8 @@ namespace Svg
var result = "";
var currentCulture = Thread.CurrentThread.CurrentCulture;
try
{
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
using (StringWriter str = new StringWriter())
{
......@@ -59,7 +61,12 @@ namespace Svg
}
}
}
finally
{
// Make sure to set back the old culture even an error occurred.
Thread.CurrentThread.CurrentCulture = currentCulture;
}
return result;
}
......@@ -73,7 +80,7 @@ namespace Svg
{
action(elem);
if(!(elem is SvgDocument)) //don't apply action to subtree of documents
if (!(elem is SvgDocument)) //don't apply action to subtree of documents
{
foreach (var element in elem.Children)
{
......@@ -84,7 +91,7 @@ namespace Svg
public static void ApplyRecursiveDepthFirst(this SvgElement elem, Action<SvgElement> action)
{
if(!(elem is SvgDocument)) //don't apply action to subtree of documents
if (!(elem is SvgDocument)) //don't apply action to subtree of documents
{
foreach (var element in elem.Children)
{
......
......@@ -133,6 +133,7 @@ namespace Svg.Text
}
}
[CLSCompliant(false)]
public struct TT_OFFSET_TABLE
{
public ushort uMajorVersion;
......@@ -143,6 +144,7 @@ namespace Svg.Text
public ushort uRangeShift;
}
[CLSCompliant(false)]
public struct TT_TABLE_DIRECTORY
{
public byte[] szTag;
......@@ -155,6 +157,7 @@ namespace Svg.Text
}
}
[CLSCompliant(false)]
public struct TT_NAME_TABLE_HEADER
{
public ushort uFSelector;
......@@ -162,6 +165,7 @@ namespace Svg.Text
public ushort uStorageOffset;
}
[CLSCompliant(false)]
public struct TT_NAME_RECORD
{
public ushort uPlatformID;
......
......@@ -62,7 +62,7 @@ namespace Svg
/// Indicates which font family is to be used to render the text.
/// </summary>
[SvgAttribute("font-family")]
public virtual string FontFamily
public override string FontFamily
{
get { return this.Attributes["font-family"] as string; }
set { this.Attributes["font-family"] = value; }
......@@ -72,7 +72,7 @@ namespace Svg
/// 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
public override SvgUnit FontSize
{
get { return (this.Attributes["font-size"] == null) ? SvgUnit.Empty : (SvgUnit)this.Attributes["font-size"]; }
set { this.Attributes["font-size"] = value; }
......@@ -82,7 +82,7 @@ namespace Svg
/// Refers to the style of the font.
/// </summary>
[SvgAttribute("font-style")]
public virtual SvgFontStyle FontStyle
public override SvgFontStyle FontStyle
{
get { return (this.Attributes["font-style"] == null) ? SvgFontStyle.All : (SvgFontStyle)this.Attributes["font-style"]; }
set { this.Attributes["font-style"] = value; }
......@@ -92,7 +92,7 @@ namespace Svg
/// Refers to the varient of the font.
/// </summary>
[SvgAttribute("font-variant")]
public virtual SvgFontVariant FontVariant
public override SvgFontVariant FontVariant
{
get { return (this.Attributes["font-variant"] == null) ? SvgFontVariant.Inherit : (SvgFontVariant)this.Attributes["font-variant"]; }
set { this.Attributes["font-variant"] = value; }
......@@ -102,7 +102,7 @@ namespace Svg
/// Refers to the boldness of the font.
/// </summary>
[SvgAttribute("font-weight")]
public virtual SvgFontWeight FontWeight
public override SvgFontWeight FontWeight
{
get { return (this.Attributes["font-weight"] == null) ? SvgFontWeight.Inherit : (SvgFontWeight)this.Attributes["font-weight"]; }
set { this.Attributes["font-weight"] = value; }
......
......@@ -79,15 +79,6 @@ namespace Svg
return _path;
}
/// <summary>
/// Gets or sets a value to determine if anti-aliasing should occur when the element is being rendered.
/// </summary>
protected override bool RequiresSmoothRendering
{
get { return true; }
}
/// <summary>
/// Gets the bounds of the element.
/// </summary>
......
......@@ -13,10 +13,10 @@ namespace Svg
{
public abstract class SvgTextBase : SvgVisualElement
{
protected SvgUnitCollection _x = new SvgUnitCollection();
protected SvgUnitCollection _y = new SvgUnitCollection();
protected SvgUnitCollection _dy = new SvgUnitCollection();
protected SvgUnitCollection _dx = new SvgUnitCollection();
[CLSCompliant(false)] protected SvgUnitCollection _x = new SvgUnitCollection();
[CLSCompliant(false)] protected SvgUnitCollection _y = new SvgUnitCollection();
[CLSCompliant(false)] protected SvgUnitCollection _dy = new SvgUnitCollection();
[CLSCompliant(false)] protected SvgUnitCollection _dx = new SvgUnitCollection();
private string _rotate;
private List<float> _rotations = new List<float>();
......@@ -215,15 +215,6 @@ namespace Svg
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>
......
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Svg.DataTypes;
using System;
using System.Diagnostics;
using System.Drawing;
using System.IO;
namespace Svg.UnitTests
{
/// <summary>
/// Test Class of rendering SVGs with marker-end elements.
/// Based on Issue 212.
/// </summary>
/// <remarks>
/// Test use the following embedded resources:
/// - Issue212_MakerEnd\OperatingPlan.svg
/// </remarks>
[TestClass]
public class MarkerEndTest : SvgTestHelper
{
protected override string TestResource { get { return GetFullResourceString("Issue212_MakerEnd.OperatingPlan.svg"); } }
protected override int ExpectedSize { get { return 5000; } } //5321 //5410
[TestMethod]
public void TestOperatingPlanRendering()
{
LoadSvg(GetXMLDocFromResource());
}
[TestMethod]
public void TestArrowCodeCreation()
{
// Sample code from Issue 212. Thanks to podostro.
const int width = 50;
const int height = 50;
var document = new SvgDocument()
{
ID = "svgMap",
ViewBox = new SvgViewBox(0, 0, width, height)
};
var defsElement = new SvgDefinitionList() { ID = "defsMap" };
document.Children.Add(defsElement);
var groupElement = new SvgGroup() { ID = "gMap" };
document.Children.Add(groupElement);
var arrowPath = new SvgPath()
{
ID = "pathMarkerArrow",
Fill = new SvgColourServer(Color.Black),
PathData = SvgPathBuilder.Parse(@"M0,0 L4,2 L0,4 L1,2 z")
};
var arrowMarker = new SvgMarker()
{
ID = "markerArrow",
MarkerUnits = SvgMarkerUnits.StrokeWidth,
MarkerWidth = 5,
MarkerHeight = 5,
RefX = 3,
RefY = 2,
Orient = new SvgOrient() { IsAuto = true },
Children = { arrowPath }
};
defsElement.Children.Add(arrowMarker);
var line = new SvgLine()
{
ID = "lineLinkedPoint",
StartX = 0,
StartY = 15,
EndX = 35,
EndY = 35,
Stroke = new SvgColourServer(Color.Black),
StrokeWidth = 3,
MarkerEnd = new Uri(string.Format("url(#{0})", arrowMarker.ID), UriKind.Relative)
};
groupElement.Children.Add(line);
var svgXml = document.GetXML();
var img = document.Draw();
var file = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
File.WriteAllText(file + ".svg", svgXml);
img.Save(file + ".png");
Debug.WriteLine(string.Format("Svg saved to '{0}'", file));
Debugger.Break();
// Remove
var svg = new FileInfo(file + ".svg");
if (svg.Exists) svg.Delete();
var png = new FileInfo(file + ".png");
if (png.Exists) png.Delete();
}
}
}
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
namespace Svg.UnitTests
{
/// <summary>
/// Test Class of rendering SVGs as meterfile.
/// Based on Issue 210.
/// </summary>
/// <remarks>
/// Test use the following embedded resources:
/// - Issue210_Metafile\3DSceneSnapshotBIG.svg
/// </remarks>
[TestClass]
public class MetafileRenderingTest : SvgTestHelper
{
protected override string TestResource { get { return GetFullResourceString("Issue210_Metafile.3DSceneSnapshotBIG.svg"); } }
protected override int ExpectedSize { get { return 12500; } } //12896
[TestMethod]
[TestProperty(name: "speed", value: "slow")]
public void TestMetafileRendering()
{
LoadSvg(GetXMLDocFromResource());
}
protected override Image DrawSvg(SvgDocument svgDoc)
{
// GDI+
Metafile metafile;
using (var stream = new MemoryStream())
using (var img = new Bitmap((int)svgDoc.Width.Value, (int)svgDoc.Height.Value)) // Not necessary if you use Control.CreateGraphics().
using (Graphics ctrlGraphics = Graphics.FromImage(img)) // Control.CreateGraphics()
{
IntPtr handle = ctrlGraphics.GetHdc();
var rect = new RectangleF(0, 0, svgDoc.Width, svgDoc.Height);
metafile = new Metafile(stream,
handle,
rect,
MetafileFrameUnit.Pixel,
EmfType.EmfPlusOnly);
using (Graphics ig = Graphics.FromImage(metafile))
{
svgDoc.Draw(ig);
}
ctrlGraphics.ReleaseHdc(handle);
}
return metafile;
}
}
}
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Svg.Exceptions;
using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Threading.Tasks;
using System.Xml;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Svg.Exceptions;
namespace Svg.UnitTests
{
[TestClass()]
public class MultiThreadingTest
[TestClass]
public class MultiThreadingTest : SvgTestHelper
{
private const string TestFile = @"d:\temp\test.svg";
private const int ExpectedSize = 600000;
private XmlDocument GetXMLDoc()
protected override string TestFile { get { return @"d:\temp\test.svg"; } }
protected override int ExpectedSize { get { return 600000; } }
private void LoadFile()
{
var xmlDoc = new XmlDocument();
if(!System.IO.File.Exists(TestFile)) { Assert.Inconclusive("Test file missing"); }
xmlDoc.LoadXml(System.IO.File.ReadAllText(TestFile));
return xmlDoc;
LoadSvg(GetXMLDocFromFile());
}
[TestMethod]
public void TestSingleThread()
{
LoadFile();
}
[TestMethod]
public void TestMultiThread()
{
bool valid = true;
Parallel.For(0, 10, (x) =>
{
LoadFile();
......@@ -42,6 +37,7 @@ namespace Svg.UnitTests
Trace.WriteLine("Done");
}
[TestMethod]
[ExpectedException(typeof(SvgMemoryException))]
public void SVGGivesMemoryExceptionOnTooManyParallelTest()
......@@ -58,19 +54,5 @@ namespace Svg.UnitTests
throw ex.InnerException;
}
}
private void LoadFile()
{
var xml = GetXMLDoc();
Trace.WriteLine("Reading and drawing file");
SvgDocument d = SvgDocument.Open(xml);
var b = d.Draw();
Trace.WriteLine("Done reading file");
using (var ms = new MemoryStream())
{
b.Save(ms, ImageFormat.Png);
ms.Flush();
Assert.IsTrue(ms.Length >= ExpectedSize, "File does not match expected minimum size");
}
}
}
}
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Drawing.Text;
using System.Runtime.InteropServices;
namespace Svg.UnitTests
{
/// <summary>
/// Test Class of the feature to use Private Fonts in SVGs.
/// Based on Issue 204.
/// </summary>
/// <remarks>
/// Test use the following embedded resources:
/// - Issue204_PrivateFont\Text.svg
/// - Issue204_PrivateFont\BrushScriptMT2.ttf
/// </remarks>
[TestClass]
public class PrivateFontsTests : SvgTestHelper
{
private const string PrivateFontSvg = "Issue204_PrivateFont.Text.svg";
private const string PrivateFont = "Issue204_PrivateFont.BrushScriptMT2.ttf";
//private const string PrivateFontName = "Brush Script MT2";
protected override int ExpectedSize { get { return 3200; } } //3512
[TestMethod]
public void TestPrivateFont()
{
AddFontFromResource(SvgElement.PrivateFonts, GetFullResourceString(PrivateFont));
LoadSvg(GetXMLDocFromResource(GetFullResourceString(PrivateFontSvg)));
}
private void AddFontFromResource(PrivateFontCollection privateFontCollection, string fullFontResourceString)
{
var fontBytes = GetResourceBytes(fullFontResourceString);
var fontData = Marshal.AllocCoTaskMem(fontBytes.Length);
Marshal.Copy(fontBytes, 0, fontData, fontBytes.Length);
privateFontCollection.AddMemoryFont(fontData, fontBytes.Length); // Add font to collection.
Marshal.FreeCoTaskMem(fontData); // Do not forget to frees the memory block.
}
}
}
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<symbol id="Lines" overflow="visible">
<text x="100" y="60" style="font-family: Brush Script MT2; font-size: 50px">
<tspan x="100" y="160">Line01</tspan>
<tspan x="100" y="220">Line02</tspan>
</text>
</symbol>
</defs>
<use xlink:href="#Lines"/>
</svg>
\ No newline at end of file
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg x="0" y="0" width="100%" height="100%" overflow="inherit" viewBox="0, 0, 265, 265" preserveAspectRatio="xMidYMid" font-size="0" id="svgmap" xml:space="default" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xml="http://www.w3.org/XML/1998/namespace" version="1.1">
<defs id="defsmap" xml:space="default">
<marker refX="3" refY="2" orient="auto" overflow="hidden" markerWidth="5" markerHeight="5" id="markerArrowLightBlue" xml:space="default">
<path d="M0 0 L4 2 L0 4 L1 2 z" id="pathMarkerArrowLightBlue" xml:space="default" style="fill:#ADD8E6;" />
</marker>
<marker refX="3" refY="2" orient="auto" overflow="hidden" markerWidth="5" markerHeight="5" id="markerArrowLightCoral" xml:space="default">
<path d="M0 0 L4 2 L0 4 L1 2 z" id="pathMarkerArrowLightCoral" xml:space="default" style="fill:#F08080;" />
</marker>
</defs>
<g id="gmap" xml:space="default">
<rect x="80" y="10" width="35" height="35" rx="7" ry="7" stroke="#ADD8E6" id="rectRoomX1Y0" xml:space="default" style="fill:#ADD8E6;" />
<line x1="78" y1="47" x2="49" y2="76" marker-end="url(#markerArrowLightBlue)" stroke="#ADD8E6" stroke-width="3" id="lineLinkedRoomX0Y1" xml:space="default" />
<rect x="150" y="10" width="35" height="35" rx="7" ry="7" stroke="#20B2AA" id="rectRoomX2Y0" xml:space="default" style="fill:#20B2AA;" />
<line x1="167.5" y1="47" x2="167.5" y2="76" marker-end="url(#markerArrowLightBlue)" stroke="#ADD8E6" stroke-width="3" id="lineLinkedRoomX2Y1" xml:space="default" />
<rect x="10" y="80" width="35" height="35" rx="7" ry="7" stroke="#ADD8E6" id="rectRoomX0Y1" xml:space="default" style="fill:#ADD8E6;" />
<line x1="47" y1="117" x2="76" y2="146" marker-end="url(#markerArrowLightBlue)" stroke="#ADD8E6" stroke-width="3" id="lineLinkedRoomX1Y2" xml:space="default" />
<rect x="80" y="80" width="35" height="35" rx="7" ry="7" stroke="#ADD8E6" id="rectRoomX1Y1" xml:space="default" style="fill:#ADD8E6;" />
<line x1="97.5" y1="78" x2="97.5" y2="49" marker-end="url(#markerArrowLightBlue)" stroke="#ADD8E6" stroke-width="3" id="lineLinkedRoomX1Y0" xml:space="default" />
<rect x="150" y="80" width="35" height="35" rx="7" ry="7" stroke="#ADD8E6" id="rectRoomX2Y1" xml:space="default" style="fill:#ADD8E6;" />
<line x1="148" y1="97.5" x2="119" y2="97.5" marker-end="url(#markerArrowLightBlue)" stroke="#ADD8E6" stroke-width="3" id="lineLinkedRoomX1Y1" xml:space="default" />
<rect x="220" y="80" width="35" height="35" rx="7" ry="7" stroke="#ADD8E6" id="rectRoomX3Y1" xml:space="default" style="fill:#ADD8E6;" />
<line x1="237.5" y1="117" x2="237.5" y2="146" marker-end="url(#markerArrowLightCoral)" stroke="#F08080" stroke-width="3" id="lineLinkedRoomX3Y2" xml:space="default" />
<rect x="10" y="150" width="35" height="35" rx="7" ry="7" stroke="#ADD8E6" id="rectRoomX0Y2" xml:space="default" style="fill:#ADD8E6;" />
<line x1="47" y1="187" x2="76" y2="216" marker-end="url(#markerArrowLightBlue)" stroke="#ADD8E6" stroke-width="3" id="lineLinkedRoomX1Y3" xml:space="default" />
<rect x="80" y="150" width="35" height="35" rx="7" ry="7" stroke="#ADD8E6" id="rectRoomX1Y2" xml:space="default" style="fill:#ADD8E6;" />
<line x1="78" y1="167.5" x2="49" y2="167.5" marker-end="url(#markerArrowLightBlue)" stroke="#ADD8E6" stroke-width="3" id="lineLinkedRoomX0Y2" xml:space="default" />
<rect x="150" y="150" width="35" height="35" rx="7" ry="7" stroke="#ADD8E6" id="rectRoomX2Y2" xml:space="default" style="fill:#ADD8E6;" />
<line x1="187" y1="148" x2="216" y2="119" marker-end="url(#markerArrowLightBlue)" stroke="#ADD8E6" stroke-width="3" id="lineLinkedRoomX3Y1" xml:space="default" />
<rect x="220" y="150" width="35" height="35" rx="7" ry="7" stroke="#F08080" id="rectRoomX3Y2" xml:space="default" style="fill:#F08080;" />
<rect x="80" y="220" width="35" height="35" rx="7" ry="7" stroke="#ADD8E6" id="rectRoomX1Y3" xml:space="default" style="fill:#ADD8E6;" />
<line x1="117" y1="218" x2="146" y2="189" marker-end="url(#markerArrowLightBlue)" stroke="#ADD8E6" stroke-width="3" id="lineLinkedRoomX2Y2" xml:space="default" />
</g>
</svg>
\ No newline at end of file
......@@ -55,8 +55,12 @@
</ItemGroup>
<ItemGroup>
<Compile Include="CssQueryTest.cs" />
<Compile Include="MarkerEndTest.cs" />
<Compile Include="MetafileRenderingTest.cs" />
<Compile Include="MultiThreadingTest.cs" />
<Compile Include="PrivateFontsTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SvgTestHelper.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Source\Svg.csproj">
......@@ -65,8 +69,18 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\Issue204_PrivateFont\BrushScriptMT2.ttf" />
<None Include="svgkey.snk" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\Issue204_PrivateFont\Text.svg" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\Issue210_Metafile\3DSceneSnapshotBIG.svg" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\Issue212_MakerEnd\OperatingPlan.svg" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
......
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Xml;
namespace Svg.UnitTests
{
public abstract class SvgTestHelper
{
/// <summary>
/// Test file path.
/// </summary>
protected virtual string TestFile
{
get
{
const string msg = "Test file not overridden.";
Assert.Inconclusive(msg);
throw new NotImplementedException(msg);
}
}
/// <summary>
/// Full Unit Test resource string for test file.
/// </summary>
/// <remarks>
/// For the full Unit Test resource string you can use <see cref="GetFullResourceString(string)"/>.
/// </remarks>
protected virtual string TestResource
{
get
{
const string msg = "Test resource not overridden.";
Assert.Inconclusive(msg);
throw new NotImplementedException(msg);
}
}
/// <summary>
/// Expected size of svg file after drawing.
/// </summary>
protected virtual int ExpectedSize
{
get
{
const string msg = "Expected size not overridden.";
Assert.Inconclusive(msg);
throw new NotImplementedException(msg);
}
}
/// <summary>
/// Get full Unit Test resource string.
/// </summary>
/// <param name="resourcePath">Resource path.</param>
/// <returns>Full resource string.</returns>
/// <example>
/// var s = GetFullResourceString("Issue204_PrivateFont.Text.svg");
/// // s content: "Svg.UnitTests.Resources.Issue204_PrivateFont.Text.svg"
/// </example>
protected virtual string GetFullResourceString(string resourcePath)
{
const string DefaultResourcesDir = "Resources";
return string.Format("{0}.{1}.{2}",
this.GetType().Assembly.GetName().Name,
DefaultResourcesDir,
resourcePath);
}
/// <summary>
/// Get embedded resource as stream from Unit Test resources.
/// </summary>
/// <param name="fullResourceString">Full Unit Test resource string.</param>
/// <returns>Embedded resource data steam.</returns>
/// <remarks>Do not forget to close, dispose the stream.</remarks>
protected virtual Stream GetResourceStream(string fullResourceString)
{
Trace.WriteLine("Get resource data.");
var s = this.GetType().Assembly.GetManifestResourceStream(fullResourceString);
if (s == null)
Assert.Fail("Unable to find embedded resource", fullResourceString);
Trace.WriteLine("Done getting resource data.");
return s;
}
/// <summary>
/// Get embedded resource as byte array from Unit Test resources.
/// </summary>
/// <param name="fullResourceString">Full Unit Test resource string.</param>
/// <returns>Embedded resource data bytes.</returns>
protected virtual byte[] GetResourceBytes(string fullResourceString)
{
using (var s = GetResourceStream(fullResourceString))
{
var resource = new byte[s.Length];
s.Read(resource, 0, (int)s.Length);
return resource;
}
}
/// <summary>
/// Get embedded resource as xml document from Unit Test resources.
/// </summary>
/// <param name="fullResourceString">Full Unit Test resource string.</param>
/// <returns>Embedded resource data xml document.</returns>
protected virtual XmlDocument GetResourceXmlDoc(string fullResourceString)
{
using (var s = GetResourceStream(fullResourceString))
{
Trace.WriteLine("Load XmlDocument from resource data.");
var xmlDoc = new XmlDocument();
xmlDoc.Load(s);
Trace.WriteLine("Done XmlDocument loading from resource data.");
return xmlDoc;
}
}
/// <summary>
/// Get xml document from <see cref="TestFile"/>.
/// </summary>
/// <returns>File data as xml document.</returns>
protected virtual XmlDocument GetXMLDocFromFile()
{
return GetXMLDocFromFile(TestFile);
}
/// <summary>
/// Get xml document from file.
/// </summary>
/// <param name="file">File to load.</param>
/// <returns>File data as xml document.</returns>
protected virtual XmlDocument GetXMLDocFromFile(string file)
{
if (!File.Exists(file))
Assert.Fail("Test file missing.", file);
var xmlDoc = new XmlDocument();
xmlDoc.LoadXml(File.ReadAllText(file));
return xmlDoc;
}
/// <summary>
/// Get xml document from <see cref="TestResource"/>.
/// </summary>
/// <returns>Resource data as xml document.</returns>
protected virtual XmlDocument GetXMLDocFromResource()
{
return GetResourceXmlDoc(TestResource);
}
/// <summary>
/// Get xml document from resource.
/// </summary>
/// <param name="fullResourceString">Full Unit Test resource string.</param>
/// <returns>Resource data as xml document.</returns>
protected virtual XmlDocument GetXMLDocFromResource(string fullResourceString)
{
return GetResourceXmlDoc(fullResourceString);
}
/// <summary>
/// Load, draw and check svg file.
/// </summary>
/// <param name="xml">Svg file data.</param>
protected virtual void LoadSvg(XmlDocument xml)
{
Trace.WriteLine("SvgDocument open xml.");
var svgDoc = OpenSvg(xml);
Trace.WriteLine("Done SvgDocument open xml.");
Trace.WriteLine("Draw svg.");
var img = DrawSvg(svgDoc);
Trace.WriteLine("Done drawing.");
CheckSvg(svgDoc, img);
}
/// <summary>
/// Open SVG document from XML document.
/// </summary>
/// <param name="xml">XML document.</param>
/// <returns>Open SVG document.</returns>
protected virtual SvgDocument OpenSvg(XmlDocument xml)
{
return SvgDocument.Open(xml);
}
/// <summary>
/// Draw SVG.
/// </summary>
/// <param name="svgDoc">SVG document to draw.</param>
/// <returns>SVG as image.</returns>
protected virtual Image DrawSvg(SvgDocument svgDoc)
{
return svgDoc.Draw();
}
/// <summary>
/// Check svg file data.
/// </summary>
/// <param name="svgDoc">Svg document.</param>
/// <param name="img">Image of svg file from <paramref name="svgDoc"/>.</param>
protected virtual void CheckSvg(SvgDocument svgDoc, Image img)
{
using (var ms = new MemoryStream())
{
img.Save(ms, ImageFormat.Png);
ms.Flush();
Assert.IsTrue(ms.Length >= ExpectedSize, "Svg file does not match expected minimum size.");
}
}
/// <summary>
/// Compare Images.
/// </summary>
/// <param name="img1">Image 1.</param>
/// <param name="img2">Image 2.</param>
/// <returns>If images are completely equal: true; otherwise: false</returns>
protected virtual bool ImagesAreEqual(Bitmap img1, Bitmap img2)
{
float imgEqualPercentage; // To ignore.
return ImagesAreEqual(img1, img2, out imgEqualPercentage);
}
/// <summary>
/// Compare Images.
/// </summary>
/// <param name="img1">Image 1.</param>
/// <param name="img2">Image 2.</param>
/// <param name="imgEqualPercentage">Image equal value in percentage. 0.0% == completely unequal. 100.0% == completely equal.</param>
/// <returns>If images are completely equal: true; otherwise: false</returns>
protected virtual bool ImagesAreEqual(Bitmap img1, Bitmap img2, out float imgEqualPercentage)
{
Bitmap imgDiff; // To ignore.
return ImagesAreEqual(img1, img2, out imgEqualPercentage, out imgDiff);
}
/// <summary>
/// Compare Images.
/// </summary>
/// <param name="img1">Image 1.</param>
/// <param name="img2">Image 2.</param>
/// <param name="imgEqualPercentage">Image equal value in percentage. 0.0% == completely unequal. 100.0% == completely equal.</param>
/// <param name="imgDiff">Image with red pixel where <paramref name="img1"/> and <paramref name="img2"/> are unequal.</param>
/// <returns>If images are completely equal: true; otherwise: false</returns>
protected virtual bool ImagesAreEqual(Bitmap img1, Bitmap img2, out float imgEqualPercentage, out Bitmap imgDiff)
{
// Defaults.
var diffColor = Color.Red;
// Reset.
imgEqualPercentage = 0;
imgDiff = null;
// Requirements.
if (img1 == null)
return false;
if (img2 == null)
return false;
if (img1.Size.Width < 1 && img1.Height < 1)
return false;
if (!img1.Size.Equals(img2.Size))
return false;
// Compare bitmaps.
imgDiff = new Bitmap(img1.Size.Width, img1.Size.Height);
int diffPixelCount = 0;
for (int i = 0; i < img1.Width; ++i)
{
for (int j = 0; j < img1.Height; ++j)
{
Color color;
if ((color = img1.GetPixel(i, j)) == img2.GetPixel(i, j))
{
imgDiff.SetPixel(i, j, color);
}
else
{
++diffPixelCount;
imgDiff.SetPixel(i, j, diffColor);
}
}
}
// Calculate percentage.
int totalPixelCount = img1.Width * img1.Height;
var imgDiffFactor = ((float)diffPixelCount / totalPixelCount);
imgEqualPercentage = imgDiffFactor * 100;
return (imgDiffFactor == 1f);
}
}
}
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