Exemplo n.º 1
0
        public static (string Path, Rect Bounds) GetGeometry(
            Character selectedChar,
            CharacterRenderingOptions options)
        {
            /*
             * Note: this only constructs the monochrome version
             * of the glyph.
             *
             * Drop into C++/CX for color / multi-variant glyphs.
             */

            using CanvasGeometry geom = CreateGeometry(selectedChar, options);
            var bounds  = geom.ComputeBounds();
            var interop = Utils.GetInterop();
            var s       = interop.GetPathData(geom);

            if (string.IsNullOrWhiteSpace(s.Path))
            {
                return(s.Path, bounds);
            }

            var t = s.Transform.Translation;

            bounds = new Rect(t.X - bounds.Left, -bounds.Top + t.Y, bounds.Width, bounds.Height);
            return(s.Path, bounds);
        }
Exemplo n.º 2
0
        public static CanvasGeometry CreateGeometry(
            Character selectedChar,
            CharacterRenderingOptions options)
        {
            CanvasDevice device = Utils.CanvasDevice;

            /* SVG Exports render at fixed size - but a) they're vectors, and b) they're
             * inside an auto-scaling viewport. So render-size is *largely* pointless */
            float canvasH = options.FontSize, canvasW = options.FontSize, fontSize = options.FontSize;

            using var typography = options.CreateCanvasTypography();
            using (CanvasTextLayout layout = new CanvasTextLayout(device, $"{selectedChar.Char}", new CanvasTextFormat
            {
                FontSize = fontSize,
                FontFamily = options.Variant.Source,
                FontStretch = options.Variant.FontFace.Stretch,
                FontWeight = options.Variant.FontFace.Weight,
                FontStyle = options.Variant.FontFace.Style,
                HorizontalAlignment = CanvasHorizontalAlignment.Center
            }, canvasW, canvasH))
            {
                layout.SetTypography(0, 1, typography);
                layout.Options = options.Analysis.GlyphFormats.Contains(GlyphImageFormat.Svg) ? CanvasDrawTextOptions.EnableColorFont : CanvasDrawTextOptions.Default;

                return(CanvasGeometry.CreateText(layout));
            }
        }
