diff --git a/demos/FreeTypeDemo/Program.cs b/demos/FreeTypeDemo/Program.cs index f7046d5..79a2517 100644 --- a/demos/FreeTypeDemo/Program.cs +++ b/demos/FreeTypeDemo/Program.cs @@ -150,9 +150,15 @@ static void Core(string fileName, string[] fontNames, Func { yield return (DefaultFonts.SansSerif , "Helvetica"); yield return (DefaultFonts.SansSerifBold , "Helvetica (bold)"); + yield return (DefaultFonts.SansSerifItalic , "Helvetica (italic)"); yield return (DefaultFonts.SansSerifBoldItalic, "Helvetica (bold, italic)"); + yield return (DefaultFonts.Serif , "DejaVu Serif"); + yield return (DefaultFonts.SerifBold , "DejaVu Serif (bold)"); + yield return (DefaultFonts.SerifItalic , "DejaVu Serif (italic)"); + yield return (DefaultFonts.SerifBoldItalic , "DejaVu Serif (bold, italic)"); yield return (DefaultFonts.MonoSpace , "Source Code Pro (monospace)"); yield return (DefaultFonts.MonoSpaceBold , "Source Code Pro (monospace, bold)"); + yield return (DefaultFonts.MonoSpaceItalic , "Source Code Pro (monospace, italic)"); yield return (DefaultFonts.MonoSpaceBoldItalic, "Source Code Pro (monospace, bold, italic)"); } } diff --git a/demos/PangoDemo/PangoDemo.csproj b/demos/PangoDemo/PangoDemo.csproj index 0354f07..1903878 100644 --- a/demos/PangoDemo/PangoDemo.csproj +++ b/demos/PangoDemo/PangoDemo.csproj @@ -8,4 +8,8 @@ + + + + diff --git a/demos/PangoDemo/Program.cs b/demos/PangoDemo/Program.cs index c5d7eb8..68c1af5 100644 --- a/demos/PangoDemo/Program.cs +++ b/demos/PangoDemo/Program.cs @@ -8,6 +8,7 @@ using Cairo.Extensions.Pango; using Cairo.Fonts; using Cairo.Surfaces.PDF; +using Cairo.Surfaces.Recording; using Cairo.Surfaces.SVG; using Cairo.Surfaces.Tee; using IOPath = System.IO.Path; @@ -21,6 +22,7 @@ PangoNative.LibPangoName => IOPath.Combine(@"C:\Program Files\msys64\ucrt64\bin", "libpango-1.0-0.dll"), PangoNative.LibPangoCairoName => IOPath.Combine(@"C:\Program Files\msys64\ucrt64\bin", "libpangocairo-1.0-0.dll"), PangoNative.LibGObjectName => IOPath.Combine(@"C:\Program Files\msys64\ucrt64\bin", "libgobject-2.0-0.dll"), + PangoNative.LibGLibName => IOPath.Combine(@"C:\Program Files\msys64\ucrt64\bin", "libglib-2.0-0.dll"), _ => null }; @@ -37,11 +39,15 @@ Directory.CreateDirectory("output"); Environment.CurrentDirectory = IOPath.Combine(Environment.CurrentDirectory, "output"); -DemoComparisonWithCairoText(); -DemoFromPangoDocsWithTextAroundCircle(); -DemoPangoFeatures(); +ComparisonWithCairoTextDemo(); +FontSelectionDemo(); +PangoDocsDemoWithTextAroundCircle(); +PangoFeaturesDemo(); +FontMapDemo(); +RecordingSurfaceDemo(); +FontListDemo(); //----------------------------------------------------------------------------- -static void DemoComparisonWithCairoText() +static void ComparisonWithCairoTextDemo() { const int Width = 600; const int Height = 150; @@ -103,7 +109,60 @@ static void DemoComparisonWithCairoText() tee.WriteToPng("cairo_comparison.png"); } //----------------------------------------------------------------------------- -static void DemoFromPangoDocsWithTextAroundCircle() +static void FontSelectionDemo() +{ + const int Width = 800; + const int Height = 100; + const string Text = "Hello from CairoSharp, w/o any AV and VA"; + + using SvgSurface svg = new("font-selection.svg", Width, Height); + using PdfSurface pdf = new("font-selection.pdf", Width, Height); + using TeeSurface tee = new(svg); + using CairoContext cr = new(tee); + tee.Add(pdf); + + cr.Color = KnownColors.White; + cr.Paint(); + cr.LineWidth = 6; + cr.Color = Color.Default; + cr.Rectangle(0, 0, Width, Height); + cr.Stroke(); + cr.LineWidth = 1; + + double curY = 10; + using PangoLayout pangoLayout = new(cr); + + cr.MoveTo(10, curY); + pangoLayout.SetText($"{Text} (Pango font from string)"); + string? fontDescriptionAsString = pangoLayout.SetFontDescriptionFromString("DejaVu Serif, Normal 22"); + Console.WriteLine(fontDescriptionAsString); + pangoLayout.ShowLayout(); + pangoLayout.GetSize(out double width, out double height); + + curY += height; + cr.MoveTo(10, curY); + using PangoFontMap fontMap = PangoFontMap.CairoFontMapGetDefault(); + using PangoFontFamily fontFamily = fontMap.GetFamily("DejaVu Serif"); + using PangoFontFace? fontFace = fontFamily.GetFace(null); + Debug.Assert(fontFace is not null); + pangoLayout.SetText($"{Text} (Pango font selected)"); + fontDescriptionAsString = pangoLayout.SetFontDescription(fontFace, size: 22); + Console.WriteLine(fontDescriptionAsString); + pangoLayout.ShowLayout(); + pangoLayout.GetSize(out width, out height); + curY += height; + + cr.SelectFontFace("DejaVu Serif"); + cr.SetFontSize(22); + cr.FontExtents(out FontExtents fontExtents); + curY += fontExtents.Ascent; + cr.MoveTo(10, curY); + cr.ShowText($"{Text} (cairo)"); + + tee.WriteToPng("font-selection.png"); +} +//----------------------------------------------------------------------------- +static void PangoDocsDemoWithTextAroundCircle() { // Demo from https://docs.gtk.org/PangoCairo/pango_cairo.html @@ -161,7 +220,7 @@ static void DemoFromPangoDocsWithTextAroundCircle() tee.WriteToPng("sample_from_docs.png"); } //----------------------------------------------------------------------------- -static void DemoPangoFeatures() +static void PangoFeaturesDemo() { const int Width = 600; const int Height = 620; @@ -278,7 +337,7 @@ static void DemoPangoFeatures() { cr.Color = KnownColors.DeepSkyBlue; cr.MoveTo(10, curY); - pangoLayout.SetText("Another long text, to showcase how justify works. And as we get on at least one more line, we can test next how JustifyLastLine works."); + pangoLayout.SetMarkup("Another long text, to showcase how Justify works. And as we get on at least one more line, we can test next how JustifyLastLine works."u8); pangoLayout.ShowLayout(); pangoLayout.GetSize(out width, out height); @@ -347,3 +406,210 @@ static void DemoPangoFeatures() tee.WriteToPng("features.png"); } +//----------------------------------------------------------------------------- +static void FontMapDemo() +{ + Console.WriteLine(); + using StreamWriter sw = File.CreateText("font-families.csv"); + sw.WriteLine("Name;IsMonospace;IsVariable"); + + using PangoFontMap fontMap = PangoFontMap.CairoFontMapGetDefault(); + fontMap.AddFontFile(IOPath.Combine(AppContext.BaseDirectory, "fonts", "SanRemo.ttf")); + + List families = []; + foreach (PangoFontFamily fontFamily in fontMap.ListFamilies()) + { + families.Add(fontFamily); + } + + string fontFamilyName = ""; + foreach (PangoFontFamily fontFamily in families.OrderBy(f => f.Name)) + { + fontFamilyName = fontFamily.Name; + + sw.WriteLine($"{fontFamilyName};{fontFamily.IsMonospace};{fontFamily.IsVariable}"); + Console.WriteLine(fontFamily); + Console.WriteLine("Faces:"); + + foreach (PangoFontFace fontFace in fontFamily.ListFaces()) + { + Console.WriteLine($"\t{fontFace.Name}\tsynthesized: {fontFace.IsSynthesized}"); + Console.WriteLine("\tSizes:"); + + foreach (double size in fontFace.ListSizes()) + { + Console.WriteLine($"\t\t{size}"); + } + } + + Console.WriteLine(); + } + + const int Width = 600; + const int Height = 120; + + using SvgSurface svg = new("font-map.svg", Width, Height); + using PdfSurface pdf = new("font-map.pdf", Width, Height); + using TeeSurface tee = new(svg); + using CairoContext cr = new(tee); + tee.Add(pdf); + + using PangoLayout pangoLayout = new(cr); + double curY = 10; + + cr.MoveTo(10, curY); + pangoLayout.SetFontDescriptionFromString($"{fontFamilyName} Normal 22"); + pangoLayout.SetText($"Font: {fontFamilyName}"); + pangoLayout.ShowLayout(); + pangoLayout.GetSize(out double width, out double height); + curY += height; + + cr.MoveTo(10, curY); + pangoLayout.SetFontDescriptionFromString("Viner Hand ITC, Normal 22"); + pangoLayout.SetText("Font: Viner Hand ITC"); + pangoLayout.ShowLayout(); + pangoLayout.GetSize(out width, out height); + curY += height; + + cr.MoveTo(10, curY); + pangoLayout.SetFontDescriptionFromString("San Remo, Normal 22"); + pangoLayout.SetText("Font: San Remo"); + pangoLayout.ShowLayout(); + pangoLayout.GetSize(out width, out height); + curY += height; + + tee.WriteToPng("font-map.png"); + + families.ForEach(f => f.Dispose()); +} +//----------------------------------------------------------------------------- +static void RecordingSurfaceDemo() +{ + using PangoFontMap fontMap = PangoFontMap.CairoFontMapGetDefault(); + fontMap.AddFontFile(IOPath.Combine(AppContext.BaseDirectory, "fonts", "SanRemo.ttf")); + + List families = []; + foreach (PangoFontFamily fontFamily in fontMap.ListFamilies()) + { + families.Add(fontFamily); + } + + string fontFamilyName = ""; + foreach (PangoFontFamily fontFamily in families.OrderBy(f => f.Name)) + { + fontFamilyName = fontFamily.Name; + } + + using RecordingSurface surface = new(); + using (CairoContext cr = new(surface)) + { + using PangoLayout pangoLayout = new(cr); + double curY = 10; + + cr.MoveTo(10, curY); + pangoLayout.SetFontDescriptionFromString($"{fontFamilyName} Normal 22"); + pangoLayout.SetText($"Font: {fontFamilyName}"); + pangoLayout.ShowLayout(); + pangoLayout.GetSize(out double width, out double height); + curY += height; + + cr.MoveTo(10, curY); + pangoLayout.SetFontDescriptionFromString("Viner Hand ITC, Normal 22"); + pangoLayout.SetText("Font: Viner Hand ITC"); + pangoLayout.ShowLayout(); + pangoLayout.GetSize(out width, out height); + curY += height; + + cr.MoveTo(10, curY); + pangoLayout.SetFontDescriptionFromString("San Remo, Normal 22"); + pangoLayout.SetText("Font: San Remo"); + pangoLayout.ShowLayout(); + pangoLayout.GetSize(out width, out height); + curY += height; + } + + Rectangle recordingExtents = surface.GetInkExtents(); + const int Padding = 10; + + using SvgSurface svg = new("recording.svg", recordingExtents.Width + 2 * Padding, recordingExtents.Height + 2 * Padding); + using PdfSurface pdf = new("recording.pdf", recordingExtents.Width + 2 * Padding, recordingExtents.Height + 2 * Padding); + using TeeSurface tee = new(svg); + using (CairoContext cr = new(tee)) + { + tee.Add(pdf); + + cr.SetSourceSurface(surface, Padding - recordingExtents.X, Padding - recordingExtents.Y); + cr.Paint(); + } + + tee.WriteToPng("recording.png"); + + families.ForEach(f => f.Dispose()); +} +//----------------------------------------------------------------------------- +static void FontListDemo() +{ + using PangoFontMap fontMap = PangoFontMap.CairoFontMapGetDefault(); + fontMap.AddFontFile(IOPath.Combine(AppContext.BaseDirectory, "fonts", "SanRemo.ttf")); + + List families = []; + foreach (PangoFontFamily fontFamily in fontMap.ListFamilies()) + { + families.Add(fontFamily); + } + families = [.. families + .OrderBy(f => f.IsMonospace) + .ThenBy (f => f.Name) + ]; + + using RecordingSurface surface = new(); + using (CairoContext cr = new(surface)) + { + using PangoLayout pangoLayout = new(cr); + double curY = 10; + + foreach (PangoFontFamily fontFamily in families) + { + using PangoFontFace? fontFace = fontFamily.GetFace(null); + + if (fontFace is null) + { + continue; + } + + cr.MoveTo(10, curY); + pangoLayout.SetText($"Font: {fontFamily.Name} {fontFace.Name}; AV and VA to see kerning or not"); + pangoLayout.SetFontDescription(fontFace, size: 22); + pangoLayout.ShowLayout(); + pangoLayout.GetSize(out double width, out double height); + curY += height; + } + } + + Rectangle recordingExtents = surface.GetInkExtents(); + const int Padding = 10; + double surfaceWidth = recordingExtents.Width + 2 * Padding; + double surfaceHeight = recordingExtents.Height + 2 * Padding; + + using SvgSurface svg = new("font-list.svg", surfaceWidth, surfaceHeight); + using PdfSurface pdf = new("font-list.pdf", surfaceWidth, surfaceHeight); + using TeeSurface tee = new(svg); + using (CairoContext cr = new(tee)) + { + tee.Add(pdf); + + cr.Color = KnownColors.White; + cr.Paint(); + cr.Color = Color.Default; + cr.LineWidth = 4; + cr.Rectangle(0, 0, surfaceWidth, surfaceHeight); + cr.Stroke(); + + cr.SetSourceSurface(surface, Padding - recordingExtents.X, Padding - recordingExtents.Y); + cr.Paint(); + } + + tee.WriteToPng("font-list.png"); + + families.ForEach(f => f.Dispose()); +} diff --git a/source/CairoSharp.Extensions/Fonts/DefaultFonts.cs b/source/CairoSharp.Extensions/Fonts/DefaultFonts.cs index 8ecc73d..bad0c52 100644 --- a/source/CairoSharp.Extensions/Fonts/DefaultFonts.cs +++ b/source/CairoSharp.Extensions/Fonts/DefaultFonts.cs @@ -78,6 +78,70 @@ public static class DefaultFonts LazyThreadSafetyMode.ExecutionAndPublication); #endif + /// + /// DejaVu Serif + /// + /// + /// A created for DejaVu Serif. + /// +#if USE_THREADSTATIC + [ThreadStatic] private static FontFace? t_serif; + public static FontFace Serif => t_serif ??= new ToyFontFace("DejaVu Serif"); +#else + public static FontFace Serif => s_serif.Value; + private static readonly Lazy s_serif = new( + () => new ToyFontFace("DejaVu Serif"), + LazyThreadSafetyMode.ExecutionAndPublication); +#endif + + /// + /// DejaVu Serif (bold) + /// + /// + /// A created for DejaVu Serif. + /// +#if USE_THREADSTATIC + [ThreadStatic] private static FontFace? t_serifBold; + public static FontFace SerifBold => t_serifBold ??= new ToyFontFace("DejaVu Serif", weight: Drawing.Text.FontWeight.Bold); +#else + public static FontFace SerifBold => s_serifBold.Value; + private static readonly Lazy s_serifBold = new( + () => new ToyFontFace("DejaVu Serif", weight: Drawing.Text.FontWeight.Bold), + LazyThreadSafetyMode.ExecutionAndPublication); +#endif + + /// + /// DejaVu Serif (italic) + /// + /// + /// A created for DejaVu Serif. + /// +#if USE_THREADSTATIC + [ThreadStatic] private static FontFace? t_serifItalic; + public static FontFace SerifItalic => t_serifItalic ??= new ToyFontFace("DejaVu Serif", slant: Drawing.Text.FontSlant.Italic); +#else + public static FontFace SerifItalic => s_serifItalic.Value; + private static readonly Lazy s_serifItalic = new( + () => new ToyFontFace("DejaVu Serif", slant: Drawing.Text.FontSlant.Italic), + LazyThreadSafetyMode.ExecutionAndPublication); +#endif + + /// + /// DejaVu Serif (bold italic) + /// + /// + /// A created for DejaVu Serif. + /// +#if USE_THREADSTATIC + [ThreadStatic] private static FontFace? t_serifBoldItalic; + public static FontFace SerifBoldItalic => t_serifBoldItalic ??= new ToyFontFace("DejaVu Serif", slant: Drawing.Text.FontSlant.Italic, weight: Drawing.Text.FontWeight.Bold); +#else + public static FontFace SerifBoldItalic => s_serifBoldItalic.Value; + private static readonly Lazy s_serifBoldItalic = new( + () => new ToyFontFace("DejaVu Serif", slant: Drawing.Text.FontSlant.Italic, weight: Drawing.Text.FontWeight.Bold), + LazyThreadSafetyMode.ExecutionAndPublication); +#endif + /// /// Source Code Pro /// diff --git a/source/CairoSharp.Extensions/GObject/GObjectException.cs b/source/CairoSharp.Extensions/GObject/GObjectException.cs new file mode 100644 index 0000000..c5a1974 --- /dev/null +++ b/source/CairoSharp.Extensions/GObject/GObjectException.cs @@ -0,0 +1,32 @@ +// (c) gfoidl, all rights reserved + +namespace Cairo.Extensions.GObject; + +[Serializable] +public abstract class GObjectException : Exception +{ + public int Domain { get; } + public int Code { get; } + + protected GObjectException() { } + protected GObjectException(string message) : base(message) { } + protected GObjectException(string message, Exception inner) : base(message, inner) { } + + private protected unsafe GObjectException(GError* error) : this(GetErrorMessage(error)) + { + this.Domain = error->Domain; + this.Code = error->Code; + } + + private static unsafe string GetErrorMessage(GError* error) + { + string msg = new(error->Message); + + // From the docs, e.g. https://gnome.pages.gitlab.gnome.org/librsvg/Rsvg-2.0/method.Handle.render_document.html + // In case of error, the argument will be set to a newly allocated GError; the caller will take + // ownership of the data, and be responsible for freeing it. + GObjectNative.g_error_free(error); + + return msg; + } +} diff --git a/source/CairoSharp.Extensions/GObject/GObjectNative.cs b/source/CairoSharp.Extensions/GObject/GObjectNative.cs index f9e4865..f2fc1e8 100644 --- a/source/CairoSharp.Extensions/GObject/GObjectNative.cs +++ b/source/CairoSharp.Extensions/GObject/GObjectNative.cs @@ -6,9 +6,18 @@ namespace Cairo.Extensions.GObject; internal static unsafe partial class GObjectNative { + public const string LibGLibName = "libglib-2.0.so.0"; public const string LibGObjectName = "libgobject-2.0.so.0"; [LibraryImport(LibGObjectName)] [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] internal static partial void g_object_unref(void* @object); + + [LibraryImport(LibGLibName)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + internal static partial void g_free(void* mem); + + [LibraryImport(LibGLibName)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + internal static partial void g_error_free(GError* error); } diff --git a/source/CairoSharp.Extensions/GObject/TypeDefs.cs b/source/CairoSharp.Extensions/GObject/TypeDefs.cs new file mode 100644 index 0000000..ca49877 --- /dev/null +++ b/source/CairoSharp.Extensions/GObject/TypeDefs.cs @@ -0,0 +1,13 @@ +// (c) gfoidl, all rights reserved + +using System.Runtime.InteropServices; + +namespace Cairo.Extensions.GObject; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe struct GError +{ + public int Domain; + public int Code; + public sbyte* Message; +} diff --git a/source/CairoSharp.Extensions/Loading/LoadingException.cs b/source/CairoSharp.Extensions/Loading/LoadingException.cs index 66a20ad..65de6bc 100644 --- a/source/CairoSharp.Extensions/Loading/LoadingException.cs +++ b/source/CairoSharp.Extensions/Loading/LoadingException.cs @@ -1,32 +1,15 @@ // (c) gfoidl, all rights reserved +using Cairo.Extensions.GObject; + namespace Cairo.Extensions.Loading; [Serializable] -public abstract class LoadingException : Exception +public abstract class LoadingException : GObjectException { - public int Domain { get; } - public int Code { get; } - protected LoadingException() { } protected LoadingException(string message) : base(message) { } protected LoadingException(string message, Exception inner) : base(message, inner) { } - private protected unsafe LoadingException(GError* error) : this(GetErrorMessage(error)) - { - this.Domain = error->Domain; - this.Code = error->Code; - } - - private static unsafe string GetErrorMessage(GError* error) - { - string msg = new(error->Message); - - // From the docs, e.g. https://gnome.pages.gitlab.gnome.org/librsvg/Rsvg-2.0/method.Handle.render_document.html - // In case of error, the argument will be set to a newly allocated GError; the caller will take - // ownership of the data, and be responsible for freeing it. - LoadingNative.g_error_free(error); - - return msg; - } + private protected unsafe LoadingException(GError* error) : base(error) { } } diff --git a/source/CairoSharp.Extensions/Loading/LoadingNative.cs b/source/CairoSharp.Extensions/Loading/LoadingNative.cs index 3d66c65..59594fd 100644 --- a/source/CairoSharp.Extensions/Loading/LoadingNative.cs +++ b/source/CairoSharp.Extensions/Loading/LoadingNative.cs @@ -14,7 +14,7 @@ namespace Cairo.Extensions.Loading; public static unsafe partial class LoadingNative { - public const string LibGLibName = "libglib-2.0.so.0"; + public const string LibGLibName = GObjectNative.LibGLibName; public const string LibGObjectName = GObjectNative.LibGObjectName; public const string LibGioName = "libgio-2.0.so.0"; public const string LibRSvgName = "librsvg-2.so.2"; @@ -93,14 +93,6 @@ private static int GetPopplerVersionAsInt() return CairoAPI.VersionEncode(major, minor, patch); } //------------------------------------------------------------------------- - [LibraryImport(LibGLibName)] - [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] - internal static partial void g_free(void* mem); - - [LibraryImport(LibGLibName)] - [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] - internal static partial void g_error_free(GError* error); - //------------------------------------------------------------------------- [LibraryImport(LibGioName, StringMarshalling = StringMarshalling.Utf8)] [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] internal static partial GFile* g_file_new_for_path(string path); diff --git a/source/CairoSharp.Extensions/Loading/Marshalling.cs b/source/CairoSharp.Extensions/Loading/Marshalling.cs index ef27a86..9ba9632 100644 --- a/source/CairoSharp.Extensions/Loading/Marshalling.cs +++ b/source/CairoSharp.Extensions/Loading/Marshalling.cs @@ -1,7 +1,7 @@ // (c) gfoidl, all rights reserved using System.Runtime.InteropServices.Marshalling; -using static Cairo.Extensions.Loading.LoadingNative; +using Cairo.Extensions.GObject; namespace Cairo.Extensions.Loading; @@ -15,5 +15,5 @@ internal static unsafe class GCharMarshaller : new string(utf8); } //------------------------------------------------------------------------- - public static void Free(sbyte* utf8) => g_free(utf8); + public static void Free(sbyte* utf8) => GObjectNative.g_free(utf8); } diff --git a/source/CairoSharp.Extensions/Loading/PDF/PopplerException.cs b/source/CairoSharp.Extensions/Loading/PDF/PopplerException.cs index a494f72..79f632b 100644 --- a/source/CairoSharp.Extensions/Loading/PDF/PopplerException.cs +++ b/source/CairoSharp.Extensions/Loading/PDF/PopplerException.cs @@ -1,5 +1,7 @@ // (c) gfoidl, all rights reserved +using Cairo.Extensions.GObject; + namespace Cairo.Extensions.Loading.PDF; [Serializable] diff --git a/source/CairoSharp.Extensions/Loading/SVG/LibRSvgException.cs b/source/CairoSharp.Extensions/Loading/SVG/LibRSvgException.cs index 798924a..c7c758c 100644 --- a/source/CairoSharp.Extensions/Loading/SVG/LibRSvgException.cs +++ b/source/CairoSharp.Extensions/Loading/SVG/LibRSvgException.cs @@ -1,5 +1,7 @@ // (c) gfoidl, all rights reserved +using Cairo.Extensions.GObject; + namespace Cairo.Extensions.Loading.SVG; [Serializable] diff --git a/source/CairoSharp.Extensions/Loading/SVG/LibRSvgExtensions.cs b/source/CairoSharp.Extensions/Loading/SVG/LibRSvgExtensions.cs index e29d9e3..6de7ce9 100644 --- a/source/CairoSharp.Extensions/Loading/SVG/LibRSvgExtensions.cs +++ b/source/CairoSharp.Extensions/Loading/SVG/LibRSvgExtensions.cs @@ -1,5 +1,6 @@ // (c) gfoidl, all rights reserved +using Cairo.Extensions.GObject; using static Cairo.Extensions.Loading.LoadingNative; namespace Cairo.Extensions.Loading.SVG; @@ -73,18 +74,13 @@ public void LoadSvg(ReadOnlySpan svgData, RsvgRectangle viewPort, RsvgHand /// /// SVG document /// viewport size at which the whole SVG would be fitted - /// - /// the DPI at which the SVG will be rendered in cairo. Common values are 75, 90 and 300 DPI. See - /// Resolution of the rendered image (dots per inch, or DPI) - /// for further information. Current CSS assumes a default DPI of 96 (the default used here). - /// /// /// The gives the position and size at which the whole SVG document will /// be rendered. The document is scaled proportionally to fit into this viewport. /// /// is null /// an error occured - public void LoadSvg(SvgDocument svgDocument, RsvgRectangle viewPort, double dpi = 96d) + public void LoadSvg(SvgDocument svgDocument, RsvgRectangle viewPort) { ArgumentNullException.ThrowIfNull(svgDocument); diff --git a/source/CairoSharp.Extensions/Loading/TypeDefs.cs b/source/CairoSharp.Extensions/Loading/TypeDefs.cs index c813ce5..ea544aa 100644 --- a/source/CairoSharp.Extensions/Loading/TypeDefs.cs +++ b/source/CairoSharp.Extensions/Loading/TypeDefs.cs @@ -1,20 +1,10 @@ // (c) gfoidl, all rights reserved -using System.Runtime.InteropServices; - namespace Cairo.Extensions.Loading; internal struct GFile; internal struct GInputStream; -[StructLayout(LayoutKind.Sequential)] -internal unsafe struct GError -{ - public int Domain; - public int Code; - public sbyte* Message; -} - internal struct RsvgHandle; internal struct PopplerDocument; diff --git a/source/CairoSharp.Extensions/Pango/PangoException.cs b/source/CairoSharp.Extensions/Pango/PangoException.cs new file mode 100644 index 0000000..870f52a --- /dev/null +++ b/source/CairoSharp.Extensions/Pango/PangoException.cs @@ -0,0 +1,15 @@ +// (c) gfoidl, all rights reserved + +using Cairo.Extensions.GObject; + +namespace Cairo.Extensions.Pango; + +[Serializable] +public class PangoException : GObjectException +{ + public PangoException() { } + public PangoException(string message) : base(message) { } + public PangoException(string message, Exception inner) : base(message, inner) { } + + internal unsafe PangoException(GError* error) : base(error) { } +} diff --git a/source/CairoSharp.Extensions/Pango/PangoFontFace.cs b/source/CairoSharp.Extensions/Pango/PangoFontFace.cs new file mode 100644 index 0000000..0f05b60 --- /dev/null +++ b/source/CairoSharp.Extensions/Pango/PangoFontFace.cs @@ -0,0 +1,143 @@ +// (c) gfoidl, all rights reserved + +using Cairo.Extensions.GObject; +using static Cairo.Extensions.Pango.PangoFontFaceNative; + +namespace Cairo.Extensions.Pango; + +/// +/// A is used to represent a group of fonts with the same family, +/// slant, weight, and width, but varying sizes. +/// +public sealed unsafe class PangoFontFace : CairoObject +{ + private readonly PangoFontFamily _family; + + internal PangoFontFace(PangoFontFamily family, pango_font_face* face) : base(face, isOwnedByCairo: true, needsDestroy: false) + => _family = family; + + protected override void DisposeCore(pango_font_face* handle) + => throw new InvalidOperationException("PangoFontFace must not be freed"); + + /// + /// Gets a name representing the style of this face. + /// + /// + /// Note that a font family may contain multiple faces with the same name (e.g. a variable + /// and a non-variable face for the same style). + /// + public string Name + { + get + { + this.CheckDisposed(); + return pango_font_face_get_face_name(this.Handle); + } + } + + /// + /// Returns whether a is synthesized. + /// + /// + /// This will be the case if the underlying font rendering engine creates this face from + /// another face, by shearing, emboldening, lightening or modifying it in some other way. + /// + public bool IsSynthesized + { + get + { + this.CheckDisposed(); + return pango_font_face_is_synthesized(this.Handle); + } + } + + /// + /// Gets the that face belongs to. + /// + public PangoFontFamily FontFamily => _family; + + /// + /// List the available sizes for a font. + /// + /// List of sizes for the font face, or an empty list for scalable fonts. + /// + /// This is only applicable to bitmap fonts. The sizes returned are in cairo units and + /// are sorted in ascending order. + /// + /// Note: the native Pango API returns the values in Pango units, whilst this API + /// returns cairo units.
+ /// + /// Pango unit = cairo unit * Pango.Scale + /// + ///
+ ///
+ public FontFaceSizeIterator ListSizes() + { + this.CheckDisposed(); + return new FontFaceSizeIterator(this.Handle); + } + + /// + /// Enumerator for the sizes in the . + /// + public struct FontFaceSizeIterator : IDisposable + { + private readonly pango_font_face* _face; + + private int* _sizes; + private int _count; + private int _i; + + internal FontFaceSizeIterator(pango_font_face* face) => _face = face; + + public readonly FontFaceSizeIterator GetEnumerator() => this; + + public readonly void Dispose() + { + if (_sizes is not null) + { + GObjectNative.g_free(_sizes); + } + } + + public readonly double Current + { + get + { + if (_i < 0) + { + throw new InvalidOperationException("Must call MoveNext() before accessing the first element"); + } + + return _sizes[_i] / (double)Pango.Scale; + } + } + + public bool MoveNext() + { + if (_i < 0) + { + fixed (int** sizes = &_sizes) + fixed (int* count = &_count) + { + pango_font_face_list_sizes(_face, sizes, count); + } + + // For scalable fonts, stores null at the location pointed to by sizes + // and 0 at the location pointed to by n_sizes. + if (_sizes is null || _count == 0) + { + return false; + } + + _i = 0; + } + else + { + _i++; + } + + return _i < _count; + } + } +} diff --git a/source/CairoSharp.Extensions/Pango/PangoFontFaceNative.cs b/source/CairoSharp.Extensions/Pango/PangoFontFaceNative.cs new file mode 100644 index 0000000..8471177 --- /dev/null +++ b/source/CairoSharp.Extensions/Pango/PangoFontFaceNative.cs @@ -0,0 +1,33 @@ +// (c) gfoidl, all rights reserved + +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace Cairo.Extensions.Pango; + +internal static unsafe partial class PangoFontFaceNative +{ + // https://docs.gtk.org/Pango/method.FontFace.describe.html + [LibraryImport(PangoNative.LibPangoName)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + internal static partial pango_font_description* pango_font_face_describe(pango_font_face* face); + + // https://docs.gtk.org/Pango/method.FontFace.get_face_name.html + [LibraryImport(PangoNative.LibPangoName)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + [return: MarshalUsing(typeof(NativeConstCharMarshaller))] + [SuppressGCTransition] + internal static partial string pango_font_face_get_face_name(pango_font_face* face); + + // https://docs.gtk.org/Pango/method.FontFace.is_synthesized.html + [LibraryImport(PangoNative.LibPangoName)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + [return: MarshalAs(UnmanagedType.U4)] + [SuppressGCTransition] + internal static partial bool pango_font_face_is_synthesized(pango_font_face* face); + + // https://docs.gtk.org/Pango/method.FontFace.list_sizes.html + [LibraryImport(PangoNative.LibPangoName)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + internal static partial void pango_font_face_list_sizes(pango_font_face* face, int** sizes, int* n_sizes); +} diff --git a/source/CairoSharp.Extensions/Pango/PangoFontFamily.cs b/source/CairoSharp.Extensions/Pango/PangoFontFamily.cs new file mode 100644 index 0000000..00e6714 --- /dev/null +++ b/source/CairoSharp.Extensions/Pango/PangoFontFamily.cs @@ -0,0 +1,173 @@ +// (c) gfoidl, all rights reserved + +using Cairo.Extensions.GObject; +using static Cairo.Extensions.Pango.PangoFontFamilyNative; + +namespace Cairo.Extensions.Pango; + +/// +/// A is used to represent a family of related font faces. +/// +/// +/// The font faces in a family share a common design, but differ in slant, weight, width +/// or other aspects. +/// +public sealed unsafe class PangoFontFamily : CairoObject +{ + internal PangoFontFamily(pango_font_family* family) : base(family, isOwnedByCairo: true, needsDestroy: false) { } + + protected override void DisposeCore(pango_font_family* handle) + => throw new InvalidOperationException("PangoFontFamily must not be freed"); + + /// + /// Gets the name of the family. + /// + /// + /// The name is unique among all fonts for the font backend and can be used in a + /// PangoFontDescription to specify that a face from this family is desired. + /// + public string Name + { + get + { + this.CheckDisposed(); + return pango_font_family_get_name(this.Handle); + } + } + + /// + /// A monospace font is a font designed for text display where the characters form a regular grid. + /// + /// + /// For Western languages this would mean that the advance width of all characters are the same, but + /// this categorization also includes Asian fonts which include double-width characters: characters + /// that occupy two grid cells. g_unichar_iswide() returns a result that indicates whether a character + /// is typically double-width in a monospace font. + /// + public bool IsMonospace + { + get + { + this.CheckDisposed(); + return pango_font_family_is_monospace(this.Handle); + } + } + + /// + /// A variable font is a font which has axes that can be modified to produce different faces. + /// + /// + /// Such axes are also known as variations. + /// + public bool IsVariable + { + get + { + this.CheckDisposed(); + return pango_font_family_is_variable(this.Handle); + } + } + + public override string ToString() => $""" + Name: {this.Name} + IsMonospace: {this.IsMonospace} + IsVariable: {this.IsVariable} + """; + + /// + /// Gets the of family with the given name. + /// + /// + /// The name of a face. If the name is null, the family’s default face + /// (fontconfig calls it "Regular") will be returned. + /// + /// + /// The , or null if no face with the given name exists. + /// + public PangoFontFace? GetFace(string? name) + { + this.CheckDisposed(); + + pango_font_face* face = pango_font_family_get_face(this.Handle, name); + + return face is not null + ? new PangoFontFace(this, face) + : null; + } + + /// + /// Lists the different font faces that make up . + /// + /// List of font faces for the family. + /// + /// The faces in a family share a common design, but differ in slant, weight, width + /// and other aspects. + /// + /// Note that the returned faces are not in any particular order, and multiple faces may + /// have the same name or characteristics. + /// + /// + public FontFaceIterator ListFaces() + { + this.CheckDisposed(); + return new FontFaceIterator(this); + } + + /// + /// Enumerator for for the . + /// + public struct FontFaceIterator : IDisposable + { + private readonly PangoFontFamily _family; + + private pango_font_face** _faces; + private int _count; + private int _i = -1; + + internal FontFaceIterator(PangoFontFamily family) => _family = family; + + public readonly FontFaceIterator GetEnumerator() => this; + + public readonly void Dispose() + { + if (_faces is not null) + { + GObjectNative.g_free(_faces); + } + } + + public readonly PangoFontFace Current + { + get + { + if (_i < 0) + { + throw new InvalidOperationException("Must call MoveNext() before accessing the first element"); + } + + pango_font_face* face = _faces[_i]; + return new PangoFontFace(_family, face); + } + } + + public bool MoveNext() + { + if (_i < 0) + { + fixed (pango_font_face*** faces = &_faces) + fixed (int* count = &_count) + { + pango_font_family_list_faces(_family.Handle, faces, count); + } + + _i = 0; + } + else + { + _i++; + } + + return _i < _count; + } + } +} diff --git a/source/CairoSharp.Extensions/Pango/PangoFontFamilyNative.cs b/source/CairoSharp.Extensions/Pango/PangoFontFamilyNative.cs new file mode 100644 index 0000000..248b523 --- /dev/null +++ b/source/CairoSharp.Extensions/Pango/PangoFontFamilyNative.cs @@ -0,0 +1,40 @@ +// (c) gfoidl, all rights reserved + +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace Cairo.Extensions.Pango; + +internal static unsafe partial class PangoFontFamilyNative +{ + // https://docs.gtk.org/Pango/method.FontFamily.get_name.html + [LibraryImport(PangoNative.LibPangoName)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + [return: MarshalUsing(typeof(NativeConstCharMarshaller))] + [SuppressGCTransition] + internal static partial string pango_font_family_get_name(pango_font_family* family); + + // https://docs.gtk.org/Pango/method.FontFamily.is_monospace.html + [LibraryImport(PangoNative.LibPangoName)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + [return: MarshalAs(UnmanagedType.U4)] + [SuppressGCTransition] + internal static partial bool pango_font_family_is_monospace(pango_font_family* family); + + // https://docs.gtk.org/Pango/method.FontFamily.is_variable.html + [LibraryImport(PangoNative.LibPangoName)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + [return: MarshalAs(UnmanagedType.U4)] + [SuppressGCTransition] + internal static partial bool pango_font_family_is_variable(pango_font_family* family); + + // https://docs.gtk.org/Pango/method.FontFamily.list_faces.html + [LibraryImport(PangoNative.LibPangoName)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + internal static partial void pango_font_family_list_faces(pango_font_family* family, pango_font_face*** face, int* n_faces); + + // https://docs.gtk.org/Pango/method.FontFamily.get_face.html + [LibraryImport(PangoNative.LibPangoName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + internal static partial pango_font_face* pango_font_family_get_face(pango_font_family* family, string? name); +} diff --git a/source/CairoSharp.Extensions/Pango/PangoFontMap.cs b/source/CairoSharp.Extensions/Pango/PangoFontMap.cs new file mode 100644 index 0000000..fdd0ee2 --- /dev/null +++ b/source/CairoSharp.Extensions/Pango/PangoFontMap.cs @@ -0,0 +1,139 @@ +// (c) gfoidl, all rights reserved + +using Cairo.Extensions.GObject; +using static Cairo.Extensions.Pango.PangoFontMapNative; + +namespace Cairo.Extensions.Pango; + +/// +/// A represents the set of fonts available for a particular rendering system. +/// +/// +/// This is a virtual object with implementations being specific to particular rendering systems. +/// +public sealed unsafe class PangoFontMap : CairoObject +{ + private PangoFontMap(pango_font_map* fontMap) : base(fontMap, isOwnedByCairo: true, needsDestroy: false) { } + + /// + /// Gets a default PangoCairoFontMap to use with cairo. + /// + /// + /// The default PangoCairo fontmap for the current thread. This object is owned by Pango and must + /// not be freed (note: you still should Dispose the object, but here disposal won't free the native + /// resource). + /// + public static PangoFontMap CairoFontMapGetDefault() + { + pango_font_map* fontMap = pango_cairo_font_map_get_default(); + return new PangoFontMap(fontMap); + } + + protected override void DisposeCore(pango_font_map* handle) + => throw new InvalidOperationException("PangoFontMap must not be freed"); + + /// + /// Loads a font file with one or more fonts into the . + /// + /// Absolute path to the font file. + /// + /// The added fonts will take precedence over preexisting fonts with the same name. + /// + /// An error occured while loading the font from the file. + public void AddFontFile(string fileName) + { + this.CheckDisposed(); + ArgumentNullException.ThrowIfNull(fileName); + + GError* error = null; + + if (!pango_font_map_add_font_file(this.Handle, fileName, &error)) + { + throw new PangoException(error); + } + } + + /// + /// Gets a font family by name. + /// + /// A family name. + /// The font family. + public PangoFontFamily GetFamily(string name) + { + this.CheckDisposed(); + + pango_font_family* family = pango_font_map_get_family(this.Handle, name); + return new PangoFontFamily(family); + } + + /// + /// List all families for a fontmap. + /// + /// All families for a fontmap. + /// + /// Note that the returned families are not in any particular order. + /// + public FontFamilyIterator ListFamilies() + { + this.CheckDisposed(); + return new FontFamilyIterator(this.Handle); + } + + /// + /// Enumerator for . + /// + public struct FontFamilyIterator : IDisposable + { + private readonly pango_font_map* _fontMap; + + private pango_font_family** _families; + private int _count; + private int _i = -1; + + internal FontFamilyIterator(pango_font_map* fontMap) => _fontMap = fontMap; + + public readonly FontFamilyIterator GetEnumerator() => this; + + public readonly void Dispose() + { + if (_families is not null) + { + GObjectNative.g_free(_families); + } + } + + public readonly PangoFontFamily Current + { + get + { + if (_i < 0) + { + throw new InvalidOperationException("Must call MoveNext() before accessing the first element"); + } + + pango_font_family* family = _families[_i]; + return new PangoFontFamily(family); + } + } + + public bool MoveNext() + { + if (_i < 0) + { + fixed (pango_font_family*** families = &_families) + fixed (int* count = &_count) + { + pango_font_map_list_families(_fontMap, families, count); + } + + _i = 0; + } + else + { + _i++; + } + + return _i < _count; + } + } +} diff --git a/source/CairoSharp.Extensions/Pango/PangoFontMapNative.cs b/source/CairoSharp.Extensions/Pango/PangoFontMapNative.cs new file mode 100644 index 0000000..bd29c06 --- /dev/null +++ b/source/CairoSharp.Extensions/Pango/PangoFontMapNative.cs @@ -0,0 +1,30 @@ +// (c) gfoidl, all rights reserved + +using System.Runtime.InteropServices; +using Cairo.Extensions.GObject; + +namespace Cairo.Extensions.Pango; + +internal static unsafe partial class PangoFontMapNative +{ + // https://docs.gtk.org/PangoCairo/type_func.FontMap.get_default.html + [LibraryImport(PangoNative.LibPangoCairoName)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + internal static partial pango_font_map* pango_cairo_font_map_get_default(); + + // https://docs.gtk.org/Pango/method.FontMap.add_font_file.html + [LibraryImport(PangoNative.LibPangoName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + [return: MarshalAs(UnmanagedType.U4)] + internal static partial bool pango_font_map_add_font_file(pango_font_map* fontmap, string filename, GError** error); + + // https://docs.gtk.org/Pango/method.FontMap.get_family.html + [LibraryImport(PangoNative.LibPangoName, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + internal static partial pango_font_family* pango_font_map_get_family(pango_font_map* fontmap, string name); + + // https://docs.gtk.org/Pango/method.FontMap.list_families.html + [LibraryImport(PangoNative.LibPangoName)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + internal static partial void pango_font_map_list_families(pango_font_map* fontmap, pango_font_family*** families, int* n_families); +} diff --git a/source/CairoSharp.Extensions/Pango/PangoLayout.cs b/source/CairoSharp.Extensions/Pango/PangoLayout.cs index 71babe0..63d0225 100644 --- a/source/CairoSharp.Extensions/Pango/PangoLayout.cs +++ b/source/CairoSharp.Extensions/Pango/PangoLayout.cs @@ -25,7 +25,7 @@ public sealed unsafe class PangoLayout : CairoObject /// font sizes given in Pango match the font size given in cairo's user space units. /// /// - /// This layout can then be used for text measurement with functions like + /// This layout can then be used for text measurement with functions like /// or drawing with functions like . If you change the transformation or target /// surface for , you need to call . /// @@ -48,6 +48,7 @@ public PangoLayout(CairoContext cr, double resolution = 72d) : base(Create(cr)) } } + [StackTraceHidden] private static pango_layout* Create(CairoContext cr) { ArgumentNullException.ThrowIfNull(cr); @@ -80,25 +81,67 @@ protected override void DisposeCore(pango_layout* handle) /// When null is set, the current font description is unset. /// /// + /// A string representation of a font description. /// /// If no font description is set on the layout, the font description from the layout’s context is used. /// - public void SetFontDescriptionFromString(string? fontDescription) + public string? SetFontDescriptionFromString(string? fontDescription) { this.CheckDisposed(); if (fontDescription is null) { pango_layout_set_font_description(this.Handle, null); + return null; } else { pango_font_description* desc = pango_font_description_from_string(fontDescription); pango_layout_set_font_description(this.Handle, desc); + + sbyte* tmp = pango_font_description_to_string(desc); pango_font_description_free(desc); + + string res = new(tmp); + GObjectNative.g_free(tmp); + return res; } } + /// + /// Sets the font description that matches the face. + /// + /// The font face. + /// + /// The size of the font in points + /// + /// A string representation of a font description. + /// + /// The resulting font description will have the family, style, variant, weight and stretch + /// of the face, but its size field will be unset. + /// + public string SetFontDescription(PangoFontFace fontFace, int size) + { + this.CheckDisposed(); + fontFace.CheckDisposed(); + ArgumentNullException.ThrowIfNull(fontFace); + + pango_font_description* desc = PangoFontFaceNative.pango_font_face_describe(fontFace.Handle); + + // Must be set before assigning it to the layout. + pango_font_description_set_size(desc, size * Pango.Scale); + //pango_font_description_set_absolute_size(desc, size * Pango.Scale); + + pango_layout_set_font_description(this.Handle, desc); + + sbyte* tmp = pango_font_description_to_string(desc); + pango_font_description_free(desc); + + string res = new(tmp); + GObjectNative.g_free(tmp); + return res; + } + /// /// Determines the logical width and height of a in cairo units. /// @@ -186,7 +229,8 @@ public void ShowLayout() } /// - /// Updates the private PangoContext of a created with + /// Updates the private PangoContext of a created with + /// /// to match the current transformation and target surface of a . /// public void UpdateLayout() diff --git a/source/CairoSharp.Extensions/Pango/PangoNative.Resolver.cs b/source/CairoSharp.Extensions/Pango/PangoNative.Resolver.cs index cd28505..4db3c60 100644 --- a/source/CairoSharp.Extensions/Pango/PangoNative.Resolver.cs +++ b/source/CairoSharp.Extensions/Pango/PangoNative.Resolver.cs @@ -23,9 +23,15 @@ static partial class PangoNative "libgobject-2.0-0.dll", // Windows "libgobject-2.0.0.dylib"); // MacOS + private static readonly Native.LibNames s_glibLibNames = new( + GObjectNative.LibGLibName, // Linux + "libglib-2.0-0.dll", // Windows + "libglib-2.0.0.dylib"); // MacOS + private static nint s_libPangoHandle; private static nint s_libPangoCairoHandle; private static nint s_libGObjectHandle; + private static nint s_libGLibHandle; //------------------------------------------------------------------------- [DisallowNull] public static DllImportResolver? DllImportResolver { get; set; } = static (libraryName, assembly, searchPath) => @@ -35,6 +41,7 @@ static partial class PangoNative LibPangoName => ResolveCore(ref s_libPangoHandle , s_pangoLibNames), LibPangoCairoName => ResolveCore(ref s_libPangoCairoHandle, s_pangoCairoLibNames), GObjectNative.LibGObjectName => ResolveCore(ref s_libGObjectHandle , s_gobjectLibNames), + GObjectNative.LibGLibName => ResolveCore(ref s_libGLibHandle , s_glibLibNames), _ => default }; }; diff --git a/source/CairoSharp.Extensions/Pango/PangoNative.cs b/source/CairoSharp.Extensions/Pango/PangoNative.cs index de7453e..46846f1 100644 --- a/source/CairoSharp.Extensions/Pango/PangoNative.cs +++ b/source/CairoSharp.Extensions/Pango/PangoNative.cs @@ -1,23 +1,17 @@ // (c) gfoidl, all rights reserved -using System.ComponentModel; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; using Cairo.Extensions.GObject; namespace Cairo.Extensions.Pango; -[EditorBrowsable(EditorBrowsableState.Never)] -public struct pango_layout; -internal struct pango_context; -internal struct pango_font_description; -internal struct pango_attr_list; - public static unsafe partial class PangoNative { public const string LibPangoName = "libpango.so.1"; public const string LibPangoCairoName = "libpangocairo.so.1"; public const string LibGObjectName = GObjectNative.LibGObjectName; + public const string LibGLibName = GObjectNative.LibGLibName; // https://docs.gtk.org/PangoCairo/func.create_layout.html [LibraryImport(LibPangoCairoName)] @@ -34,6 +28,21 @@ public static unsafe partial class PangoNative [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] internal static partial pango_font_description* pango_font_description_from_string(string str); + // https://docs.gtk.org/Pango/method.FontDescription.set_size.html + [LibraryImport(LibPangoName)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + internal static partial void pango_font_description_set_size(pango_font_description* desc, int size); + + // https://docs.gtk.org/Pango/method.FontDescription.set_absolute_size.html + [LibraryImport(LibPangoName)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + internal static partial void pango_font_description_set_absolute_size(pango_font_description* desc, double size); + + // https://docs.gtk.org/Pango/method.FontDescription.to_string.html + [LibraryImport(LibPangoName)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] + internal static partial sbyte* pango_font_description_to_string(pango_font_description* desc); + // https://docs.gtk.org/Pango/method.FontDescription.free.html [LibraryImport(LibPangoName)] [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })] diff --git a/source/CairoSharp.Extensions/Pango/ReadMe.md b/source/CairoSharp.Extensions/Pango/ReadMe.md index 94225a2..5a3b762 100644 --- a/source/CairoSharp.Extensions/Pango/ReadMe.md +++ b/source/CairoSharp.Extensions/Pango/ReadMe.md @@ -26,6 +26,7 @@ if (OperatingSystem.IsWindows()) PangoNative.LibPangoName => IOPath.Combine(@"C:\Program Files\msys64\ucrt64\bin", "libpango-1.0-0.dll"), PangoNative.LibPangoCairoName => IOPath.Combine(@"C:\Program Files\msys64\ucrt64\bin", "libpangocairo-1.0-0.dll"), PangoNative.LibGObjectName => IOPath.Combine(@"C:\Program Files\msys64\ucrt64\bin", "libgobject-2.0-0.dll"), + PangoNative.LibGLibName => IOPath.Combine(@"C:\Program Files\msys64\ucrt64\bin", "libglib-2.0-0.dll"), _ => null }; diff --git a/source/CairoSharp.Extensions/Pango/TypeDefs.cs b/source/CairoSharp.Extensions/Pango/TypeDefs.cs new file mode 100644 index 0000000..0097afb --- /dev/null +++ b/source/CairoSharp.Extensions/Pango/TypeDefs.cs @@ -0,0 +1,21 @@ +// (c) gfoidl, all rights reserved + +using System.ComponentModel; + +namespace Cairo.Extensions.Pango; + +[EditorBrowsable(EditorBrowsableState.Never)] +public struct pango_layout; + +internal struct pango_context; +internal struct pango_font_description; +internal struct pango_attr_list; + +[EditorBrowsable(EditorBrowsableState.Never)] +public struct pango_font_map; + +[EditorBrowsable(EditorBrowsableState.Never)] +public struct pango_font_family; + +[EditorBrowsable(EditorBrowsableState.Never)] +public struct pango_font_face; diff --git a/source/CairoSharp/Surfaces/StreamSurface.cs b/source/CairoSharp/Surfaces/StreamSurface.cs index 07394e7..0468644 100644 --- a/source/CairoSharp/Surfaces/StreamSurface.cs +++ b/source/CairoSharp/Surfaces/StreamSurface.cs @@ -29,6 +29,7 @@ protected override void DisposeCore(cairo_surface_t* surface) // Need to free the surface first, so that the write function (if any) // can be called on a valid handle. // So it's like: dispose -> write func -> stream handle free + // See also Surface.Finish for more info. if (_stateHandle.IsAllocated) { _stateHandle.Free(); diff --git a/source/CairoSharp/Surfaces/Surface.cs b/source/CairoSharp/Surfaces/Surface.cs index ee73707..3119abd 100644 --- a/source/CairoSharp/Surfaces/Surface.cs +++ b/source/CairoSharp/Surfaces/Surface.cs @@ -184,12 +184,12 @@ public Status Status /// This method finishes the surface and drops all references to external resources. For example, /// for the Xlib backend it means that cairo will no longer access the drawable, which can be freed. /// After calling the only valid operations on a surface are checking status, getting - /// and setting user, referencing and destroying, and flushing and finishing it. Further drawing to the + /// and setting user data, referencing and destroying, and flushing and finishing it. Further drawing to the /// surface will not affect the surface but will instead trigger a error. /// /// /// When the last call to decreases the reference count to zero, - /// cairo will call cairo_surface_finish() if it hasn't been called already, before freeing the resources + /// cairo will call cairo_surface_finish() if it hasn't been called already, before freeing the resources /// associated with the surface. /// public void Finish()