public static async void RequestExportFontFile(FontVariant variant) { var scheme = ResourceHelper.AppSettings.ExportNamingScheme; var interop = Utils.GetInterop(); if (DirectWrite.IsFontLocal(variant.FontFace)) { string filePath = GetFileName(variant, scheme); string name = Path.GetFileNameWithoutExtension(filePath); string ext = Path.GetExtension(filePath); if (await PickFileAsync(name, Localization.Get("ExportFontFile/Text"), new[] { ext }, PickerLocationId.DocumentsLibrary) is StorageFile file) { try { bool success = await TryWriteToFileAsync(variant, file); Messenger.Default.Send(new AppNotificationMessage(true, new ExportFontFileResult(success, file))); return; } catch { } } } Messenger.Default.Send(new AppNotificationMessage(true, new ExportFontFileResult(null, false))); }
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); }
internal static async Task ExportFontsToFolderAsync(List <FontVariant> fonts) { FolderPicker picker = new FolderPicker { SuggestedStartLocation = PickerLocationId.DocumentsLibrary }; picker.FileTypeFilter.Add("*"); if (await picker.PickSingleFolderAsync() is StorageFolder folder) { await Task.Run(async() => { var interop = Utils.GetInterop(); ExportNamingScheme scheme = ResourceHelper.AppSettings.ExportNamingScheme; foreach (var font in fonts) { if (interop.IsFontLocal(font.FontFace)) { string fileName = GetFileName(interop, font, scheme); StorageFile file = await folder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting); await TryWriteToFileAsync(interop, font, file); } } }); Messenger.Default.Send(new AppNotificationMessage(true, new ExportFontFileResult(folder, true))); } }
internal static async Task ExportFontsAsZipAsync(List <FontVariant> fonts, string name) { if (await PickFileAsync(name, "ZIP", new[] { ".zip" }) is StorageFile file) { await Task.Run(async() => { var interop = Utils.GetInterop(); ExportNamingScheme scheme = ResourceHelper.AppSettings.ExportNamingScheme; using var i = await file.OpenStreamForWriteAsync(); i.SetLength(0); using ZipArchive z = new ZipArchive(i, ZipArchiveMode.Create); foreach (var font in fonts) { if (interop.IsFontLocal(font.FontFace)) { string fileName = GetFileName(interop, font, scheme); ZipArchiveEntry entry = z.CreateEntry(fileName); using IOutputStream s = entry.Open().AsOutputStream(); await interop.WriteToStreamAsync(font.FontFace, s); } } }); Messenger.Default.Send(new AppNotificationMessage(true, new ExportFontFileResult(true, file))); } }
public static (string Path, Rect Bounds) GetGeometry( float size, FontVariant selectedVariant, Character selectedChar, CanvasTextLayoutAnalysis analysis, CanvasTypography typography) { /* * Note: this only constructs the monochrome version * of the glyph. * * Drop into C++/CX for color / multi-variant glyphs. */ using CanvasGeometry geom = CreateGeometry(size, selectedVariant, selectedChar, analysis, typography); var bounds = geom.ComputeBounds(); var interop = Utils.GetInterop(); var s = interop.GetPathData(geom); var t = s.Transform.Translation; bounds = new Rect(t.X - bounds.Left, -bounds.Top + t.Y, bounds.Width, bounds.Height); return(s.Path, bounds); }
public static async Task <ExportResult> ExportSvgAsync( ExportStyle style, InstalledFont selectedFont, FontVariant selectedVariant, Character selectedChar, CanvasTextLayoutAnalysis analysis, CanvasTypography typography) { try { string name = GetFileName(selectedFont, selectedVariant, selectedChar, "svg"); if (await PickFileAsync(name, "SVG", new[] { ".svg" }) is StorageFile file) { CachedFileManager.DeferUpdates(file); 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 && analysis.HasColorGlyphs && !analysis.GlyphFormats.Contains(GlyphImageFormat.Svg)) { NativeInterop interop = Utils.GetInterop(); List <string> paths = new List <string>(); Rect bounds = Rect.Empty; foreach (var thing in analysis.Indicies) { var path = interop.GetPathDatas(selectedVariant.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, analysis.Colors, invertBounds: false)) { await Utils.WriteSvgAsync(document, file); } return(new ExportResult(true, file)); } var data = GetGeometry(1024, selectedVariant, selectedChar, analysis, typography); async Task SaveMonochromeAsync() { using CanvasSvgDocument document = Utils.GenerateSvgDocument(device, data.Bounds, data.Path, textColor); await Utils.WriteSvgAsync(document, file); } // If the font uses SVG glyphs, we can extract the raw SVG from the font file if (analysis.GlyphFormats.Contains(GlyphImageFormat.Svg)) { string str = null; IBuffer b = GetGlyphBuffer(selectedVariant.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)); } await Utils.WriteSvgAsync(document, file); } } 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 await SaveMonochromeAsync(); } } else { await SaveMonochromeAsync(); } await CachedFileManager.CompleteUpdatesAsync(file); return(new ExportResult(true, file)); } } catch (Exception ex) { await SimpleIoc.Default.GetInstance <IDialogService>() .ShowMessageBox(ex.Message, Localization.Get("SaveImageError")); } return(new ExportResult(false, null)); }
private static IBuffer GetGlyphBuffer(CanvasFontFace fontface, uint unicodeIndex, GlyphImageFormat format) { return(Utils.GetInterop().GetImageDataBuffer(fontface, 1024, unicodeIndex, format)); }