├── .gitignore ├── src ├── pango │ ├── font_map.zig │ ├── font_family.zig │ ├── bit_fields.zig │ ├── util.zig │ ├── language.zig │ ├── attributes.zig │ ├── enums.zig │ └── matrix.zig ├── pangocairo.zig ├── cairo │ ├── surface.zig │ ├── drawing │ │ ├── tags_and_links.zig │ │ └── transformations.zig │ ├── surfaces │ │ ├── recording.zig │ │ ├── win32.zig │ │ ├── script.zig │ │ └── svg.zig │ ├── fonts │ │ └── font_face.zig │ └── context.zig ├── pango.zig ├── cairo.zig ├── pangocairo │ ├── fonts.zig │ ├── c.zig │ ├── pango_context.zig │ └── context.zig └── safety.zig ├── data └── romedalen.png ├── examples ├── generated │ ├── arc.png │ ├── clip.png │ ├── dash.png │ ├── grid.png │ ├── group.png │ ├── image.png │ ├── mask.png │ ├── text.png │ ├── bezier.png │ ├── curve_to.png │ ├── ellipse.png │ ├── glyphs.png │ ├── glyphs2.png │ ├── gradient.png │ ├── report.pdf │ ├── singular.png │ ├── spiral.png │ ├── clip_image.png │ ├── fill_style.png │ ├── line-chart.png │ ├── sierpinski.png │ ├── spirograph.png │ ├── test-image.png │ ├── arc_negative.png │ ├── compositing.png │ ├── glyphs_path.png │ ├── image_pattern.png │ ├── pango_shape.png │ ├── pango_simple.png │ ├── pango_twisted.png │ ├── set_line_cap.png │ ├── set_line_join.png │ ├── text_extents.png │ ├── three_phases.png │ ├── curve_rectangle.png │ ├── pythagoras_tree.png │ ├── fill_and_stroke2.png │ ├── multi_segment_caps.png │ ├── rounded_rectangle.png │ ├── save_and_restore.png │ ├── text_align_center.png │ ├── cairoscript │ └── line-chart.svg ├── utils.zig ├── surface_image.zig ├── surface_svg.zig ├── README.md ├── cairoscript.zig ├── multi_segment_caps.zig ├── surface_pdf.zig ├── clip.zig ├── image.zig ├── clip_image.zig ├── grid.zig ├── mask.zig ├── fill_and_stroke2.zig ├── text.zig ├── curve_to.zig ├── set_line_join.zig ├── arc.zig ├── gradient.zig ├── text_extents.zig ├── arc_negative.zig ├── text_align_center.zig ├── ellipse.zig ├── glyphs.zig ├── set_line_cap.zig ├── rounded_rectangle.zig ├── fill_style.zig ├── spirograph.zig ├── image_pattern.zig ├── group.zig ├── glyphs_path.zig ├── dash.zig ├── spiral.zig ├── save_and_restore.zig ├── sierpinski.zig ├── pythagoras_tree.zig ├── three_phases.zig ├── render.zig ├── pango_simple.zig ├── bezier.zig ├── curve_rectangle.zig ├── compositing.zig ├── singular.zig └── pango_shape.zig └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /zig-cache/* 2 | /zig-out/* 3 | -------------------------------------------------------------------------------- /src/pango/font_map.zig: -------------------------------------------------------------------------------- 1 | pub const FontMap = opaque {}; 2 | -------------------------------------------------------------------------------- /src/pango/font_family.zig: -------------------------------------------------------------------------------- 1 | pub const FontFamily = opaque {}; 2 | -------------------------------------------------------------------------------- /data/romedalen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/giza/master/data/romedalen.png -------------------------------------------------------------------------------- /examples/generated/arc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/giza/master/examples/generated/arc.png -------------------------------------------------------------------------------- /examples/generated/clip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/giza/master/examples/generated/clip.png -------------------------------------------------------------------------------- /examples/generated/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/giza/master/examples/generated/dash.png -------------------------------------------------------------------------------- /examples/generated/grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/giza/master/examples/generated/grid.png -------------------------------------------------------------------------------- /examples/generated/group.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/giza/master/examples/generated/group.png -------------------------------------------------------------------------------- /examples/generated/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/giza/master/examples/generated/image.png -------------------------------------------------------------------------------- /examples/generated/mask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/giza/master/examples/generated/mask.png -------------------------------------------------------------------------------- /examples/generated/text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/giza/master/examples/generated/text.png -------------------------------------------------------------------------------- /examples/generated/bezier.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/giza/master/examples/generated/bezier.png -------------------------------------------------------------------------------- /examples/generated/curve_to.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/giza/master/examples/generated/curve_to.png -------------------------------------------------------------------------------- /examples/generated/ellipse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/giza/master/examples/generated/ellipse.png -------------------------------------------------------------------------------- /examples/generated/glyphs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/giza/master/examples/generated/glyphs.png -------------------------------------------------------------------------------- /examples/generated/glyphs2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/giza/master/examples/generated/glyphs2.png -------------------------------------------------------------------------------- /examples/generated/gradient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/giza/master/examples/generated/gradient.png -------------------------------------------------------------------------------- /examples/generated/report.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/giza/master/examples/generated/report.pdf -------------------------------------------------------------------------------- /examples/generated/singular.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/giza/master/examples/generated/singular.png -------------------------------------------------------------------------------- /examples/generated/spiral.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/giza/master/examples/generated/spiral.png -------------------------------------------------------------------------------- /examples/generated/clip_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/giza/master/examples/generated/clip_image.png -------------------------------------------------------------------------------- /examples/generated/fill_style.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/giza/master/examples/generated/fill_style.png -------------------------------------------------------------------------------- /examples/generated/line-chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/giza/master/examples/generated/line-chart.png -------------------------------------------------------------------------------- /examples/generated/sierpinski.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/giza/master/examples/generated/sierpinski.png -------------------------------------------------------------------------------- /examples/generated/spirograph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/giza/master/examples/generated/spirograph.png -------------------------------------------------------------------------------- /examples/generated/test-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/giza/master/examples/generated/test-image.png -------------------------------------------------------------------------------- /examples/generated/arc_negative.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/giza/master/examples/generated/arc_negative.png -------------------------------------------------------------------------------- /examples/generated/compositing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/giza/master/examples/generated/compositing.png -------------------------------------------------------------------------------- /examples/generated/glyphs_path.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/giza/master/examples/generated/glyphs_path.png -------------------------------------------------------------------------------- /examples/generated/image_pattern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/giza/master/examples/generated/image_pattern.png -------------------------------------------------------------------------------- /examples/generated/pango_shape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/giza/master/examples/generated/pango_shape.png -------------------------------------------------------------------------------- /examples/generated/pango_simple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/giza/master/examples/generated/pango_simple.png -------------------------------------------------------------------------------- /examples/generated/pango_twisted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/giza/master/examples/generated/pango_twisted.png -------------------------------------------------------------------------------- /examples/generated/set_line_cap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/giza/master/examples/generated/set_line_cap.png -------------------------------------------------------------------------------- /examples/generated/set_line_join.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/giza/master/examples/generated/set_line_join.png -------------------------------------------------------------------------------- /examples/generated/text_extents.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/giza/master/examples/generated/text_extents.png -------------------------------------------------------------------------------- /examples/generated/three_phases.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/giza/master/examples/generated/three_phases.png -------------------------------------------------------------------------------- /examples/generated/curve_rectangle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/giza/master/examples/generated/curve_rectangle.png -------------------------------------------------------------------------------- /examples/generated/pythagoras_tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/giza/master/examples/generated/pythagoras_tree.png -------------------------------------------------------------------------------- /examples/generated/fill_and_stroke2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/giza/master/examples/generated/fill_and_stroke2.png -------------------------------------------------------------------------------- /examples/generated/multi_segment_caps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/giza/master/examples/generated/multi_segment_caps.png -------------------------------------------------------------------------------- /examples/generated/rounded_rectangle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/giza/master/examples/generated/rounded_rectangle.png -------------------------------------------------------------------------------- /examples/generated/save_and_restore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/giza/master/examples/generated/save_and_restore.png -------------------------------------------------------------------------------- /examples/generated/text_align_center.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patterns/giza/master/examples/generated/text_align_center.png -------------------------------------------------------------------------------- /examples/utils.zig: -------------------------------------------------------------------------------- 1 | const cairo = @import("cairo"); 2 | 3 | /// Set a cairo Context with the same gray background used on the cairo 4 | /// [website](https://www.cairographics.org/samples/). 5 | pub fn setBackground(cr: *cairo.Context) void { 6 | cr.setSourceRgb(0.93, 0.93, 0.93); // gray 7 | cr.paintWithAlpha(1.0); 8 | } 9 | -------------------------------------------------------------------------------- /examples/generated/cairoscript: -------------------------------------------------------------------------------- 1 | %!CairoScript 2 | % render a gray background 3 | << /content //COLOR /width 640 /height 480 >> surface context 4 | 0.93 g set-source 5 | paint 6 | % render a red rectangle with a thick blue outline 7 | 1 0 0 0.95 rgba set-source 8 | n 0 0 256 256 rectangle 9 | fill+ 10 | 0 0 1 rgb set-source 11 | 8 set-line-width 12 | stroke+ 13 | pop 14 | -------------------------------------------------------------------------------- /src/pangocairo.zig: -------------------------------------------------------------------------------- 1 | pub const c = @import("pangocairo/c.zig"); 2 | 3 | pub usingnamespace @import("pangocairo/fonts.zig"); 4 | pub const context = @import("pangocairo/context.zig"); 5 | pub const pango_context = @import("pangocairo/pango_context.zig"); 6 | 7 | const cairo = @import("cairo"); 8 | const pango = @import("pango"); 9 | const c_bool = c_int; 10 | pub const ShapeRendererFunc = ?*const fn (cr: ?*cairo.Context, attr: [*c]pango.AttrShape, do_path: c_bool, data: ?*anyopaque) callconv(.C) void; 11 | -------------------------------------------------------------------------------- /src/cairo/surface.zig: -------------------------------------------------------------------------------- 1 | const svg = @import("surfaces/svg.zig"); 2 | const script = @import("surfaces/script.zig"); 3 | const pdf = @import("surfaces/pdf.zig"); 4 | 5 | pub const ImageSurface = @import("surfaces/image.zig").ImageSurface; 6 | pub const RecordingSurface = @import("surfaces/recording.zig").RecordingSurface; 7 | pub const Surface = @import("surfaces/base.zig").Surface; 8 | pub usingnamespace svg; 9 | pub usingnamespace script; 10 | pub usingnamespace pdf; 11 | pub const Win32Surface = @import("surfaces/win32.zig").Win32Surface; 12 | -------------------------------------------------------------------------------- /examples/generated/line-chart.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /examples/surface_image.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const cairo = @import("cairo"); 3 | const render = @import("render.zig"); 4 | 5 | pub fn main() !void { 6 | const width: u16 = 640; 7 | const height: u16 = 480; 8 | const surface = try cairo.ImageSurface.create(.argb32, width, height); 9 | defer surface.destroy(); 10 | 11 | const cr = try cairo.Context.create(surface.asSurface()); 12 | defer cr.destroy(); 13 | 14 | render.testImage(cr, width, height); 15 | try surface.writeToPng("examples/generated/test-image.png"); 16 | 17 | render.lineChart(cr, width, height); 18 | try surface.writeToPng("examples/generated/line-chart.png"); 19 | } 20 | -------------------------------------------------------------------------------- /src/pango/bit_fields.zig: -------------------------------------------------------------------------------- 1 | /// The bits in a `pango.FontMask` correspond to the set fields in a 2 | /// `pango.FontDescriptions`. 3 | pub const FontMask = packed struct(u32) { 4 | /// The font family is specified. 5 | family: bool, 6 | /// The font style is specified. 7 | style: bool, 8 | /// The font variant is specified. 9 | variant: bool, 10 | /// The font weight is specified. 11 | weight: bool, 12 | /// The font stretch is specified. 13 | stretch: bool, 14 | /// The font size is specified. 15 | size: bool, 16 | /// The font gravity is specified. 17 | gravity: bool, 18 | /// OpenType font variations are specified. 19 | variations: bool, 20 | _: u24, 21 | }; 22 | -------------------------------------------------------------------------------- /examples/surface_svg.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const cairo = @import("cairo"); 3 | const render = @import("render.zig"); 4 | 5 | pub fn main() !void { 6 | const width_pt: f64 = 640; 7 | const height_pt: f64 = 480; 8 | const surface = try cairo.SvgSurface.create("examples/generated/test-image.svg", width_pt, height_pt); 9 | defer surface.destroy(); 10 | 11 | const cr = try cairo.Context.create(surface.asSurface()); 12 | defer cr.destroy(); 13 | 14 | render.testImage(cr, width_pt, height_pt); 15 | 16 | const surface2 = try cairo.SvgSurface.create("examples/generated/line-chart.svg", width_pt, height_pt); 17 | defer surface2.destroy(); 18 | 19 | const cr2 = try cairo.Context.create(surface2.asSurface()); 20 | defer cr2.destroy(); 21 | 22 | render.lineChart(cr2, width_pt, height_pt); 23 | } 24 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | This directory contains a few examples on how to use giza. Examples are copied and adapted from [another](https://github.com/jackdbd/zig-cairo) cairo binding written in Zig by [@jackdbd](https://github.com/jackdbd). There are examples ported from: 4 | 5 | - cairographics.org [samples](https://www.cairographics.org/samples/) 6 | - Pycairo [examples](https://github.com/pygobject/pycairo/blob/master/examples/pycairo_examples.ipynb) 7 | - Perl Cairo [tutorial](https://www.lemoda.net/cairo/cairo-tutorial/) 8 | - Pango [examples](https://gitlab.gnome.org/GNOME/pango/-/tree/main/examples) 9 | - cairo's micro benchmark performance [test suite](https://gitlab.freedesktop.org/cairo/cairo/-/tree/master/perf/micro) 10 | - cairo [demos](https://gitlab.com/cairo/cairo-demos/-/tree/master/png) 11 | - [CairoSample](https://github.com/preshing/CairoSample) by [Jeff Preshing](https://github.com/preshing) -------------------------------------------------------------------------------- /src/pango.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | pub const safety = @import("safety"); 3 | 4 | pub usingnamespace @import("pango/enums.zig"); 5 | pub usingnamespace @import("pango/bit_fields.zig"); 6 | pub usingnamespace @import("pango/util.zig"); 7 | 8 | pub const Matrix = @import("pango/matrix.zig").Matrix; 9 | 10 | pub const Context = @import("pango/context.zig").Context; 11 | pub const Layout = @import("pango/layout.zig").Layout; 12 | pub const Language = @import("pango/language.zig").Language; 13 | pub const FontDescription = @import("pango/font_description.zig").FontDescription; 14 | pub const FontMap = @import("pango/font_map.zig").FontMap; 15 | pub const FontFamily = @import("pango/font_family.zig").FontFamily; 16 | 17 | pub usingnamespace @import("pango/attributes.zig"); 18 | 19 | pub const SCALE = 1024; 20 | 21 | pub const c = @import("pango/c.zig"); 22 | 23 | comptime { 24 | std.testing.refAllDeclsRecursive(@This()); 25 | } 26 | -------------------------------------------------------------------------------- /src/cairo.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub usingnamespace @import("cairo/enums.zig"); 4 | pub usingnamespace @import("cairo/util.zig"); 5 | 6 | pub usingnamespace @import("cairo/surface.zig"); 7 | pub const Device = @import("cairo/device.zig").Device; 8 | 9 | pub usingnamespace @import("cairo/context.zig"); 10 | pub usingnamespace @import("cairo/drawing/pattern.zig"); 11 | const path = @import("cairo/drawing/paths.zig"); 12 | pub const Path = path.Path; 13 | pub const PathData = path.PathData; 14 | pub const Region = @import("cairo/drawing/regions.zig").Region; 15 | 16 | const text = @import("cairo/drawing/text.zig"); 17 | pub const Glyph = text.Glyph; 18 | pub const TextCluster = text.TextCluster; 19 | pub const FontFace = @import("cairo/fonts/font_face.zig").FontFace; 20 | pub const ToyFontFace = text.ToyFontFace; 21 | pub usingnamespace @import("cairo/fonts/font_options.zig"); 22 | pub usingnamespace @import("cairo/fonts/scaled_font.zig"); 23 | 24 | pub const c = @import("cairo/c.zig"); 25 | 26 | test { 27 | std.testing.refAllDeclsRecursive(@This()); 28 | } 29 | -------------------------------------------------------------------------------- /examples/cairoscript.zig: -------------------------------------------------------------------------------- 1 | //! Render these Cairo operations on a script. 2 | const std = @import("std"); 3 | const pi = std.math.pi; 4 | const cairo = @import("cairo"); 5 | 6 | pub fn main() !void { 7 | const OUTPUT_DEVICE_FILENAME = "examples/generated/cairoscript"; 8 | const width: f64 = 640; 9 | const height: f64 = 480; 10 | 11 | const script = try cairo.Script.create(OUTPUT_DEVICE_FILENAME); 12 | defer script.destroy(); 13 | 14 | const surface = try cairo.ScriptSurface.create(script, cairo.Content.Color, width, height); 15 | defer surface.destroy(); 16 | 17 | const cr = try cairo.Context.create(surface.asSurface()); 18 | defer cr.destroy(); 19 | 20 | script.writeComment("render a gray background"); 21 | cr.setSourceRgb(0.93, 0.93, 0.93); // gray 22 | cr.paintWithAlpha(1.0); 23 | 24 | script.writeComment("render a red rectangle with a thick blue outline"); 25 | cr.setSourceRgba(1, 0, 0, 0.95); 26 | cr.rectangle(cairo.Rectangle.init(.{ 0, 0, 256, 256 })); 27 | cr.fillPreserve(); 28 | cr.setLineWidth(8.0); 29 | cr.setSourceRgb(0, 0, 1); 30 | cr.stroke(); 31 | } 32 | -------------------------------------------------------------------------------- /examples/multi_segment_caps.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const pi = std.math.pi; 3 | const cairo = @import("cairo"); 4 | const setBackground = @import("utils.zig").setBackground; 5 | 6 | /// https://www.cairographics.org/samples/multi_segment_caps/ 7 | fn multiSegmentCaps(cr: *cairo.Context) void { 8 | cr.setSourceRgb(0.0, 0.0, 0.0); // black 9 | 10 | cr.moveTo(50.0, 75.0); 11 | cr.lineTo(200.0, 75.0); 12 | 13 | cr.moveTo(50.0, 125.0); 14 | cr.lineTo(200.0, 125.0); 15 | 16 | cr.moveTo(50.0, 175.0); 17 | cr.lineTo(200.0, 175.0); 18 | 19 | cr.setLineWidth(30.0); 20 | cr.setLineCap(cairo.Context.LineCap.Round); 21 | cr.stroke(); 22 | } 23 | 24 | pub fn main() !void { 25 | const width: u16 = 256; 26 | const height: u16 = 256; 27 | const surface = try cairo.ImageSurface.create(.argb32, width, height); 28 | defer surface.destroy(); 29 | 30 | const cr = try cairo.Context.create(surface.asSurface()); 31 | defer cr.destroy(); 32 | 33 | setBackground(cr); 34 | multiSegmentCaps(cr); 35 | try surface.writeToPng("examples/generated/multi_segment_caps.png"); 36 | } 37 | -------------------------------------------------------------------------------- /examples/surface_pdf.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const cairo = @import("cairo"); 3 | const render = @import("render.zig"); 4 | 5 | pub fn main() !void { 6 | const width_pt: f64 = 640; 7 | const height_pt: f64 = 480; 8 | const surface = try cairo.PdfSurface.create("examples/generated/report.pdf", width_pt, height_pt); 9 | defer surface.destroy(); 10 | 11 | surface.setMetadata(cairo.PdfSurface.Metadata.Title, "Some Title"); 12 | surface.setMetadata(cairo.PdfSurface.Metadata.Author, "Some author"); 13 | surface.setMetadata(cairo.PdfSurface.Metadata.CreateDate, "2021-01-28T19:49+02:00"); 14 | surface.setMetadata(cairo.PdfSurface.Metadata.Keywords, "foo,bar"); 15 | 16 | const cr = try cairo.Context.create(surface.asSurface()); 17 | defer cr.destroy(); 18 | 19 | cr.tagBegin("H1", null); 20 | cr.moveTo(20, 30); 21 | cr.showText("Heading 1"); 22 | cr.tagEnd("H1"); 23 | 24 | cr.tagBegin(cairo.Context.TagLink, "uri='https://cairographics.org'"); 25 | cr.moveTo(100, 200); 26 | cr.showText("This is a hyperlink to the Cairo website."); 27 | cr.tagEnd(cairo.Context.TagLink); 28 | } 29 | -------------------------------------------------------------------------------- /examples/clip.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const pi = std.math.pi; 3 | const cairo = @import("cairo"); 4 | const setBackground = @import("utils.zig").setBackground; 5 | 6 | /// https://www.cairographics.org/samples/clip/ 7 | fn clip(cr: *cairo.Context) void { 8 | cr.setSourceRgb(0.0, 0.0, 0.0); // black 9 | 10 | cr.arc(128.0, 128.0, 76.8, 0, 2 * pi); 11 | cr.clip(); 12 | 13 | // current path is not consumed by cr.clip() 14 | cr.newPath(); 15 | cr.rectangle(cairo.Rectangle.init(.{ 0, 0, 256, 256 })); 16 | cr.fill(); 17 | cr.setSourceRgb(0, 1, 0); 18 | cr.moveTo(0, 0); 19 | cr.lineTo(256, 256); 20 | cr.moveTo(256, 0); 21 | cr.lineTo(0, 256); 22 | cr.setLineWidth(10.0); 23 | cr.stroke(); 24 | } 25 | 26 | pub fn main() !void { 27 | const width: u16 = 256; 28 | const height: u16 = 256; 29 | const surface = try cairo.ImageSurface.create(.argb32, width, height); 30 | defer surface.destroy(); 31 | 32 | const cr = try cairo.Context.create(surface.asSurface()); 33 | defer cr.destroy(); 34 | 35 | setBackground(cr); 36 | clip(cr); 37 | try surface.writeToPng("examples/generated/clip.png"); 38 | } 39 | -------------------------------------------------------------------------------- /examples/image.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const pi = std.math.pi; 3 | const cairo = @import("cairo"); 4 | const setBackground = @import("utils.zig").setBackground; 5 | 6 | /// https://www.cairographics.org/samples/image/ 7 | fn image(cr: *cairo.Context) !void { 8 | const surface = try cairo.ImageSurface.createFromPng("data/romedalen.png"); 9 | defer surface.destroy(); 10 | 11 | const w: f64 = @floatFromInt(surface.getWidth()); 12 | const h: f64 = @floatFromInt(surface.getHeight()); 13 | 14 | cr.translate(128.0, 128.0); 15 | cr.rotate(45 * pi / 180.0); 16 | cr.scale(256.0 / w, 256.0 / h); 17 | cr.translate(-0.5 * w, -0.5 * h); 18 | 19 | cr.setSourceSurface(surface.asSurface(), 0, 0); 20 | cr.paint(); 21 | } 22 | 23 | pub fn main() !void { 24 | const width: u16 = 256; 25 | const height: u16 = 256; 26 | const surface = try cairo.ImageSurface.create(.argb32, width, height); 27 | defer surface.destroy(); 28 | 29 | const cr = try cairo.Context.create(surface.asSurface()); 30 | defer cr.destroy(); 31 | 32 | setBackground(cr); 33 | try image(cr); 34 | try surface.writeToPng("examples/generated/image.png"); 35 | } 36 | -------------------------------------------------------------------------------- /examples/clip_image.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const pi = std.math.pi; 3 | const cairo = @import("cairo"); 4 | const setBackground = @import("utils.zig").setBackground; 5 | 6 | /// https://www.cairographics.org/samples/clip_image/ 7 | fn clipImage(cr: *cairo.Context) !void { 8 | cr.setSourceRgb(0.0, 0.0, 0.0); // black 9 | 10 | const image = try cairo.ImageSurface.createFromPng("data/romedalen.png"); 11 | defer image.destroy(); 12 | 13 | const w = image.getWidth(); 14 | const h = image.getHeight(); 15 | 16 | cr.arc(128.0, 128.0, 76.8, 0, 2 * pi); 17 | cr.clip(); 18 | cr.newPath(); // path not consumed by cr.clip() 19 | 20 | cr.scale(256.0 / @as(f64, @floatFromInt(w)), 256.0 / @as(f64, @floatFromInt(h))); 21 | cr.setSourceSurface(image.asSurface(), 0, 0); 22 | cr.paint(); 23 | } 24 | 25 | pub fn main() !void { 26 | const width: u16 = 256; 27 | const height: u16 = 256; 28 | const surface = try cairo.ImageSurface.create(.argb32, width, height); 29 | defer surface.destroy(); 30 | 31 | const cr = try cairo.Context.create(surface.asSurface()); 32 | defer cr.destroy(); 33 | 34 | setBackground(cr); 35 | try clipImage(cr); 36 | try surface.writeToPng("examples/generated/clip_image.png"); 37 | } 38 | -------------------------------------------------------------------------------- /examples/grid.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const builtin = @import("builtin"); 3 | const pi = std.math.pi; 4 | const cairo = @import("cairo"); 5 | const setBackground = @import("utils.zig").setBackground; 6 | 7 | /// Zig porting of this example from the Perl Cairo tutorial. 8 | /// https://www.lemoda.net/cairo/cairo-tutorial/grid.html 9 | fn drawGrid(cr: *cairo.Context, size: u16, divisions: usize) void { 10 | cr.setSourceRgb(0.4, 0.4, 1.0); // blue 11 | 12 | var i: usize = 0; 13 | while (i < divisions) : (i += 1) { 14 | const s: f64 = @floatFromInt(size); 15 | const k = s * @as(f64, @floatFromInt(i)) / @as(f64, @floatFromInt(divisions)); 16 | cr.moveTo(k, 0); 17 | cr.lineTo(k, s); 18 | cr.moveTo(0, k); 19 | cr.lineTo(s, k); 20 | } 21 | cr.stroke(); 22 | } 23 | 24 | pub fn main() !void { 25 | const size: u16 = 400; 26 | const surface = try cairo.ImageSurface.create(.argb32, size, size); 27 | defer surface.destroy(); 28 | 29 | const cr = try cairo.Context.create(surface.asSurface()); 30 | defer cr.destroy(); 31 | 32 | setBackground(cr); 33 | const divisions: usize = 10; 34 | drawGrid(cr, size, divisions); 35 | try surface.writeToPng("examples/generated/grid.png"); 36 | } 37 | -------------------------------------------------------------------------------- /examples/mask.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const pi = std.math.pi; 3 | const cairo = @import("cairo"); 4 | const setBackground = @import("utils.zig").setBackground; 5 | 6 | /// https://github.com/pygobject/pycairo/blob/master/examples/pycairo_examples.ipynb 7 | fn maskExample(cr: *cairo.Context) !void { 8 | const pattern = try cairo.LinearGradientPattern.create(0, 0, 256, 256); 9 | defer pattern.destroy(); 10 | 11 | pattern.addColorStopRgb(0, 0, 0.3, 0.8); 12 | pattern.addColorStopRgb(1, 0, 0.8, 0.3); 13 | 14 | const mask = try cairo.RadialGradientPattern.create(128, 128, 64, 128, 128, 128); 15 | defer mask.destroy(); 16 | 17 | mask.addColorStopRgba(0, 0, 0, 0, 1); 18 | mask.addColorStopRgba(0.5, 0, 0, 0, 0); 19 | 20 | cr.setSource(pattern.asPattern()); 21 | cr.mask(mask.asPattern()); 22 | } 23 | 24 | pub fn main() !void { 25 | const width: u16 = 256; 26 | const height: u16 = 256; 27 | const surface = try cairo.ImageSurface.create(.argb32, width, height); 28 | defer surface.destroy(); 29 | 30 | const cr = try cairo.Context.create(surface.asSurface()); 31 | defer cr.destroy(); 32 | 33 | setBackground(cr); 34 | try maskExample(cr); 35 | try surface.writeToPng("examples/generated/mask.png"); 36 | } 37 | -------------------------------------------------------------------------------- /examples/fill_and_stroke2.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const cairo = @import("cairo"); 3 | const setBackground = @import("utils.zig").setBackground; 4 | 5 | /// https://www.cairographics.org/samples/fill_and_stroke2/ 6 | fn fillAndStroke2(cr: *cairo.Context) void { 7 | cr.setSourceRgb(0.0, 0.0, 0.0); // black 8 | 9 | // const offset: f64 = -50.0; 10 | 11 | cr.moveTo(128.0, 25.6); 12 | cr.lineTo(230.4, 230.4); 13 | cr.relLineTo(-102.4, 0.0); 14 | cr.curveTo(51.2, 230.4, 51.2, 128.0, 128.0, 128.0); 15 | cr.closePath(); 16 | 17 | cr.moveTo(64.0, 25.6); 18 | cr.relLineTo(51.2, 51.2); 19 | cr.relLineTo(-51.2, 51.2); 20 | cr.relLineTo(-51.2, -51.2); 21 | cr.closePath(); 22 | 23 | cr.setLineWidth(10.0); 24 | cr.setSourceRgb(0.0, 0.0, 1.0); // blue 25 | cr.fillPreserve(); 26 | cr.setSourceRgb(0.0, 0.0, 0.0); // black 27 | cr.stroke(); 28 | } 29 | 30 | pub fn main() !void { 31 | const width: u16 = 256; 32 | const height: u16 = 256; 33 | const surface = try cairo.ImageSurface.create(.argb32, width, height); 34 | defer surface.destroy(); 35 | 36 | const cr = try cairo.Context.create(surface.asSurface()); 37 | defer cr.destroy(); 38 | 39 | setBackground(cr); 40 | fillAndStroke2(cr); 41 | try surface.writeToPng("examples/generated/fill_and_stroke2.png"); 42 | } 43 | -------------------------------------------------------------------------------- /examples/text.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const pi = std.math.pi; 3 | const cairo = @import("cairo"); 4 | const setBackground = @import("utils.zig").setBackground; 5 | 6 | /// https://www.cairographics.org/samples/text/ 7 | fn text(cr: *cairo.Context) void { 8 | cr.selectFontFace("Sans", .Normal, .Bold); 9 | cr.setFontSize(90.0); 10 | 11 | cr.moveTo(10.0, 135.0); 12 | cr.setSourceRgb(0.0, 0.0, 0.0); // black 13 | cr.showText("Hello"); 14 | 15 | cr.moveTo(70.0, 165.0); 16 | cr.textPath("void"); 17 | cr.setSourceRgb(0.5, 0.5, 1); // bluish-violet 18 | cr.fillPreserve(); 19 | cr.setSourceRgb(0.0, 0.0, 0.0); // black 20 | cr.setLineWidth(2.56); 21 | cr.stroke(); 22 | 23 | // draw helping lines 24 | cr.setSourceRgba(1, 0.2, 0.2, 0.6); 25 | cr.arc(10.0, 135.0, 5.12, 0, 2 * pi); 26 | cr.closePath(); 27 | cr.arc(70.0, 165.0, 5.12, 0, 2 * pi); 28 | cr.fill(); 29 | } 30 | 31 | pub fn main() !void { 32 | const width: u16 = 256; 33 | const height: u16 = 256; 34 | const surface = try cairo.ImageSurface.create(.argb32, width, height); 35 | defer surface.destroy(); 36 | 37 | const cr = try cairo.Context.create(surface.asSurface()); 38 | defer cr.destroy(); 39 | 40 | setBackground(cr); 41 | text(cr); 42 | try surface.writeToPng("examples/generated/text.png"); 43 | } 44 | -------------------------------------------------------------------------------- /examples/curve_to.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const cairo = @import("cairo"); 3 | const setBackground = @import("utils.zig").setBackground; 4 | 5 | /// https://www.cairographics.org/samples/curve_to/ 6 | fn curveTo(cr: *cairo.Context, _: usize, _: usize) void { 7 | const x: f64 = 25.6; 8 | const y: f64 = 128.0; 9 | 10 | const x1: f64 = 102.4; 11 | const y1: f64 = 230.4; 12 | const x2: f64 = 153.6; 13 | const y2: f64 = 25.6; 14 | const x3: f64 = 230.4; 15 | const y3: f64 = 128.0; 16 | 17 | cr.setSourceRgb(0.0, 0.0, 0.0); // black 18 | 19 | cr.moveTo(x, y); 20 | cr.curveTo(x1, y1, x2, y2, x3, y3); 21 | 22 | cr.setLineWidth(10.0); 23 | cr.stroke(); 24 | 25 | cr.setSourceRgba(1, 0.2, 0.2, 0.6); 26 | cr.setLineWidth(6.0); 27 | 28 | cr.moveTo(x, y); 29 | cr.lineTo(x1, y1); 30 | 31 | cr.moveTo(x2, y2); 32 | cr.lineTo(x3, y3); 33 | 34 | cr.stroke(); 35 | } 36 | 37 | pub fn main() !void { 38 | const width: u16 = 256; 39 | const height: u16 = 256; 40 | const surface = try cairo.ImageSurface.create(.argb32, width, height); 41 | defer surface.destroy(); 42 | 43 | const cr = try cairo.Context.create(surface.asSurface()); 44 | defer cr.destroy(); 45 | 46 | setBackground(cr); 47 | curveTo(cr, width, height); 48 | try surface.writeToPng("examples/generated/curve_to.png"); 49 | } 50 | -------------------------------------------------------------------------------- /examples/set_line_join.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const pi = std.math.pi; 3 | const cairo = @import("cairo"); 4 | const setBackground = @import("utils.zig").setBackground; 5 | 6 | /// https://www.cairographics.org/samples/set_line_join/ 7 | fn setLineJoin(cr: *cairo.Context) void { 8 | cr.setSourceRgb(0.0, 0.0, 0.0); // black 9 | 10 | cr.setLineWidth(40.96); 11 | 12 | cr.moveTo(76.8, 84.48); 13 | cr.relLineTo(51.2, -51.2); 14 | cr.relLineTo(51.2, 51.2); 15 | cr.setLineJoin(cairo.Context.LineJoin.Miter); // default 16 | cr.stroke(); 17 | 18 | cr.moveTo(76.8, 161.28); 19 | cr.relLineTo(51.2, -51.2); 20 | cr.relLineTo(51.2, 51.2); 21 | cr.setLineJoin(cairo.Context.LineJoin.Bevel); 22 | cr.stroke(); 23 | 24 | cr.moveTo(76.8, 238.08); 25 | cr.relLineTo(51.2, -51.2); 26 | cr.relLineTo(51.2, 51.2); 27 | cr.setLineJoin(cairo.Context.LineJoin.Round); 28 | cr.stroke(); 29 | } 30 | 31 | pub fn main() !void { 32 | const width: u16 = 256; 33 | const height: u16 = 256; 34 | const surface = try cairo.ImageSurface.create(.argb32, width, height); 35 | defer surface.destroy(); 36 | 37 | const cr = try cairo.Context.create(surface.asSurface()); 38 | defer cr.destroy(); 39 | 40 | setBackground(cr); 41 | setLineJoin(cr); 42 | try surface.writeToPng("examples/generated/set_line_join.png"); 43 | } 44 | -------------------------------------------------------------------------------- /examples/arc.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const pi = std.math.pi; 3 | const cairo = @import("cairo"); 4 | const setBackground = @import("utils.zig").setBackground; 5 | 6 | /// https://www.cairographics.org/samples/arc/ 7 | fn arc(cr: *cairo.Context) void { 8 | const xc: f64 = 128.0; 9 | const yc: f64 = 128.0; 10 | const radius: f64 = 100.0; 11 | // angles are specified in radians 12 | const angle1 = 45.0 * (pi / 180.0); 13 | const angle2 = 180.0 * (pi / 180.0); 14 | 15 | cr.setSourceRgb(0.0, 0.0, 0.0); // black 16 | 17 | cr.setLineWidth(10.0); 18 | cr.arc(xc, yc, radius, angle1, angle2); 19 | cr.stroke(); 20 | 21 | // draw helping lines 22 | cr.setSourceRgba(1, 0.2, 0.2, 0.6); 23 | cr.arc(xc, yc, 10.0, 0, 2 * pi); 24 | cr.fill(); 25 | cr.setLineWidth(6.0); 26 | cr.arc(xc, yc, radius, angle1, angle1); 27 | cr.lineTo(xc, yc); 28 | cr.arc(xc, yc, radius, angle2, angle2); 29 | cr.lineTo(xc, yc); 30 | cr.stroke(); 31 | } 32 | 33 | pub fn main() !void { 34 | const width: u16 = 256; 35 | const height: u16 = 256; 36 | const surface = try cairo.ImageSurface.create(.argb32, width, height); 37 | defer surface.destroy(); 38 | 39 | const cr = try cairo.Context.create(surface.asSurface()); 40 | defer cr.destroy(); 41 | 42 | setBackground(cr); 43 | arc(cr); 44 | try surface.writeToPng("examples/generated/arc.png"); 45 | } 46 | -------------------------------------------------------------------------------- /examples/gradient.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const pi = std.math.pi; 3 | const cairo = @import("cairo"); 4 | const Rect = cairo.Rectangle; 5 | const setBackground = @import("utils.zig").setBackground; 6 | 7 | /// https://www.cairographics.org/samples/gradient/ 8 | fn gradient(cr: *cairo.Context) !void { 9 | const linear = try cairo.LinearGradientPattern.create(0.0, 0.0, 0.0, 256.0); 10 | defer linear.destroy(); 11 | 12 | linear.addColorStopRgba(1, 0, 0, 0, 1); 13 | linear.addColorStopRgba(0, 1, 1, 1, 1); 14 | cr.rectangle(Rect.init(.{ 0, 0, 256, 256 })); 15 | cr.setSource(linear.asPattern()); 16 | cr.fill(); 17 | 18 | const radial = try cairo.RadialGradientPattern.create(115.2, 102.4, 25.6, 102.4, 102.4, 128.0); 19 | defer radial.destroy(); 20 | 21 | radial.addColorStopRgba(0, 1, 1, 1, 1); 22 | radial.addColorStopRgba(1, 0, 0, 0, 1); 23 | cr.setSource(radial.asPattern()); 24 | cr.arc(128.0, 128.0, 76.8, 0, 2 * pi); 25 | cr.fill(); 26 | } 27 | 28 | pub fn main() !void { 29 | const width: u16 = 256; 30 | const height: u16 = 256; 31 | const surface = try cairo.ImageSurface.create(.argb32, width, height); 32 | defer surface.destroy(); 33 | 34 | const cr = try cairo.Context.create(surface.asSurface()); 35 | defer cr.destroy(); 36 | 37 | setBackground(cr); 38 | try gradient(cr); 39 | try surface.writeToPng("examples/generated/gradient.png"); 40 | } 41 | -------------------------------------------------------------------------------- /examples/text_extents.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const pi = std.math.pi; 3 | const cairo = @import("cairo"); 4 | const setBackground = @import("utils.zig").setBackground; 5 | 6 | /// https://www.cairographics.org/samples/text_extents/ 7 | fn textExtents(cr: *cairo.Context) void { 8 | cr.selectFontFace("Sans", .Normal, .Normal); 9 | cr.setFontSize(100.0); 10 | const some_text = "cairo"; // TODO: check that text is UTF8-encoded 11 | 12 | const te = cr.textExtents(some_text); 13 | const x = 25.0; 14 | const y = 150.0; 15 | cr.moveTo(x, y); 16 | cr.setSourceRgb(0.0, 0.0, 0.0); // black 17 | cr.showText(some_text); 18 | 19 | // draw helping lines 20 | cr.setSourceRgba(1, 0.2, 0.2, 0.6); 21 | cr.setLineWidth(6.0); 22 | cr.arc(x, y, 10.0, 0, 2 * pi); 23 | cr.fill(); 24 | cr.moveTo(x, y); 25 | cr.relLineTo(0, -te.height); 26 | cr.relLineTo(te.width, 0); 27 | cr.relLineTo(te.x_bearing, -te.y_bearing); 28 | cr.stroke(); 29 | } 30 | 31 | pub fn main() !void { 32 | const width: u16 = 256; 33 | const height: u16 = 256; 34 | const surface = try cairo.ImageSurface.create(.argb32, width, height); 35 | defer surface.destroy(); 36 | 37 | const cr = try cairo.Context.create(surface.asSurface()); 38 | defer cr.destroy(); 39 | 40 | setBackground(cr); 41 | textExtents(cr); 42 | try surface.writeToPng("examples/generated/text_extents.png"); 43 | } 44 | -------------------------------------------------------------------------------- /examples/arc_negative.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const pi = std.math.pi; 3 | const cairo = @import("cairo"); 4 | const setBackground = @import("utils.zig").setBackground; 5 | 6 | /// https://www.cairographics.org/samples/arc_negative/ 7 | fn arcNegative(cr: *cairo.Context) void { 8 | const xc: f64 = 128.0; 9 | const yc: f64 = 128.0; 10 | const radius: f64 = 100.0; 11 | // angles are specified in radians 12 | const angle1 = 45.0 * (pi / 180.0); 13 | const angle2 = 180.0 * (pi / 180.0); 14 | 15 | cr.setSourceRgb(0.0, 0.0, 0.0); // black 16 | 17 | cr.setLineWidth(10.0); 18 | cr.arcNegative(xc, yc, radius, angle1, angle2); 19 | cr.stroke(); 20 | 21 | // draw helping lines 22 | cr.setSourceRgba(1, 0.2, 0.2, 0.6); 23 | cr.arc(xc, yc, 10.0, 0, 2 * pi); 24 | cr.fill(); 25 | cr.setLineWidth(6.0); 26 | cr.arc(xc, yc, radius, angle1, angle1); 27 | cr.lineTo(xc, yc); 28 | cr.arc(xc, yc, radius, angle2, angle2); 29 | cr.lineTo(xc, yc); 30 | cr.stroke(); 31 | } 32 | 33 | pub fn main() !void { 34 | const width: u16 = 256; 35 | const height: u16 = 256; 36 | const surface = try cairo.ImageSurface.create(.argb32, width, height); 37 | defer surface.destroy(); 38 | 39 | const cr = try cairo.Context.create(surface.asSurface()); 40 | defer cr.destroy(); 41 | 42 | setBackground(cr); 43 | arcNegative(cr); 44 | try surface.writeToPng("examples/generated/arc_negative.png"); 45 | } 46 | -------------------------------------------------------------------------------- /examples/text_align_center.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const pi = std.math.pi; 3 | const cairo = @import("cairo"); 4 | const setBackground = @import("utils.zig").setBackground; 5 | 6 | /// https://www.cairographics.org/samples/text_align_center/ 7 | fn textAlignCenter(cr: *cairo.Context) void { 8 | cr.selectFontFace("Sans", .Normal, .Normal); 9 | cr.setFontSize(52.0); 10 | const some_text = "cairo"; // TODO: check that text is UTF8-encoded 11 | 12 | const te = cr.textExtents(some_text); 13 | const x = 128.0 - (te.width / 2 + te.x_bearing); 14 | const y = 128.0 - (te.height / 2 + te.y_bearing); 15 | cr.moveTo(x, y); 16 | cr.setSourceRgb(0.0, 0.0, 0.0); // black 17 | cr.showText(some_text); 18 | 19 | // draw helping lines 20 | cr.setSourceRgba(1, 0.2, 0.2, 0.6); 21 | cr.setLineWidth(6.0); 22 | cr.arc(x, y, 10.0, 0, 2 * pi); 23 | cr.fill(); 24 | cr.moveTo(128.0, 0.0); 25 | cr.relLineTo(0, 256); 26 | cr.moveTo(0.0, 128.0); 27 | cr.relLineTo(256, 0); 28 | cr.stroke(); 29 | } 30 | 31 | pub fn main() !void { 32 | const width: u16 = 256; 33 | const height: u16 = 256; 34 | const surface = try cairo.ImageSurface.create(.argb32, width, height); 35 | defer surface.destroy(); 36 | 37 | const cr = try cairo.Context.create(surface.asSurface()); 38 | defer cr.destroy(); 39 | 40 | setBackground(cr); 41 | textAlignCenter(cr); 42 | try surface.writeToPng("examples/generated/text_align_center.png"); 43 | } 44 | -------------------------------------------------------------------------------- /examples/ellipse.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const pi = std.math.pi; 3 | const cairo = @import("cairo"); 4 | const setBackground = @import("utils.zig").setBackground; 5 | 6 | /// Draw an ellipse. 7 | /// x - center x 8 | /// y - center y 9 | /// width - width of ellipse (in x direction when angle=0) 10 | /// height - height of ellipse (in y direction when angle=0) 11 | /// angle - angle in radians to rotate, clockwise 12 | fn pathEllipse(cr: *cairo.Context, x: f64, y: f64, width: f64, height: f64, angle: f64) void { 13 | cr.save(); 14 | cr.translate(x, y); 15 | cr.rotate(angle); 16 | cr.scale(width / 2.0, height / 2.0); 17 | cr.arc(0.0, 0.0, 1.0, 0.0, 2.0 * pi); 18 | cr.restore(); 19 | } 20 | 21 | /// https://github.com/pygobject/pycairo/blob/master/examples/pycairo_examples.ipynb 22 | fn ellipseExample(cr: *cairo.Context) void { 23 | cr.setSourceRgba(1, 0, 0, 1); 24 | pathEllipse(cr, 128, 128, 256, 76.8, pi / 4.0); 25 | cr.fillPreserve(); 26 | 27 | cr.setLineWidth(3.0); 28 | cr.setSourceRgba(0, 0, 0, 1); 29 | cr.stroke(); 30 | } 31 | 32 | pub fn main() !void { 33 | const width: u16 = 256; 34 | const height: u16 = 256; 35 | const surface = try cairo.ImageSurface.create(.argb32, width, height); 36 | defer surface.destroy(); 37 | 38 | const cr = try cairo.Context.create(surface.asSurface()); 39 | defer cr.destroy(); 40 | 41 | setBackground(cr); 42 | ellipseExample(cr); 43 | try surface.writeToPng("examples/generated/ellipse.png"); 44 | } 45 | -------------------------------------------------------------------------------- /examples/glyphs.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const pi = std.math.pi; 3 | const cairo = @import("cairo"); 4 | const setBackground = @import("utils.zig").setBackground; 5 | 6 | /// https://github.com/pygobject/pycairo/blob/master/examples/pycairo_examples.ipynb 7 | fn glyphsExample(cr: *cairo.Context) !void { 8 | cr.selectFontFace("Sans", .Normal, .Normal); 9 | 10 | // draw 0.08 glyphs in 0.10 squares, at(0.01, 0.02) from left corner 11 | cr.setFontSize(0.08); 12 | 13 | const glyphs = try cairo.Glyph.allocate(100); 14 | defer cairo.Glyph.free(glyphs); 15 | for (0..10) |y| { 16 | for (0..10) |x| { 17 | const index = y * 10 + x; 18 | glyphs[index].index = index; 19 | glyphs[index].x = @as(f64, @floatFromInt(x)) / 10.0 + 0.01; 20 | glyphs[index].y = @as(f64, @floatFromInt(y)) / 10.0 + 0.08; 21 | } 22 | } 23 | 24 | cr.setSourceRgb(0, 0, 0); 25 | cr.showGlyphs(glyphs); 26 | } 27 | 28 | pub fn main() !void { 29 | const width: u16 = 256; 30 | const height: u16 = 256; 31 | const surface = try cairo.ImageSurface.create(.argb32, width, height); 32 | defer surface.destroy(); 33 | 34 | const cr = try cairo.Context.create(surface.asSurface()); 35 | defer cr.destroy(); 36 | 37 | // scale context as python example suggests 38 | cr.scale(@floatFromInt(width), @floatFromInt(height)); 39 | 40 | setBackground(cr); 41 | try glyphsExample(cr); 42 | try surface.writeToPng("examples/generated/glyphs.png"); 43 | } 44 | -------------------------------------------------------------------------------- /examples/set_line_cap.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const pi = std.math.pi; 3 | const cairo = @import("cairo"); 4 | const setBackground = @import("utils.zig").setBackground; 5 | 6 | /// https://www.cairographics.org/samples/set_line_cap/ 7 | fn setLineCap(cr: *cairo.Context) void { 8 | cr.setSourceRgb(0.0, 0.0, 0.0); // black 9 | 10 | cr.setLineWidth(30.0); 11 | 12 | cr.setLineCap(cairo.Context.LineCap.Butt); // default 13 | cr.moveTo(64.0, 50.0); 14 | cr.lineTo(64.0, 200.0); 15 | cr.stroke(); 16 | 17 | cr.setLineCap(cairo.Context.LineCap.Round); 18 | cr.moveTo(128.0, 50.0); 19 | cr.lineTo(128.0, 200.0); 20 | cr.stroke(); 21 | 22 | cr.setLineCap(cairo.Context.LineCap.Square); 23 | cr.moveTo(192.0, 50.0); 24 | cr.lineTo(192.0, 200.0); 25 | cr.stroke(); 26 | 27 | // draw helping lines 28 | cr.setSourceRgb(1, 0.2, 0.2); 29 | cr.setLineWidth(2.56); 30 | cr.moveTo(64.0, 50.0); 31 | cr.lineTo(64.0, 200.0); 32 | cr.moveTo(128.0, 50.0); 33 | cr.lineTo(128.0, 200.0); 34 | cr.moveTo(192.0, 50.0); 35 | cr.lineTo(192.0, 200.0); 36 | cr.stroke(); 37 | } 38 | 39 | pub fn main() !void { 40 | const width: u16 = 256; 41 | const height: u16 = 256; 42 | const surface = try cairo.ImageSurface.create(.argb32, width, height); 43 | defer surface.destroy(); 44 | 45 | const cr = try cairo.Context.create(surface.asSurface()); 46 | defer cr.destroy(); 47 | 48 | setBackground(cr); 49 | setLineCap(cr); 50 | try surface.writeToPng("examples/generated/set_line_cap.png"); 51 | } 52 | -------------------------------------------------------------------------------- /examples/rounded_rectangle.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const pi = std.math.pi; 3 | const cairo = @import("cairo"); 4 | const setBackground = @import("utils.zig").setBackground; 5 | 6 | /// https://www.cairographics.org/samples/rounded_rectangle/ 7 | fn roundedRectangle(cr: *cairo.Context) void { 8 | const x: f64 = 25.6; 9 | const y: f64 = 25.6; 10 | const width: f64 = 204.8; 11 | const height: f64 = 204.8; 12 | const aspect: f64 = 1.0; 13 | const corner_radius: f64 = 10.0; 14 | 15 | const radius = corner_radius / aspect; 16 | const degrees = pi / 180.0; 17 | 18 | cr.newSubPath(); 19 | cr.arc(x + width - radius, y + radius, radius, -90 * degrees, 0 * degrees); 20 | cr.arc(x + width - radius, y + height - radius, radius, 0 * degrees, 90 * degrees); 21 | cr.arc(x + radius, y + height - radius, radius, 90 * degrees, 180 * degrees); 22 | cr.arc(x + radius, y + radius, radius, 180 * degrees, 270 * degrees); 23 | cr.closePath(); 24 | 25 | cr.setSourceRgb(0.5, 0.5, 1); 26 | cr.fillPreserve(); 27 | cr.setSourceRgba(0.5, 0, 0, 0.5); 28 | cr.setLineWidth(10.0); 29 | cr.stroke(); 30 | } 31 | 32 | pub fn main() !void { 33 | const width: u16 = 256; 34 | const height: u16 = 256; 35 | const surface = try cairo.ImageSurface.create(.argb32, width, height); 36 | defer surface.destroy(); 37 | 38 | const cr = try cairo.Context.create(surface.asSurface()); 39 | defer cr.destroy(); 40 | 41 | setBackground(cr); 42 | roundedRectangle(cr); 43 | try surface.writeToPng("examples/generated/rounded_rectangle.png"); 44 | } 45 | -------------------------------------------------------------------------------- /examples/fill_style.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const pi = std.math.pi; 3 | const cairo = @import("cairo"); 4 | const setBackground = @import("utils.zig").setBackground; 5 | 6 | /// https://www.cairographics.org/samples/fill_style/ 7 | fn fillStyle(cr: *cairo.Context) void { 8 | cr.setSourceRgb(0.0, 0.0, 0.0); // black 9 | 10 | cr.setLineWidth(6); 11 | 12 | cr.rectangle(cairo.Rectangle.init(.{ 12, 12, 232, 70 })); 13 | cr.newSubPath(); 14 | cr.arc(64, 64, 40, 0, 2 * pi); 15 | cr.newSubPath(); 16 | cr.arcNegative(192, 64, 40, 0, -2 * pi); 17 | 18 | cr.setFillRule(cairo.Context.FillRule.EvenOdd); 19 | cr.setSourceRgb(0, 0.7, 0); 20 | cr.fillPreserve(); 21 | cr.setSourceRgb(0, 0, 0); 22 | cr.stroke(); 23 | 24 | cr.translate(0, 128); 25 | cr.rectangle(cairo.Rectangle.init(.{ 12, 12, 232, 70 })); 26 | cr.newSubPath(); 27 | cr.arc(64, 64, 40, 0, 2 * pi); 28 | cr.newSubPath(); 29 | cr.arcNegative(192, 64, 40, 0, -2 * pi); 30 | 31 | cr.setFillRule(cairo.Context.FillRule.Winding); 32 | cr.setSourceRgb(0, 0, 0.9); 33 | cr.fillPreserve(); 34 | cr.setSourceRgb(0, 0, 0); 35 | cr.stroke(); 36 | } 37 | 38 | pub fn main() !void { 39 | const width: u16 = 256; 40 | const height: u16 = 256; 41 | const surface = try cairo.ImageSurface.create(.argb32, width, height); 42 | defer surface.destroy(); 43 | 44 | const cr = try cairo.Context.create(surface.asSurface()); 45 | defer cr.destroy(); 46 | 47 | setBackground(cr); 48 | fillStyle(cr); 49 | try surface.writeToPng("examples/generated/fill_style.png"); 50 | } 51 | -------------------------------------------------------------------------------- /examples/spirograph.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const pi = std.math.pi; 3 | const cos = std.math.cos; 4 | const sin = std.math.sin; 5 | const cairo = @import("cairo"); 6 | const setBackground = @import("utils.zig").setBackground; 7 | 8 | /// https://github.com/preshing/CairoSample/blob/master/Main.cpp 9 | fn spirograph(cr: *cairo.Context, width: f64, height: f64) void { 10 | cr.setSourceRgb(0, 0, 0); // black 11 | 12 | const line_width: f64 = 4.0; 13 | // const half_lw = line_width / 2.0; 14 | cr.setLineWidth(line_width); 15 | 16 | const xc = width / 2.0; 17 | const yc = height / 2.0; 18 | const phi: f64 = 500.0; 19 | const a: f64 = 70.0; 20 | const b: f64 = 110.0; 21 | 22 | const k: f64 = 10000; 23 | var i: f64 = 0; 24 | while (i < k) : (i += 1) { 25 | const x = xc + cos(2.0 * pi * i / phi) * a + cos(2.0 * pi * i / k) * b; 26 | const y = yc + sin(2.0 * pi * i / phi) * a + sin(2.0 * pi * i / k) * b; 27 | if (i == 0) { 28 | cr.moveTo(x, y); 29 | } else { 30 | cr.lineTo(x, y); 31 | } 32 | } 33 | cr.closePath(); 34 | cr.stroke(); 35 | } 36 | 37 | pub fn main() !void { 38 | const width: u16 = 400; 39 | const height: u16 = 400; 40 | const surface = try cairo.ImageSurface.create(.argb32, width, height); 41 | defer surface.destroy(); 42 | 43 | const cr = try cairo.Context.create(surface.asSurface()); 44 | defer cr.destroy(); 45 | 46 | setBackground(cr); 47 | spirograph(cr, @floatFromInt(width), @floatFromInt(height)); 48 | try surface.writeToPng("examples/generated/spirograph.png"); 49 | } 50 | -------------------------------------------------------------------------------- /examples/image_pattern.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const pi = std.math.pi; 3 | const cairo = @import("cairo"); 4 | const Rect = cairo.Rectangle; 5 | const setBackground = @import("utils.zig").setBackground; 6 | 7 | /// https://www.cairographics.org/samples/imagepattern/ 8 | fn imagePattern(cr: *cairo.Context) !void { 9 | const image = try cairo.ImageSurface.createFromPng("data/romedalen.png"); 10 | defer image.destroy(); 11 | 12 | const w: f64 = @floatFromInt(image.getWidth()); 13 | const h: f64 = @floatFromInt(image.getHeight()); 14 | 15 | const pattern = try cairo.SurfacePattern.createFor(image.asSurface()); 16 | defer pattern.destroy(); 17 | 18 | pattern.setExtend(cairo.Pattern.Extend.Repeat); 19 | 20 | cr.translate(128.0, 128.0); 21 | cr.rotate(pi / 4.0); 22 | cr.scale(1.0 / @sqrt(2.0), 1.0 / std.math.sqrt(2.0)); 23 | cr.translate(-128.0, -128.0); 24 | 25 | const sx = w / 256.0 * 5.0; 26 | const sy = h / 256.0 * 5.0; 27 | 28 | const matrix = cairo.Matrix.scaling(sx, sy); 29 | pattern.setMatrix(&matrix); 30 | 31 | cr.setSource(pattern.asPattern()); 32 | cr.rectangle(Rect.init(.{ 0, 0, 256, 256 })); 33 | cr.fill(); 34 | } 35 | 36 | pub fn main() !void { 37 | const width: u16 = 256; 38 | const height: u16 = 256; 39 | const surface = try cairo.ImageSurface.create(.argb32, width, height); 40 | defer surface.destroy(); 41 | 42 | const cr = try cairo.Context.create(surface.asSurface()); 43 | defer cr.destroy(); 44 | 45 | setBackground(cr); 46 | 47 | try imagePattern(cr); 48 | try surface.writeToPng("examples/generated/image_pattern.png"); 49 | } 50 | -------------------------------------------------------------------------------- /examples/group.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const pi = std.math.pi; 3 | const cairo = @import("cairo"); 4 | const Rect = cairo.Rectangle; 5 | const setBackground = @import("utils.zig").setBackground; 6 | 7 | /// https://github.com/pygobject/pycairo/blob/master/examples/pycairo_examples.ipynb 8 | fn groupExample(cr: *cairo.Context) !void { 9 | cr.setSourceRgb(0.8, 0.8, 0.8); // gray 10 | cr.rectangle(Rect.init(.{ 25.6, 25.6, 153.6, 153.6 })); 11 | cr.fill(); 12 | 13 | const red_pattern = try cairo.SolidPattern.createRgb(1, 0, 0); 14 | defer red_pattern.destroy(); 15 | 16 | const black_pattern = try cairo.SolidPattern.createRgb(0, 0, 0); 17 | defer black_pattern.destroy(); 18 | 19 | cr.pushGroup(); 20 | // define a red rectangle 21 | cr.setSource(red_pattern.asPattern()); 22 | cr.rectangle(Rect.init(.{ 76.8, 76.8, 153.6, 153.6 })); 23 | // fill the path we have just defined (i.e. the rectangle) and preserve it 24 | cr.fillPreserve(); 25 | // define a black rectangular frame 26 | cr.setLineWidth(8.0); 27 | cr.setSource(black_pattern.asPattern()); 28 | cr.stroke(); 29 | cr.popGroupToSource(); 30 | // paint the entire group with a semi-transparent alpha channel 31 | cr.paintWithAlpha(0.5); 32 | } 33 | 34 | pub fn main() !void { 35 | const width: u16 = 256; 36 | const height: u16 = 256; 37 | const surface = try cairo.ImageSurface.create(.argb32, width, height); 38 | defer surface.destroy(); 39 | 40 | const cr = try cairo.Context.create(surface.asSurface()); 41 | defer cr.destroy(); 42 | 43 | setBackground(cr); 44 | try groupExample(cr); 45 | try surface.writeToPng("examples/generated/group.png"); 46 | } 47 | -------------------------------------------------------------------------------- /examples/glyphs_path.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const pi = std.math.pi; 3 | const cairo = @import("cairo"); 4 | const setBackground = @import("utils.zig").setBackground; 5 | 6 | /// https://github.com/pygobject/pycairo/blob/master/examples/pycairo_examples.ipynb 7 | fn glyphsExample(cr: *cairo.Context) !void { 8 | cr.setLineWidth(0.04); 9 | cr.selectFontFace("Sans", .Normal, .Normal); 10 | 11 | // draw 0.16 glyphs in 0.20 squares, at (0.02, 0.04) from left corner 12 | cr.setFontSize(0.16); 13 | 14 | const glyphs = try cairo.Glyph.allocate(25); 15 | defer cairo.Glyph.free(glyphs); 16 | const index_offset = 20; 17 | for (0..5) |y| { 18 | for (0..5) |x| { 19 | const index = y * 5 + x; 20 | glyphs[index].index = index + index_offset; 21 | glyphs[index].x = @as(f64, @floatFromInt(x)) / 5.0 + 0.02; 22 | glyphs[index].y = @as(f64, @floatFromInt(y)) / 5.0 + 0.16; 23 | } 24 | } 25 | 26 | cr.glyphPath(glyphs); 27 | cr.setSourceRgb(0.5, 0.5, 1); 28 | cr.fillPreserve(); 29 | cr.setSourceRgb(0, 0, 0); 30 | cr.setLineWidth(0.005); 31 | cr.stroke(); 32 | } 33 | 34 | pub fn main() !void { 35 | const width: u16 = 256; 36 | const height: u16 = 256; 37 | const surface = try cairo.ImageSurface.create(.argb32, width, height); 38 | defer surface.destroy(); 39 | 40 | const cr = try cairo.Context.create(surface.asSurface()); 41 | defer cr.destroy(); 42 | 43 | // scale context as python example suggests 44 | cr.scale(@floatFromInt(width), @floatFromInt(height)); 45 | 46 | setBackground(cr); 47 | try glyphsExample(cr); 48 | try surface.writeToPng("examples/generated/glyphs2.png"); 49 | } 50 | -------------------------------------------------------------------------------- /examples/dash.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const cairo = @import("cairo"); 3 | const setBackground = @import("utils.zig").setBackground; 4 | 5 | /// https://www.cairographics.org/samples/dash/ 6 | fn dash(cr: *cairo.Context) !void { 7 | cr.setSourceRgb(0.0, 0.0, 0.0); // black 8 | 9 | const offset: f64 = -50.0; 10 | 11 | // Option 1 //////////////////////////////////////////////////////////////// 12 | const dashes_arr = [_]f64{ 13 | 50.0, // ink 14 | 10.0, // skip 15 | 10.0, // ink 16 | 10.0, // skip 17 | }; 18 | cr.setDash(dashes_arr[0..], offset); 19 | 20 | // Option 2 //////////////////////////////////////////////////////////////// 21 | var dashes = std.ArrayList(f64).init(std.heap.c_allocator); 22 | defer dashes.deinit(); 23 | try dashes.append(50.0); // ink 24 | try dashes.append(10.0); // skip 25 | try dashes.append(10.0); // ink 26 | try dashes.append(10.0); // skip 27 | cr.setDash(dashes.items, offset); 28 | //////////////////////////////////////////////////////////////////////////// 29 | 30 | cr.setLineWidth(10.0); 31 | 32 | cr.moveTo(128.0, 25.6); 33 | cr.lineTo(230.4, 230.4); 34 | cr.relLineTo(-102.4, 0.0); 35 | cr.curveTo(51.2, 230.4, 51.2, 128.0, 128.0, 128.0); 36 | 37 | cr.stroke(); 38 | } 39 | 40 | pub fn main() !void { 41 | const width: u16 = 256; 42 | const height: u16 = 256; 43 | const surface = try cairo.ImageSurface.create(.argb32, width, height); 44 | defer surface.destroy(); 45 | 46 | const cr = try cairo.Context.create(surface.asSurface()); 47 | defer cr.destroy(); 48 | 49 | setBackground(cr); 50 | try dash(cr); 51 | try surface.writeToPng("examples/generated/dash.png"); 52 | } 53 | -------------------------------------------------------------------------------- /examples/spiral.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const pi = std.math.pi; 3 | const cairo = @import("cairo"); 4 | const setBackground = @import("utils.zig").setBackground; 5 | 6 | /// https://github.com/pygobject/pycairo/blob/master/examples/pycairo_examples.ipynb 7 | /// https://gitlab.com/cairo/cairo-demos/-/blob/master/png/spiral.c 8 | fn spiral(cr: *cairo.Context, width: f64, height: f64) void { 9 | cr.setSourceRgb(0, 0, 1); // blue 10 | 11 | const line_width: f64 = 4.0; 12 | const half_lw = line_width / 2.0; 13 | cr.setLineWidth(line_width); 14 | 15 | // k controls the space between the spiral's windings 16 | const k = line_width * 0.01; 17 | const wd = k * width; 18 | const hd = k * height; 19 | const w = width - line_width; 20 | const h = height - line_width; 21 | 22 | // start from the top right corner 23 | cr.moveTo(w + half_lw, -hd + half_lw); 24 | 25 | const num_windings: f64 = 12; 26 | var i: f64 = 0; 27 | while (i < num_windings) : (i += 1) { 28 | cr.relLineTo(0, h - hd * (2 * i - 1)); // go down 29 | cr.relLineTo(-(w - wd * (2 * i)), 0); // go left 30 | cr.relLineTo(0, -(h - hd * (2 * i))); // go up 31 | cr.relLineTo(w - wd * (2 * i + 1), 0); // go right 32 | } 33 | cr.stroke(); 34 | } 35 | 36 | pub fn main() !void { 37 | const width: u16 = 400; 38 | const height: u16 = 400; 39 | const surface = try cairo.ImageSurface.create(.argb32, width, height); 40 | defer surface.destroy(); 41 | 42 | const cr = try cairo.Context.create(surface.asSurface()); 43 | defer cr.destroy(); 44 | 45 | setBackground(cr); 46 | spiral(cr, @floatFromInt(width), @floatFromInt(height)); 47 | try surface.writeToPng("examples/generated/spiral.png"); 48 | } 49 | -------------------------------------------------------------------------------- /examples/save_and_restore.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const pi = std.math.pi; 3 | const cairo = @import("cairo"); 4 | const setBackground = @import("utils.zig").setBackground; 5 | 6 | /// https://github.com/pygobject/pycairo/blob/master/examples/pycairo_examples.ipynb 7 | /// https://github.com/pygobject/pycairo/blob/master/examples/cairo_snippets/snippets/hering.py 8 | /// https://gitlab.com/cairo/cairo-demos/-/blob/master/png/hering.c 9 | fn saveAndRestore(cr: *cairo.Context, width: f64, height: f64) void { 10 | const LINES: usize = 32; 11 | const MAX_THETA = 0.80 * pi * 2.0; 12 | const THETA_INC = 2.0 * MAX_THETA / @as(f64, @floatFromInt(LINES - 1)); 13 | 14 | cr.save(); 15 | 16 | cr.setSourceRgb(0, 0, 0); // black 17 | cr.setLineWidth(3.0); 18 | 19 | cr.translate(width / 2, height / 2); 20 | cr.rotate(MAX_THETA); 21 | 22 | var i: usize = 0; 23 | while (i < LINES) : (i += 1) { 24 | cr.moveTo(-2 * width, 0); 25 | cr.lineTo(2 * width, 0); 26 | cr.stroke(); 27 | cr.rotate(-THETA_INC); 28 | } 29 | 30 | cr.restore(); 31 | 32 | cr.setSourceRgb(1, 0, 0); 33 | cr.setLineWidth(9.0); 34 | 35 | cr.moveTo(width / 4.0, 0); 36 | cr.relLineTo(0, height); 37 | cr.stroke(); 38 | 39 | cr.moveTo(3 * width / 4.0, 0); 40 | cr.relLineTo(0, height); 41 | cr.stroke(); 42 | } 43 | 44 | pub fn main() !void { 45 | const width: u16 = 256; 46 | const height: u16 = 256; 47 | const surface = try cairo.ImageSurface.create(.argb32, width, height); 48 | defer surface.destroy(); 49 | 50 | const cr = try cairo.Context.create(surface.asSurface()); 51 | defer cr.destroy(); 52 | 53 | setBackground(cr); 54 | saveAndRestore(cr, @as(f64, @floatFromInt(width)), @as(f64, @floatFromInt(height))); 55 | try surface.writeToPng("examples/generated/save_and_restore.png"); 56 | } 57 | -------------------------------------------------------------------------------- /src/pango/util.zig: -------------------------------------------------------------------------------- 1 | const safety = @import("safety"); 2 | const pango = @import("../pango.zig"); 3 | const c = pango.c; 4 | 5 | /// The `pango.Rectangle` structure represents a rectangle. 6 | /// 7 | /// `pango.Rectangle` is frequently used to represent the logical or ink 8 | /// extents of a single glyph or section of text. (See, for instance, 9 | /// `pango.Font.getGlyphExtents()`.) 10 | pub const Rectangle = extern struct { 11 | /// X coordinate of the left side of the rectangle. 12 | x: c_int, 13 | /// Y coordinate of the the top side of the rectangle. 14 | y: c_int, 15 | /// Width of the rectangle. 16 | width: c_int, 17 | /// Height of the rectangle. 18 | height: c_int, 19 | 20 | pub fn init(x: c_int, y: c_int, width: c_int, height: c_int) Rectangle { 21 | return .{ .x = x, .y = y, .width = width, .height = height }; 22 | } 23 | }; 24 | 25 | pub fn free(memory: anytype) void { 26 | const info = @typeInfo(@TypeOf(memory)); 27 | switch (info) { 28 | .Pointer => c.g_free(@ptrCast(memory)), 29 | else => @compileError("wrong type"), 30 | } 31 | if (safety.tracing) safety.destroy(@ptrCast(memory)); 32 | } 33 | 34 | /// Type of a function that can duplicate user data for an attribute. 35 | /// 36 | /// **Parameters** 37 | /// - `user_data`: user data to copy 38 | /// 39 | /// **Returns** 40 | /// 41 | /// new copy of `user_data`. 42 | pub const AttrDataCopyFunc = ?*const fn (?*const anyopaque) callconv(.C) ?*anyopaque; 43 | // TODO: clarify ownership 44 | 45 | /// Specifies the type of function which is called when a data element is 46 | /// destroyed. It is passed the pointer to the data element and should free any 47 | /// memory and resources allocated for it. 48 | pub const GDestroyNotify = ?*const fn (?*anyopaque) callconv(.C) void; 49 | 50 | pub const AttrFilterFunc = ?*const fn ([*c]pango.Attribute, ?*anyopaque) callconv(.C) c_int; // bool 51 | -------------------------------------------------------------------------------- /examples/sierpinski.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const builtin = @import("builtin"); 3 | const pi = std.math.pi; 4 | const cairo = @import("cairo"); 5 | const setBackground = @import("utils.zig").setBackground; 6 | 7 | const m_1_sqrt_3: f64 = 0.577359269; // 1.0 / std.math.sqrt(3) 8 | 9 | fn triangle(cr: *cairo.Context, size: f64) void { 10 | cr.moveTo(0, 0); 11 | cr.lineTo(size, 0); 12 | cr.lineTo(size * 0.5, size * m_1_sqrt_3); 13 | cr.lineTo(0, 0); 14 | 15 | const half = size * 0.5; 16 | if (half >= 4) { 17 | triangle(cr, half); 18 | cr.save(); 19 | cr.translate(half, 0); 20 | triangle(cr, half); 21 | cr.restore(); 22 | cr.save(); 23 | cr.translate(half * 0.5, half * m_1_sqrt_3); 24 | triangle(cr, half); 25 | cr.restore(); 26 | } 27 | } 28 | 29 | /// Zig porting of this example in C. 30 | /// https://github.com/freedesktop/cairo/blob/master/perf/micro/sierpinski.c 31 | fn drawSierpinski(cr: *cairo.Context, _: f64, height: f64) void { 32 | const t_height = height / 2.0; 33 | const t_width = t_height / m_1_sqrt_3; 34 | 35 | cr.setSourceRgb(1, 1, 1); // white 36 | cr.paint(); 37 | 38 | cr.setSourceRgb(0, 0, 0); // black 39 | cr.setLineWidth(1.0); 40 | 41 | cr.save(); 42 | triangle(cr, t_width); 43 | cr.translate(0, height); 44 | cr.scale(1, -1); 45 | triangle(cr, t_width); 46 | cr.stroke(); 47 | cr.restore(); 48 | } 49 | 50 | pub fn main() !void { 51 | const width: u16 = 400; 52 | const height: u16 = 400; 53 | const surface = try cairo.ImageSurface.create(.argb32, width, height); 54 | defer surface.destroy(); 55 | 56 | const cr = try cairo.Context.create(surface.asSurface()); 57 | defer cr.destroy(); 58 | 59 | setBackground(cr); 60 | drawSierpinski(cr, @floatFromInt(width), @floatFromInt(height)); 61 | try surface.writeToPng("examples/generated/sierpinski.png"); 62 | } 63 | -------------------------------------------------------------------------------- /src/pangocairo/fonts.zig: -------------------------------------------------------------------------------- 1 | const pango = @import("pango"); 2 | const cairo = @import("cairo"); 3 | const pangocairo = @import("../pangocairo.zig"); 4 | const c = pangocairo.c; 5 | 6 | /// `pangocairo.Font` is an interface exported by fonts for use with Cairo. 7 | /// 8 | /// The actual type of the font will depend on the particular font technology 9 | /// Cairo was compiled to use. 10 | pub const Font = opaque { 11 | /// Gets the `cairo.ScaledFont` used by font. 12 | /// 13 | /// **Returns** 14 | /// 15 | /// the `cairo.ScaledFont` used by font. The scaled font can be referenced 16 | /// and kept using `cairo.ScaledFont.reference()`. 17 | pub fn getScaledFont(self: *Font) !*cairo.ScaledFont { 18 | return c.pango_cairo_font_get_scaled_font(self) orelse error.NullPointer; 19 | } 20 | }; 21 | 22 | pub const FontMap = opaque { 23 | pub fn new() !*FontMap { 24 | return c.pango_cairo_font_map_new() orelse error.NullPointer; 25 | } 26 | 27 | pub fn newForFontType(font_type: cairo.FontFace.Type) !*FontMap { 28 | return c.pango_cairo_font_map_new_for_font_type(font_type) orelse error.NullPointer; 29 | } 30 | 31 | pub fn getDefault() !*FontMap { 32 | return c.pango_cairo_font_map_get_default() orelse error.NullPointer; 33 | } 34 | 35 | pub fn setDefault(self: ?*FontMap) void { 36 | c.pango_cairo_font_map_set_default(self); 37 | } 38 | 39 | pub fn getFontType(self: *FontMap) void { 40 | return c.pango_cairo_font_map_get_font_type(self); 41 | } 42 | 43 | pub fn setResolution(self: *FontMap, dpi: f64) void { 44 | c.pango_cairo_font_map_set_resolution(self, dpi); 45 | } 46 | 47 | pub fn getResolution(self: *FontMap) f64 { 48 | return c.pango_cairo_font_map_get_resolution(self); 49 | } 50 | 51 | pub fn createContext(self: *FontMap) !*pango.Context { 52 | return c.pango_cairo_font_map_create_context(self) orelse error.NullPointer; 53 | } 54 | }; 55 | -------------------------------------------------------------------------------- /examples/pythagoras_tree.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const builtin = @import("builtin"); 3 | const pi = std.math.pi; 4 | const cairo = @import("cairo"); 5 | const setBackground = @import("utils.zig").setBackground; 6 | 7 | const m_sqrt_2 = std.math.sqrt(2.0); 8 | 9 | // this function fails with cairo.Error.NoCurrentPoint if no current point is set 10 | fn addRectangle(cr: *cairo.Context, size: f64) cairo.CairoError!void { 11 | if (size < 1) { 12 | return; 13 | } 14 | 15 | const point = cr.getCurrentPoint(); 16 | 17 | cr.relMoveTo(-size / 2.0, -size / 2.0); 18 | cr.relLineTo(size, 0); 19 | cr.relLineTo(0, size); 20 | cr.relLineTo(-size, 0); 21 | cr.closePath(); 22 | 23 | cr.save(); 24 | cr.translate(-size / 2.0, size); 25 | cr.moveTo(point.x, point.y); 26 | cr.rotate(pi / 4.0); 27 | try addRectangle(cr, size / m_sqrt_2); 28 | cr.restore(); 29 | 30 | cr.save(); 31 | cr.translate(size / 2.0, size); 32 | cr.moveTo(point.x, point.y); 33 | cr.rotate(-pi / 4.0); 34 | try addRectangle(cr, size / m_sqrt_2); 35 | cr.restore(); 36 | return cr.status().toErr(); 37 | } 38 | 39 | /// Zig porting of this example in C. 40 | /// https://github.com/freedesktop/cairo/blob/master/perf/micro/pythagoras-tree.c 41 | fn drawPythagorasTree(cr: *cairo.Context, width: f64, height: f64) cairo.CairoError!void { 42 | const size = 128.0; 43 | 44 | cr.save(); 45 | cr.translate(0, height); 46 | cr.scale(1, -1); 47 | 48 | cr.moveTo(width / 2.0, size / 2.0); 49 | try addRectangle(cr, size); 50 | cr.setSourceRgb(0, 0, 0); 51 | cr.fill(); 52 | cr.restore(); 53 | } 54 | 55 | pub fn main() !void { 56 | const width: u16 = 400; 57 | const height: u16 = 400; 58 | const surface = try cairo.ImageSurface.create(.argb32, width, height); 59 | defer surface.destroy(); 60 | 61 | const cr = try cairo.Context.create(surface.asSurface()); 62 | defer cr.destroy(); 63 | 64 | setBackground(cr); 65 | try drawPythagorasTree(cr, @as(f64, @floatFromInt(width)), @as(f64, @floatFromInt(height))); 66 | try surface.writeToPng("examples/generated/pythagoras_tree.png"); 67 | } 68 | -------------------------------------------------------------------------------- /examples/three_phases.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const builtin = @import("builtin"); 3 | const pi = std.math.pi; 4 | const cairo = @import("cairo"); 5 | const setBackground = @import("utils.zig").setBackground; 6 | 7 | const scale_x = 80.0; 8 | const scale_y = 120.0; 9 | const offset_x = 50.0; 10 | const offset_y = 150.0; 11 | const gap = 20.0; 12 | const xmax = 3.0 * pi; 13 | 14 | fn drawAxes(cr: *cairo.Context) void { 15 | cr.setSourceRgb(0, 0, 0); // black 16 | cr.moveTo(offset_x, offset_y); 17 | cr.lineTo(offset_x + scale_x * xmax, offset_y); 18 | cr.stroke(); 19 | cr.moveTo(offset_x, offset_y - scale_y); 20 | cr.lineTo(offset_x, offset_y + scale_y); 21 | cr.stroke(); 22 | } 23 | 24 | const Color = struct { 25 | r: f64, 26 | g: f64, 27 | b: f64, 28 | }; 29 | 30 | const Point = struct { 31 | x: f64, 32 | y: f64, 33 | }; 34 | 35 | fn point(x: f64, offset: f64) Point { 36 | const y = std.math.cos(x + offset); 37 | return Point{ 38 | .x = x * scale_x + offset_x, 39 | .y = -y * scale_y + offset_y, 40 | }; 41 | } 42 | 43 | fn drawCosine(cr: *cairo.Context, color: Color, offset: f64) void { 44 | cr.setSourceRgb(color.r, color.g, color.b); 45 | var x: f64 = 0; 46 | const p0 = point(x, offset); 47 | cr.moveTo(p0.x, p0.y); 48 | while (x < xmax) : (x += pi / gap) { 49 | const p1 = point(x, offset); 50 | cr.lineTo(p1.x, p1.y); 51 | } 52 | cr.stroke(); 53 | } 54 | 55 | /// This program draws a graph of three phase electrical voltages. 56 | /// Zig porting of this example in Perl. 57 | /// https://www.lemoda.net/electricity/three-phase-graph/index.html 58 | fn drawThreePhases(cr: *cairo.Context) void { 59 | cr.setLineWidth(5.0); 60 | drawAxes(cr); 61 | drawCosine(cr, .{ .r = 1, .g = 0, .b = 0 }, 0); 62 | drawCosine(cr, .{ .r = 0, .g = 1, .b = 0 }, 2.0 * pi / 3.0); 63 | drawCosine(cr, .{ .r = 0, .g = 0, .b = 1 }, 4.0 * pi / 3.0); 64 | } 65 | 66 | pub fn main() !void { 67 | const size_y: u16 = 300; 68 | const size_x = size_y * 3; 69 | const surface = try cairo.ImageSurface.create(.argb32, size_x, size_y); 70 | defer surface.destroy(); 71 | 72 | const cr = try cairo.Context.create(surface.asSurface()); 73 | defer cr.destroy(); 74 | 75 | setBackground(cr); 76 | drawThreePhases(cr); 77 | try surface.writeToPng("examples/generated/three_phases.png"); 78 | } 79 | -------------------------------------------------------------------------------- /examples/render.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const cairo = @import("cairo"); 3 | const Rect = cairo.Rectangle; 4 | const Point = cairo.Point; 5 | 6 | /// Render a test image on a Cairo context. 7 | pub fn testImage(cr: *cairo.Context, width: usize, height: usize) void { 8 | const w: f64 = @floatFromInt(width); 9 | const h: f64 = @floatFromInt(height); 10 | 11 | // white background 12 | cr.setSourceRgb(1.0, 1.0, 1.0); 13 | cr.paintWithAlpha(1.0); 14 | 15 | // green rectangle 16 | cr.rectangle(Rect.init(.{ 0, 0, w / 2, h / 2 })); 17 | cr.setSourceRgba(0, 1, 0, 0.75); 18 | cr.fill(); 19 | 20 | // red rectangle 21 | cr.rectangle(Rect.init(.{ w / 2, h / 2, w, h })); 22 | cr.setSourceRgba(1, 0, 0, 0.75); 23 | cr.fill(); 24 | 25 | // thick line 26 | cr.setSourceRgba(0, 0.68, 0.68, 1.0); 27 | cr.setLineWidth(10.0); 28 | cr.moveTo(0, 0); 29 | cr.lineTo(w / 2, h / 2); 30 | cr.stroke(); 31 | 32 | // some text 33 | const zig_motto = "all your codebase are belong to us"; 34 | cr.selectFontFace("Georgia", .Normal, .Bold); 35 | cr.setFontSize(24.0); 36 | var te = cr.textExtents(zig_motto); 37 | cr.moveTo(w / 2 - te.width / 2 - te.x_bearing, h / 2 - te.height / 2 - te.y_bearing); 38 | cr.setSourceRgb(0.0, 0.0, 1.0); 39 | cr.showText(zig_motto); 40 | } 41 | 42 | /// Render a line chart on a Cairo context. 43 | pub fn lineChart(cr: *cairo.Context, width: usize, height: usize) void { 44 | const points = [_]Point{ 45 | .{ .x = 0, .y = 10 }, 46 | .{ .x = 1, .y = 15 }, 47 | .{ .x = 2, .y = 14 }, 48 | .{ .x = 3, .y = 18 }, 49 | .{ .x = 4, .y = 25 }, 50 | }; 51 | const max_height: f64 = 25; 52 | 53 | const w: f64 = @floatFromInt(width); 54 | const h: f64 = @floatFromInt(height); 55 | const origin = .{ .x = 0, .y = h }; 56 | const n: f64 = @floatFromInt(points.len); 57 | const max_width = n - 1; 58 | 59 | // white background 60 | cr.setSourceRgb(1, 1, 1); 61 | cr.paintWithAlpha(1.0); 62 | 63 | // semi-transparent blue line 64 | cr.setLineWidth(2.0); 65 | cr.setSourceRgba(0.0, 0.0, 1.0, 0.5); 66 | cr.moveTo(origin.x, origin.y); 67 | 68 | var i: usize = 0; 69 | while (i < n) : (i += 1) { 70 | const x = points[i].x * w / max_width; 71 | const y = h - (points[i].y * h / max_height); 72 | cr.lineTo(x, y); 73 | cr.moveTo(x, y); 74 | } 75 | cr.stroke(); 76 | } 77 | -------------------------------------------------------------------------------- /examples/pango_simple.zig: -------------------------------------------------------------------------------- 1 | //! Simple example to use pangocairo to render rotated text. 2 | //! https://gitlab.gnome.org/GNOME/pango/-/blob/master/examples/cairosimple.c 3 | const std = @import("std"); 4 | const pi = std.math.pi; 5 | const cos = std.math.cos; 6 | const cairo = @import("cairo"); 7 | const pango = @import("pango"); 8 | 9 | const RADIUS: f64 = 300; 10 | const TWEAKABLE_SCALE: f64 = 0.8; 11 | const FONT_WITH_MANUAL_SIZE = "Times new roman,Sans"; 12 | // const FONT_WITH_MANUAL_SIZE = "Cantarell Italic Light 15 @wght=200"; 13 | const FONT_SIZE: f64 = 36; 14 | const DEVICE_DPI: f64 = 72; 15 | const N_WORDS: usize = 8; 16 | 17 | fn drawText(cr: *cairo.Context) !void { 18 | cr.translate(RADIUS / TWEAKABLE_SCALE, RADIUS / TWEAKABLE_SCALE); 19 | 20 | const layout: *pango.Layout = try cr.createLayout(); 21 | defer layout.destroy(); 22 | 23 | layout.setText("Hello\nПривет\nこんにちは\n你好\nسَلام"); // arabic is written from right 24 | 25 | const desc = try pango.FontDescription.fromString(FONT_WITH_MANUAL_SIZE); 26 | defer desc.free(); 27 | 28 | const scale: f64 = pango.SCALE; 29 | desc.setAbsoluteSize(FONT_SIZE * DEVICE_DPI * scale / (72.0 / TWEAKABLE_SCALE)); 30 | layout.setFontDescription(desc); 31 | 32 | // Draw the layout N_WORDS times in a circle 33 | var i: usize = 0; 34 | while (i < N_WORDS) : (i += 1) { 35 | const angle: f64 = (360.0 * @as(f64, @floatFromInt(i))) / @as(f64, @floatFromInt(N_WORDS)); 36 | 37 | cr.save(); 38 | // gradient from red at angle == 60 to blue at angle == 240 39 | const red = (1.0 + cos((angle - 60.0) * pi / 180.0)) / 2.0; 40 | cr.setSourceRgb(red, 0, 1.0 - red); 41 | cr.rotate(angle * pi / 180.0); 42 | // tell Pango to re-layout the text with the new transformation 43 | cr.updateLayout(layout); 44 | var width: i32 = undefined; 45 | layout.getSize(&width, null); 46 | cr.moveTo(-(@as(f64, @floatFromInt(width)) / scale / 2.0), -(RADIUS / TWEAKABLE_SCALE)); 47 | cr.showLayout(layout); 48 | cr.restore(); 49 | } 50 | } 51 | 52 | pub fn main() !void { 53 | const surface = try cairo.ImageSurface.create(.argb32, 2.0 * RADIUS, 2.0 * RADIUS); 54 | defer surface.destroy(); 55 | 56 | const cr = try cairo.Context.create(surface.asSurface()); 57 | defer cr.destroy(); 58 | 59 | cr.scale(TWEAKABLE_SCALE, TWEAKABLE_SCALE); 60 | cr.setSourceRgb(1.0, 1.0, 1.0); // white 61 | cr.paint(); 62 | try drawText(cr); 63 | try surface.writeToPng("examples/generated/pango_simple.png"); 64 | } 65 | -------------------------------------------------------------------------------- /examples/bezier.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const pi = std.math.pi; 3 | const cairo = @import("cairo"); 4 | const setBackground = @import("utils.zig").setBackground; 5 | 6 | const BezierCubic = struct { 7 | x1: f64, // (x1, y1) is the first control point 8 | y1: f64, 9 | x2: f64, // (x2, y2) is the second control point 10 | y2: f64, 11 | x3: f64, // (x3, y3) is where the curve ends 12 | y3: f64, 13 | }; 14 | 15 | fn drawCubicBezier(cr: *cairo.Context, x0: f64, y0: f64, x1: f64, y1: f64, x2: f64, y2: f64, x3: f64, y3: f64) void { 16 | // bezier curve 17 | cr.setSourceRgb(0.0, 0.0, 0.0); 18 | const bc = BezierCubic{ .x1 = x1, .y1 = y1, .x2 = x2, .y2 = y2, .x3 = x3, .y3 = y3 }; 19 | cr.lineTo(x0, y0); 20 | cr.curveTo(bc.x1, bc.y1, bc.x2, bc.y2, bc.x3, bc.y3); 21 | cr.stroke(); 22 | 23 | // red lines 24 | cr.setSourceRgba(1, 0, 0, 0.75); 25 | cr.moveTo(x0, y0); 26 | cr.lineTo(bc.x1, bc.y1); 27 | cr.moveTo(bc.x3, bc.y3); 28 | cr.lineTo(bc.x2, bc.y2); 29 | cr.stroke(); 30 | 31 | // red dots 32 | const radius = 1.5; 33 | cr.arc(x0, y0, radius, 0, 2 * pi); 34 | cr.arc(x1, y1, radius, 0, 2 * pi); 35 | cr.fill(); 36 | cr.arc(x2, y2, radius, 0, 2 * pi); 37 | cr.arc(x3, y3, radius, 0, 2 * pi); 38 | cr.fill(); 39 | } 40 | 41 | /// Draw 9 cubic Bézier curves with helping lines that highlight each curve control points. 42 | /// https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths#curve_commands 43 | fn drawBezierCurves(cr: *cairo.Context) void { 44 | cr.scale(2.0, 2.0); 45 | cr.setLineWidth(1.0); // to counterbalance the scaling 46 | 47 | drawCubicBezier(cr, 10, 10, 20, 20, 40, 20, 50, 10); 48 | drawCubicBezier(cr, 70, 10, 70, 20, 110, 20, 110, 10); 49 | drawCubicBezier(cr, 130, 10, 120, 20, 180, 20, 170, 10); 50 | 51 | drawCubicBezier(cr, 10, 60, 20, 80, 40, 80, 50, 60); 52 | drawCubicBezier(cr, 70, 60, 70, 80, 110, 80, 110, 60); 53 | drawCubicBezier(cr, 130, 60, 120, 80, 180, 80, 170, 60); 54 | 55 | drawCubicBezier(cr, 10, 110, 20, 140, 40, 140, 50, 110); 56 | drawCubicBezier(cr, 70, 110, 70, 140, 110, 140, 110, 110); 57 | drawCubicBezier(cr, 130, 110, 120, 140, 180, 140, 170, 110); 58 | } 59 | 60 | pub fn main() !void { 61 | const width: u16 = 400; 62 | const height: u16 = 400; 63 | const surface = try cairo.ImageSurface.create(.argb32, width, height); 64 | defer surface.destroy(); 65 | 66 | const cr = try cairo.Context.create(surface.asSurface()); 67 | defer cr.destroy(); 68 | 69 | setBackground(cr); 70 | drawBezierCurves(cr); 71 | try surface.writeToPng("examples/generated/bezier.png"); 72 | } 73 | -------------------------------------------------------------------------------- /examples/curve_rectangle.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const cairo = @import("cairo"); 3 | const setBackground = @import("utils.zig").setBackground; 4 | 5 | /// https://www.cairographics.org/samples/curve_rectangle/ 6 | fn curveRectangle(cr: *cairo.Context, _: usize, _: usize) void { 7 | const x0: f64 = 25.6; 8 | const y0: f64 = 25.6; 9 | const rect_width: f64 = 204.8; 10 | const rect_height: f64 = 204.8; 11 | const radius: f64 = 102.4; 12 | const x1 = x0 + rect_width; 13 | const y1 = y0 + rect_height; 14 | 15 | if (rect_width / 2 < radius) { 16 | if (rect_height / 2 < radius) { 17 | cr.moveTo(x0, (y0 + y1) / 2); 18 | cr.curveTo(x0, y0, x0, y0, (x0 + x1) / 2, y0); 19 | cr.curveTo(x1, y0, x1, y0, x1, (y0 + y1) / 2); 20 | cr.curveTo(x1, y1, x1, y1, (x1 + x0) / 2, y1); 21 | cr.curveTo(x0, y1, x0, y1, x0, (y0 + y1) / 2); 22 | } else { 23 | cr.moveTo(x0, y0 + radius); 24 | cr.curveTo(x0, y0, x0, y0, (x0 + x1) / 2, y0); 25 | cr.curveTo(x1, y0, x1, y0, x1, y0 + radius); 26 | cr.lineTo(x1, y1 - radius); 27 | cr.curveTo(x1, y1, x1, y1, (x1 + x0) / 2, y1); 28 | cr.curveTo(x0, y1, x0, y1, x0, y1 - radius); 29 | } 30 | } else { 31 | if (rect_height / 2 < radius) { 32 | cr.moveTo(x0, (y0 + y1) / 2); 33 | cr.curveTo(x0, y0, x0, y0, x0 + radius, y0); 34 | cr.lineTo(x1 - radius, y0); 35 | cr.curveTo(x1, y0, x1, y0, x1, (y0 + y1) / 2); 36 | cr.curveTo(x1, y1, x1, y1, x1 - radius, y1); 37 | cr.lineTo(x0 + radius, y1); 38 | cr.curveTo(x0, y1, x0, y1, x0, (y0 + y1) / 2); 39 | } else { 40 | cr.moveTo(x0, y0 + radius); 41 | cr.curveTo(x0, y0, x0, y0, x0 + radius, y0); 42 | cr.lineTo(x1 - radius, y0); 43 | cr.curveTo(x1, y0, x1, y0, x1, y0 + radius); 44 | cr.lineTo(x1, y1 - radius); 45 | cr.curveTo(x1, y1, x1, y1, x1 - radius, y1); 46 | cr.lineTo(x0 + radius, y1); 47 | cr.curveTo(x0, y1, x0, y1, x0, y1 - radius); 48 | } 49 | } 50 | cr.closePath(); 51 | 52 | cr.setSourceRgb(0.5, 0.5, 1); 53 | cr.fillPreserve(); 54 | cr.setSourceRgba(0.5, 0, 0, 0.5); 55 | cr.setLineWidth(10.0); 56 | cr.stroke(); 57 | } 58 | 59 | pub fn main() !void { 60 | const width: u16 = 256; 61 | const height: u16 = 256; 62 | const surface = try cairo.ImageSurface.create(.argb32, width, height); 63 | defer surface.destroy(); 64 | 65 | const cr = try cairo.Context.create(surface.asSurface()); 66 | defer cr.destroy(); 67 | 68 | setBackground(cr); 69 | curveRectangle(cr, width, height); 70 | try surface.writeToPng("examples/generated/curve_rectangle.png"); 71 | } 72 | -------------------------------------------------------------------------------- /src/pangocairo/c.zig: -------------------------------------------------------------------------------- 1 | const cairo = @import("cairo"); 2 | const pango = @import("pango"); 3 | const pangocairo = @import("../pangocairo.zig"); 4 | 5 | pub extern fn pango_cairo_font_get_scaled_font(font: ?*pangocairo.Font) ?*cairo.ScaledFont; 6 | 7 | pub extern fn pango_cairo_font_map_new() ?*pangocairo.FontMap; 8 | pub extern fn pango_cairo_font_map_new_for_font_type(font_type: cairo.FontFace.Type) ?*pangocairo.FontMap; 9 | pub extern fn pango_cairo_font_map_get_default() ?*pangocairo.FontMap; 10 | pub extern fn pango_cairo_font_map_set_default(font_map: ?*pangocairo.FontMap) void; 11 | pub extern fn pango_cairo_font_map_get_font_type(font_map: ?*pangocairo.FontMap) cairo.FontFace.Type; 12 | pub extern fn pango_cairo_font_map_set_resolution(font_map: ?*pangocairo.FontMap, dpi: f64) void; 13 | pub extern fn pango_cairo_font_map_get_resolution(font_map: ?*pangocairo.FontMap) f64; 14 | pub extern fn pango_cairo_font_map_create_context(font_map: ?*pangocairo.FontMap) ?*pango.Context; 15 | 16 | pub extern fn pango_cairo_create_context(cr: ?*cairo.Context) ?*pango.Context; 17 | pub extern fn pango_cairo_update_context(cr: ?*cairo.Context, context: ?*pango.Context) void; 18 | pub extern fn pango_cairo_create_layout(cr: ?*cairo.Context) ?*pango.Layout; 19 | pub extern fn pango_cairo_update_layout(cr: ?*cairo.Context, layout: ?*pango.Layout) void; 20 | // pub extern fn pango_cairo_show_glyph_string(cr: ?*cairo.Context, font: [*c]PangoFont, glyphs: [*c]PangoGlyphString) void; 21 | // pub extern fn pango_cairo_show_glyph_item(cr: ?*cairo.Context, text: [*c]const u8, glyph_item: [*c]PangoGlyphItem) void; 22 | pub extern fn pango_cairo_show_layout_line(cr: ?*cairo.Context, line: ?*pango.Layout.Line) void; 23 | pub extern fn pango_cairo_show_layout(cr: ?*cairo.Context, layout: ?*pango.Layout) void; 24 | pub extern fn pango_cairo_show_error_underline(cr: ?*cairo.Context, x: f64, y: f64, width: f64, height: f64) void; 25 | // pub extern fn pango_cairo_glyph_string_path(cr: ?*cairo.Context, font: [*c]PangoFont, glyphs: [*c]PangoGlyphString) void; 26 | pub extern fn pango_cairo_layout_path(cr: ?*cairo.Context, layout: ?*pango.Layout) void; 27 | pub extern fn pango_cairo_layout_line_path(cr: ?*cairo.Context, line: ?*pango.Layout.Line) void; 28 | pub extern fn pango_cairo_error_underline_path(cr: ?*cairo.Context, x: f64, y: f64, width: f64, height: f64) void; 29 | 30 | pub extern fn pango_cairo_context_set_font_options(context: ?*pango.Context, options: ?*const cairo.FontOptions) void; 31 | pub extern fn pango_cairo_context_get_font_options(context: ?*pango.Context) ?*const cairo.FontOptions; 32 | pub extern fn pango_cairo_context_set_resolution(context: ?*pango.Context, dpi: f64) void; 33 | pub extern fn pango_cairo_context_get_resolution(context: ?*pango.Context) f64; 34 | pub extern fn pango_cairo_context_set_shape_renderer(context: ?*pango.Context, func: pangocairo.ShapeRendererFunc, data: ?*anyopaque, dnotify: pango.GDestroyNotify) void; 35 | pub extern fn pango_cairo_context_get_shape_renderer(context: ?*pango.Context, data: [*c]?*anyopaque) pangocairo.ShapeRendererFunc; 36 | -------------------------------------------------------------------------------- /examples/compositing.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const pi = std.math.pi; 3 | const cairo = @import("cairo"); 4 | const Content = cairo.Content; 5 | const Operator = cairo.Context.Operator; 6 | const Rectangle = cairo.Rectangle; 7 | const setBackground = @import("utils.zig").setBackground; 8 | 9 | fn draw(cr: *cairo.Context, width: u16, height: u16, x: f64, y: f64, rw: f64, rh: f64, op: Operator) !void { 10 | const surface = try cr.getTarget(); 11 | 12 | const red = try surface.createSimilar(Content.ColorAlpha, width, height); 13 | defer red.destroy(); 14 | const blue = try surface.createSimilar(Content.ColorAlpha, width, height); 15 | defer blue.destroy(); 16 | 17 | const red_cr = try cairo.Context.create(red); 18 | defer red_cr.destroy(); 19 | 20 | red_cr.setSourceRgba(0.7, 0, 0, 0.8); 21 | red_cr.rectangle(Rectangle.init(.{ x, y, rw, rh })); 22 | red_cr.fill(); 23 | 24 | const blue_cr = try cairo.Context.create(blue); 25 | defer blue_cr.destroy(); 26 | 27 | blue_cr.setSourceRgba(0, 0, 0.9, 0.4); 28 | blue_cr.rectangle(Rectangle.init(.{ x + 40.0, y + 30.0, rw, rh })); 29 | blue_cr.fill(); 30 | 31 | red_cr.setOperator(op); 32 | 33 | // use the `blue` cairo.Surface to create a cairo.Pattern, then set that 34 | // pattern as the source for the `red_cr` cairo.Context. 35 | red_cr.setSourceSurface(blue, 0, 0); 36 | red_cr.paint(); 37 | 38 | cr.setSourceSurface(red, 0, 0); 39 | cr.paint(); 40 | 41 | cr.moveTo(x, y); 42 | cr.setSourceRgb(0.0, 0.0, 0.0); 43 | cr.showText(@tagName(op)); 44 | } 45 | 46 | const OpAndName = struct { 47 | op: Operator, 48 | name: []const u8, 49 | }; 50 | 51 | /// https://www.cairographics.org/operators/ 52 | fn drawAll(cr: *cairo.Context, width: u16, height: u16) !void { 53 | const operators = [_]Operator{ .Add, .Atop, .Clear, .ColorBurn, .ColorDodge, .Darken, .Dest, .DestAtop, .DestIn, .DestOut, .DestOver, .Difference, .Exclusion, .HardLight, .HslColor, .HslHue, .HslLuminosity, .HslSaturation, .In, .Lighten, .Multiply, .Out, .Over, .Overlay, .Saturate, .Screen, .SoftLight, .Source, .Xor }; 54 | const k: usize = 6; // figures per row 55 | const rw = 120.0; // rectangle width 56 | const rh = 90.0; // rectangle height 57 | const margin = 20.0; 58 | const padding = 60.0; 59 | for (operators, 0..) |op, i| { 60 | const row = @divTrunc(i, k); 61 | const col = @mod(i, k); 62 | const pad_x = padding * @as(f64, @floatFromInt(col)); 63 | const pad_y = padding * @as(f64, @floatFromInt(row)); 64 | const x = margin + rw * @as(f64, @floatFromInt(col)) + pad_x; 65 | const y = margin + (rh * @as(f64, @floatFromInt(row))) + pad_y; 66 | try draw(cr, width, height, x, y, rw, rh, op); 67 | } 68 | } 69 | 70 | pub fn main() !void { 71 | const width: u16 = 1200; 72 | const height: u16 = 800; 73 | const surface = try cairo.ImageSurface.create(.argb32, width, height); 74 | defer surface.destroy(); 75 | 76 | const cr = try cairo.Context.create(surface.asSurface()); 77 | defer cr.destroy(); 78 | 79 | cr.selectFontFace("Sans", cairo.FontFace.FontSlant.Normal, cairo.FontFace.FontWeight.Normal); 80 | cr.setFontSize(18.0); 81 | 82 | setBackground(cr); 83 | try drawAll(cr, width, height); 84 | try surface.writeToPng("examples/generated/compositing.png"); 85 | } 86 | -------------------------------------------------------------------------------- /src/pangocairo/pango_context.zig: -------------------------------------------------------------------------------- 1 | const cairo = @import("cairo"); 2 | const pango = @import("pango"); 3 | const pangocairo = @import("../pangocairo.zig"); 4 | const c = pangocairo.c; 5 | 6 | /// Sets the font options used when rendering text with this context. 7 | /// 8 | /// These options override any options that 9 | /// `cairo.Context.updatePangoContext()` derives from the target surface. 10 | /// 11 | /// **Parameters** 12 | /// - `options`: a `cairo.FontOptions`, or `null` to unset any previously set 13 | /// options. A copy is made. 14 | pub fn setFontOptions(self: *pango.Context, options: *const cairo.FontOptions) void { 15 | c.pango_cairo_context_set_font_options(self, options); 16 | } 17 | 18 | /// Retrieves any font rendering options previously set with 19 | /// `pango.Context.setFontOptions()`. 20 | /// 21 | /// This function does not report options that are derived from the target 22 | /// surface by `cairo.Context.updatePangoContext()`. 23 | /// 24 | /// **Returns** 25 | /// 26 | /// the font options previously set on the context, or `null` if no options 27 | /// have been set. This value is owned by the context and must not be modified 28 | /// or freed. 29 | pub fn getFontOptions(self: *pango.Context) !*const cairo.FontOptions { 30 | return c.pango_cairo_context_get_font_options(self) orelse error.NullPointer; 31 | } 32 | 33 | /// Sets the resolution for the context. 34 | /// 35 | /// This is a scale factor between points specified in a 36 | /// `pango.FontDescription` and Cairo units. The default value is 96, meaning 37 | /// that a 10 point font will be 13 units high. (10 * 96. / 72. = 13.3). 38 | /// 39 | /// **Parameters** 40 | /// - `dpi`: The resolution in “dots per inch”. (Physical inches aren’t 41 | /// actually involved; the terminology is conventional.) A 0 or negative value 42 | /// means to use the resolution from the font map. 43 | pub fn setResolution(self: *pango.Context, dpi: f64) void { 44 | c.pango_cairo_context_set_resolution(self, dpi); 45 | } 46 | 47 | /// Gets the resolution for the context. 48 | /// 49 | /// See `pango.Context.setResolution()`. 50 | /// 51 | /// **Returns** 52 | /// 53 | /// the resolution in “dots per inch”. A negative value will be returned if no 54 | /// resolution has previously been set. 55 | pub fn getResolution(self: *pango.Context) f64 { 56 | return c.pango_cairo_context_get_resolution(self); 57 | } 58 | 59 | /// Sets callback function for context to use for rendering attributes of type 60 | /// `pango.AttrType.Shape`. 61 | /// 62 | /// **Parameters** 63 | /// - `func`: callback function for rendering attributes of type 64 | /// `pango.AttrType.Shape`, or `null` to disable shape rendering 65 | /// - `data`: user data that will be passed to `func` 66 | /// - `dnotify`: callback that will be called when the context is freed to 67 | /// release `data` 68 | pub fn setShapeRenderer(self: *pango.Context, func: pangocairo.ShapeRendererFunc, data: ?*anyopaque, dnotify: pango.GDestroyNotify) void { 69 | c.pango_cairo_context_set_shape_renderer(self, func, data, dnotify); 70 | } 71 | 72 | /// Sets callback function for context to use for rendering attributes of type 73 | /// `pango.AttrType.Shape`. 74 | /// 75 | /// See `pangocairo.ShapeRendererFunc` for details. 76 | /// 77 | /// Retrieves callback function and associated user data for rendering 78 | /// attributes of type `pango.AttrType.Shape` as set by 79 | /// `pango.Context.setShapeRenderer()`, if any. 80 | /// 81 | /// **Parameters** 82 | /// - `data`: pointer to `?*anyopaque` to return user data 83 | /// 84 | /// **Returns** 85 | /// 86 | /// the shape rendering callback previously set on the context, or `null` if no 87 | /// shape rendering callback have been set. 88 | pub fn getShapeRenderer(self: *pango.Context, data: ?*?*anyopaque) pangocairo.ShapeRendererFunc { 89 | return c.pango_cairo_context_get_shape_renderer(self, data); 90 | } 91 | -------------------------------------------------------------------------------- /examples/singular.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const builtin = @import("builtin"); 3 | const pi = std.math.pi; 4 | const cairo = @import("cairo"); 5 | const setBackground = @import("utils.zig").setBackground; 6 | 7 | /// Finds the singular values of the non-translation part of matrix. 8 | /// 9 | /// Let M be the cairo transformation matrix in question: 10 | /// 11 | /// ⌈xx xy⌉ 12 | /// M = |yx yy| 13 | /// ⌊x0 y0⌋ 14 | /// 15 | /// The non-translation part is: 16 | /// 17 | /// A = ⌈xx xy⌉ 18 | /// ⌊yx yy⌋ 19 | /// 20 | /// The non-zero singular values of A are the square roots of the non-zero 21 | /// eigenvalues of A⁺ A, where A⁺ is A-transpose. 22 | /// 23 | /// A⁺ A = ⌈xx yx⌉⌈xx xy⌉ = ⌈xx²+yx² xx*xy+yx*yy⌉ 24 | /// ⌊xy yy⌋⌊yx yy⌋ ⌊xx*xy+yx*yy xy²+yy²⌋ 25 | /// 26 | /// Name those: 27 | /// 28 | /// B = A⁺ A = ⌈a k⌉ 29 | /// ⌊k b⌋ 30 | /// 31 | /// The eigenvalues of B satisfy: 32 | /// 33 | /// λ² - (a+b).λ + a.b - k² = 0 34 | /// 35 | /// The eigenvalues are: 36 | /// __________________ 37 | /// (a+b) ± √(a+b)² - 4(a.b-k²) 38 | /// λ = --------------------------- 39 | /// 2 40 | /// that simplifies to: 41 | /// _______________ 42 | /// λ = (a+b)/2 ± √((a-b)/2)² + k² 43 | /// 44 | /// And the Singular values are the root of λs. 45 | fn getSingularValues(matrix: *cairo.Matrix, major: *f64, minor: *f64) void { 46 | const xx = matrix.xx; 47 | const xy = matrix.xy; 48 | const yx = matrix.yx; 49 | const yy = matrix.yy; 50 | 51 | const a = xx * xx + yx * yx; 52 | const b = xy * xy + yy * yy; 53 | const k = xx * xy + yx * yy; 54 | 55 | const f = (a + b) * 0.5; 56 | const g = (a - b) * 0.5; 57 | const delta = std.math.sqrt(g * g + k * k); 58 | 59 | major.* = std.math.sqrt(f + delta); 60 | minor.* = std.math.sqrt(f - delta); 61 | } 62 | 63 | /// Find the length of the major and minor axes of the pen for a cairo_t, 64 | /// identified by the current transformation matrix and line width. 65 | /// Returned values are in device units. 66 | fn getPenAxes(cr: *cairo.Context, major: *f64, minor: *f64) void { 67 | const width = cr.getLineWidth(); 68 | var matrix: cairo.Matrix = undefined; 69 | cr.getMatrix(&matrix); 70 | getSingularValues(&matrix, major, minor); 71 | major.* = major.* * width; 72 | minor.* = minor.* * width; 73 | } 74 | 75 | /// Use Singular values of transformation matrix to find the length of the major 76 | /// and minor axes of the scaled pen. 77 | /// Ported in Zig from this example in C. 78 | /// https://github.com/freedesktop/cairo/blob/master/doc/tutorial/src/singular.c 79 | fn draw(cr: *cairo.Context, width: f64, height: f64) void { 80 | // not sure what this `b` is. Boundary? And why dividing by 16? 81 | const b = (width + height) / 16.0; 82 | var major_width: f64 = 0.0; 83 | var minor_width: f64 = 0.0; 84 | 85 | // the spline we want to stroke 86 | cr.moveTo(width - b, b); 87 | cr.curveTo(-width, b, 2.0 * width, height - b, b, height - b); 88 | 89 | // the effect can be seen better with round caps 90 | cr.setLineCap(cairo.Context.LineCap.Round); 91 | 92 | // set the skewed pen 93 | cr.rotate(0.7); 94 | cr.scale(0.5, 2.0); 95 | cr.rotate(-0.7); 96 | cr.setLineWidth(b); 97 | 98 | getPenAxes(cr, &major_width, &minor_width); 99 | 100 | // stroke with "major" pen in translucent red 101 | cr.save(); 102 | cr.identityMatrix(); 103 | cr.setLineWidth(major_width); 104 | cr.setSourceRgba(1.0, 0.0, 0.0, 0.9); 105 | cr.strokePreserve(); 106 | cr.restore(); 107 | 108 | // stroke with skewed pen in translucent black 109 | cr.setSourceRgba(0.0, 0.0, 0.0, 0.9); 110 | cr.strokePreserve(); 111 | 112 | // stroke with "minor" pen in translucent yellow 113 | cr.save(); 114 | cr.identityMatrix(); 115 | cr.setLineWidth(minor_width); 116 | cr.setSourceRgba(1.0, 1.0, 0.0, 0.9); 117 | cr.strokePreserve(); 118 | cr.restore(); 119 | 120 | // stroke with hairline in black 121 | cr.save(); 122 | cr.identityMatrix(); 123 | cr.setLineWidth(1); 124 | cr.setSourceRgb(0.0, 0.0, 0.0); 125 | cr.strokePreserve(); 126 | cr.restore(); 127 | 128 | cr.newPath(); 129 | } 130 | 131 | pub fn main() !void { 132 | const width: u16 = 400; 133 | const height: u16 = 400; 134 | const surface = try cairo.ImageSurface.create(.argb32, width, height); 135 | defer surface.destroy(); 136 | 137 | const cr = try cairo.Context.create(surface.asSurface()); 138 | defer cr.destroy(); 139 | 140 | setBackground(cr); 141 | draw(cr, width, height); 142 | try surface.writeToPng("examples/generated/singular.png"); 143 | } 144 | -------------------------------------------------------------------------------- /src/pangocairo/context.zig: -------------------------------------------------------------------------------- 1 | const cairo = @import("cairo"); 2 | const pango = @import("pango"); 3 | const pangocairo = @import("../pangocairo.zig"); 4 | const c = pangocairo.c; 5 | const safety = @import("safety"); 6 | 7 | /// Creates a context object set up to match the current transformation and 8 | /// target surface of the Cairo context. 9 | /// 10 | /// This context can then be used to create a layout using 11 | /// `pango.Layout.create()`. 12 | /// 13 | /// This function is a convenience function that creates a context using the 14 | /// default font map, then updates it to `self`. If you just need to create a 15 | /// layout for use with `cr` and do not need to access `pango.Context` 16 | /// directly, you can use `cairo.Context.createLayout()` instead. 17 | /// 18 | /// **Returns** 19 | /// 20 | /// the newly created `pango.Context`. 21 | /// 22 | /// **NOTE**: The caller owns the created context and should call 23 | /// `pango.Context.destroy()` when done with it. You can use idiomatic Zig 24 | /// pattern with `defer`: 25 | /// ```zig 26 | /// const pg_ctx = try cairo_ctx.createPangoContext(); 27 | /// defer pg_ctx.destroy(); 28 | /// ``` 29 | pub fn createPangoContext(self: *cairo.Context) cairo.CairoError!*pango.Context { 30 | const context = c.pango_cairo_create_context(self) orelse return error.NullPointer; 31 | if (safety.tracing) try safety.markForLeakDetection(@returnAddress(), context); 32 | return context; 33 | } 34 | 35 | /// Updates a `pango.Context` previously created for use with Cairo to match 36 | /// the current transformation and target surface of a Cairo context. 37 | /// 38 | /// If any layouts have been created for the context, it’s necessary to call 39 | /// `.contextChanged()` on those layouts. 40 | /// 41 | /// **Parameters** 42 | /// - `context`: a `pango.Context`, from a pangocairo font map. 43 | pub fn updatePangoContext(self: *cairo.Context, context: *pango.Context) void { 44 | c.pango_cairo_update_context(self, context); 45 | } 46 | 47 | /// Creates a layout object set up to match the current transformation and 48 | /// target surface of the Cairo context. 49 | /// 50 | /// This layout can then be used for text measurement with functions like 51 | /// `pango.Layout.getSize()` or drawing with functions like 52 | /// `cairo.Context.showLayout()`. If you change the transformation or target 53 | /// surface for `self`, you need to call `cairo.Context.updateLayout()`. 54 | /// 55 | /// **Returns** 56 | /// 57 | /// the newly created `pango.Layout`. 58 | /// 59 | /// **NOTE**: The caller owns the created layout and should call 60 | /// `layout.destroy()` when done with it. You can use idiomatic Zig pattern 61 | /// with `defer`: 62 | /// ```zig 63 | /// const layout = try cairo_ctx.createLayout(); 64 | /// defer layout.destroy(); 65 | /// ``` 66 | pub fn createLayout(self: *cairo.Context) cairo.CairoError!*pango.Layout { 67 | const layout = c.pango_cairo_create_layout(self) orelse return error.NullPointer; 68 | if (safety.tracing) try safety.markForLeakDetection(@returnAddress(), layout); 69 | return layout; 70 | } 71 | 72 | /// Updates the private `pango.Context` of a `pango.Layout` created with 73 | /// `cairo.Context.createLayout()` to match the current transformation and 74 | /// target surface of a Cairo context. 75 | /// 76 | /// **Parameters** 77 | /// - `layout`: A `pango.Layout`, from `cairo.Context.createLayout()`. 78 | pub fn updateLayout(self: *cairo.Context, layout: *pango.Layout) void { 79 | c.pango_cairo_update_layout(self, layout); 80 | } 81 | 82 | // pub extern fn pango_cairo_show_glyph_string(cr: ?*cairo.Context, font: [*c]PangoFont, glyphs: [*c]PangoGlyphString) void; 83 | // pub extern fn pango_cairo_show_glyph_item(cr: ?*cairo.Context, text: [*c]const u8, glyph_item: [*c]PangoGlyphItem) void; 84 | 85 | pub fn showLayoutLine(self: *cairo.Context, line: *pango.Layout.Line) void { 86 | c.pango_cairo_show_layout_line(self, line); 87 | } 88 | 89 | pub fn showLayout(self: *cairo.Context, layout: *pango.Layout) void { 90 | c.pango_cairo_show_layout(self, layout); 91 | } 92 | 93 | pub fn showErrorUnderline(self: *cairo.Context, x: f64, y: f64, width: f64, height: f64) void { 94 | c.pango_cairo_show_error_underline(self, x, y, width, height); 95 | } 96 | 97 | // pub extern fn pango_cairo_glyph_string_path(cr: ?*cairo.Context, font: [*c]PangoFont, glyphs: [*c]PangoGlyphString) void; 98 | 99 | /// Adds the text in a `pango.Layout` to the current path in the specified 100 | /// cairo context. 101 | /// 102 | /// The top-left corner of the PangoLayout will be at the current point of the 103 | /// cairo context. 104 | pub fn layoutPath(self: *cairo.Context, line: *pango.Layout) void { 105 | c.pango_cairo_layout_path(self, line); 106 | } 107 | 108 | pub fn layoutLinePath(self: *cairo.Context, line: *pango.Layout.Line) void { 109 | c.pango_cairo_layout_line_path(self, line); 110 | } 111 | 112 | pub fn layoutLine(self: *cairo.Context, layout: *pango.Layout) void { 113 | c.pango_cairo_layout_path(self, layout); 114 | } 115 | 116 | pub fn errorUnderlinePath(self: *cairo.Context, x: f64, y: f64, width: f64, height: f64) void { 117 | c.pango_cairo_error_underline_path(self, x, y, width, height); 118 | } 119 | -------------------------------------------------------------------------------- /src/cairo/drawing/tags_and_links.zig: -------------------------------------------------------------------------------- 1 | //! Tags and Links — Hyperlinks and document structure 2 | //! 3 | //! The tag functions provide the ability to specify hyperlinks and document 4 | //! logical structure on supported backends. The following tags are supported: 5 | //! - [Link](https://www.cairographics.org/manual/cairo-Tags-and-Links.html#link) — Create a hyperlink 6 | //! - [Destinations](https://www.cairographics.org/manual/cairo-Tags-and-Links.html#dest) — Create a hyperlink destination 7 | //! - [Document Structure Tags](https://www.cairographics.org/manual/cairo-Tags-and-Links.html#doc-struct) — Create PDF Document Structure 8 | //! 9 | //! ## Link Tags 10 | //! 11 | //! A hyperlink is specified by enclosing the hyperlink text with the 12 | //! `cairo.TagLink` tag. 13 | //! 14 | //! For example: 15 | //! ```zig 16 | //! // cr is cairo.Context 17 | //! cr.tagBegin(cairo.TagLink, "uri='https://cairographics.org'"); 18 | //! cr.moveTo(50, 50); 19 | //! cr.showText("This is a link to the cairo website."); 20 | //! cr.tagEnd(cairo.TagLink); 21 | //! ``` 22 | //! 23 | //! The PDF backend uses one or more rectangles to define the clickable area of 24 | //! the link. By default cairo will use the extents of the drawing operations 25 | //! enclosed by the begin/end link tags to define the clickable area. In some 26 | //! cases, such as a link split across two lines, the default rectangle is 27 | //! undesirable. 28 | //! 29 | //! `rect`: [optional] The "rect" attribute allows the application to specify 30 | //! one or more rectangles that form the clickable region. The value of this 31 | //! attribute is an array of floats. Each rectangle is specified by four 32 | //! elements in the array: x, y, width, height. The array size must be a 33 | //! multiple of four. 34 | //! 35 | //! An example of creating a link with user specified clickable region: 36 | //! ```zig 37 | //! // cr is cairo.Context 38 | //! const text1 = "This link is split"; 39 | //! const text2 = "across two lines"; 40 | //! const font_extents = cr.fontExtents(); // ? 41 | //! _ = font_extents; 42 | //! cr.moveTo(450, 50); 43 | //! const text1_extents = cr.textExtents(text1); 44 | //! cr.moveTo(50, 70); 45 | //! const text2_extents = cr.textExtents(text2); 46 | //! var attribsBuf: [1024]u8 = undefined; 47 | //! const attribs = try std.fmt.bufPrintZ(&attribsBuf, "rect=[ {d:.3} {d:.3} {d:.3} {d:.3} {d:.3} {d:.3} {d:.3} {d:.3}] uri='https://cairographics.org'", .{ 48 | //! text1_extents.x_bearing, 49 | //! text1_extents.y_bearing, 50 | //! text1_extents.width, 51 | //! text1_extents.height, 52 | //! text2_extents.x_bearing, 53 | //! text2_extents.y_bearing, 54 | //! text2_extents.width, 55 | //! text2_extents.height, 56 | //! }); 57 | //! std.debug.print("{s}\n", .{attribs}); 58 | //! cr.tagBegin(TagLink, attribs); 59 | //! cr.showText("This is a link to the cairo website"); 60 | //! cr.moveTo(450, 50); 61 | //! cr.showText(text1); 62 | //! cr.moveTo(50, 70); 63 | //! cr.showText(text2); 64 | //! cr.tagEnd(TagLink); 65 | //! ``` 66 | //! 67 | //! additional documentation is at https://www.cairographics.org/manual/cairo-Tags-and-Links.html 68 | 69 | const cairo = @import("../../cairo.zig"); 70 | const c = cairo.c; 71 | const Context = cairo.Context; 72 | 73 | /// Marks the beginning of the `tag_name` structure. Call `cr.tagEnd()` with 74 | /// the same `tag_name` to mark the end of the structure. 75 | /// 76 | /// The `attributes` string is of the form `"key1=value2 key2=value2 ..."`. 77 | /// Values may be boolean (true/false or 1/0), integer, float, string, or an 78 | /// array. 79 | /// 80 | /// String values are enclosed in single quotes (`'`). Single quotes and 81 | /// backslashes inside the string should be escaped with a backslash. 82 | /// 83 | /// Boolean values may be set to true by only specifying the key. eg the 84 | /// attribute string `"key"` is the equivalent to `"key=true"`. 85 | /// 86 | /// Arrays are enclosed in `[]`. eg `"rect=[1.2 4.3 2.0 3.0]"`. 87 | /// 88 | /// If no attributes are required, `attributes` can be an empty string or 89 | /// `null`. 90 | /// 91 | /// See [Tags and Links Description](https://www.cairographics.org/manual/cairo-Tags-and-Links.html#cairo-Tags-and-Links.description) 92 | /// for the list of tags and attributes. 93 | /// 94 | /// **Parameters** 95 | /// - `tag_name`: tag name 96 | /// - `attributes`: tag attributes 97 | /// 98 | /// [Link to Cairo documentation](https://www.cairographics.org/manual/cairo-Tags-and-Links.html#cairo-tag-begin) 99 | pub fn tagBegin(self: *Context, tag_name: [:0]const u8, attributes: ?[:0]const u8) void { 100 | c.cairo_tag_begin(self, tag_name, attributes orelse null); 101 | } 102 | 103 | /// Marks the end of the `tag_name` structure. 104 | /// 105 | /// Invalid nesting of tags will cause `self` to shutdown with a status of 106 | /// `cairo.Status.TagError`. 107 | /// 108 | /// See `cairo.Context.tagBegin()`. 109 | /// 110 | /// **Parameters** 111 | /// - `tag_name`: tag name 112 | /// 113 | /// [Link to Cairo documentation](https://www.cairographics.org/manual/cairo-Tags-and-Links.html#cairo-tag-end) 114 | pub fn tagEnd(self: *Context, tag_name: [:0]const u8) void { 115 | c.cairo_tag_end(self, tag_name); 116 | } 117 | 118 | pub const TagDest = "cairo.dest"; 119 | pub const TagLink = "Link"; 120 | -------------------------------------------------------------------------------- /src/cairo/surfaces/recording.zig: -------------------------------------------------------------------------------- 1 | //! Recording Surfaces — Records all drawing operations 2 | 3 | const cairo = @import("../../cairo.zig"); 4 | const c = cairo.c; 5 | const safety = @import("safety"); 6 | 7 | const Mixin = @import("base.zig").Base; 8 | 9 | const CairoError = cairo.CairoError; 10 | const Content = cairo.Content; 11 | const Rectangle = cairo.Rectangle; 12 | 13 | /// A recording surface is a surface that records all drawing operations at the 14 | /// highest level of the surface backend interface, (that is, the level of 15 | /// paint, mask, stroke, fill, and showTextGlyphs). The recording surface can 16 | /// then be "replayed" against any target surface by using it as a source 17 | /// surface. 18 | /// 19 | /// If you want to replay a surface so that the results in target will be 20 | /// identical to the results that would have been obtained if the original 21 | /// operations applied to the recording surface had instead been applied to the 22 | /// target surface, you can use code like this: 23 | /// ```zig 24 | /// const context = cairo.Context.create(target); 25 | /// context.setSourceSurface(recording_surface, 0.0, 0.0); 26 | /// context.paint(); 27 | /// context.destroy(); 28 | /// ``` 29 | /// A recording surface is logically unbounded, i.e. it has no implicit 30 | /// constraint on the size of the drawing surface. However, in practice this is 31 | /// rarely useful as you wish to replay against a particular target surface 32 | /// with known bounds. For this case, it is more efficient to specify the 33 | /// target extents to the recording surface upon creation. 34 | /// 35 | /// The recording phase of the recording surface is careful to snapshot all 36 | /// necessary objects (paths, patterns, etc.), in order to achieve accurate 37 | /// replay. The efficiency of the recording surface could be improved by 38 | /// improving the implementation of snapshot for the various objects. For 39 | /// example, it would be nice to have a copy-on-write implementation for 40 | /// _cairo_surface_snapshot. (idk what that means) 41 | /// 42 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-Recording-Surfaces.html) 43 | pub const RecordingSurface = opaque { 44 | pub usingnamespace Mixin(@This()); 45 | 46 | /// Creates a recording-surface which can be used to record all drawing 47 | /// operations at the highest level (that is, the level of paint, mask, 48 | /// stroke, fill and showTextGlyphs). The recording surface can then be 49 | /// "replayed" against any target surface by using it as a source to 50 | /// drawing operations. 51 | /// 52 | /// The recording phase of the recording surface is careful to snapshot all 53 | /// necessary objects (paths, patterns, etc.), in order to achieve accurate 54 | /// replay. 55 | /// 56 | /// **Parameters** 57 | /// - `content`: the content of the recording surface 58 | /// - `extents`:the extents to record in pixels, can be `null` to record 59 | /// unbounded operations. 60 | /// 61 | /// **Returns** 62 | /// 63 | /// a pointer to the newly created surface. 64 | /// 65 | /// **NOTE**: The caller owns the created surface and should call 66 | /// `surface.destroy()` when done with it. You can use idiomatic Zig 67 | /// pattern with `defer`: 68 | /// ```zig 69 | /// const surface = try cairo.RecordingSurface.create(.ColorAlpha, null); 70 | /// defer surface.destroy(); 71 | /// ``` 72 | /// 73 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-Recording-Surfaces.html#cairo-recording-surface-create) 74 | pub fn create(content: Content, extents: ?*const Rectangle) CairoError!*RecordingSurface { 75 | const surface = c.cairo_recording_surface_create(content, extents orelse null).?; 76 | try surface.status().toErr(); 77 | if (safety.tracing) try safety.markForLeakDetection(@returnAddress(), surface); 78 | return surface; 79 | } 80 | 81 | /// Measures the extents of the operations stored within the 82 | /// recording-surface. This is useful to compute the required size of an 83 | /// image surface (or equivalent) into which to replay the full sequence of 84 | /// drawing operations. 85 | /// 86 | /// **Parameters** 87 | /// - `x0`: the x-coordinate of the top-left of the ink bounding box 88 | /// - `y0`: the y-coordinate of the top-left of the ink bounding box 89 | /// - `width`: the width of the ink bounding box 90 | /// - `height`: the height of the ink bounding box 91 | /// 92 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-Recording-Surfaces.html#cairo-recording-surface-ink-extents) 93 | pub fn inkExtents(self: *RecordingSurface, x0: *f64, y0: *f64, width: *f64, height: *f64) void { 94 | c.cairo_recording_surface_ink_extents(self, x0, y0, width, height); 95 | } 96 | 97 | /// Get the extents of the recording-surface. 98 | /// 99 | /// **Parameters** 100 | /// - `extents`: the `cairo.Rectangle` to be assigned the extents 101 | /// 102 | /// **Returns** 103 | /// 104 | /// `true` if the surface is bounded, of recording type, and not in an 105 | /// error state, otherwise `false`. 106 | /// 107 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-Recording-Surfaces.html#cairo-recording-surface-get-extents) 108 | pub fn getExtents(self: *RecordingSurface, extents: *Rectangle) bool { 109 | return c.cairo_recording_surface_get_extents(self, extents) != 0; 110 | } 111 | }; 112 | -------------------------------------------------------------------------------- /src/safety.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const builtin = @import("builtin"); 3 | 4 | var arena: ?std.heap.ArenaAllocator = undefined; 5 | pub const tracing = builtin.mode == .Debug; 6 | 7 | const ib_T = if (tracing) [16 * 16]usize else [0]usize; 8 | var instructions_buffer: ib_T = undefined; 9 | var ib_end: usize = 0; 10 | 11 | const LeakInfo = struct { 12 | count: usize, 13 | st: std.builtin.StackTrace, 14 | type_name: []const u8, 15 | ptr: *anyopaque, 16 | }; 17 | const li_T = if (tracing) [16]LeakInfo else [0]LeakInfo; 18 | var leak_infos: li_T = undefined; 19 | var li_end: usize = 0; 20 | 21 | var instructions: []usize = undefined; 22 | var leaks: std.AutoArrayHashMap(usize, LeakInfo) = undefined; 23 | 24 | const LeaksWriter = union(enum) { 25 | FileWriter: std.fs.File.Writer, 26 | BufferWriter: std.io.FixedBufferStream([]u8).Writer, 27 | 28 | pub fn toAny(self: LeaksWriter) *anyopaque { 29 | return switch (self) { 30 | inline else => |val| @ptrCast(val), 31 | }; 32 | } 33 | 34 | pub fn print(self: LeaksWriter, comptime format: []const u8, args: anytype) anyerror!void { 35 | return switch (self) { 36 | inline else => |val| val.print(format, args), 37 | }; 38 | } 39 | 40 | pub fn writeAll(self: LeaksWriter, bytes: []const u8) anyerror!void { 41 | return switch (self) { 42 | inline else => |val| val.writeAll(bytes), 43 | }; 44 | } 45 | 46 | pub fn writeByte(self: LeaksWriter, byte: u8) anyerror!void { 47 | return switch (self) { 48 | inline else => |val| val.writeByte(byte), 49 | }; 50 | } 51 | 52 | pub fn writeByteNTimes(self: LeaksWriter, byte: u8, n: usize) anyerror!void { 53 | return switch (self) { 54 | inline else => |val| val.writeByteNTimes(byte, n), 55 | }; 56 | } 57 | }; 58 | 59 | var leaks_writer: LeaksWriter = undefined; 60 | var tty_config: ?std.io.tty.Config = null; 61 | 62 | fn init() void { 63 | arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); 64 | instructions = arena.?.allocator().alloc(usize, 512) catch unreachable; 65 | tty_config = tty_config orelse std.io.tty.detectConfig(std.io.getStdErr()); 66 | leaks = @TypeOf(leaks).init(arena.?.allocator()); 67 | leaks_writer = LeaksWriter{ .FileWriter = std.io.getStdErr().writer() }; 68 | _ = atexit(&detectLeaks); 69 | } 70 | 71 | pub fn markForLeakDetection(address: usize, ptr: anytype) !void { 72 | if (arena == null) init(); 73 | 74 | if (ib_end >= instructions.len) { 75 | const new_slice = try arena.?.allocator().alloc(usize, instructions.len * 2); 76 | @memcpy(new_slice[0..instructions.len], instructions); 77 | instructions = new_slice; 78 | } 79 | 80 | var leak_info: LeakInfo = undefined; 81 | leak_info.count = 1; 82 | leak_info.st.instruction_addresses = instructions[ib_end..]; 83 | leak_info.type_name = trimmedTypeName(@TypeOf(ptr)); 84 | leak_info.ptr = ptr; 85 | 86 | std.debug.captureStackTrace(address, &(leak_info.st)); 87 | 88 | try leaks.put(@intFromPtr(ptr), leak_info); 89 | ib_end += leak_info.st.index; 90 | li_end += 1; 91 | } 92 | 93 | pub fn reference(address: usize, ptr: anytype) void { 94 | const entry = leaks.getPtr(@intFromPtr(ptr)); 95 | if (entry) |e| { 96 | e.*.count += 1; 97 | return; 98 | } 99 | markForLeakDetection(address, ptr) catch |e| std.debug.panic("{any}", .{e}); 100 | } 101 | 102 | pub fn destroy(ptr: *anyopaque) void { 103 | const entry = leaks.getPtr(@intFromPtr(ptr)) orelse std.debug.panic("Double free!", .{}); 104 | entry.*.count -= 1; 105 | if (entry.count == 0) { 106 | _ = leaks.swapRemove(@intFromPtr(ptr)); 107 | } 108 | } 109 | 110 | pub fn markAsDestroyed(ptr: *anyopaque) void { 111 | _ = leaks.swapRemove(@intFromPtr(ptr)); 112 | } 113 | 114 | pub export fn detectLeaks() void { 115 | var error_state = false; 116 | defer { 117 | if (arena) |a| a.deinit(); 118 | arena = null; 119 | leak_infos = undefined; 120 | ib_end = 0; 121 | li_end = 0; 122 | if (error_state) @panic("giza safety check failure"); 123 | } 124 | const writer = leaks_writer; 125 | var it = leaks.iterator(); 126 | while (it.next()) |pair| { 127 | error_state = true; 128 | const debug_info = std.debug.getSelfDebugInfo() catch |err| { 129 | writer.print("\nUnable to print stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}) catch return; 130 | return; 131 | }; 132 | const li = pair.value_ptr; 133 | writer.print("[giza] (err): Leak detected! {s}@{x} leaked:\n", .{ li.type_name, @intFromPtr(li.ptr) }) catch |err| { 134 | writer.print("Unable to print stack trace: {s}\n", .{@errorName(err)}) catch return; 135 | }; 136 | ////std.debug.writeStackTrace(li.st, writer, arena.?.allocator(), debug_info, tty_config.?) catch |err| { 137 | std.debug.writeStackTrace(li.st, writer, debug_info, tty_config.?) catch |err| { 138 | writer.print("Unable to print stack trace: {s}\n", .{@errorName(err)}) catch return; 139 | }; 140 | writer.print("\n", .{}) catch unreachable; 141 | } 142 | } 143 | 144 | pub inline fn trimmedTypeName(comptime T: type) []const u8 { 145 | comptime { 146 | var type_name: []const u8 = @typeName(T); 147 | const start_ptr = std.mem.lastIndexOf(u8, type_name, "*") orelse 0; 148 | type_name = type_name[start_ptr + 1 ..]; 149 | const startType = std.mem.lastIndexOf(u8, type_name, ".") orelse 0; 150 | return type_name[startType + 1 ..]; 151 | } 152 | } 153 | 154 | test "trimmedTypeName" { 155 | const E = enum {}; 156 | const T = *E; 157 | try std.testing.expectEqualStrings("E", trimmedTypeName(T)); 158 | } 159 | 160 | extern fn atexit(?*const fn () callconv(.C) void) c_int; 161 | -------------------------------------------------------------------------------- /src/pango/language.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const pango = @import("../pango.zig"); 3 | const c = pango.c; 4 | 5 | /// The `pango.Language` structure is used to represent a language. 6 | /// 7 | /// `pango.Language` pointers can be efficiently copied and compared with each 8 | /// other. 9 | pub const Language = opaque { 10 | /// Returns the `pango.Language` for the current locale of the process. 11 | /// 12 | /// On Unix systems, this is the return value is derived from 13 | /// `setlocale (LC_CTYPE, NULL)`, and the user can affect this through the 14 | /// environment variables LC_ALL, LC_CTYPE or LANG (checked in that order). 15 | /// The locale string typically is in the form lang_COUNTRY, where lang is 16 | /// an ISO-639 language code, and COUNTRY is an ISO-3166 country code. For 17 | /// instance, sv_FI for Swedish as written in Finland or pt_BR for 18 | /// Portuguese as written in Brazil. 19 | /// 20 | /// On Windows, the C library does not use any such environment variables, 21 | /// and setting them won’t affect the behavior of functions like `ctime()`. 22 | /// The user sets the locale through the Regional Options in the Control 23 | /// Panel. The C library (in the `setlocale()` function) does not use 24 | /// country and language codes, but country and language names spelled out 25 | /// in English. However, this function does check the above environment 26 | /// variables, and does return a Unix-style locale string based on either 27 | /// said environment variables or the thread’s current locale. 28 | /// 29 | /// Your application should call `setlocale(LC_ALL, "")` for the user 30 | /// settings to take effect. GTK does this in its initialization functions 31 | /// automatically (by calling `gtk_set_locale()`). See the `setlocale()` 32 | /// manpage for more details. 33 | /// 34 | /// Note that the default language can change over the life of an 35 | /// application. 36 | /// 37 | /// Also note that this function will not do the right thing if you use 38 | /// per-thread locales with `uselocale()`. In that case, you should just 39 | /// call `pango.Language.fromString()` yourself. 40 | /// 41 | /// **Returns** 42 | /// 43 | /// the default language as a `pango.Language`. 44 | pub fn getDefault() !*Language { 45 | return c.pango_language_get_default() orelse error.NullPointer; 46 | } 47 | 48 | /// Returns the list of languages that the user prefers. 49 | /// 50 | /// The list is specified by the PANGO_LANGUAGE or LANGUAGE environment 51 | /// variables, in order of preference. Note that this list does not 52 | /// necessarily include the language returned by 53 | /// `pango.Language.getDefault()`. 54 | /// 55 | /// When choosing language-specific resources, such as the sample text 56 | /// returned by `pango.Language.getSampleString()`, you should first try 57 | /// the default language, followed by the languages returned by this 58 | /// function. 59 | /// 60 | /// **Returns** 61 | /// 62 | /// a slice of `*pango.Language`. 63 | pub fn getPreferred() []?*Language { 64 | const ptr: [*:null]?*Language = c.pango_language_get_preferred() orelse return &.{}; 65 | return std.mem.span(ptr); 66 | } 67 | 68 | /// Convert a language tag to a `pango.Language`. 69 | /// 70 | /// The language tag must be in a RFC-3066 format. `pango.Language` 71 | /// pointers can be efficiently copied (copy the pointer) and compared with 72 | /// other language tags (compare the pointer.) 73 | /// 74 | /// This function first canonicalizes the string by converting it to 75 | /// lowercase, mapping ‘_’ to ‘-‘, and stripping all characters other than 76 | /// letters and ‘-‘. 77 | /// 78 | /// Use `pango.Language.getDefault()` if you want to get the 79 | /// `pango.Language` for the current locale of the process. 80 | /// 81 | /// **Parameters** 82 | /// - `language`: A string representing a language tag. 83 | pub fn fromString(language: [:0]const u8) !*Language { 84 | return c.pango_language_from_string(language) orelse error.NullPointer; 85 | } 86 | 87 | /// Gets the RFC-3066 format string representing the given language tag. 88 | /// 89 | /// **Returns** 90 | /// 91 | /// a string representing the language tag. 92 | pub fn toString(self: *Language) [:0]const u8 { 93 | return std.mem.span(c.pango_language_to_string(self) orelse return &.{}); 94 | } 95 | 96 | /// Get a string that is representative of the characters needed to render 97 | /// a particular language. 98 | /// 99 | /// The sample text may be a pangram, but is not necessarily. It is chosen 100 | /// to be demonstrative of normal text in the language, as well as exposing 101 | /// font feature requirements unique to the language. It is suitable for 102 | /// use as sample text in a font selection dialog. 103 | /// 104 | /// If Pango does not have a sample string for language, the classic “The 105 | /// quick brown fox…” is returned. This can be detected by comparing the 106 | /// returned pointer value to that returned for (non-existent) language 107 | /// code “xx”. That is, compare to: 108 | /// ```zig 109 | /// (pango.Language.fromString("xx") catch unreachable).getSampleString() 110 | /// ``` 111 | /// 112 | /// **Returns** 113 | /// 114 | /// the sample string. 115 | pub fn getSampleString(self: *Language) [:0]const u8 { 116 | return std.mem.span(c.pango_language_get_sample_string(self) orelse return &.{}); 117 | } 118 | 119 | /// Checks if a language tag matches one of the elements in a list of 120 | /// language ranges. 121 | /// 122 | /// A language tag is considered to match a range in the list if the range 123 | /// is ‘*’, the range is exactly the tag, or the range is a prefix of the 124 | /// tag, and the character after it in the tag is ‘-‘. 125 | /// 126 | /// **Parameters** 127 | /// - `range_list`: a list of language ranges, separated by ‘;’, ‘:’, ‘,’, 128 | /// or space characters. Each element must either be ‘*’, or a RFC 3066 129 | /// language range canonicalized as by `pango.Language.fromString()`. 130 | /// 131 | /// **Returns** 132 | /// 133 | /// `true` if a match was found. 134 | pub fn matches(self: *Language, range_list: [:0]const u8) bool { 135 | return c.pango_language_matches(self, range_list) != 0; 136 | } 137 | }; 138 | -------------------------------------------------------------------------------- /src/pango/attributes.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const safety = @import("safety"); 3 | 4 | const pango = @import("../pango.zig"); 5 | const c = pango.c; 6 | 7 | /// The `pango.AttrType` distinguishes between different types of attributes. 8 | /// 9 | /// Along with the predefined values, it is possible to allocate additional 10 | /// values for custom attributes using `pango_attr_type_register()`. The 11 | /// predefined values are given below. The type of structure used to store the 12 | /// attribute is listed in parentheses after the description. 13 | pub const AttrType = enum(c_uint) { 14 | /// Does not happen. 15 | Invalid, 16 | /// 17 | Language, 18 | Family, 19 | Style, 20 | Weight, 21 | Variant, 22 | Stretch, 23 | Size, 24 | FontDesc, 25 | Foreground, 26 | Background, 27 | Underline, 28 | Strikethrough, 29 | Rise, 30 | Shape, 31 | Scale, 32 | Fallback, 33 | LetterSpacing, 34 | UnderlineColor, 35 | StrikethroughColor, 36 | AbsoluteSize, 37 | Gravity, 38 | GravityHint, 39 | FontFeatures, 40 | ForegroundAlpha, 41 | BackgroundAlpha, 42 | AllowBreaks, 43 | Show, 44 | InsertHyphens, 45 | Overline, 46 | OverlineColor, 47 | LineHeight, 48 | AbsoluteLineHeight, 49 | TextTransform, 50 | Word, 51 | Sentence, 52 | BaselineShift, 53 | FontScale, 54 | }; 55 | 56 | pub const AttrList = @import("attributes/attr_list.zig").AttrList; 57 | 58 | /// The `pango.Attribute` structure represents the common portions of all 59 | /// attributes. 60 | /// 61 | /// Particular types of attributes include this structure as their initial 62 | /// portion. The common portion of the attribute holds the range to which the 63 | /// value in the type-specific part of the attribute applies and should be 64 | /// initialized using `pango.Attribute.init()`. By default, an attribute will 65 | /// have an all-inclusive range of [0,`std.math.maxInt(c_uint)`]. 66 | pub const Attribute = extern struct { 67 | /// The class structure holding information about the type of the 68 | /// attribute. 69 | klass: *const AttrClass, 70 | /// The start index of the range (in bytes). 71 | start_index: c_uint, 72 | /// End index of the range (in bytes). The character at this index is not 73 | /// included in the range. 74 | end_index: c_uint, 75 | 76 | pub fn destroy(self: *Attribute) void { 77 | c.pango_attribute_destroy(self); 78 | if (safety.tracing) safety.destroy(self); 79 | } 80 | }; 81 | 82 | /// The `pango.AttrClass` structure stores the type and operations for a 83 | /// particular type of attribute. 84 | /// 85 | /// The functions in this structure should not be called directly. Instead, one 86 | /// should use the wrapper functions provided for `pango.Attribute`. 87 | pub const AttrClass = extern struct { 88 | /// The type ID for this attribute. 89 | type: AttrType, 90 | copy: ?*const fn ([*c]const Attribute) callconv(.C) [*c]Attribute, 91 | destroy: ?*const fn ([*c]Attribute) callconv(.C) void, 92 | equal: ?*const fn ([*c]const Attribute, [*c]const Attribute) callconv(.C) c_int, // bool 93 | }; 94 | 95 | /// The `pango.AttrShape` structure is used to represent attributes which 96 | /// impose shape restrictions. 97 | pub const AttrShape = extern struct { 98 | /// The common portion of the attribute. 99 | attr: Attribute, 100 | /// The ink rectangle to restrict to. 101 | ink_rect: pango.Rectangle, 102 | /// The logical rectangle to restrict to. 103 | logical_rect: pango.Rectangle, 104 | /// User data set (see `pango.AttrShape.newWithData()`) 105 | data: ?*anyopaque, 106 | /// Copy function for the user data. 107 | copy_func: pango.AttrDataCopyFunc, 108 | /// Destroy function for the user data. 109 | destroy_func: pango.GDestroyNotify, 110 | 111 | /// Create a new shape attribute. 112 | /// 113 | /// A shape is used to impose a particular ink and logical rectangle on the 114 | /// result of shaping a particular glyph. This might be used, for instance, 115 | /// for embedding a picture or a widget inside a `pango.Layout`. 116 | /// 117 | /// **Parameters** 118 | /// - `ink_rect`: ink rectangle to assign to each character 119 | /// - `logical_rect`: logical rectangle to assign to each character 120 | /// 121 | /// **Returns** 122 | /// 123 | /// the newly allocated `pango.Attribute`. 124 | /// 125 | /// **NOTE**: The caller owns the created `pango.Attribute` and should call 126 | /// `.destroy()` when done with it. You can use idiomatic Zig pattern 127 | /// with `defer`: 128 | /// ```zig 129 | /// const attr = try pango.AttrShape.new(); 130 | /// defer attr.destroy(); 131 | /// ``` 132 | pub fn new(ink_rect: *const pango.Rectangle, logical_rect: *const pango.Rectangle) !*Attribute { 133 | const ptr = c.pango_attr_shape_new(ink_rect, logical_rect) orelse return error.NullPointer; 134 | if (safety.tracing) try safety.markForLeakDetection(@returnAddress(), ptr); 135 | return ptr; 136 | } 137 | 138 | /// Creates a new shape attribute. 139 | /// 140 | /// Like `pango.AttrShape.new()`, but a user data pointer is also provided; 141 | /// this pointer can be accessed when later rendering the glyph. 142 | /// 143 | /// **Parameters** 144 | /// - `ink_rect`: ink rectangle to assign to each character 145 | /// - `logical_rect`: logical rectangle to assign to each character 146 | /// - `data`: user data pointer 147 | /// - `copy_func`: function to copy `data` when the attribute is copied; If 148 | /// `null`, data is simply copied as a pointer 149 | /// - `destroy_func`: function to free `data` when the attribute is freed 150 | /// 151 | /// **Returns** 152 | /// 153 | /// the newly allocated `pango.Attribute`. 154 | /// 155 | /// **NOTE**: The caller owns the created `pango.Attribute` and should call 156 | /// `.destroy()` when done with it. You can use idiomatic Zig pattern 157 | /// with `defer`: 158 | /// ```zig 159 | /// const attr = try pango.AttrShape.newWithData(...); 160 | /// defer attr.destroy(); 161 | /// ``` 162 | pub fn newWithData(ink_rect: *const pango.Rectangle, logical_rect: *const pango.Rectangle, data: ?*anyopaque, copy_func: pango.AttrDataCopyFunc, destroy_func: pango.GDestroyNotify) !*Attribute { 163 | // TODO: fix example 164 | const ptr = c.pango_attr_shape_new_with_data(ink_rect, logical_rect, data, copy_func, destroy_func) orelse return error.NullPointer; 165 | if (safety.tracing) try safety.markForLeakDetection(@returnAddress(), ptr); 166 | return ptr; 167 | } 168 | }; 169 | -------------------------------------------------------------------------------- /src/cairo/drawing/transformations.zig: -------------------------------------------------------------------------------- 1 | //! Transformations — Manipulating the current transformation matrix 2 | //! 3 | //! The current transformation matrix, *ctm*, is a two-dimensional affine 4 | //! transformation that maps all coordinates and other drawing instruments from 5 | //! the *user space* into the surface's canonical coordinate system, also known 6 | //! as the *device space*. 7 | //! 8 | //! [Link to Cairo manual](https://www.cairographics.org/manual/cairo-Transformations.html) 9 | 10 | const cairo = @import("../../cairo.zig"); 11 | const c = cairo.c; 12 | 13 | const Context = cairo.Context; 14 | const Matrix = cairo.Matrix; 15 | 16 | /// Modifies the current transformation matrix (CTM) by translating the 17 | /// user-space origin by `(tx, ty)`. This offset is interpreted as a 18 | /// user-space coordinate according to the CTM in place before the new call 19 | /// to `cr.translate()`. In other words, the translation of the user-space 20 | /// origin takes place after any existing transformation. 21 | /// 22 | /// **Parameters** 23 | /// - `tx`: amount to translate in the X direction 24 | /// - `ty`: amount to translate in the Y direction 25 | /// 26 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-Transformations.html#cairo-translate) 27 | pub fn translate(self: *Context, tx: f64, ty: f64) void { 28 | c.cairo_translate(self, tx, ty); 29 | } 30 | 31 | /// Modifies the current transformation matrix (CTM) by scaling the X and Y 32 | /// user-space axes by `sx` and `sy` respectively. The scaling of the axes 33 | /// takes place after any existing transformation of user space. 34 | /// 35 | /// **Parameters** 36 | /// - `sx`: scale factor for the X dimension 37 | /// - `sy`: scale factor for the Y dimension 38 | /// 39 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-Transformations.html#cairo-scale) 40 | pub fn scale(self: *Context, sx: f64, sy: f64) void { 41 | c.cairo_scale(self, sx, sy); 42 | } 43 | 44 | /// Modifies the current transformation matrix (CTM) by rotating the 45 | /// user-space axes by `angle` radians. The rotation of the axes takes 46 | /// places after any existing transformation of user space. The rotation 47 | /// direction for positive angles is from the positive X axis toward the 48 | /// positive Y axis. 49 | /// 50 | /// **Parameters** 51 | /// - `angle`: angle (in radians) by which the user-space axes will be 52 | /// rotated 53 | /// 54 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-Transformations.html#cairo-rotate) 55 | pub fn rotate(self: *Context, angle: f64) void { 56 | c.cairo_rotate(self, angle); 57 | } 58 | 59 | /// Modifies the current transformation `matrix` (CTM) by applying matrix 60 | /// as an additional transformation. The new transformation of user space 61 | /// takes place after any existing transformation. 62 | /// 63 | /// **Parameters** 64 | /// - `matrix`: a transformation to be applied to the user-space axes 65 | /// 66 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-Transformations.html#cairo-transform) 67 | pub fn transform(self: *Context, matrix: *const Matrix) void { 68 | c.cairo_transform(self, matrix); 69 | } 70 | 71 | /// Modifies the current transformation matrix (CTM) by setting it equal to 72 | /// `matrix`. 73 | /// 74 | /// **Parameters** 75 | /// - `matrix`: a transformation matrix from user space to device space 76 | /// 77 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-Transformations.html#cairo-set-matrix) 78 | pub fn setMatrix(self: *Context, matrix: *const Matrix) void { 79 | c.cairo_set_matrix(self, matrix); 80 | } 81 | 82 | /// Stores the current transformation matrix (CTM) into `matrix`. 83 | /// 84 | /// **Parameters** 85 | /// - `matrix`: return value for the matrix 86 | /// 87 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-Transformations.html#cairo-get-matrix) 88 | pub fn getMatrix(self: *Context, matrix: *Matrix) void { 89 | c.cairo_get_matrix(self, matrix); 90 | } 91 | 92 | /// Resets the current transformation matrix (CTM) by setting it equal to 93 | /// the identity matrix. That is, the user-space and device-space axes will 94 | /// be aligned and one user-space unit will transform to one device-space 95 | /// unit. 96 | /// 97 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-Transformations.html#cairo-identity-matrix) 98 | pub fn identityMatrix(self: *Context) void { 99 | c.cairo_identity_matrix(self); 100 | } 101 | 102 | /// Transform a coordinate from user space to device space by multiplying 103 | /// the given point by the current transformation matrix (CTM). 104 | /// 105 | /// **Parameters** 106 | /// - `x`: X value of coordinate (in/out parameter) 107 | /// - `y`: Y value of coordinate (in/out parameter) 108 | /// 109 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-Transformations.html#cairo-user-to-device) 110 | pub fn userToDevice(self: *Context, x: *f64, y: *f64) void { 111 | c.cairo_user_to_device(self, x, y); 112 | } 113 | 114 | /// Transform a distance vector from user space to device space. This 115 | /// function is similar to `cr.userToDevice()` except that the translation 116 | /// components of the CTM will be ignored when transforming `(dx, dy)`. 117 | /// 118 | /// **Parameters** 119 | /// - `x`: X component of a distance vector (in/out parameter) 120 | /// - `y`: Y component of a distance vector (in/out parameter) 121 | /// 122 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-Transformations.html#cairo-user-to-device-distance) 123 | pub fn userToDeviceDistance(self: *Context, dx: *f64, dy: *f64) void { 124 | c.cairo_user_to_device_distance(self, dx, dy); 125 | } 126 | 127 | /// Transform a coordinate from device space to user space by multiplying 128 | /// the given point by the inverse of the current transformation matrix 129 | /// (CTM). 130 | /// 131 | /// **Parameters** 132 | /// - `x`: X value of coordinate (in/out parameter) 133 | /// - `y`: Y value of coordinate (in/out parameter) 134 | /// 135 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-Transformations.html#cairo-device-to-user) 136 | pub fn deviceToUser(self: *Context, x: *f64, y: *f64) void { 137 | c.cairo_device_to_user(self, x, y); 138 | } 139 | 140 | /// Transform a distance vector from device space to user space. This 141 | /// function is similar to `cr.userToDevice()` except that the translation 142 | /// components of the inverse CTM will be ignored when transforming 143 | /// `(dx, dy)`. 144 | /// 145 | /// **Parameters** 146 | /// - `x`: X component of a distance vector (in/out parameter) 147 | /// - `y`: Y component of a distance vector (in/out parameter) 148 | /// 149 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-Transformations.html#cairo-device-to-user-distance) 150 | pub fn deviceToUserDistance(self: *Context, dx: *f64, dy: *f64) void { 151 | c.cairo_device_to_user_distance(self, dx, dy); 152 | } 153 | -------------------------------------------------------------------------------- /examples/pango_shape.zig: -------------------------------------------------------------------------------- 1 | //! Simple example to use pangocairo to render rotated text. 2 | //! https://gitlab.gnome.org/GNOME/pango/-/blob/main/examples/cairoshape.c 3 | const std = @import("std"); 4 | const pi = std.math.pi; 5 | const cos = std.math.cos; 6 | const cairo = @import("cairo"); 7 | const pango = @import("pango"); 8 | 9 | const BULLET = "•"; 10 | 11 | const text = 12 | \\The GNOME project provides two things: 13 | \\ 14 | \\ • The GNOME desktop environment 15 | \\ • The GNOME development platform 16 | \\ • Planet GNOME 17 | ; 18 | 19 | const MiniSvg = struct { 20 | width: f64, 21 | height: f64, 22 | path: [:0]const u8, 23 | }; 24 | 25 | var GnomeFootLogo = MiniSvg{ 26 | .width = 96.2152, 27 | .height = 118.26, 28 | .path = "M 86.068,1 C 61.466,0 56.851,35.041 70.691,35.041 C 84.529,35.041 110.671,0 86.068,0 z " ++ 29 | "M 45.217,30.699 C 52.586,31.149 60.671,2.577 46.821,4.374 C 32.976,6.171 37.845,30.249 45.217,30.699 z " ++ 30 | "M 11.445,48.453 C 16.686,46.146 12.12,23.581 3.208,29.735 C -5.7,35.89 6.204,50.759 11.445,48.453 z " ++ 31 | "M 26.212,36.642 C 32.451,35.37 32.793,9.778 21.667,14.369 C 10.539,18.961 19.978,37.916 26.212,36.642 L 26.212,36.642 z " ++ 32 | "M 58.791,93.913 C 59.898,102.367 52.589,106.542 45.431,101.092 C 22.644,83.743 83.16,75.088 79.171,51.386 C 75.86,31.712 15.495,37.769 8.621,68.553 C 3.968,89.374 27.774,118.26 52.614,118.26 C 64.834,118.26 78.929,107.226 81.566,93.248 C 83.58,82.589 57.867,86.86 58.791,93.913 L 58.791,93.913 z ", 33 | }; 34 | 35 | fn readFloatPair(reader: anytype, x: *f64, y: *f64) !void { 36 | var floatBuf: [10]u8 = undefined; 37 | const f1 = try reader.readUntilDelimiter(&floatBuf, ','); 38 | x.* = try std.fmt.parseFloat(f64, f1); 39 | const f2 = try reader.readUntilDelimiter(&floatBuf, ' '); 40 | y.* = try std.fmt.parseFloat(f64, f2); 41 | } 42 | 43 | fn miniSvgRender(shape: *const MiniSvg, cr: *cairo.Context, do_path: bool) !void { 44 | const point = cr.getCurrentPoint(); 45 | var x: f64 = point.x; 46 | var y: f64 = point.y; 47 | cr.translate(x, y); 48 | 49 | var p = std.io.fixedBufferStream(shape.path); 50 | const reader = p.reader(); 51 | var op: u8 = undefined; 52 | 53 | while (p.pos != p.buffer.len) { 54 | op = try reader.readByte(); 55 | if (!try reader.isBytes(" ")) return error.MalformedMiniSvg; 56 | switch (op) { 57 | 'M' => { 58 | try readFloatPair(reader, &x, &y); 59 | cr.moveTo(x, y); 60 | }, 61 | 'L' => { 62 | try readFloatPair(reader, &x, &y); 63 | cr.lineTo(x, y); 64 | }, 65 | 'C' => { 66 | var curve: [6]f64 = undefined; 67 | try readFloatPair(reader, &curve[0], &curve[1]); 68 | try readFloatPair(reader, &curve[2], &curve[3]); 69 | try readFloatPair(reader, &curve[4], &curve[5]); 70 | cr.curveTo(curve[0], curve[1], curve[2], curve[3], curve[4], curve[5]); 71 | }, 72 | 'z' => cr.closePath(), 73 | else => std.debug.panic("Invalid MiniSvg operation '{c}'\n", .{op}), 74 | } 75 | } 76 | if (!do_path) cr.fill(); 77 | } 78 | 79 | fn miniSvgShapeRenderer(cr: *cairo.Context, attr: *pango.AttrShape, do_path: c_int, _: ?*anyopaque) callconv(.C) void { 80 | const shape: *MiniSvg = @alignCast(@ptrCast(attr.data.?)); 81 | var scale_x = @as(f64, @floatFromInt(attr.ink_rect.width)) / (pango.SCALE * shape.width); 82 | var scale_y = @as(f64, @floatFromInt(attr.ink_rect.height)) / (pango.SCALE * shape.height); 83 | cr.relMoveTo( 84 | @as(f64, @floatFromInt(attr.ink_rect.x)) / pango.SCALE, 85 | @as(f64, @floatFromInt(attr.ink_rect.y)) / pango.SCALE, 86 | ); 87 | cr.scale(scale_x, scale_y); 88 | miniSvgRender(shape, cr, do_path != 0) catch unreachable; 89 | } 90 | 91 | fn getLayout(cr: *cairo.Context) !*pango.Layout { 92 | const ink_rect = pango.Rectangle.init(1 * pango.SCALE, -11 * pango.SCALE, 8 * pango.SCALE, 10 * pango.SCALE); 93 | const logical_rect = pango.Rectangle.init(0 * pango.SCALE, -12 * pango.SCALE, 10 * pango.SCALE, 12 * pango.SCALE); 94 | 95 | // Create a PangoLayout, set the font and text 96 | const layout: *pango.Layout = try cr.createLayout(); 97 | const pango_context = try layout.getContext(); 98 | pango_context.setShapeRenderer(@ptrCast(&miniSvgShapeRenderer), null, null); 99 | layout.setText(text); 100 | 101 | const attrs = try pango.AttrList.create(); 102 | defer attrs.destroy(); 103 | 104 | // Set gnome shape attributes for all bullets 105 | var p: []const u8 = text; 106 | var offset: usize = 0; 107 | while (std.mem.indexOf(u8, p, BULLET)) |index| { 108 | offset += index; 109 | const end = offset + BULLET.len; 110 | const attr = try pango.AttrShape.newWithData( 111 | &ink_rect, 112 | &logical_rect, 113 | &GnomeFootLogo, 114 | null, 115 | null, 116 | ); 117 | 118 | attr.start_index = @intCast(offset); 119 | attr.end_index = @intCast(end); 120 | attrs.insert(attr); 121 | offset = end; 122 | p = text[offset..]; 123 | } 124 | 125 | layout.setAttributes(attrs); 126 | return layout; 127 | } 128 | 129 | fn drawText(cr: *cairo.Context, width: ?*i32, height: ?*i32) !void { 130 | const layout = try getLayout(cr); 131 | 132 | // Adds a fixed 10-pixel margin on the sides. 133 | if (width != null or height != null) { 134 | layout.getPixelSize(width, height); 135 | if (width) |w| w.* += 20; 136 | if (height) |h| h.* += 20; 137 | } 138 | 139 | cr.moveTo(10, 10); 140 | cr.showLayout(layout); 141 | 142 | layout.destroy(); 143 | } 144 | 145 | pub fn main() !void { 146 | var surface: *cairo.ImageSurface = undefined; 147 | var context: *cairo.Context = undefined; 148 | var width: i32 = undefined; 149 | var height: i32 = undefined; 150 | 151 | // First create and use a 0x0 surface, to measure how large the final 152 | // surface needs to be 153 | surface = try cairo.ImageSurface.create(.argb32, 0, 0); 154 | context = try cairo.Context.create(surface.asSurface()); 155 | try drawText(context, &width, &height); 156 | context.destroy(); 157 | surface.destroy(); 158 | 159 | // Now create the final surface and draw to it 160 | surface = try cairo.ImageSurface.create(.argb32, @intCast(width), @intCast(height)); 161 | defer surface.destroy(); 162 | context = try cairo.Context.create(surface.asSurface()); 163 | defer context.destroy(); 164 | 165 | context.setSourceRgb(1, 1, 1); 166 | context.paint(); 167 | context.setSourceRgb(0, 0, 0.5); 168 | try drawText(context, null, null); 169 | 170 | // Write out the surface as PNG 171 | try surface.writeToPng("examples/generated/pango_shape.png"); 172 | } 173 | -------------------------------------------------------------------------------- /src/cairo/surfaces/win32.zig: -------------------------------------------------------------------------------- 1 | //! Win32 Surfaces — Microsoft Windows surface support 2 | 3 | const safety = @import("safety"); 4 | 5 | const cairo = @import("../../cairo.zig"); 6 | const c = cairo.c; 7 | const CairoError = cairo.CairoError; 8 | 9 | const Mixin = @import("base.zig").Base; 10 | 11 | /// The Microsoft Windows surface is used to render cairo graphics to Microsoft 12 | /// Windows windows, bitmaps, and printing device contexts. 13 | /// 14 | /// The surface returned by `cairo.Win32Surface.createPrintingSurface()` is of 15 | /// surface type `cairo.Surface.Type.Win32Printing` and is a multi-page vector 16 | /// surface type. 17 | /// 18 | /// The surface returned by the other win32 constructors is of surface type 19 | /// `cairo.Surface.Type.Win32` and is a raster surface type. 20 | pub const Win32Surface = opaque { 21 | pub usingnamespace Mixin(@This()); 22 | 23 | /// Creates a cairo surface that targets the given DC. The DC will be 24 | /// queried for its initial clip extents, and this will be used as the size 25 | /// of the cairo surface. The resulting surface will always be of format 26 | /// `cairo.Surface.Format.rgb24`; should you need another surface format, 27 | /// you will need to create one through 28 | /// `cairo.Win32Surface.createWithFormat()` or 29 | /// `cairo.Win32Surface.createWithDib()`. 30 | /// 31 | /// **Parameters** 32 | /// - `hdc`: the DC to create a surface for. 33 | /// 34 | /// **Returns** 35 | /// 36 | /// the newly created surface 37 | pub fn create(hdc: *anyopaque) CairoError!*Win32Surface { 38 | const ptr = c.cairo_win32_surface_create(hdc) orelse return CairoError.Win32GdiError; 39 | try ptr.status().toErr(); 40 | if (safety.tracing) try safety.markForLeakDetection(@returnAddress(), ptr); 41 | return ptr; 42 | } 43 | 44 | /// Creates a device-independent-bitmap surface not associated with any 45 | /// particular existing surface or device context. The created bitmap will 46 | /// be uninitialized. 47 | /// 48 | /// **Parameters** 49 | /// - `format`: format of pixels in the surface to create 50 | /// - `width`: width of the surface, in pixels 51 | /// - `height`: height of the surface, in pixels 52 | /// 53 | /// **Returns** 54 | /// 55 | /// the newly created surface. 56 | pub fn createWithDib(format: cairo.Surface.Format, width: i32, height: i32) CairoError!*Win32Surface { 57 | const ptr = c.cairo_win32_surface_create_with_dib(format, @intCast(width), @intCast(height)) orelse return CairoError.Win32GdiError; 58 | try ptr.status().toErr(); 59 | if (safety.tracing) try safety.markForLeakDetection(@returnAddress(), ptr); 60 | return ptr; 61 | } 62 | 63 | /// Creates a device-dependent-bitmap surface not associated with any 64 | /// particular existing surface or device context. The created bitmap will 65 | /// be uninitialized. 66 | /// 67 | /// **Parameters** 68 | /// - `hdc: a DC compatible with the surface to create 69 | /// - `format`: format of pixels in the surface to create 70 | /// - `width`: width of the surface, in pixels 71 | /// - `height`: height of the surface, in pixels 72 | /// 73 | /// **Returns** 74 | /// 75 | /// the newly created surface. 76 | pub fn createWithDdb(hdc: ?*anyopaque, format: cairo.Surface.Format, width: i32, height: i32) CairoError!*Win32Surface { 77 | const ptr = c.cairo_win32_surface_create_with_ddb(hdc, format, @intCast(width), @intCast(height)) orelse return CairoError.Win32GdiError; 78 | try ptr.status().toErr(); 79 | if (safety.tracing) try safety.markForLeakDetection(@returnAddress(), ptr); 80 | return ptr; 81 | } 82 | 83 | /// Creates a cairo surface that targets the given DC. The DC will be 84 | /// queried for its initial clip extents, and this will be used as the size 85 | /// of the cairo surface. 86 | /// 87 | /// Supported formats are: `cairo.Surface.Format.argb32`, 88 | /// `cairo.Surface.Format.rgb24`. 89 | /// 90 | /// Note: `format` only tells cairo how to draw on the surface, not what 91 | /// the format of the surface is. Namely, cairo does not (and cannot) check 92 | /// that hdc actually supports alpha-transparency. 93 | /// 94 | /// **Parameters** 95 | /// - `hdc: the DC to create a surface for 96 | /// - `format`: format of pixels in the surface to create 97 | /// 98 | /// **Returns** 99 | /// 100 | /// the newly created surface. 101 | pub fn createWithFormat(hdc: ?*anyopaque, format: cairo.Surface.Format) CairoError!*Win32Surface { 102 | const ptr = c.cairo_win32_surface_create_with_format(hdc, format) orelse return CairoError.Win32GdiError; 103 | try ptr.status().toErr(); 104 | if (safety.tracing) try safety.markForLeakDetection(@returnAddress(), ptr); 105 | return ptr; 106 | } 107 | 108 | /// Creates a cairo surface that targets the given DC. The DC will be 109 | /// queried for its initial clip extents, and this will be used as the size 110 | /// of the cairo surface. The DC should be a printing DC; antialiasing will 111 | /// be ignored, and GDI will be used as much as possible to draw to the 112 | /// surface. 113 | /// 114 | /// The returned surface will be wrapped using the paginated surface to 115 | /// provide correct complex rendering behaviour; `cairo.Surface.showPage()` 116 | /// and associated methods must be used for correct output. 117 | /// 118 | /// **Parameters** 119 | /// - `hdc`: the DC to create a surface for. 120 | /// 121 | /// **Returns** 122 | /// 123 | /// the newly created surface 124 | pub fn createPrintingSurface(hdc: *anyopaque) CairoError!*Win32Surface { 125 | const ptr = c.cairo_win32_printing_surface_create(hdc) orelse return CairoError.Win32GdiError; 126 | try ptr.status().toErr(); 127 | if (safety.tracing) try safety.markForLeakDetection(@returnAddress(), ptr); 128 | return ptr; 129 | } 130 | 131 | /// Returns the HDC associated with this surface, or `null` if none. 132 | /// 133 | /// A call to `cairo.Surface.flush()` is required before using the HDC to 134 | /// ensure that all pending drawing operations are finished and to restore 135 | /// any temporary modification cairo has made to its state. A call to 136 | /// `cairo.Surface.markDirty()` is required after the state or the content 137 | /// of the HDC has been modified. 138 | /// 139 | /// **Returns** 140 | /// 141 | /// HDC or `null` if no HDC available. 142 | pub fn getDc(self: *Win32Surface) ?*anyopaque { 143 | return c.cairo_win32_surface_get_dc(self); 144 | } 145 | 146 | /// Returns a `cairo.ImageSurface` that refers to the same bits as the DIB 147 | /// of the Win32 surface. 148 | /// 149 | /// **Returns** 150 | /// 151 | /// a `cairo.ImageSurface` (owned by the `cairo.Win32Surface`), or 152 | /// `error.SurfaceTypeMismatch` if the win32 surface is not a DIB. 153 | pub fn getImage(self: *Win32Surface) CairoError!*cairo.ImageSurface { 154 | return c.cairo_win32_surface_get_image(self) orelse CairoError.SurfaceTypeMismatch; 155 | } 156 | }; 157 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # giza 2 | 3 | An attempt to make a binding for the popular [cairo](https://gitlab.freedesktop.org/cairo/cairo) and [pango](https://gitlab.gnome.org/GNOME/pango) graphics libraries with some idiomatic Zig elements (and some that will make true Zig enjoyers' skin crawl). 4 | 5 | Many thanks to [jackdbd](https://github.com/jackdbd) for his [implementation](https://github.com/jackdbd/zig-cairo) of similar binding. 6 | 7 | Zig version is 0.11.0. 8 | 9 | ## Quick start 10 | 11 | 1. Add giza as a dependency in your build.zig.zon as follows: 12 | 13 | ```diff 14 | .{ 15 | .name = "your-project", 16 | .version = "1.0.0", 17 | .dependencies = .{ 18 | + .giza = .{ 19 | + .url = "https://github.com/koenigskraut/giza/archive/refs/tags/0.1.0.tar.gz", 20 | + .hash = "12202e1b6ae20694324cef241beacaa745ee9e2611cda002830bb0ee681791970ffd", 21 | + }, 22 | }, 23 | } 24 | ``` 25 | 26 | 2. In your build.zig add giza as a dependency and attach its modules to your project: 27 | 28 | ```diff 29 | const std = @import("std"); 30 | 31 | pub fn build(b: *std.Build) void { 32 | const target = b.standardTargetOptions(.{}); 33 | const optimize = b.standardOptimizeOption(.{}); 34 | 35 | + const opts = .{ .target = target, .optimize = optimize }; 36 | + const dep = b.dependency("giza", opts); 37 | + 38 | + const cairo_module = dep.module("cairo"); 39 | + const pango_module = dep.module("pango"); 40 | + const pangocairo_module = dep.module("pangocairo"); 41 | 42 | const exe = b.addExecutable(.{ 43 | .name = "test", 44 | .root_source_file = .{ .path = "src/main.zig" }, 45 | .target = target, 46 | .optimize = optimize, 47 | }); 48 | + exe.addModule("cairo", cairo_module); 49 | + exe.addModule("pango", pango_module); 50 | + exe.addModule("pangocairo", pangocairo_module); 51 | + exe.linkSystemLibrary("pangocairo"); // if you need both cairo and pango, use this 52 | exe.install(); 53 | 54 | ... 55 | } 56 | ``` 57 | 58 | Then you can check cairo website for reference, [samples](https://www.cairographics.org/samples/) section in particular. You will find ports of these snippets to Zig along with some other examples in giza [examples](https://github.com/koenigskraut/giza/blob/master/examples). 59 | 60 | ## Killer features 61 | 62 | There are just the two of them, but what are these two! 63 | 64 | ### Safety checks 65 | 66 | **cairo** sometimes returns allocated objects with user-managed lifetime, take for example: 67 | ```zig 68 | const surface = try cairo.ImageSurface.create(.argb32, 600, 400); // cairo.ImageSurface with alpha 600x400px 69 | ``` 70 | `surface` is owned by user and should be destroyed at some point with `surface.destroy()`. It can be done nicely with Zig `defer` pattern: 71 | ```zig 72 | const surface = try cairo.ImageSurface.create(.argb32, 600, 400); 73 | defer surface.destroy(); 74 | ``` 75 | But what if we can't do this? Let's see some example: 76 | ```zig 77 | const cairo = @import("cairo"); 78 | 79 | test "safety" { 80 | const surface = try cairo.ImageSurface.create(.argb32, 600, 400); 81 | _ = surface; // surface should be destroyed somewhere else, but we forgot 82 | } 83 | ``` 84 | Run it: 85 | ```bash 86 | $ zig build test 87 | run test: error: [giza] (err): Leak detected! ImageSurface@1cc4470 leaked: 88 | /some/path/to/safety_test.zig:4:50: 0x235821 in test.safety (test) 89 | const surface = try cairo.ImageSurface.create(.argb32, 600, 400); 90 | ^ 91 | ... 92 | Build Summary: 1/3 steps succeeded; 1 failed; 1/1 tests passed (disable with --summary none) 93 | test transitive failure 94 | └─ run test failure 95 | ``` 96 | Yes, **giza** detected a leak, printed stacktrace and panicked at return of the program, just like if it was a native Zig allocator leak! 97 | 98 | Since we are linking libc anyway, I just interject some pointer counting in `create()`/`.reference()`/`.destroy()` functions and check for leaks with `atexit()` at the end. This happens only in debug and shouldn't be a performance issue then. 99 | 100 | ### `opaque` types 101 | 102 | We have a C object, but what we want is a native one. Popular pattern: pointer to C object is stored as a field of the native struct/class and passed under the hood to C functions. That's fine, but can we do better? 103 | 104 | Thanks to Zig's easy C interop, in **giza** there is no such pattern. All C objects behave like native ones. What are advantages and should you care? Let's see. 105 | 106 | Every function in **giza**, that returns object with `.status()` method **and** could set it into an error state, checks its status and raises error immediately. It's not always the intended behavoir: **cairo** safely allows errors to propagate in these cases, plus it's overhead. 107 | 108 | So for example let's create `ImageSurface` unsafely and manage it manually: 109 | ```zig 110 | const std = @import("std"); 111 | const cairo = @import("cairo"); 112 | const c = @cImport(@cInclude("cairo.h")); 113 | 114 | test "interop 1" { 115 | const surface_c = c.cairo_image_surface_create_from_png("does_not_exist.png").?; // cairo usually don't return null objects 116 | defer c.cairo_surface_destroy(surface_c); 117 | 118 | const image: *cairo.ImageSurface = @ptrCast(surface_c); 119 | try std.testing.expect(image.status() == .FileNotFound); 120 | } 121 | ``` 122 | We can use prepared C functions from `cairo` module to avoid pointer casting: 123 | ```zig 124 | test "interop 2" { 125 | const image = cairo_image_surface_create_from_png("does_not_exist.png").?; 126 | defer cairo.c.cairo_surface_destroy(image); 127 | try std.testing.expect(image.status() == .FileNotFound); 128 | } 129 | ``` 130 | **giza**'s matrix functions return matrices by value: 131 | ```zig 132 | var m = cairo.Matrix.identity(); 133 | ``` 134 | `m` is `cairo.Matrix` which is an `extern struct` of 6 `f64` fields. But if you want, you can use underlying C function directly: 135 | ```zig 136 | const cairo = @import("cairo"); 137 | const c = @cImport(@cInlude("cairo.h")); 138 | 139 | test "interop 3" { 140 | var m: cairo.Matrix = undefined; 141 | c.cairo_matrix_init_identity(@ptrCast(&m)); 142 | ... 143 | } 144 | ``` 145 | or with `giza`'s prepared `extern fn`s: 146 | ```zig 147 | test "interop 4" { 148 | var m: cairo.Matrix = undefined; 149 | cairo.c.cairo_matrix_init_identity(&m); 150 | ... 151 | } 152 | ``` 153 | Every C object in **giza** is a valid Zig `opaque`/`extern struct`/`enum`, so you can call C functions with them like nothing yourself! 154 | 155 | ## Coverage and progress 156 | 157 | Cairo is somewhat covered (info [here](https://github.com/koenigskraut/giza/blob/master/coverage_cairo.md)), current progress is 80.6% regarding **cairo** its functionality, __*but*__ it should work already. The only missing parts are font support for FreeType/Windows etc. and real devices (other than script one). If you only need cairo to write some PNG/PDF/SVG files, that is already covered: see [examples](https://github.com/koenigskraut/giza/tree/master/examples). Further progress is planned, but that would require using header files, which this wrapping has avoided so far, so we'll see. 158 | 159 | Pango support is really lacking for now, but there is basic support (see [this](https://github.com/koenigskraut/giza/blob/master/examples/pango_simple.zig) and [this](https://github.com/koenigskraut/giza/blob/master/examples/pango_shape.zig)). 160 | -------------------------------------------------------------------------------- /src/pango/enums.zig: -------------------------------------------------------------------------------- 1 | /// `pango.Alignment` describes how to align the lines of a `pango.Layout` 2 | /// within the available space. 3 | /// 4 | /// If the `pango.Layout` is set to justify using `pango.Layout.setJustify()`, 5 | /// this only affects partial lines. 6 | /// 7 | /// See `pango.Layout.setAutoDir()` for how text direction affects the 8 | /// interpretation of `pango.Alignment` values. 9 | pub const Alignment = enum(c_uint) { 10 | /// Put all available space on the right. 11 | Left, 12 | /// Center the line within the available space. 13 | Center, 14 | /// Put all available space on the left. 15 | Right, 16 | }; 17 | 18 | /// `pango.Direction` represents a direction in the Unicode bidirectional 19 | /// algorithm. 20 | /// 21 | /// Not every value in this enumeration makes sense for every usage of 22 | /// `pango.Direction`; for example, the return value of 23 | /// `pango_unichar_direction()` and `pango_find_base_dir()` cannot be 24 | /// `.WeakLtr` or `.WeakRtl`, since every character is either neutral or has a 25 | /// strong direction; on the other hand `.Neutral` doesn’t make sense to pass 26 | /// to `pango_itemize_with_base_dir()`. 27 | /// 28 | /// The `.TtbLtr`, `.TtbRtl` values come from an earlier interpretation of this 29 | /// enumeration as the writing direction of a block of text and are no longer 30 | /// used. See `pango.Gravity` for how vertical text is handled in Pango. 31 | /// 32 | /// If you are interested in text direction, you should really use fribidi 33 | /// directly. `pango.Direction` is only retained because it is used in some 34 | /// public apis. 35 | pub const Direction = enum(c_uint) { 36 | // TODO: fix desc 37 | ///A strong left-to-right direction. 38 | Ltr, 39 | /// A strong right-to-left direction. 40 | Rtl, 41 | /// Deprecated value; treated the same as `.Rtl`. 42 | TtbLtr, 43 | /// Deprecated value; treated the same as `.Ltr`. 44 | TtbRtl, 45 | /// A weak left-to-right direction. 46 | WeakLtr, 47 | /// A weak right-to-left direction. 48 | WeakRtl, 49 | /// No direction specified. 50 | Neutral, 51 | }; 52 | 53 | /// `pango.EllipsizeMode` describes what sort of ellipsization should be 54 | /// applied to text. 55 | /// 56 | /// In the ellipsization process characters are removed from the text in order 57 | /// to make it fit to a given width and replaced with an ellipsis. 58 | pub const EllipsizeMode = enum(c_uint) { 59 | /// No ellipsization. 60 | None, 61 | /// Omit characters at the start of the text. 62 | Start, 63 | /// Omit characters in the middle of the text. 64 | Middle, 65 | /// Omit characters at the end of the text. 66 | End, 67 | }; 68 | 69 | /// `pango.Gravity` represents the orientation of glyphs in a segment of text. 70 | /// 71 | /// This is useful when rendering vertical text layouts. In those situations, 72 | /// the layout is rotated using a non-identity `pango.Matrix`, and then glyph 73 | /// orientation is controlled using `pango.Gravity`. 74 | /// 75 | /// Not every value in this enumeration makes sense for every usage of 76 | /// `pango.Gravity`; for example, `.Auto` only can be passed to 77 | /// `pango.Context.setBaseGravity()` and can only be returned by 78 | /// `pango.Context.getBaseGravity()`. 79 | /// 80 | /// See also: `pango.GravityHint`. 81 | pub const Gravity = enum(c_uint) { 82 | /// Glyphs stand upright (default). 83 | South, 84 | /// Glyphs are rotated 90 degrees counter-clockwise. 85 | East, 86 | /// Glyphs are upside-down. 87 | North, 88 | /// Glyphs are rotated 90 degrees clockwise. 89 | West, 90 | /// Gravity is resolved from the context matrix. 91 | Auto, 92 | 93 | // pub fn getForMatrix() 94 | }; 95 | 96 | /// `pango.GravityHint` defines how horizontal scripts should behave in a 97 | /// vertical context. 98 | /// 99 | /// That is, English excerpts in a vertical paragraph for example. 100 | /// 101 | /// See also `pango.Gravity` 102 | pub const GravityHint = enum(c_uint) { 103 | /// Scripts will take their natural gravity based on the base gravity and 104 | /// the script. This is the default. 105 | Natural, 106 | /// Always use the base gravity set, regardless of the script. 107 | Strong, 108 | /// For scripts not in their natural direction (eg. Latin in East gravity), 109 | /// choose per-script gravity such that every script respects the line 110 | /// progression. This means, Latin and Arabic will take opposite gravities 111 | /// and both flow top-to-bottom for example. 112 | Line, 113 | }; 114 | 115 | /// An enumeration specifying the width of the font relative to other designs 116 | /// within a family. 117 | pub const Stretch = enum(c_uint) { 118 | /// Ultra condensed width. 119 | UltraCondensed, 120 | /// Extra condensed width. 121 | ExtraCondensed, 122 | /// Condensed width. 123 | Condensed, 124 | /// Semi condensed width. 125 | SemiCondensed, 126 | /// The normal width. 127 | Normal, 128 | /// Semi expanded width. 129 | SemiExpanded, 130 | /// Expanded width. 131 | Expanded, 132 | /// Extra expanded width. 133 | ExtraExpanded, 134 | /// Ultra expanded width. 135 | UltraExpanded, 136 | }; 137 | 138 | /// An enumeration specifying the various slant styles possible for a font. 139 | pub const Style = enum(c_uint) { 140 | /// The font is upright. 141 | Normal, 142 | /// The font is slanted, but in a roman style. 143 | Oblique, 144 | /// The font is slanted in an italic style. 145 | Italic, 146 | }; 147 | 148 | /// An enumeration specifying capitalization variant of the font. 149 | pub const Variant = enum(c_uint) { 150 | /// A normal font. 151 | Normal, 152 | /// A font with the lower case characters replaced by smaller variants of 153 | /// the capital characters. 154 | SmallCaps, 155 | /// A font with all characters replaced by smaller variants of the capital 156 | /// characters. 157 | AllSmallCaps, 158 | /// A font with the lower case characters replaced by smaller variants of 159 | /// the capital characters. Petite Caps can be even smaller than Small Caps. 160 | PetiteCaps, 161 | /// A font with all characters replaced by smaller variants of the capital 162 | /// characters. Petite Caps can be even smaller than Small Caps. 163 | AllPetiteCaps, 164 | /// A font with the upper case characters replaced by smaller variants of 165 | /// the capital letters. 166 | Unicase, 167 | /// A font with capital letters that are more suitable for all-uppercase 168 | /// titles. 169 | TitleCaps, 170 | }; 171 | 172 | /// An enumeration specifying the weight (boldness) of a font. 173 | /// 174 | /// Weight is specified as a numeric value ranging from 100 to 1000. This 175 | /// enumeration simply provides some common, predefined values. 176 | pub const Weight = enum(c_uint) { 177 | /// The thin weight. 178 | thin = 100, 179 | /// The ultralight weight. 180 | ultralight = 200, 181 | /// The light weight. 182 | ligth = 300, 183 | /// The semilight weight. 184 | semilight = 350, 185 | /// The book weight. 186 | book = 380, 187 | /// The default weight. 188 | normal = 400, 189 | /// The medium weight. 190 | medium = 500, 191 | /// The semibold weight. 192 | semibold = 600, 193 | /// The bold weight. 194 | bold = 700, 195 | /// The ultrabold weight. 196 | Ultrabold = 800, 197 | /// The heavy weight. 198 | heavy = 900, 199 | /// The ultraheavy weight. 200 | ultraheavy = 1000, 201 | }; 202 | 203 | /// `pango.WrapMode` describes how to wrap the lines of a `pango.Layout` to the 204 | /// desired width. 205 | /// 206 | /// For `pango.WrapMode.Word`, Pango uses break opportunities that are 207 | /// determined by the Unicode line breaking algorithm. For `.Char`, Pango 208 | /// allows breaking at grapheme boundaries that are determined by the Unicode 209 | /// text segmentation algorithm. 210 | pub const WrapMode = enum(c_uint) { 211 | /// Wrap lines at word boundaries. 212 | Word, 213 | /// Wrap lines at character boundaries. 214 | Char, 215 | /// Wrap lines at word boundaries, but fall back to character boundaries if 216 | /// there is not enough space for a full word. 217 | WordChar, 218 | }; 219 | -------------------------------------------------------------------------------- /src/cairo/surfaces/script.zig: -------------------------------------------------------------------------------- 1 | //! Script Surfaces — Rendering to replayable scripts 2 | //! 3 | //! The script surface provides the ability to render to a native script that 4 | //! matches the cairo drawing model. The scripts can be replayed using tools 5 | //! under the util/cairo-script directory, or with cairo-perf-trace. 6 | 7 | const cairo = @import("../../cairo.zig"); 8 | const c = cairo.c; 9 | const safety = @import("safety"); 10 | 11 | const CairoError = cairo.CairoError; 12 | const Content = cairo.Content; 13 | const Device = cairo.Device; 14 | const RecordingSurface = cairo.RecordingSurface; 15 | const Status = cairo.Status; 16 | const Surface = cairo.Surface; 17 | const WriteFn = cairo.WriteFn; 18 | 19 | const SurfaceMixin = @import("base.zig").Base; 20 | const DeviceMixin = @import("../device.zig").Base; 21 | 22 | /// An instance of `cairo.Device` 23 | pub const Script = opaque { 24 | pub usingnamespace DeviceMixin(@This()); 25 | 26 | /// A set of script output variants. 27 | /// 28 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-Script-Surfaces.html#cairo-script-mode-t) 29 | pub const Mode = enum(c_uint) { 30 | // the output will be in readable text (default). 31 | Ascii, 32 | /// the output will use byte codes. 33 | Binary, 34 | }; 35 | 36 | /// Creates a output device for emitting the script, used when creating the 37 | /// individual surfaces. 38 | /// 39 | /// **Parameters** 40 | /// - `filename`: the name (path) of the file to write the script to 41 | /// 42 | /// **Returns** 43 | /// 44 | /// a pointer to the newly created device. 45 | /// 46 | /// **NOTE**: The caller owns the created device and should call 47 | /// `device.destroy()` when done with it. You can use idiomatic Zig pattern 48 | /// with `defer`: 49 | /// ```zig 50 | /// const device = try cairo.Script.create("script.txt"); 51 | /// defer device.destroy(); 52 | /// ``` 53 | /// 54 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-Script-Surfaces.html#cairo-script-create) 55 | pub fn create(filename: [:0]const u8) CairoError!*Script { 56 | const device = c.cairo_script_create(filename).?; 57 | try device.status().toErr(); 58 | if (safety.tracing) try safety.markForLeakDetection(@returnAddress(), device); 59 | return device; 60 | } 61 | 62 | /// Creates a output device for emitting the script, used when creating the 63 | /// individual surfaces. 64 | /// 65 | /// **Parameters** 66 | /// - `writer`: pointer to a writeable object, e.g. `std.io.Writer` or 67 | /// `std.fs.File` (it should have a `.writeAll()` function) 68 | /// 69 | /// **Returns** 70 | /// 71 | /// a pointer to the newly created device. 72 | /// 73 | /// **NOTE**: The caller owns the created device and should call 74 | /// `device.destroy()` when done with it. You can use idiomatic Zig pattern 75 | /// with `defer`: 76 | /// ```zig 77 | /// var file = try std.fs.cwd().createFile("script.txt"); 78 | /// const device = try cairo.Script.createForStream(&file); 79 | /// defer device.destroy(); 80 | /// ``` 81 | /// 82 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-Script-Surfaces.html#cairo-script-create-for-stream) 83 | pub fn createForStream(writer: anytype) CairoError!*Script { 84 | const writeFn = cairo.createWriteFn(writer); 85 | const device = c.cairo_script_create_for_stream(writeFn, writer).?; 86 | try device.status().toErr(); 87 | if (safety.tracing) try safety.markForLeakDetection(@returnAddress(), device); 88 | return device; 89 | } 90 | 91 | /// Converts the record operations in `recording_surface` into a script. 92 | /// 93 | /// **Parameters** 94 | /// - `recording_surface`: the recording surface to replay 95 | /// 96 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-Script-Surfaces.html#cairo-script-from-recording-surface) 97 | pub fn fromRecordingSurface(self: *Script, recording_surface: *RecordingSurface) CairoError!void { 98 | return c.cairo_script_from_recording_surface(self, recording_surface).toErr(); 99 | } 100 | 101 | /// Change the output mode of the script. 102 | /// 103 | /// **Parameters** 104 | /// - `mode`: the new mode 105 | /// 106 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-Script-Surfaces.html#cairo-script-set-mode) 107 | pub fn setMode(self: *Script, mode: Script.Mode) void { 108 | c.cairo_script_set_mode(self, mode); 109 | } 110 | 111 | /// Queries the script for its current output mode. 112 | /// 113 | /// **Returns** 114 | /// 115 | /// the current output mode of the script. 116 | /// 117 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-Script-Surfaces.html#cairo-script-get-mode) 118 | pub fn getMode(self: *Script) Script.Mode { 119 | return c.cairo_script_get_mode(self); 120 | } 121 | 122 | /// Emit a string verbatim into the script. 123 | /// 124 | /// **Parameters** 125 | /// - `comment`: the string to emit 126 | /// 127 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-Script-Surfaces.html#cairo-script-write-comment) 128 | pub fn writeComment(self: *Script, comment: []const u8) void { 129 | c.cairo_script_write_comment(self, comment.ptr, @intCast(comment.len)); 130 | } 131 | }; 132 | 133 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-Script-Surfaces.html) 134 | pub const ScriptSurface = opaque { 135 | pub usingnamespace SurfaceMixin(@This()); 136 | 137 | /// Create a new surface that will emit its rendering through `script`. 138 | /// 139 | /// **Parameters** 140 | /// - `script`: the script (output device) 141 | /// - `content`: the content of the surface 142 | /// - `width`: width in pixels 143 | /// - `height`: height in pixels 144 | /// 145 | /// **Returns** 146 | /// 147 | /// a pointer to the newly created surface. 148 | /// 149 | /// **NOTE**: The caller owns the created surface and should call 150 | /// `surface.destroy()` when done with it. You can use idiomatic Zig 151 | /// pattern with `defer`: 152 | /// ```zig 153 | /// const device = try cairo.Script.create("script.txt"); 154 | /// defer device.destroy(); 155 | /// const surface = try cairo.ScriptSurface.create(device, .ColorAlpha, 100, 100); 156 | /// defer surface.destroy(); 157 | /// ``` 158 | /// 159 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-Script-Surfaces.html#cairo-script-surface-create) 160 | pub fn create(script: *Script, content: Content, width: f64, height: f64) CairoError!*ScriptSurface { 161 | const surface = c.cairo_script_surface_create(script, content, width, height).?; 162 | try surface.status().toErr(); 163 | if (safety.tracing) try safety.markForLeakDetection(@returnAddress(), surface); 164 | return surface; 165 | } 166 | 167 | /// Create a proxy surface that will render to target and record the 168 | /// operations to `device`. 169 | /// 170 | /// **Parameters** 171 | /// - `target`: a target surface to wrap 172 | /// 173 | /// **Returns** 174 | /// 175 | /// a pointer to the newly created surface. 176 | /// 177 | /// **NOTE**: The caller owns the created surface and should call 178 | /// `surface.destroy()` when done with it. You can use idiomatic Zig 179 | /// pattern with `defer`: 180 | /// ```zig 181 | /// const device = try cairo.Script.create("script.txt"); 182 | /// defer device.destroy(); 183 | /// const image = try cairo.ImageSurface.create(.argb32, 100, 100); 184 | /// defer image.destroy(); 185 | /// const surface = try cairo.ScriptSurface.createForTarget(device, surface.asSurface()); 186 | /// defer surface.destroy(); 187 | /// ``` 188 | /// 189 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-Script-Surfaces.html#cairo-script-surface-create-for-target) 190 | pub fn createForTarget(script: *Script, target: *Surface) CairoError!*ScriptSurface { 191 | const surface = c.cairo_script_surface_create_for_target(script, target).?; 192 | try surface.status().toErr(); 193 | if (safety.tracing) try safety.markForLeakDetection(@returnAddress(), surface); 194 | return surface; 195 | } 196 | }; 197 | -------------------------------------------------------------------------------- /src/cairo/fonts/font_face.zig: -------------------------------------------------------------------------------- 1 | //! `cairo.FontFace` — Base class for font faces 2 | //! 3 | //! `cairo.FontFace` represents a particular font at a particular weight, 4 | //! slant, and other characteristic but no size, transformation, or size. 5 | //! 6 | //! Font faces are created using *font-backend*-specific constructors, 7 | //! typically of the form cairo_backend_font_face_create(), or implicitly using 8 | //! the toy text API by way of `cairo.Context.selectFontFace()`. The resulting 9 | //! face can be accessed using `cairo.Context.getFontFace()`. 10 | // TODO: fix desc 11 | 12 | const cairo = @import("../../cairo.zig"); 13 | const c = cairo.c; 14 | const safety = @import("safety"); 15 | 16 | const CairoError = cairo.CairoError; 17 | const Status = cairo.Status; 18 | const UserDataKey = cairo.UserDataKey; 19 | const DestroyFn = cairo.DestroyFn; 20 | 21 | pub fn Base(comptime Self: type) type { 22 | return struct { 23 | /// Increases the reference count on `self` by one. This prevents `self` 24 | /// from being destroyed until a matching call to `.destroy()` is made. 25 | /// 26 | /// Use `cairo.FontFace.getReferenceCount()` to get the number of 27 | /// references to a `cairo.FontFace`. 28 | /// 29 | /// **Returns** 30 | /// 31 | /// the referenced `cairo.FontFace`. 32 | /// 33 | /// [Link to Cairo documentation](https://www.cairographics.org/manual/cairo-cairo-font-face-t.html#cairo-font-face-reference) 34 | pub fn reference(self: *Self) *Self { 35 | if (safety.tracing) safety.reference(@returnAddress(), self); 36 | return @ptrCast(c.cairo_font_face_reference(self).?); 37 | } 38 | 39 | /// Decreases the reference count on `self` by one. If the result is zero, 40 | /// then `self` and all associated resources are freed. See 41 | /// `cairo.FontFace.reference()`. 42 | /// 43 | /// [Link to Cairo documentation](https://www.cairographics.org/manual/cairo-cairo-font-face-t.html#cairo-font-face-destroy) 44 | pub fn destroy(self: *Self) void { 45 | c.cairo_font_face_destroy(self); 46 | if (safety.tracing) safety.destroy(self); 47 | } 48 | 49 | /// Checks whether an error has previously occurred for this font face. 50 | /// 51 | /// [Link to Cairo documentation](https://www.cairographics.org/manual/cairo-cairo-font-face-t.html#cairo-font-face-status) 52 | pub fn status(self: *Self) Status { 53 | return c.cairo_font_face_status(self); 54 | } 55 | 56 | /// This function returns the type of the backend used to create a font 57 | /// face. See `cairo.FontFace.Type` for available types. 58 | /// 59 | /// **Returns** 60 | /// 61 | /// the type of `self`. 62 | /// 63 | /// [Link to Cairo documentation](https://www.cairographics.org/manual/cairo-cairo-font-face-t.html#cairo-font-face-get-type) 64 | pub fn getType(self: *Self) FontFace.Type { 65 | return c.cairo_font_face_get_type(self); 66 | } 67 | 68 | /// Returns the current reference count of `self`. 69 | /// 70 | /// [Link to Cairo documentation](https://www.cairographics.org/manual/cairo-cairo-font-face-t.html#cairo-font-face-get-reference-count) 71 | pub fn getReferenceCount(self: *Self) usize { 72 | return @intCast(c.cairo_font_face_get_reference_count(self)); 73 | } 74 | 75 | /// Attach user data to `font_face`. To remove user data from a font face, 76 | /// call this function with the key that was used to set it and `null` for 77 | /// `data`. 78 | /// 79 | /// **Parameters** 80 | /// - `key`: the address of a `cairo.UserDataKey` to attach the user data 81 | /// to 82 | /// - `user_data`: the user data to attach to the font face 83 | /// - `destroyFn`: a `cairo.DestroyFn` which will be called when the font 84 | /// face is destroyed or when new user data is attached using the same key. 85 | /// 86 | /// The only possible error is `error.OutOfMemory` if a slot could not be 87 | /// allocated for the user data. 88 | /// 89 | /// [Link to Cairo documentation](https://www.cairographics.org/manual/cairo-cairo-font-face-t.html#cairo-font-face-get-user-data) 90 | pub fn setUserData(self: *Self, key: *const UserDataKey, user_data: ?*anyopaque, destroyFn: DestroyFn) CairoError!void { 91 | try c.cairo_font_face_set_user_data(self, key, user_data, destroyFn).toErr(); 92 | } 93 | 94 | /// Return user data previously attached to `self` using the specified key. 95 | /// If no user data has been attached with the given key this function 96 | /// returns `null`. 97 | /// 98 | /// **Parameters** 99 | /// - `key`: the address of the `cairo.UserDataKey` the user data was 100 | /// attached to 101 | /// 102 | /// **Returns** 103 | /// 104 | /// the user data previously attached or `null`. 105 | /// 106 | /// [Link to Cairo documentation](https://www.cairographics.org/manual/cairo-cairo-font-face-t.html#cairo-font-face-set-user-data) 107 | pub fn getUserData(self: *Self, key: *const UserDataKey) ?*anyopaque { 108 | return c.cairo_font_face_get_user_data(self, key); 109 | } 110 | }; 111 | } 112 | 113 | /// A `cairo.FontFace` specifies all aspects of a font other than the size or 114 | /// font matrix (a font matrix is used to distort a font by shearing it or 115 | /// scaling it unequally in the two directions). A font face can be set on a 116 | /// `cairo.Context` by using `cr.setFontFace()` on it; the size and font 117 | /// matrix are set with `cr.setFontSize()` and `cr.setFontMatrix()`. 118 | /// 119 | /// There are various types of font faces, depending on the *font backend* they 120 | /// use. The type of a font face can be queried using `fontFace.getType()`. 121 | /// 122 | /// Memory management of `cairo.FontFace` is done with `fontFace.reference()` 123 | /// and `fontFace.destroy()` 124 | /// 125 | /// [Link to Cairo documentation](https://www.cairographics.org/manual/cairo-cairo-font-face-t.html#cairo-font-face-t) 126 | pub const FontFace = opaque { 127 | pub usingnamespace Base(@This()); 128 | /// `cairo.FontFace.Type` is used to describe the type of a given font face 129 | /// or scaled font. The font types are also known as "font backends" within 130 | /// cairo. 131 | /// 132 | /// The type of a font face is determined by the function used to create 133 | /// it, which will generally be of the form `cairo_type_font_face_create`. 134 | /// The font face type can be queried with `fontFace.getType()`. 135 | /// 136 | /// The various `cairo.FontFace` functions can be used with a font face of 137 | /// any type. 138 | /// 139 | /// The type of a scaled font is determined by the type of the font face 140 | /// passed to `cairo_scaled_font_create()`. The scaled font type can be 141 | /// queried with `cairo_scaled_font_get_type()`. 142 | /// 143 | /// The various `cairo.ScaledFont` functions can be used with scaled fonts 144 | /// of any type, but some font backends also provide type-specific 145 | /// functions that must only be called with a scaled font of the 146 | /// appropriate type. These functions have names that begin with 147 | /// `cairo_type_scaled_font()` such as `cairo_ft_scaled_font_lock_face()`. 148 | /// 149 | /// The behavior of calling a type-specific function with a scaled font of 150 | /// the wrong type is undefined. 151 | /// 152 | /// New entries may be added in future versions. 153 | pub const Type = enum(c_uint) { 154 | // TODO: fix desc 155 | /// The font was created using cairo's toy font api 156 | Toy, 157 | /// The font is of type FreeType 158 | Ft, 159 | /// The font is of type Win32 160 | Win32, 161 | /// The font is of type Quartz 162 | Quartz, 163 | /// The font was create using cairo's user font api 164 | User, 165 | }; 166 | 167 | /// Specifies variants of a font face based on their slant. 168 | /// 169 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-text.html#cairo-font-slant-t) 170 | pub const FontSlant = enum(c_uint) { 171 | /// Upright font style 172 | Normal, 173 | /// Italic font style 174 | Italic, 175 | /// Oblique font style 176 | Oblique, 177 | }; 178 | 179 | /// Specifies variants of a font face based on their weight. 180 | /// 181 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-text.html#cairo-font-weight-t) 182 | pub const FontWeight = enum(c_uint) { 183 | /// Normal font weight 184 | Normal, 185 | /// Bold font weight 186 | Bold, 187 | }; 188 | }; 189 | -------------------------------------------------------------------------------- /src/pango/matrix.zig: -------------------------------------------------------------------------------- 1 | const safety = @import("safety"); 2 | const pango = @import("../pango.zig"); 3 | const c = pango.c; 4 | 5 | /// A `Matrix` specifies a transformation between user-space and device 6 | /// coordinates. 7 | /// 8 | /// The transformation is given by 9 | /// ```zig 10 | /// x_device = x_user * matrix.xx + y_user * matrix.xy + matrix.x0; 11 | /// y_device = x_user * matrix.yx + y_user * matrix.yy + matrix.y0; 12 | /// ``` 13 | pub const Matrix = extern struct { 14 | /// 1st component of the transformation matrix. 15 | xx: f64, 16 | /// 2nd component of the transformation matrix. 17 | xy: f64, 18 | /// 3rd component of the transformation matrix. 19 | yx: f64, 20 | /// 4th component of the transformation matrix. 21 | yy: f64, 22 | /// X translation. 23 | x0: f64, 24 | /// Y translation. 25 | y0: f64, 26 | 27 | /// Copies a `pango.Matrix`. 28 | /// 29 | /// **Returns** 30 | /// 31 | /// the newly allocated `pango.Matrix`. 32 | /// 33 | /// **NOTE**: The caller owns the created matrix and should call 34 | /// `matrix.free()` when done with it. You can use idiomatic Zig pattern 35 | /// with `defer`: 36 | /// ```zig 37 | /// const matrix = try another_matrix.copy(); 38 | /// defer matrix.free(); 39 | /// ``` 40 | pub fn copy(self: *Matrix) !*Matrix { 41 | const ptr = c.pango_matrix_copy(self) orelse return error.NullPointer; 42 | if (safety.tracing) try safety.markForLeakDetection(@returnAddress(), ptr); 43 | return ptr; 44 | } 45 | 46 | /// Free a `pango.Matrix`. 47 | pub fn free(self: *Matrix) void { 48 | c.pango_matrix_free(self); 49 | if (safety.tracing) safety.destroy(self); 50 | } 51 | 52 | /// Changes the transformation represented by `self` to be the 53 | /// transformation given by first translating by (`tx`, `ty`) then applying 54 | /// the original transformation. 55 | /// 56 | /// **Parameters** 57 | /// - `tx`: amount to translate in the X direction 58 | /// - `ty`: amount to translate in the Y direction 59 | pub fn translate(self: *Matrix, tx: f64, ty: f64) void { 60 | c.pango_matrix_translate(self, tx, ty); 61 | } 62 | 63 | /// Changes the transformation represented by `self` to be the 64 | /// transformation given by first scaling by `sx` in the X direction and 65 | /// `sy` in the Y direction then applying the original transformation. 66 | /// 67 | /// **Parameters** 68 | /// - `scale_x`: amount to scale by in X direction 69 | /// - `scale_y`: amount to scale by in Y direction 70 | pub fn scale(self: *Matrix, scale_x: f64, scale_y: f64) void { 71 | c.pango_matrix_scale(self, scale_x, scale_y); 72 | } 73 | 74 | /// Changes the transformation represented by `self` to be the 75 | /// transformation given by first rotating by `degrees` degrees 76 | /// counter-clockwise then applying the original transformation. 77 | /// 78 | /// **Parameters** 79 | /// - `degrees`: degrees to rotate counter-clockwise 80 | pub fn rotate(self: *Matrix, degrees: f64) void { 81 | c.pango_matrix_rotate(self, degrees); 82 | } 83 | 84 | /// Changes the transformation represented by `self` to be the 85 | /// transformation given by first applying transformation given by 86 | /// `new_matrix` then applying the original transformation. 87 | /// 88 | /// **Parameters** 89 | /// - `new_matrix`: a `pango.Matrix` 90 | /// - `aboba`: 91 | pub fn concat(self: *Matrix, new_matrix: *const Matrix) void { 92 | c.pango_matrix_concat(self, new_matrix); 93 | } 94 | 95 | /// Transforms the point (`x`, `y`) by `self` matrix. 96 | /// 97 | /// **Parameters** 98 | /// - `x`: in/out X position 99 | /// - `y`: in/out Y position 100 | pub fn transformPoint(self: *const Matrix, x: *f64, y: *f64) void { 101 | c.pango_matrix_transform_point(self, x, y); 102 | } 103 | 104 | /// Transforms the distance vector (`dx`, `dy`) by `self` matrix. 105 | /// 106 | /// This is similar to `pango.Matrix.transformPoint()`, except that the 107 | /// translation components of the transformation are ignored. The 108 | /// calculation of the returned vector is as follows. 109 | /// ```zig 110 | /// dx2 = dx1 * xx + dy1 * xy; 111 | /// dy2 = dx1 * yx + dy1 * yy; 112 | /// ``` 113 | /// Affine transformations are position invariant, so the same vector 114 | /// always transforms to the same vector. If (`x1`,`y1`) transforms to 115 | /// (`x2`,`y2`) then (`x1`+`dx1`,`y1`+`dy1`) will transform to 116 | /// (`x1`+`dx2`,`y1`+`dy2`) for all values of `x1` and `x2`. 117 | /// 118 | /// **Parameters** 119 | /// - `dx`: in/out X component of a distance vector 120 | /// - `dy`: in/out Y component of a distance vector 121 | pub fn transformDistance(self: *const Matrix, dx: *f64, dy: *f64) void { 122 | c.pango_matrix_transform_distance(self, dx, dy); 123 | } 124 | 125 | /// First transforms `rect` using `self`, then calculates the bounding box 126 | /// of the transformed rectangle. 127 | /// 128 | /// This function is useful for example when you want to draw a rotated 129 | /// `pango.Layout` to an image buffer, and want to know how large the image 130 | /// should be and how much you should shift the layout when rendering. 131 | /// 132 | /// If you have a rectangle in device units (pixels), use 133 | /// `pango.Matrix.transformPixelRectangle()`. 134 | /// 135 | /// If you have the rectangle in Pango units and want to convert to 136 | /// transformed pixel bounding box, it is more accurate to transform it 137 | /// first (using this function) and pass the result to `pango_extents_to_pixels()`, 138 | /// first argument, for an inclusive rounded rectangle. However, there are 139 | /// valid reasons that you may want to convert to pixels first and then 140 | /// transform, for example when the transformed coordinates may overflow in 141 | /// Pango units (large matrix translation for example). 142 | /// 143 | /// **Parameters** 144 | /// - `rect`: in/out bounding box in Pango units 145 | pub fn transformRectangle(self: *const Matrix, rect: *pango.Rectangle) void { 146 | // TODO: fix desc 147 | c.pango_matrix_transform_rectangle(self, rect); 148 | } 149 | 150 | /// First transforms the `rect` using `self`, then calculates the bounding 151 | /// box of the transformed rectangle. 152 | /// 153 | /// This function is useful for example when you want to draw a rotated 154 | /// `pango.Layout` to an image buffer, and want to know how large the image 155 | /// should be and how much you should shift the layout when rendering. 156 | /// 157 | /// For better accuracy, you should use `pango.Matrix.transformRectangle()` 158 | /// on original rectangle in Pango units and convert to pixels afterward 159 | /// `using pango_extents_to_pixels()`‘s first argument. 160 | /// 161 | /// **Parameters** 162 | /// - `rect`: in/out bounding box in device units 163 | pub fn transformPixelRectangle(self: *const Matrix, rect: *pango.Rectangle) void { 164 | // TODO: fix desc 165 | c.pango_matrix_transform_pixel_rectangle(self, rect); 166 | } 167 | 168 | /// Returns the scale factor of a matrix on the height of the font. 169 | /// 170 | /// That is, the scale factor in the direction perpendicular to the vector 171 | /// that the X coordinate is mapped to. If the scale in the X coordinate is 172 | /// needed as well, use `pango.Matrix.getFontScaleFactors()`. 173 | /// 174 | /// **Returns** 175 | /// 176 | /// the scale factor of `self` matrix on the height of the font. 177 | pub fn getFontScaleFactor(self: *const Matrix) f64 { 178 | return c.pango_matrix_get_font_scale_factor(self); 179 | } 180 | 181 | /// Calculates the scale factor of a matrix on the width and height of the 182 | /// font. 183 | /// 184 | /// That is, `xscale` is the scale factor in the direction of the X 185 | /// coordinate, and `yscale` is the scale factor in the direction 186 | /// perpendicular to the vector that the X coordinate is mapped to. 187 | /// 188 | /// Note that output numbers will always be non-negative. 189 | /// 190 | /// **Parameters** 191 | /// - `xscale`: output scale factor in the x direction 192 | /// - `yscale`: output scale factor in the y direction 193 | pub fn getFontScaleFactors(self: *const Matrix, xscale: ?*f64, yscale: ?*f64) void { 194 | c.pango_matrix_get_font_scale_factors(self, xscale, yscale); 195 | } 196 | 197 | /// Gets the slant ratio of a matrix. 198 | /// 199 | /// For a simple shear matrix in the form: 200 | /// ```code 201 | /// 1 λ 202 | /// 0 1 203 | /// ``` 204 | /// this is simply λ. 205 | /// 206 | /// **Returns** 207 | /// 208 | /// the slant ratio of `self` matrix. 209 | pub fn getSlantRatio(self: *const Matrix) f64 { 210 | return c.pango_matrix_get_slant_ratio(self); 211 | } 212 | }; 213 | -------------------------------------------------------------------------------- /src/cairo/surfaces/svg.zig: -------------------------------------------------------------------------------- 1 | //! SVG Surfaces — Rendering SVG documents 2 | //! 3 | //! [Link to Cairo manual](https://www.cairographics.org/manual/cairo-SVG-Surfaces.html) 4 | 5 | const std = @import("std"); 6 | const testing = std.testing; 7 | 8 | const safety = @import("safety"); 9 | const cairo = @import("../../cairo.zig"); 10 | const c = cairo.c; 11 | 12 | const Mixin = @import("base.zig").Base; 13 | const Status = cairo.Status; 14 | const CairoError = cairo.CairoError; 15 | const WriteFn = cairo.WriteFn; 16 | 17 | /// The SVG surface is used to render cairo graphics to SVG files and is a 18 | /// multi-page vector surface backend. 19 | /// 20 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-SVG-Surfaces.html) 21 | pub const SvgSurface = opaque { 22 | pub usingnamespace Mixin(SvgSurface); 23 | 24 | /// `cairo.SvgSurface.SvgUnit` is used to describe the units valid for 25 | /// coordinates and lengths in the SVG specification. 26 | /// 27 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-SVG-Surfaces.html#cairo-svg-unit-t) 28 | pub const SvgUnit = enum(c_uint) { 29 | /// User unit, a value in the current coordinate system. If used in the 30 | /// root element for the initial coordinate systems it corresponds to 31 | /// pixels. 32 | user = 0, 33 | /// The size of the element's font. 34 | em, 35 | /// The x-height of the element’s font 36 | ex, 37 | /// Pixels (1px = 1/96th of 1in). 38 | px, 39 | /// Inches (1in = 2.54cm = 96px). 40 | in, 41 | /// Centimeters (1cm = 96px/2.54). 42 | cm, 43 | /// Millimeters (1mm = 1/10th of 1cm). 44 | mm, 45 | /// Points (1pt = 1/72th of 1in). 46 | pt, 47 | /// Picas (1pc = 1/6th of 1in). 48 | pc, 49 | /// Percent, a value that is some fraction of another reference value. 50 | percent, 51 | }; 52 | 53 | /// Used to retrieve the list of supported SVG versions. See 54 | /// `cairo.SvgSurface.restrictToVersion()` 55 | /// 56 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-SVG-Surfaces.html#cairo-svg-get-versions) 57 | pub fn getSvgVersions() []const SvgVersion { 58 | var ptr: [*c]SvgVersion = undefined; 59 | var num_versions: c_int = undefined; 60 | c.cairo_svg_get_versions(@ptrCast(&ptr), &num_versions); 61 | return ptr[0..@as(usize, @intCast(num_versions))]; 62 | // TODO 63 | } 64 | 65 | /// `cairo.SvgSurface.SvgVersion` is used to describe the version number of 66 | /// the SVG specification that a generated SVG file will conform to. 67 | /// 68 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-SVG-Surfaces.html#cairo-svg-version-t) 69 | pub const SvgVersion = enum(c_uint) { 70 | @"1.1", 71 | @"1.2", 72 | 73 | /// Get the string representation of the given version id. See 74 | /// `cairo.getSvgVersions()` for a way to get the list of valid 75 | /// versions ids. 76 | /// 77 | /// **Zig binding's author here**, nevermind previous comment, and 78 | /// maybe these two functions at all, you have solid Zig enums. 79 | /// 80 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-SVG-Surfaces.html#cairo-svg-version-to-string) 81 | pub fn toString(self: SvgSurface.SvgVersion) []const u8 { 82 | const str = c.cairo_svg_version_to_string(self); 83 | return std.mem.span(str); 84 | } 85 | }; 86 | 87 | /// Creates a SVG surface of the specified size in points to be written to 88 | /// `filename`. 89 | /// 90 | /// The SVG surface backend recognizes the following MIME types 91 | /// (`cairo.Surface.MimeType`) for the data attached to a surface (see 92 | /// `cairo.Surface.setMimeData()`) when it is used as a source pattern for 93 | /// drawing on this surface: `.Jpeg`, `.Png`, `.Uri` If any of them is 94 | /// specified, the SVG backend emits a href with the content of MIME data 95 | /// instead of a surface snapshot (PNG, Base64-encoded) in the 96 | /// corresponding image tag. 97 | /// 98 | /// The unofficial MIME type `.Uri` is examined first. If 99 | /// present, the URI is emitted as is: assuring the correctness of URI is 100 | /// left to the client code. 101 | /// 102 | /// If `cairo.Surface.MimeType.Uri` is not present, but `.Jpeg` or `.Png` 103 | /// is specified, the corresponding data is Base64-encoded and emitted. 104 | /// 105 | /// If `cairo.Surface.MimeType.UniqueID` is present, all surfaces with the 106 | /// same unique identifier will only be embedded once. 107 | /// 108 | /// **Parameters** 109 | /// - `filename`: a filename for the SVG output (must be writable), `null` 110 | /// may be used to specify no output. This will generate a SVG surface that 111 | /// may be queried and used as a source, without generating a temporary 112 | /// file. 113 | /// - `width_in_points`: width of the surface, in points (1 point == 1/72.0 114 | /// inch) 115 | /// - `height_in_points`: height of the surface, in points (1 point == 116 | /// 1/72.0 inch) 117 | /// 118 | /// **Returns** 119 | /// 120 | /// a pointer to the newly created surface. 121 | /// **NOTE**: The caller owns the created surface and should call 122 | /// `surface.destroy()` when done with it. You can use idiomatic Zig 123 | /// pattern with `defer`: 124 | /// ```zig 125 | /// const surface = cairo.SvgSurface.create("file.svg", 100.0, 100.0); 126 | /// defer surface.destroy(); 127 | /// ``` 128 | /// 129 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-SVG-Surfaces.html#cairo-svg-surface-create) 130 | pub fn create(filename: [:0]const u8, width: f64, height: f64) CairoError!*SvgSurface { 131 | const svg = c.cairo_svg_surface_create(filename.ptr, width, height).?; 132 | try svg.status().toErr(); 133 | if (safety.tracing) try safety.markForLeakDetection(@returnAddress(), svg); 134 | return svg; 135 | } 136 | 137 | /// Creates a SVG surface of the specified size in points to be written 138 | /// incrementally to the `writer` stream. 139 | /// 140 | /// **Parameters** 141 | /// - `writer`: pointer to a writeable object, e.g. `std.io.Writer` or 142 | /// `std.fs.File` (it should have a `.writeAll()` function) 143 | /// - `width`: width of the surface, in points (1 point == 1/72.0 inch) 144 | /// - `height`: height of the surface, in points (1 point == 1/72.0 inch) 145 | /// 146 | /// **Returns** 147 | /// 148 | /// a pointer to the newly created surface. 149 | /// **NOTE**: The caller owns the surface and should call 150 | /// `surface.destroy()` when done with it. 151 | /// 152 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-SVG-Surfaces.html#cairo-svg-surface-create-for-stream) 153 | pub fn createForStream(writer: anytype, width: f64, height: f64) CairoError!*SvgSurface { 154 | const writeFn = cairo.createWriteFn(@TypeOf(writer)); 155 | const svg = c.cairo_svg_surface_create_for_stream(writeFn, writer, width, height).?; 156 | try svg.status().toErr(); 157 | if (safety.tracing) try safety.markForLeakDetection(@returnAddress(), svg); 158 | return svg; 159 | } 160 | 161 | /// Get the unit of the SVG surface. 162 | /// 163 | /// If the surface passed as an argument is not a SVG surface, the function 164 | /// sets the error status to `cairo.Status.SurfaceTypeMismatch` and returns 165 | /// `cairo.SvgSurface.SvgUnit.user` 166 | /// 167 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-SVG-Surfaces.html#cairo-svg-surface-get-document-unit) 168 | pub fn getDocumentUnit(self: *SvgSurface) SvgSurface.SvgUnit { 169 | return c.cairo_svg_surface_get_document_unit(self); 170 | } 171 | 172 | /// Use the specified unit for the width and height of the generated SVG 173 | /// file. See `cairo.SvgSurface.SvgUnit` for a list of available unit 174 | /// values that can be used here. 175 | /// 176 | /// This function can be called at any time before generating the SVG file. 177 | /// 178 | /// However **to minimize the risk of ambiguities it's recommended to call 179 | /// it before any drawing operations have been performed on the given 180 | /// surface**, to make it clearer what the unit used in the drawing 181 | /// operations is. 182 | /// 183 | /// The simplest way to do this is to call this function immediately after 184 | /// creating the SVG surface. 185 | /// 186 | /// Note if this function is never called, the default unit for SVG 187 | /// documents generated by cairo will be "pt". This is for historical 188 | /// reasons. 189 | /// 190 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-SVG-Surfaces.html#cairo-svg-surface-set-document-unit) 191 | pub fn setDocumentUnit(self: *SvgSurface, unit: SvgSurface.SvgUnit) void { 192 | c.cairo_svg_surface_set_document_unit(self, unit); 193 | } 194 | 195 | /// Restricts the generated SVG file to `version`. See 196 | /// `cairo.getSvgVersions()` for a list of available version values that 197 | /// can be used here. 198 | /// 199 | /// **This function should only be called before any drawing operations 200 | /// have been performed on the given surface.** The simplest way to do this 201 | /// is to call this function immediately after creating the surface. 202 | /// 203 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-SVG-Surfaces.html#cairo-svg-surface-restrict-to-version) 204 | pub fn restrictToVersion(self: *SvgSurface, version: SvgSurface.SvgVersion) void { 205 | c.cairo_svg_surface_restrict_to_version(self, version); 206 | } 207 | }; 208 | 209 | test "versions" { 210 | const versions = SvgSurface.getSvgVersions(); 211 | try testing.expectEqualSlices(SvgSurface.SvgVersion, &.{ .@"1.1", .@"1.2" }, versions); 212 | try testing.expectEqualStrings("SVG 1.1", SvgSurface.SvgVersion.toString(.@"1.1")); 213 | try testing.expectEqualStrings("SVG 1.2", SvgSurface.SvgVersion.toString(.@"1.2")); 214 | } 215 | -------------------------------------------------------------------------------- /src/cairo/context.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const testing = std.testing; 3 | const build = @import("build_options"); 4 | 5 | const cairo = @import("../cairo.zig"); 6 | const c = cairo.c; 7 | const safety = @import("safety"); 8 | 9 | const Status = cairo.Status; 10 | 11 | /// A `cairo.Context` contains the current state of the rendering device, 12 | /// including coordinates of yet to be drawn shapes. 13 | /// 14 | /// Cairo contexts, are central to cairo and all drawing with cairo is always 15 | /// done to a `cairo.Context` object. 16 | /// 17 | /// Memory management of `cairo.Context` is done with `cr.reference()` and 18 | /// `cr.destroy()`. 19 | /// 20 | /// [Link to Cairo Manual](https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-t) 21 | pub const Context = opaque { 22 | pub usingnamespace @import("drawing/basic.zig").Mixin; 23 | pub usingnamespace @import("drawing/paths.zig").Mixin; 24 | pub usingnamespace @import("drawing/transformations.zig"); 25 | pub usingnamespace @import("drawing/text.zig").Mixin; 26 | pub usingnamespace @import("drawing/tags_and_links.zig"); 27 | pub usingnamespace if (build.@"no-pango") struct { 28 | pub fn createPangoContext(_: *cairo.Context) noreturn { 29 | @compileError("pango support disabled"); 30 | } 31 | 32 | pub fn updatePangoContext(_: *cairo.Context, _: *anyopaque) noreturn { 33 | @compileError("pango support disabled"); 34 | } 35 | 36 | pub fn createLayout(_: *cairo.Context) noreturn { 37 | @compileError("pango support disabled"); 38 | } 39 | 40 | pub fn updateLayout(_: *cairo.Context, _: *anyopaque) noreturn { 41 | @compileError("pango support disabled"); 42 | } 43 | } else @import("pangocairo").context; 44 | 45 | /// `cairo.Context.Operator` is used to set the compositing operator for 46 | /// all cairo drawing operations. 47 | /// 48 | /// The default operator is `.Over`. 49 | /// 50 | /// The operators marked as *unbounded* modify their destination even 51 | /// outside of the mask layer (that is, their effect is not bound by the 52 | /// mask layer). However, their effect can still be limited by way of 53 | /// clipping. 54 | /// 55 | /// To keep things simple, the operator descriptions here document the 56 | /// behavior for when both source and destination are either fully 57 | /// transparent or fully opaque. The actual implementation works for 58 | /// translucent layers too. For a more detailed explanation of the effects 59 | /// of each operator, including the mathematical definitions, see 60 | /// [link](https://cairographics.org/operators/). 61 | /// 62 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-operator-t) 63 | pub const Operator = enum(c_uint) { 64 | /// clear destination layer (bounded) 65 | Clear, 66 | /// replace destination layer (bounded) 67 | Source, 68 | /// draw source layer on top of destination layer (bounded) 69 | Over, 70 | /// draw source where there was destination content (unbounded) 71 | In, 72 | /// draw source where there was no destination content (unbounded) 73 | Out, 74 | /// draw source on top of destination content and only there 75 | Atop, 76 | /// ignore the source 77 | Dest, 78 | /// draw destination on top of source 79 | DestOver, 80 | /// leave destination only where there was source content (unbounded) 81 | DestIn, 82 | /// leave destination only where there was no source content 83 | DestOut, 84 | /// leave destination on top of source content and only there (unbounded) 85 | DestAtop, 86 | /// source and destination are shown where there is only one of them 87 | Xor, 88 | /// source and destination layers are accumulated 89 | Add, 90 | /// like over, but assuming source and dest are disjoint geometries 91 | Saturate, 92 | /// source and destination layers are multiplied. This causes the result to 93 | /// be at least as dark as the darker inputs. 94 | Multiply, 95 | /// source and destination are complemented and multiplied. This causes the 96 | /// result to be at least as light as the lighter inputs. 97 | Screen, 98 | /// multiplies or screens, depending on the lightness of the destination 99 | /// color. 100 | Overlay, 101 | /// replaces the destination with the source if it is darker, otherwise 102 | /// keeps the source. 103 | Darken, 104 | /// replaces the destination with the source if it is lighter, otherwise 105 | /// keeps the source. 106 | Lighten, 107 | /// brightens the destination color to reflect the source color. 108 | ColorDodge, 109 | /// darkens the destination color to reflect the source color. 110 | ColorBurn, 111 | /// Multiplies or screens, dependent on source color. 112 | HardLight, 113 | /// Darkens or lightens, dependent on source color. 114 | SoftLight, 115 | /// Takes the difference of the source and destination color. 116 | Difference, 117 | /// Produces an effect similar to difference, but with lower contrast. 118 | Exclusion, 119 | /// Creates a color with the hue of the source and the saturation and 120 | /// luminosity of the target. 121 | HslHue, 122 | /// Creates a color with the saturation of the source and the hue and 123 | /// luminosity of the target. Painting with this mode onto a gray area 124 | /// produces no change. 125 | HslSaturation, 126 | /// Creates a color with the hue and saturation of the source and the 127 | /// luminosity of the target. This preserves the gray levels of the target 128 | /// and is useful for coloring monochrome images or tinting color images. 129 | HslColor, 130 | /// Creates a color with the luminosity of the source and the hue and 131 | /// saturation of the target. This produces an inverse effect to 132 | /// `.HslColor`. 133 | HslLuminosity, 134 | }; 135 | 136 | /// `cairo.FillRule` is used to select how paths are filled. For both fill 137 | /// rules, whether or not a point is included in the fill is determined by 138 | /// taking a ray from that point to infinity and looking at intersections 139 | /// with the path. The ray can be in any direction, as long as it doesn't 140 | /// pass through the end point of a segment or have a tricky intersection 141 | /// such as intersecting tangent to the path. (Note that filling is not 142 | /// actually implemented in this way. This is just a description of the 143 | /// rule that is applied.) 144 | /// 145 | /// The default fill rule is `.Winding`. 146 | /// 147 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-fill-rule-t) 148 | pub const FillRule = enum(c_uint) { 149 | Winding, 150 | EvenOdd, 151 | }; 152 | 153 | /// Specifies how to render the endpoints of the path when stroking. 154 | /// 155 | /// The default line cap style is `.Butt`. 156 | /// 157 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-line-cap-t) 158 | pub const LineCap = enum(c_uint) { 159 | /// start(stop) the line exactly at the start(end) point ( 160 | Butt, 161 | /// use a round ending, the center of the circle is the end point 162 | Round, 163 | /// use squared ending, the center of the square is the end point 164 | Square, 165 | }; 166 | 167 | /// Specifies how to render the junction of two lines when stroking. 168 | /// 169 | /// The default line join style is `.Miter`. 170 | /// 171 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-line-join-t) 172 | pub const LineJoin = enum(c_uint) { 173 | /// use a sharp (angled) corner, see `cr.setMiterLimit()` 174 | Miter, 175 | /// use a rounded join, the center of the circle is the joint point 176 | Round, 177 | /// use a cut-off join, the join is cut off at half the line width from the 178 | /// joint point 179 | Bevel, 180 | }; 181 | }; 182 | 183 | /// A data structure for holding a rectangle. 184 | /// 185 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-rectangle-t) 186 | pub const Rectangle = extern struct { 187 | /// X coordinate of the left side of the rectangle 188 | x: f64, 189 | /// Y coordinate of the the top side of the rectangle 190 | y: f64, 191 | /// width of the rectangle 192 | width: f64, 193 | /// height of the rectangle 194 | height: f64, 195 | 196 | /// Init `cairo.Rectangle` from [4]c_int array. Values are 197 | /// `.{x, y, width, height}` 198 | pub fn init(arr: [4]f64) Rectangle { 199 | return @bitCast(arr); 200 | } 201 | }; 202 | 203 | /// A data structure for holding a dynamically allocated array of rectangles. 204 | /// 205 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-rectangle-list-t) 206 | pub const RectangleList = extern struct { 207 | /// Error status of the rectangle list 208 | status: Status, 209 | /// Array containing the rectangles 210 | rectangles: [*c]Rectangle, 211 | /// Number of rectangles in this list 212 | num_rectangles: c_int, 213 | 214 | /// Unconditionally frees `self` and all associated references. After this 215 | /// call, the `self` pointer must not be dereferenced. 216 | /// 217 | /// [Link to Cairo manual](https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-rectangle-list-destroy) 218 | pub fn destroy(self: *RectangleList) void { 219 | c.cairo_rectangle_list_destroy(self); 220 | if (safety.tracing) safety.destroy(self); 221 | } 222 | 223 | /// Converts C-style structure `cairo.RectangleList` into a friendly Zig 224 | /// slice. **Note**: the caller owns the memory and should call 225 | /// `.destroy()` on the **original** `cairo.RectangleList`. 226 | pub fn asSlice(self: *RectangleList) []Rectangle { 227 | const end: usize = @intCast(self.num_rectangles); 228 | return self.rectangles[0..end]; 229 | } 230 | }; 231 | 232 | test "RectangleList" { 233 | const surface = try cairo.ImageSurface.create(.argb32, 10, 10); 234 | defer surface.destroy(); 235 | const context = try cairo.Context.create(surface.asSurface()); 236 | defer context.destroy(); 237 | context.lineTo(0, 5); 238 | context.lineTo(5, 5); 239 | context.lineTo(5, 0); 240 | context.lineTo(0, 0); 241 | context.clip(); 242 | 243 | const rects = try context.copyClipRectangleList(); 244 | defer rects.destroy(); 245 | 246 | const expect_rect = Rectangle{ .x = 0, .y = 0, .width = 5, .height = 5 }; 247 | try testing.expect(rects.num_rectangles == 1); 248 | try testing.expectEqual(expect_rect, rects.rectangles[0]); 249 | try testing.expectEqualSlices(Rectangle, &.{expect_rect}, rects.asSlice()); 250 | } 251 | --------------------------------------------------------------------------------