From 17f53c0275627b6d4ac2f7378eed988eb596cd2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=AF=9B=E6=89=8D=E5=90=9B?= Date: Fri, 6 Sep 2019 08:34:22 +0800 Subject: [PATCH 1/4] Solving bug of error resolution "ul ol+li{fill:#000000;font-family:ZWBIQX+HelveticaUnicodeMS;}" --- Source/External/ExCSS/Model/Enumerations.cs | 3 +- .../External/ExCSS/Model/Values/TermList.cs | 12 +++++- Source/External/ExCSS/Parser.Blocks.cs | 5 +++ Source/Svg.csproj | 3 +- Tests/Svg.UnitTests/ExCSSLexerTests.cs | 38 +++++++++++++++++++ 5 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 Tests/Svg.UnitTests/ExCSSLexerTests.cs diff --git a/Source/External/ExCSS/Model/Enumerations.cs b/Source/External/ExCSS/Model/Enumerations.cs index d26571b73..2c8b87168 100644 --- a/Source/External/ExCSS/Model/Enumerations.cs +++ b/Source/External/ExCSS/Model/Enumerations.cs @@ -124,7 +124,8 @@ internal enum GrammarSegment Colon, Comma, Semicolon, - Whitespace + Whitespace, + PlusSign } public enum RuleType diff --git a/Source/External/ExCSS/Model/Values/TermList.cs b/Source/External/ExCSS/Model/Values/TermList.cs index c88c20da8..a3fcb3e57 100644 --- a/Source/External/ExCSS/Model/Values/TermList.cs +++ b/Source/External/ExCSS/Model/Values/TermList.cs @@ -52,6 +52,11 @@ public void AddSeparator(TermSeparator termSeparator) AddSeparator(GrammarSegment.Whitespace); break; } + case (TermSeparator.PlusSign): + { + AddSeparator(GrammarSegment.PlusSign); + break; + } } } @@ -103,6 +108,10 @@ public override string ToString() builder.Append(","); break; + case GrammarSegment.PlusSign: + builder.Append("+"); + break; + default: throw new NotImplementedException(); } @@ -117,7 +126,8 @@ public override string ToString() public enum TermSeparator { Comma, - Space + Space, + PlusSign } internal void SetLastTerm(Term term) diff --git a/Source/External/ExCSS/Parser.Blocks.cs b/Source/External/ExCSS/Parser.Blocks.cs index 2aed24135..899f1eb26 100644 --- a/Source/External/ExCSS/Parser.Blocks.cs +++ b/Source/External/ExCSS/Parser.Blocks.cs @@ -534,6 +534,11 @@ private bool ParseValueDelimiter(DelimiterBlock token) _isFraction = true; return true; + case Specification.PlusSign: + _terms.AddSeparator(TermList.TermSeparator.PlusSign); + SetParsingContext(ParsingContext.InValuePool); + return true; + default: return false; } diff --git a/Source/Svg.csproj b/Source/Svg.csproj index d9b7422f0..cc8939a49 100644 --- a/Source/Svg.csproj +++ b/Source/Svg.csproj @@ -152,7 +152,8 @@ - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Tests/Svg.UnitTests/ExCSSLexerTests.cs b/Tests/Svg.UnitTests/ExCSSLexerTests.cs new file mode 100644 index 000000000..3137e59db --- /dev/null +++ b/Tests/Svg.UnitTests/ExCSSLexerTests.cs @@ -0,0 +1,38 @@ +using NUnit.Framework; +using System.Linq; + +namespace Svg.UnitTests +{ + /// + ///This is a test class for CssQueryTest and is intended + ///to contain all CssQueryTest Unit Tests + /// + [TestFixture] + public class ExCSSLexerTests + { + [Test] + public void LexerStylesheet() + { + // + TestStylesheetReader("#a{fill:#000000;font-family:ZWBIQX+HelveticaUnicodeMS;}", 14); + TestStylesheetParser("#a{fill:#000000;font-family:ZWBIQX+HelveticaUnicodeMS;}", 2, "#a"); + TestStylesheetReader("ul ol+li{fill:#000000;font-family:ZWBIQX+HelveticaUnicodeMS;}", 18); + TestStylesheetParser("ul ol+li{fill:#000000;font-family:ZWBIQX+HelveticaUnicodeMS;}", 2, "ul ol+li"); + } + private void TestStylesheetReader(string css, int length) + { + var lexer = new ExCSS.Lexer(new ExCSS.StylesheetReader(css)); + var tokens = lexer.Tokens.ToArray(); + Assert.AreEqual(length, tokens.Length); + } + private void TestStylesheetParser(string css, int declarationsCount, string selector) + { + var styleSheet = new ExCSS.Parser().Parse(css); + var styleRules = styleSheet.StyleRules.ToArray(); + Assert.AreEqual(1, styleRules.Length); + Assert.AreEqual(declarationsCount, styleRules[0].Declarations.Count); + Assert.AreEqual(selector, styleRules[0].Selector.ToString()); + Assert.AreEqual(Svg.ExCSS.RuleType.Style, styleRules[0].RuleType); + } + } +} From 7bc94242bcfd8eccbb4ee51a75817b4b6874a700 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=AF=9B=E6=89=8D=E5=90=9B?= Date: Fri, 18 Oct 2019 17:54:13 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E8=A7=A3=E5=86=B3=20style=20=20=E6=A0=B7?= =?UTF-8?q?=E5=BC=8F=E5=86=85=E5=AD=97=E4=BD=93=E6=97=A0=E6=B3=95=E8=A7=A3?= =?UTF-8?q?=E6=9E=90=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Source/SvgDocument.cs | 50 +++++++++++++++-- Source/SvgElementIdManager.cs | 18 +----- Source/SvgElementStyle.cs | 2 + Source/Utility.cs | 102 ++++++++++++++++++++++++++++++++++ 4 files changed, 150 insertions(+), 22 deletions(-) create mode 100644 Source/Utility.cs diff --git a/Source/SvgDocument.cs b/Source/SvgDocument.cs index 6d44ed6c9..514e2681a 100644 --- a/Source/SvgDocument.cs +++ b/Source/SvgDocument.cs @@ -27,6 +27,8 @@ public class SvgDocument : SvgFragment, ITypeDescriptorContext private SvgElementIdManager _idManager; private Dictionary> _fontDefns = null; + private PrivateFontCollection _privateFonts = null; + private IList _fontFaceDirectives = null; private static int GetSystemDpi() { @@ -36,7 +38,7 @@ private static int GetSystemDpi() isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); #else var platform = Environment.OSVersion.Platform; - isWindows = platform == PlatformID.Win32NT; + isWindows = platform == PlatformID.Win32NT; #endif if (isWindows) @@ -74,6 +76,33 @@ group f by f.FontFamily into family } return _fontDefns; } + internal FontFamily[] PrivateFontDefns() + { + if (_privateFonts == null) + { + _privateFonts = new PrivateFontCollection(); + if (_fontFaceDirectives != null) + { + foreach (var item in _fontFaceDirectives) + { + try + { + var bytes = downBytes(item.Src, this.BaseUri); + + IntPtr MeAdd = Marshal.AllocHGlobal(bytes.Length); + Marshal.Copy(bytes, 0, MeAdd, bytes.Length); + _privateFonts.AddMemoryFont(MeAdd, bytes.Length); + } + catch (Exception ex) + { + Trace.TraceWarning(ex.Message); + } + } + } + } + return _privateFonts.Families; + } + /// /// Initializes a new instance of the class. @@ -182,15 +211,15 @@ public virtual TSvgElement GetElementById(string id) where TSvgElem /// Boolean whether the system is capable of using GDI+ public static bool SystemIsGdiPlusCapable() { - try + try { EnsureSystemIsGdiPlusCapable(); } - catch(SvgGdiPlusCannotBeLoadedException) + catch (SvgGdiPlusCannotBeLoadedException) { return false; } - catch(Exception) + catch (Exception) { //If somehow another type of exception is raised by the ensure function we will let it bubble up, since that might indicate other issues/problems throw; @@ -218,7 +247,7 @@ public static void EnsureSystemIsGdiPlusCapable() throw new SvgGdiPlusCannotBeLoadedException(e); } //If the Matrix creation is causing another type of exception we should just raise that one - throw; + throw; } } @@ -482,12 +511,21 @@ public static SvgDocument Open(string path) } } } + + svgDocument._fontFaceDirectives = sheet.FontFaceDirectives; } svgDocument?.FlushStyles(true); return svgDocument; } - + private static byte[] downBytes(string url, Uri baseUri) + { + var urlString = Utility.GetUrlString(url); + var uri = new Uri(urlString, UriKind.RelativeOrAbsolute); + if (!uri.IsAbsoluteUri && baseUri != null) + uri = new Uri(baseUri, uri); + return Utility.GetBytesFromUri(uri)?.DataBytes; + } /// /// Opens an SVG document from the specified . /// diff --git a/Source/SvgElementIdManager.cs b/Source/SvgElementIdManager.cs index a1445ac80..469255205 100644 --- a/Source/SvgElementIdManager.cs +++ b/Source/SvgElementIdManager.cs @@ -21,7 +21,7 @@ public class SvgElementIdManager /// An of one exists with the specified ID; otherwise false. public virtual SvgElement GetElementById(string id) { - id = GetUrlString(id); + id = Utility.GetUrlString(id); if (id.StartsWith("#")) { id = id.Substring(1); @@ -35,7 +35,7 @@ public virtual SvgElement GetElementById(string id) public virtual SvgElement GetElementById(Uri uri) { - var urlString = GetUrlString(uri.ToString()); + var urlString = Utility.GetUrlString(uri.ToString()); if (!urlString.StartsWith("#")) { @@ -70,20 +70,6 @@ public virtual SvgElement GetElementById(Uri uri) return GetElementById(urlString); } - - private static string GetUrlString(string url) - { - url = url.Trim(); - if (url.StartsWith("url(") && url.EndsWith(")")) - { - url = new StringBuilder(url).Remove(url.Length - 1, 1).Remove(0, 4).ToString().Trim(); - - if ((url.StartsWith("\"") && url.EndsWith("\"")) || (url.StartsWith("'") && url.EndsWith("'"))) - url = new StringBuilder(url).Remove(url.Length - 1, 1).Remove(0, 1).ToString().Trim(); - } - return url; - } - /// /// Adds the specified for ID management. /// diff --git a/Source/SvgElementStyle.cs b/Source/SvgElementStyle.cs index 2986e5236..f3e005b8b 100644 --- a/Source/SvgElementStyle.cs +++ b/Source/SvgElementStyle.cs @@ -442,6 +442,8 @@ public static object ValidateFontFamily(string fontFamilyList, SvgDocument doc) foreach (var f in fontParts) { if (doc != null && doc.FontDefns().TryGetValue(f, out sFaces)) return sFaces; + family = doc.PrivateFontDefns().FirstOrDefault(ff => string.Equals(ff.Name, f, StringComparison.OrdinalIgnoreCase)); + if (family != null) return family; family = SvgFontManager.FindFont(f); if (family != null) return family; family = PrivateFonts.Families.FirstOrDefault(ff => string.Equals(ff.Name, f, StringComparison.OrdinalIgnoreCase)); diff --git a/Source/Utility.cs b/Source/Utility.cs new file mode 100644 index 000000000..8b1ba5339 --- /dev/null +++ b/Source/Utility.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml; +using System.IO; +using System.Collections.Specialized; +using System.Net; +using System.Diagnostics; +using System.Drawing; + +namespace Svg +{ + internal static class Utility + { + public static string GetUrlString(string url) + { + url = url.Trim(); + if (url.StartsWith("url(") && url.EndsWith(")")) + { + url = new StringBuilder(url).Remove(url.Length - 1, 1).Remove(0, 4).ToString().Trim(); + + if ((url.StartsWith("\"") && url.EndsWith("\"")) || (url.StartsWith("'") && url.EndsWith("'"))) + url = new StringBuilder(url).Remove(url.Length - 1, 1).Remove(0, 1).ToString().Trim(); + } + return url; + } + public static FileData GetBytesFromUri(Uri uri) + { + if (uri.IsAbsoluteUri && uri.Scheme == "data") + return GetBytesFromDataUri(uri.ToString()); + var request = WebRequest.Create(uri); + using (var response = request.GetResponse()) + { + var responseStream = response.GetResponseStream(); + if (responseStream.CanSeek) + responseStream.Position = 0; + + byte[] receiveBytes = new byte[1024 * 100]; + var ms = new MemoryStream(); + var size = 0; + do + { + size = responseStream.Read(receiveBytes, 0, receiveBytes.Length); + ms.Write(receiveBytes, 0, size); + } while (size > 0); + return new FileData(ms.GetBuffer(), response.ContentType, null); + } + } + public static FileData GetBytesFromDataUri(string uriString) + { + var headerStartIndex = 5; + var headerEndIndex = uriString.IndexOf(",", headerStartIndex); + if (headerEndIndex < 0 || headerEndIndex + 1 >= uriString.Length) + throw new Exception("Invalid data URI"); + + var mimeType = "text/plain"; + var charset = System.Text.Encoding.ASCII; + var base64 = false; + + var headers = new List(uriString.Substring(headerStartIndex, headerEndIndex - headerStartIndex).Split(';')); + if (headers[0].Contains("/")) + { + mimeType = headers[0].Trim(); + headers.RemoveAt(0); + } + + if (headers.Count > 0 && headers[headers.Count - 1].Trim().Equals("base64", StringComparison.InvariantCultureIgnoreCase)) + { + base64 = true; + headers.RemoveAt(headers.Count - 1); + } + + foreach (var param in headers) + { + var p = param.Split('='); + if (p.Length < 2) + continue; + + var attribute = p[0].Trim(); + if (attribute.Equals("charset", StringComparison.InvariantCultureIgnoreCase)) + charset = System.Text.Encoding.GetEncoding(p[1].Trim()); + } + + var data = uriString.Substring(headerEndIndex + 1); + var dataBytes = base64 ? Convert.FromBase64String(data) : (charset ?? Encoding.UTF8).GetBytes(data); + return new FileData(dataBytes, mimeType, charset); + } + } + internal class FileData + { + public FileData(byte[] dataBytes, string mimeType, Encoding charset) + { + this.Charset = charset; + this.MimeType = mimeType; + this.DataBytes = dataBytes; + } + public Encoding Charset { get; } + public string MimeType { get; } + public byte[] DataBytes { get; } + } +} \ No newline at end of file From 13e9baef524189ff3de53b605be3c1c0bf48215b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=AF=9B=E6=89=8D=E5=90=9B?= Date: Sat, 19 Oct 2019 08:14:47 +0800 Subject: [PATCH 3/4] Solve the bug of test exception --- Source/SvgElementStyle.cs | 2 +- Source/Utility.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/SvgElementStyle.cs b/Source/SvgElementStyle.cs index f3e005b8b..53472226b 100644 --- a/Source/SvgElementStyle.cs +++ b/Source/SvgElementStyle.cs @@ -442,7 +442,7 @@ public static object ValidateFontFamily(string fontFamilyList, SvgDocument doc) foreach (var f in fontParts) { if (doc != null && doc.FontDefns().TryGetValue(f, out sFaces)) return sFaces; - family = doc.PrivateFontDefns().FirstOrDefault(ff => string.Equals(ff.Name, f, StringComparison.OrdinalIgnoreCase)); + family = doc?.PrivateFontDefns()?.FirstOrDefault(ff => string.Equals(ff.Name, f, StringComparison.OrdinalIgnoreCase)); if (family != null) return family; family = SvgFontManager.FindFont(f); if (family != null) return family; diff --git a/Source/Utility.cs b/Source/Utility.cs index 8b1ba5339..7c375c65a 100644 --- a/Source/Utility.cs +++ b/Source/Utility.cs @@ -16,7 +16,7 @@ internal static class Utility public static string GetUrlString(string url) { url = url.Trim(); - if (url.StartsWith("url(") && url.EndsWith(")")) + if (url.StartsWith("url(", StringComparison.OrdinalIgnoreCase) && url.EndsWith(")")) { url = new StringBuilder(url).Remove(url.Length - 1, 1).Remove(0, 4).ToString().Trim(); From 2fdeb18b5116d1ead513ba7ac07171a10e636e77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=AF=9B=E6=89=8D=E5=90=9B?= Date: Sat, 19 Oct 2019 08:49:31 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E6=B8=85=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Source/Utility.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Source/Utility.cs b/Source/Utility.cs index 7c375c65a..52dec1ef7 100644 --- a/Source/Utility.cs +++ b/Source/Utility.cs @@ -1,13 +1,8 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Text; -using System.Xml; using System.IO; -using System.Collections.Specialized; using System.Net; -using System.Diagnostics; -using System.Drawing; namespace Svg {