Fix some issues for netstandard2.0 target (#630)

* Bring ColorConverter for netstandard2.0
* Update System.Drawing.Common package to latest version
* Update ReleaseNotes.md
This commit is contained in:
Wiesław Šoltés 2020-01-01 15:04:52 +01:00 коммит произвёл mrbean-bremen
Родитель fe9f714018
Коммит 3b489404bb
5 изменённых файлов: 440 добавлений и 12 удалений

Просмотреть файл

@ -6,7 +6,6 @@ using System.Threading;
namespace Svg
{
#if !NETSTANDARD20
/// <summary>
/// Converts string representations of colours into <see cref="Color"/> objects.
/// </summary>
@ -278,5 +277,4 @@ namespace Svg
return rgb;
}
}
#endif
}

Просмотреть файл

@ -7,14 +7,13 @@ namespace Svg
{
internal class SvgPaintServerFactory : TypeConverter
{
#if !NETSTANDARD20
private static readonly SvgColourConverter _colourConverter;
static SvgPaintServerFactory()
{
_colourConverter = new SvgColourConverter();
}
#endif
public static SvgPaintServer Create(string value, SvgDocument document)
{
if (value == null)
@ -41,12 +40,8 @@ namespace Svg
return new SvgDeferredPaintServer(id, fallbackServer);
}
#if NETSTANDARD20
return SvgPaintServer.NotSet;
#else
// Otherwise try and parse as colour
return new SvgColourServer((Color)_colourConverter.ConvertFrom(colorValue));
#endif
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
@ -84,13 +79,13 @@ namespace Svg
// check for constant
if (value == SvgPaintServer.None || value == SvgPaintServer.Inherit || value == SvgPaintServer.NotSet)
return value.ToString();
#if !NETSTANDARD20
var colourServer = value as SvgColourServer;
if (colourServer != null)
{
return new SvgColourConverter().ConvertTo(colourServer.Colour, typeof(string));
}
#endif
var deferred = value as SvgDeferredPaintServer;
if (deferred != null)
{

Просмотреть файл

@ -34,6 +34,7 @@
</PackageReleaseNotes>
<PackageProjectUrl>https://github.com/vvvv/SVG</PackageProjectUrl>
<PackageIconUrl>https://www.w3.org/Icons/SVG/svg-logo-v.png</PackageIconUrl>
<LangVersion>7.3</LangVersion>
</PropertyGroup>
<ItemGroup>
@ -77,7 +78,7 @@
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp2.2'">
<PackageReference Include="System.Drawing.Common">
<Version>4.5.1</Version>
<Version>4.7.0</Version>
</PackageReference>
<PackageReference Include="System.ObjectModel">
<Version>4.3.0</Version>
@ -86,7 +87,7 @@
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="System.Drawing.Common">
<Version>4.5.1</Version>
<Version>4.7.0</Version>
</PackageReference>
<PackageReference Include="System.ObjectModel">
<Version>4.3.0</Version>

Просмотреть файл

@ -0,0 +1,433 @@
// https://github.com/dotnet/runtime/blob/master/src/libraries/System.Drawing.Common/src/System/Drawing/ColorConverter.cs
#if NETSTANDARD20
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace System.Drawing {
using System.Runtime.Serialization.Formatters;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Win32;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.Globalization;
using System.Reflection;
using System.Threading;
/// <include file='doc\ColorConverter.uex' path='docs/doc[@for="ColorConverter"]/*' />
/// <devdoc>
/// ColorConverter is a class that can be used to convert
/// colors from one data type to another. Access this
/// class through the TypeDescriptor.
/// </devdoc>
public class ColorConverter : TypeConverter {
private static string ColorConstantsLock = "colorConstants";
private static Hashtable colorConstants;
private static string SystemColorConstantsLock = "systemColorConstants";
private static Hashtable systemColorConstants;
private static string ValuesLock = "values";
private static StandardValuesCollection values;
/// <include file='doc\ColorConverter.uex' path='docs/doc[@for="ColorConverter.ColorConverter"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public ColorConverter() {
}
/// <include file='doc\ColorConverter.uex' path='docs/doc[@for="ColorConverter.Colors"]/*' />
/// <devdoc>
/// Hashtable of color / value pairs (color name is key)
/// for standard colors.
/// </devdoc>
private static Hashtable Colors {
get {
if (colorConstants == null) {
lock(ColorConstantsLock) {
if (colorConstants == null) {
Hashtable tempHash = new Hashtable(StringComparer.OrdinalIgnoreCase);
FillConstants(tempHash, typeof(Color));
colorConstants = tempHash;
}
}
}
return colorConstants;
}
}
/// <include file='doc\ColorConverter.uex' path='docs/doc[@for="ColorConverter.SystemColors"]/*' />
/// <devdoc>
/// Hashtable of color / value pairs (color name is key)
/// for system colors.
/// </devdoc>
private static Hashtable SystemColors {
get {
if (systemColorConstants == null) {
lock (SystemColorConstantsLock) {
if (systemColorConstants == null) {
Hashtable tempHash = new Hashtable(StringComparer.OrdinalIgnoreCase);
FillConstants(tempHash, typeof(System.Drawing.SystemColors));
systemColorConstants = tempHash;
}
}
}
return systemColorConstants;
}
}
/// <include file='doc\ColorConverter.uex' path='docs/doc[@for="ColorConverter.CanConvertFrom"]/*' />
/// <devdoc>
/// Determines if this converter can convert an object in the given source
/// type to the native type of the converter.
/// </devdoc>
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) {
if (sourceType == typeof(string)) {
return true;
}
return base.CanConvertFrom(context, sourceType);
}
/// <include file='doc\ColorConverter.uex' path='docs/doc[@for="ColorConverter.CanConvertTo"]/*' />
/// <devdoc>
/// <para>Gets a value indicating whether this converter can
/// convert an object to the given destination type using the context.</para>
/// </devdoc>
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) {
if (destinationType == typeof(InstanceDescriptor)) {
return true;
}
return base.CanConvertTo(context, destinationType);
}
internal static object GetNamedColor(string name) {
object color = null;
// First, check to see if this is a standard name.
//
color = Colors[name];
if (color != null) {
return color;
}
// Ok, how about a system color?
//
color = SystemColors[name];
return color;
}
/// <include file='doc\ColorConverter.uex' path='docs/doc[@for="ColorConverter.ConvertFrom"]/*' />
/// <devdoc>
/// Converts the given object to the converter's native type.
/// </devdoc>
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) {
string strValue = value as string;
if (strValue != null) {
object obj = null;
string text = strValue.Trim();
if (text.Length == 0) {
obj = Color.Empty;
}
else {
// First, check to see if this is a standard name.
//
obj = GetNamedColor(text);
if (obj == null) {
if (culture == null) {
culture = CultureInfo.CurrentCulture;
}
char sep = culture.TextInfo.ListSeparator[0];
bool tryMappingToKnownColor = true;
TypeConverter intConverter = TypeDescriptor.GetConverter(typeof(int));
// If the value is a 6 digit hex number only, then
// we want to treat the Alpha as 255, not 0
//
if (text.IndexOf(sep) == -1) {
// text can be '' (empty quoted string)
if (text.Length >= 2 && (text[0] == '\'' || text[0] == '"') && text[0] == text[text.Length -1]) {
// In quotes means a named value
string colorName = text.Substring(1, text.Length - 2);
obj = Color.FromName(colorName);
tryMappingToKnownColor = false;
}
else if ((text.Length == 7 && text[0] == '#') ||
(text.Length == 8 && (text.StartsWith("0x") || text.StartsWith("0X"))) ||
(text.Length == 8 && (text.StartsWith("&h") || text.StartsWith("&H")))) {
// Note: ConvertFromString will raise exception if value cannot be converted.
obj = Color.FromArgb(unchecked((int)(0xFF000000 | (uint)(int)intConverter.ConvertFromString(context, culture, text))));
}
}
// Nope. Parse the RGBA from the text.
//
if (obj == null) {
string[] tokens = text.Split(new char[] {sep});
int[] values = new int[tokens.Length];
for (int i = 0; i < values.Length; i++) {
values[i] = unchecked((int)intConverter.ConvertFromString(context, culture, tokens[i]));
}
// We should now have a number of parsed integer values.
// We support 1, 3, or 4 arguments:
//
// 1 -- full ARGB encoded
// 3 -- RGB
// 4 -- ARGB
//
switch (values.Length) {
case 1:
obj = Color.FromArgb(values[0]);
break;
case 3:
obj = Color.FromArgb(values[0], values[1], values[2]);
break;
case 4:
obj = Color.FromArgb(values[0], values[1], values[2], values[3]);
break;
}
tryMappingToKnownColor = true;
}
if ((obj != null) && tryMappingToKnownColor) {
// Now check to see if this color matches one of our known colors.
// If it does, then substitute it. We can only do this for "Colors"
// because system colors morph with user settings.
//
int targetARGB = ((Color)obj).ToArgb();
foreach (Color c in Colors.Values) {
if (c.ToArgb() == targetARGB) {
obj = c;
break;
}
}
}
}
#if NETSTANDARD20
if (obj == null)
{
throw new ArgumentException(nameof(value));
}
#else
if (obj == null) {
throw new ArgumentException(SR.Format(SR.InvalidColor, text));
}
#endif
}
return obj;
}
return base.ConvertFrom(context, culture, value);
}
/// <include file='doc\ColorConverter.uex' path='docs/doc[@for="ColorConverter.ConvertTo"]/*' />
/// <devdoc>
/// Converts the given object to another type. The most common types to convert
/// are to and from a string object. The default implementation will make a call
/// to ToString on the object if the object is valid and if the destination
/// type is string. If this cannot convert to the desitnation type, this will
/// throw a NotSupportedException.
/// </devdoc>
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) {
if (destinationType == null) {
throw new ArgumentNullException(nameof(destinationType));
}
if ( value is Color ){
if (destinationType == typeof(string)) {
Color c = (Color)value;
if (c == Color.Empty) {
return string.Empty;
}
else {
// If this is a known color, then Color can provide its own
// name. Otherwise, we fabricate an ARGB value for it.
//
#if NETSTANDARD20
if (c.IsNamedColor) {
return "'" + c.Name + "'";
}
#else
if (c.IsKnownColor) {
return c.Name;
}
else if (c.IsNamedColor) {
return "'" + c.Name + "'";
#endif
else
{
if (culture == null) {
culture = CultureInfo.CurrentCulture;
}
string sep = culture.TextInfo.ListSeparator + " ";
TypeConverter intConverter = TypeDescriptor.GetConverter(typeof(int));
string[] args;
int nArg = 0;
if (c.A < 255) {
args = new string[4];
args[nArg++] = intConverter.ConvertToString(context, culture, (object)c.A);
}
else {
args = new string[3];
}
// Note: ConvertToString will raise exception if value cannot be converted.
args[nArg++] = intConverter.ConvertToString(context, culture, (object)c.R);
args[nArg++] = intConverter.ConvertToString(context, culture, (object)c.G);
args[nArg++] = intConverter.ConvertToString(context, culture, (object)c.B);
// Now slam all of these together with the fantastic Join
// method.
//
return string.Join(sep, args);
}
}
}
if (destinationType == typeof(InstanceDescriptor)) {
MemberInfo member = null;
object[] args = null;
Color c = (Color)value;
if (c.IsEmpty) {
member = typeof(Color).GetField("Empty");
}
#if !NETSTANDARD20
else if (c.IsSystemColor) {
member = typeof(SystemColors).GetProperty(c.Name);
}
else if (c.IsKnownColor) {
member = typeof(Color).GetProperty(c.Name);
}
#endif
else if (c.A != 255) {
member = typeof(Color).GetMethod("FromArgb", new Type[] {typeof(int), typeof(int), typeof(int), typeof(int)});
args = new object[] {c.A, c.R, c.G, c.B};
}
else if (c.IsNamedColor) {
member = typeof(Color).GetMethod("FromName", new Type[] {typeof(string)});
args = new object[] {c.Name};
}
else {
member = typeof(Color).GetMethod("FromArgb", new Type[] {typeof(int), typeof(int), typeof(int)});
args = new object[] {c.R, c.G, c.B};
}
Debug.Assert(member != null, "Could not convert color to member. Did someone change method name / signature and not update Colorconverter?");
if (member != null) {
return new InstanceDescriptor(member, args);
}
else {
return null;
}
}
}
return base.ConvertTo(context, culture, value, destinationType);
}
/// <include file='doc\ColorConverter.uex' path='docs/doc[@for="ColorConverter.FillConstants"]/*' />
/// <devdoc>
/// Fills the given hashtable with field name / value pairs. It walks all public static
/// properties of enumType that have a property type of Color.
/// </devdoc>
private static void FillConstants(Hashtable hash, Type enumType) {
MethodAttributes attrs = MethodAttributes.Public | MethodAttributes.Static;
PropertyInfo[] props = enumType.GetProperties();
for (int i = 0; i < props.Length; i++) {
PropertyInfo prop = props[i];
if (prop.PropertyType == typeof(Color)) {
MethodInfo method = prop.GetGetMethod();
if (method != null && (method.Attributes & attrs) == attrs) {
object[] tempIndex = null;
hash[prop.Name] = prop.GetValue(null, tempIndex);
}
}
}
}
/// <include file='doc\ColorConverter.uex' path='docs/doc[@for="ColorConverter.GetStandardValues"]/*' />
/// <devdoc>
/// Retrieves a collection containing a set of standard values
/// for the data type this validator is designed for. This
/// will return null if the data type does not support a
/// standard set of values.
/// </devdoc>
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) {
if (values == null) {
lock (ValuesLock) {
if (values == null) {
// We must take the value from each hashtable and combine them.
//
ArrayList arrayValues = new ArrayList();
arrayValues.AddRange(Colors.Values);
arrayValues.AddRange(SystemColors.Values);
// Now, we have a couple of colors that have the same names but
// are identical values. Look for these and remove them. Too
// bad this is n^2.
//
int count = arrayValues.Count;
for (int i = 0; i < count - 1; i++) {
for (int j = i + 1; j < count; j++) {
if (arrayValues[i].Equals(arrayValues[j])) {
// Remove this item!
//
arrayValues.RemoveAt(j);
count--;
j--;
}
}
}
// Sort the array.
//
arrayValues.Sort(0, arrayValues.Count, new ColorComparer());
values = new StandardValuesCollection(arrayValues.ToArray());
}
}
}
return values;
}
/// <include file='doc\ColorConverter.uex' path='docs/doc[@for="ColorConverter.GetStandardValuesSupported"]/*' />
/// <devdoc>
/// Determines if this object supports a standard set of values
/// that can be picked from a list.
/// </devdoc>
public override bool GetStandardValuesSupported(ITypeDescriptorContext context) {
return true;
}
/// <include file='doc\ColorConverter.uex' path='docs/doc[@for="ColorConverter.ColorComparer"]/*' />
/// <devdoc>
/// IComparer for color values. This takes color values but compares their
/// names.
/// </devdoc>
private class ColorComparer : IComparer {
public int Compare(object left, object right) {
Color cLeft = (Color)left;
Color cRight = (Color)right;
return string.Compare(cLeft.Name, cRight.Name, false, CultureInfo.InvariantCulture);
}
}
}
}
#endif

Просмотреть файл

@ -10,6 +10,7 @@ The release versions are NuGet releases.
### Enhancements
* check that there is a `moveto` command at the beginning of a path (see [PR #616](https://github.com/vvvv/SVG/pull/616))
* add support for `<a>` element (see [#626](https://github.com/vvvv/SVG/issues/626) and [PR #628](https://github.com/vvvv/SVG/pull/628)))
* added ColorConverter from dotnet runtime codebase to make Netstandard 2.0 target more complete (see [PR #630](https://github.com/vvvv/SVG/pull/630))
### Fixes
* fixed nested svg tags not rendered properly (see [#622](https://github.com/vvvv/SVG/issues/622))