зеркало из https://github.com/wieslawsoltes/SVG.git
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:
Родитель
fe9f714018
Коммит
3b489404bb
|
@ -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))
|
||||
|
|
Загрузка…
Ссылка в новой задаче