Exemplo n.º 3
0
        /// <summary>
        /// Creates an SVG / XAML path syntax compatible string representing the filled geometry
        /// of a glyph.
        /// </summary>
        /// <param name="c"></param>
        /// <param name="o"></param>
        /// <returns></returns>
        public static string GetOutlineGeometry(
            Character c,
            CharacterRenderingOptions o)
        {
            /*
             * We use a cache because creating geometry is currently a little bit more expensive than it needs to be
             * (we're not actually reading the data from the font, but using D2D to "Draw" the geometry to a custom sink)
             * and we might be creating it multiple times for a single glyph depending on how our dev providers are
             * configured, so a small cache will help performance
             */

            if (_geometryCache.FirstOrDefault(p => p.Key is GeometryCacheEntry e && e.Options == o && e.Character == c) is KeyValuePair <GeometryCacheEntry, string> pair &&
                pair.Value != null)
            {
                return(pair.Value);
            }


            string pathIconData = null;

            if (o.Variant != null)
            {
                // We use a font size of 20 as this metrically maps to the size of SegoeMDL2 icons used
                // in FontIcon / SymbolIcon controls.
                using var geom = ExportManager.CreateGeometry(c, o with { FontSize = 20 });
Exemplo n.º 4
0
 /// <summary>
 /// Creates the context menu for the Font List or the "..." button.
 /// Both of these have a font as their main target.
 /// </summary>
 /// <param name="menu"></param>
 /// <param name="font"></param>
 /// <param name="variant"></param>
 /// <param name="headerContent"></param>
 /// <param name="standalone"></param>
 /// <param name="showAdvanced"></param>
 public static void CreateMenu(
     MenuFlyout menu,
     InstalledFont font,
     CharacterRenderingOptions options,
     FrameworkElement headerContent,
     bool standalone,
     bool showAdvanced = false)
 {
     MainViewModel main = Ioc.Default.GetService <MainViewModel>();
Exemplo n.º 5
0
 /// <summary>
 /// Creates dev providers for a glyph.
 /// Register all known providers here.
 /// </summary>
 /// <param name="o"></param>
 /// <param name="c"></param>
 /// <returns></returns>
 public static IReadOnlyList <DevProviderBase> GetProviders(CharacterRenderingOptions o, Character c)
 {
     return(new List <DevProviderBase>
     {
         new DevProviderNone(o, c),
         new XamlDevProvider(o, c),
         new CSharpDevProvider(o, c),
         new CppCxDevProvider(o, c),
         new CppWinrtDevProvider(o, c),
         new XamarinFormsDevProvider(o, c)
     });
 }
Exemplo n.º 6
0
        /// <summary>
        /// Creates the context menu for the Font List or the "..." button.
        /// Both of these have a font as their main target.
        /// </summary>
        /// <param name="menu"></param>
        /// <param name="font"></param>
        /// <param name="variant"></param>
        /// <param name="headerContent"></param>
        /// <param name="standalone"></param>
        /// <param name="showAdvanced"></param>
        public static void CreateMenu(
            MenuFlyout menu,
            InstalledFont font,
            CharacterRenderingOptions options,
            FrameworkElement headerContent,
            FlyoutArgs args)
        {
            MainViewModel main = Ioc.Default.GetService <MainViewModel>();

            bool standalone     = args.Standalone;
            bool showAdvanced   = args.ShowAdvanced;
            bool isExternalFile = args.IsExternalFile;
Exemplo n.º 7
0
        public static async Task <ExportResult> ExportSvgAsync(
            ExportStyle style,
            InstalledFont selectedFont,
            CharacterRenderingOptions options,
            Character selectedChar)
        {
            try
            {
                // We want to prepare geometry at 1024px, so force this
                options = options with {
                    FontSize = 1024
                };
                using var typography = options.CreateCanvasTypography();

                // Get save file
                string name = GetFileName(selectedFont, options.Variant, selectedChar, "svg");
                if (await PickFileAsync(name, "SVG", new[] { ".svg" }) is StorageFile file)
                {
                    try
                    {
                        CachedFileManager.DeferUpdates(file);
                        // Generate SVG doc and write to file
                        await Utils.WriteSvgAsync(GetSVG(style, options, selectedChar), file);

                        return(new ExportResult(true, file));
                    }
                    finally
                    {
                        await CachedFileManager.CompleteUpdatesAsync(file);
                    }
                }
            }
            catch (Exception ex)
            {
                await Ioc.Default.GetService <IDialogService>()
                .ShowMessageAsync(ex.Message, Localization.Get("SaveImageError"));
            }

            return(new ExportResult(false, null));
        }
Exemplo n.º 8
0
 public CppCxDevProvider(CharacterRenderingOptions r, Character c) : base(r, c)
 {
     DisplayName = "C++/CX";
 }
 public CSharpDevProvider(CharacterRenderingOptions r, Character c) : base(r, c)
 {
     DisplayName = "C# (UWP)";
 }
Exemplo n.º 10
0
 public DevProviderNone(CharacterRenderingOptions r, Character character) : base(r, character)
 {
     DisplayName = Localization.Get(ResourceKey);
 }
Exemplo n.º 11
0
 public XamlDevProvider(CharacterRenderingOptions o, Character c) : base(o, c)
 {
     DisplayName = "XAML (UWP)";
 }
Exemplo n.º 12
0
 public VBDevProvider(CharacterRenderingOptions r, Character c) : base(r, c)
 {
     DisplayName = "VB (UWP)";
 }
Exemplo n.º 13
0
        public static async Task <ExportResult> ExportPngAsync(
            ExportStyle style,
            InstalledFont selectedFont,
            CharacterRenderingOptions options,
            Character selectedChar,
            AppSettings settings)
        {
            try
            {
                using var typography = options.CreateCanvasTypography();

                string name = GetFileName(selectedFont, options.Variant, selectedChar, "png");
                if (await PickFileAsync(name, "PNG Image", new[] { ".png" }) is StorageFile file)
                {
                    CachedFileManager.DeferUpdates(file);

                    if (options.Analysis.GlyphFormats.Contains(GlyphImageFormat.Png))
                    {
                        IBuffer buffer = GetGlyphBuffer(options.Variant.FontFace, selectedChar.UnicodeIndex, GlyphImageFormat.Png);
                        await FileIO.WriteBufferAsync(file, buffer);
                    }
                    else
                    {
                        var device   = Utils.CanvasDevice;
                        var localDpi = 96; //Windows.Graphics.Display.DisplayInformation.GetForCurrentView().LogicalDpi;

                        var canvasH = (float)settings.PngSize;
                        var canvasW = (float)settings.PngSize;

                        using var renderTarget = new CanvasRenderTarget(device, canvasW, canvasH, localDpi);
                        using (var ds = renderTarget.CreateDrawingSession())
                        {
                            ds.Clear(Colors.Transparent);
                            var d = settings.PngSize;
                            var r = settings.PngSize / 2;

                            var textColor = style == ExportStyle.Black ? Colors.Black : Colors.White;
                            var fontSize  = (float)d;

                            using CanvasTextLayout layout = new CanvasTextLayout(device, $"{selectedChar.Char}", new CanvasTextFormat
                            {
                                FontSize            = fontSize,
                                FontFamily          = options.Variant.Source,
                                FontStretch         = options.Variant.FontFace.Stretch,
                                FontWeight          = options.Variant.FontFace.Weight,
                                FontStyle           = options.Variant.FontFace.Style,
                                HorizontalAlignment = CanvasHorizontalAlignment.Center,
                                Options             = style == ExportStyle.ColorGlyph ? CanvasDrawTextOptions.EnableColorFont : CanvasDrawTextOptions.Default
                            }, canvasW, canvasH);

                            if (style == ExportStyle.ColorGlyph)
                            {
                                layout.Options = CanvasDrawTextOptions.EnableColorFont;
                            }

                            layout.SetTypography(0, 1, typography);

                            var    db    = layout.DrawBounds;
                            double scale = Math.Min(1, Math.Min(canvasW / db.Width, canvasH / db.Height));
                            var    x     = -db.Left + ((canvasW - (db.Width * scale)) / 2d);
                            var    y     = -db.Top + ((canvasH - (db.Height * scale)) / 2d);

                            ds.Transform =
                                Matrix3x2.CreateTranslation(new Vector2((float)x, (float)y))
                                * Matrix3x2.CreateScale(new Vector2((float)scale));

                            ds.DrawTextLayout(layout, new Vector2(0), textColor);
                        }

                        using var fileStream = await file.OpenAsync(FileAccessMode.ReadWrite);

                        fileStream.Size = 0;
                        await renderTarget.SaveAsync(fileStream, CanvasBitmapFileFormat.Png, 1f);
                    }

                    await CachedFileManager.CompleteUpdatesAsync(file);

                    return(new ExportResult(true, file));
                }
            }
            catch (Exception ex)
            {
                await Ioc.Default.GetService <IDialogService>()
                .ShowMessageAsync(ex.Message, Localization.Get("SaveImageError"));
            }

            return(new ExportResult(false, null));
        }
Exemplo n.º 14
0
 public CSharpJupiterDevProviderBase(CharacterRenderingOptions r, Character c) : base(r, c)
 {
 }
Exemplo n.º 15
0
 public XamarinFormsDevProvider(CharacterRenderingOptions o, Character c) : base(o, c)
 {
     DisplayName = "Xamarin Forms";
 }
Exemplo n.º 16
0
        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());
            }
        }
 public CppWinrtDevProvider(CharacterRenderingOptions r, Character c) : base(r, c)
 {
     DisplayName = "C++/WinRT";
 }
Exemplo n.º 18
0
 public GeometryCacheEntry(Character character, CharacterRenderingOptions options)
 {
     Character = character;
     Options   = options;
 }