private void canvas_CreateResources(Microsoft.Graphics.Canvas.UI.Xaml.CanvasControl sender, Microsoft.Graphics.Canvas.UI.CanvasCreateResourcesEventArgs args) //Yet more boilerplate code, this runs before canvas_Draw and is meant to be used to load any assets that need to be loaded from files { Board = LoadAsset(sender, "Chess Board"); //These are all basically identical; they set the value of each variable to whatever LoadAsset returns given the file name. PawnBlack = LoadAsset(sender, "PawnBlack"); PawnWhite = LoadAsset(sender, "PawnWhite"); KnightBlack = LoadAsset(sender, "KnightBlack"); KnightWhite = LoadAsset(sender, "KnightWhite"); BishopBlack = LoadAsset(sender, "BishopBlack"); BishopWhite = LoadAsset(sender, "BishopWhite"); RookBlack = LoadAsset(sender, "RookBlack"); RookWhite = LoadAsset(sender, "RookWhite"); QueenBlack = LoadAsset(sender, "QueenBlack"); QueenWhite = LoadAsset(sender, "QueenWhite"); KingBlack = LoadAsset(sender, "KingBlack"); KingWhite = LoadAsset(sender, "KingWhite"); PlaceholderBlack = CanvasSvgDocument.LoadFromXml(sender, PawnBlack.GetXml().Replace("opacity=\"1\"", "opacity=\"0.5\"")); PlaceholderWhite = CanvasSvgDocument.LoadFromXml(sender, PawnWhite.GetXml().Replace("opacity=\"1\"", "opacity=\"0.5\"")); translator = new RenderTranslator(PawnBlack, PawnWhite, KnightBlack, KnightWhite, BishopBlack, BishopWhite, RookBlack, RookWhite, QueenBlack, QueenWhite, KingBlack, KingWhite, PlaceholderBlack, PlaceholderWhite); pieceSize = new Size(sender.Size.Width / 16, sender.Size.Height / 16); pawn = new Pawn(new Position(3, 3), true); MoveHighlight = new Color(); MoveHighlight.A = 100; MoveHighlight.R = 255; MoveHighlight.G = 255; MoveHighlight.B = 255; }
public static Task WriteSvgAsync(CanvasSvgDocument document, IStorageFile file) { StringBuilder sb = new StringBuilder(); sb.AppendLine("<!-- Exported by Character Map UWP -->"); sb.Append(document.GetXml()); return(FileIO.WriteTextAsync(file, sb.ToString()).AsTask()); }
public static Task WriteSvgAsync(CanvasSvgDocument document, IStorageFile file) { return(WriteSvgAsync(document.GetXml(), file)); }
public static string GetSVG( ExportStyle style, CharacterRenderingOptions options, Character selectedChar) { // We want to prepare geometry at 1024px, so force this options = options with { FontSize = 1024 }; using var typography = options.CreateCanvasTypography(); CanvasDevice device = Utils.CanvasDevice; Color textColor = style == ExportStyle.Black ? Colors.Black : Colors.White; // If COLR format (e.g. Segoe UI Emoji), we have special export path. if (style == ExportStyle.ColorGlyph && options.Analysis.HasColorGlyphs && !options.Analysis.GlyphFormats.Contains(GlyphImageFormat.Svg)) { NativeInterop interop = Utils.GetInterop(); List <string> paths = new List <string>(); Rect bounds = Rect.Empty; // Try to find the bounding box of all glyph layers combined foreach (var thing in options.Analysis.Indicies) { var path = interop.GetPathDatas(options.Variant.FontFace, thing.ToArray()).First(); paths.Add(path.Path); if (!path.Bounds.IsEmpty) { var left = Math.Min(bounds.Left, path.Bounds.Left); var top = Math.Min(bounds.Top, path.Bounds.Top); var right = Math.Max(bounds.Right, path.Bounds.Right); var bottom = Math.Max(bounds.Bottom, path.Bounds.Bottom); bounds = new Rect( left, top, right - left, bottom - top); } } using CanvasSvgDocument document = Utils.GenerateSvgDocument(device, bounds, paths, options.Analysis.Colors, invertBounds: false); return(document.GetXml()); } var data = GetGeometry(selectedChar, options); string GetMonochrome() { using CanvasSvgDocument document = string.IsNullOrWhiteSpace(data.Path) ? new CanvasSvgDocument(Utils.CanvasDevice) : Utils.GenerateSvgDocument(device, data.Bounds, data.Path, textColor); return(document.GetXml()); } // If the font uses SVG glyphs, we can extract the raw SVG from the font file if (options.Analysis.GlyphFormats.Contains(GlyphImageFormat.Svg)) { string str = null; IBuffer b = GetGlyphBuffer(options.Variant.FontFace, selectedChar.UnicodeIndex, GlyphImageFormat.Svg); if (b.Length > 2 && b.GetByte(0) == 31 && b.GetByte(1) == 139) { using var stream = b.AsStream(); using var gzip = new GZipStream(stream, CompressionMode.Decompress); using var reader = new StreamReader(gzip); str = reader.ReadToEnd(); } else { using var dataReader = DataReader.FromBuffer(b); dataReader.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf8; str = dataReader.ReadString(b.Length); } if (str.StartsWith("<?xml")) { str = str.Remove(0, str.IndexOf(">") + 1); } str = str.TrimStart(); try { using (CanvasSvgDocument document = CanvasSvgDocument.LoadFromXml(Utils.CanvasDevice, str)) { // We need to transform the SVG to fit within the default document bounds, as characters // are based *above* the base origin of (0,0) as (0,0) is the Baseline (bottom left) position for a character, // so by default a will appear out of bounds of the default SVG viewport (towards top left). //if (!document.Root.IsAttributeSpecified("viewBox")) // Specified viewbox requires baseline transform? { // We'll regroup all the elements inside a "g" / group tag, // and apply a transform to the "g" tag to try and put in // in the correct place. There's probably a more accurate way // to do this by directly setting the root viewBox, if anyone // can find the correct calculation... List <ICanvasSvgElement> elements = new List <ICanvasSvgElement>(); double minTop = 0; double minLeft = double.MaxValue; double maxWidth = double.MinValue; double maxHeight = double.MinValue; void ProcessChildren(CanvasSvgNamedElement root) { CanvasSvgNamedElement ele = root.FirstChild as CanvasSvgNamedElement; while (true) { CanvasSvgNamedElement next = root.GetNextSibling(ele) as CanvasSvgNamedElement; if (ele.Tag == "g") { ProcessChildren(ele); } else if (ele.Tag == "path") { // Create a XAML geometry to try and find the bounds of each character // Probably more efficient to do in Win2D, but far less code to do with XAML. Geometry gm = XamlBindingHelper.ConvertValue(typeof(Geometry), ele.GetStringAttribute("d")) as Geometry; minTop = Math.Min(minTop, gm.Bounds.Top); minLeft = Math.Min(minLeft, gm.Bounds.Left); maxWidth = Math.Max(maxWidth, gm.Bounds.Width); maxHeight = Math.Max(maxHeight, gm.Bounds.Height); } ele = next; if (ele == null) { break; } } } ProcessChildren(document.Root); double top = minTop < 0 ? minTop : 0; double left = minLeft; document.Root.SetRectangleAttribute("viewBox", new Rect(left, top, data.Bounds.Width, data.Bounds.Height)); } return(document.GetXml()); } } catch { // Certain fonts seem to have their SVG glyphs encoded with... I don't even know what encoding. // for example: https://github.com/adobe-fonts/emojione-color // In these cases, fallback to monochrome black return(GetMonochrome()); } } else { return(GetMonochrome()); } }