diff --git a/freetype/build.zig b/freetype/build.zig
index 26e99abb..1bd6ebbd 100644
--- a/freetype/build.zig
+++ b/freetype/build.zig
@@ -23,6 +23,26 @@ pub fn build(b: *std.build.Builder) !void {
const test_step = b.step("test", "Run library tests");
test_step.dependOn(&dedicated_tests.step);
test_step.dependOn(&main_tests.step);
+
+ inline for ([_][]const u8{
+ "single_glyph",
+ "glyph_to_svg",
+ }) |example| {
+ const example_exe = b.addExecutable("example-" ++ example, "examples/" ++ example ++ ".zig");
+ example_exe.setBuildMode(mode);
+ example_exe.setTarget(target);
+ example_exe.addPackage(pkg);
+ link(b, example_exe, .{});
+ example_exe.install();
+
+ const example_compile_step = b.step("example-" ++ example, "Compile '" ++ example ++ "' example");
+ example_compile_step.dependOn(b.getInstallStep());
+
+ const example_run_cmd = example_exe.run();
+ example_run_cmd.step.dependOn(b.getInstallStep());
+ const example_run_step = b.step("run-example-" ++ example, "Run '" ++ example ++ "' example");
+ example_run_step.dependOn(&example_run_cmd.step);
+ }
}
pub const Options = struct {
diff --git a/freetype/examples/glyph_to_svg.zig b/freetype/examples/glyph_to_svg.zig
new file mode 100644
index 00000000..bc488126
--- /dev/null
+++ b/freetype/examples/glyph_to_svg.zig
@@ -0,0 +1,130 @@
+const std = @import("std");
+const freetype = @import("freetype");
+
+const OutlinePrinter = struct {
+ library: freetype.Library,
+ face: freetype.Face,
+ output_file: std.fs.File,
+ path_stream: std.io.FixedBufferStream([]u8),
+
+ xMin: isize,
+ yMin: isize,
+ width: isize,
+ height: isize,
+
+ const Self = @This();
+
+ pub fn init(file: std.fs.File) freetype.Error!Self {
+ var lib = try freetype.Library.init();
+ return Self{
+ .library = lib,
+ .face = try lib.newFace("upstream/assets/FiraSans-Regular.ttf", 0),
+ .output_file = file,
+ .path_stream = std.io.fixedBufferStream(&std.mem.zeroes([1024 * 10]u8)),
+ .xMin = 0,
+ .yMin = 0,
+ .width = 0,
+ .height = 0,
+ };
+ }
+
+ pub fn deinit(self: Self) void {
+ self.library.deinit();
+ }
+
+ pub fn outlineExists(self: Self) bool {
+ const outline = self.face.glyph.outline() orelse return false;
+ if (outline.numContours() <= 0 or outline.numPoints() <= 0)
+ return false;
+ outline.check() catch return false;
+ return true;
+ }
+
+ pub fn flipOutline(self: Self) void {
+ const multiplier = 65536;
+ const matrix = freetype.Matrix{
+ .xx = 1 * multiplier,
+ .xy = 0 * multiplier,
+ .yx = 0 * multiplier,
+ .yy = -1 * multiplier,
+ };
+ self.face.glyph.outline().?.transform(matrix);
+ }
+
+ pub fn extractOutline(self: *Self) !void {
+ try self.path_stream.writer().writeAll("");
+ }
+
+ pub fn computeViewBox(self: *Self) !void {
+ const boundingBox = try self.face.glyph.outline().?.bbox();
+ self.xMin = boundingBox.xMin;
+ self.yMin = boundingBox.yMin;
+ self.width = boundingBox.xMax - boundingBox.xMin;
+ self.height = boundingBox.yMax - boundingBox.yMin;
+ }
+
+ pub fn printSVG(self: Self) !void {
+ try self.output_file.writer().print(
+ \\
+ , .{ self.xMin, self.yMin, self.width, self.height, self.path_stream.getWritten() });
+ }
+
+ pub fn moveToFunction(to: [*c]const freetype.Vector, self_ptr: ?*anyopaque) callconv(.C) c_int {
+ var self = @ptrCast(*Self, @alignCast(std.meta.alignment(Self), self_ptr));
+ self.path_stream.writer().print("M {d} {d}\t", .{ to.*.x, to.*.y }) catch unreachable;
+ return 0;
+ }
+
+ pub fn lineToFunction(to: [*c]const freetype.Vector, self_ptr: ?*anyopaque) callconv(.C) c_int {
+ var self = @ptrCast(*Self, @alignCast(std.meta.alignment(Self), self_ptr));
+ self.path_stream.writer().print("L {d} {d}\t", .{ to.*.x, to.*.y }) catch unreachable;
+ return 0;
+ }
+
+ pub fn conicToFunction(control: [*c]const freetype.Vector, to: [*c]const freetype.Vector, self_ptr: ?*anyopaque) callconv(.C) c_int {
+ var self = @ptrCast(*Self, @alignCast(std.meta.alignment(Self), self_ptr));
+ self.path_stream.writer().print("Q {d} {d}, {d} {d}\t", .{ control.*.x, control.*.y, to.*.x, to.*.y }) catch unreachable;
+ return 0;
+ }
+
+ pub fn cubicToFunction(control_0: [*c]const freetype.Vector, control_1: [*c]const freetype.Vector, to: [*c]const freetype.Vector, self_ptr: ?*anyopaque) callconv(.C) c_int {
+ var self = @ptrCast(*Self, @alignCast(std.meta.alignment(Self), self_ptr));
+ self.path_stream.writer().print("C {d} {d}, {d} {d}, {d} {d}\t", .{ control_0.*.x, control_0.*.y, control_1.*.x, control_1.*.y, to.*.x, to.*.y }) catch unreachable;
+ return 0;
+ }
+
+ pub fn run(self: *Self, symbol: u8) !void {
+ try self.face.loadChar(symbol, .{ .no_scale = true, .no_bitmap = true });
+
+ if (!self.outlineExists())
+ return error.OutlineDoesntExists;
+
+ self.flipOutline();
+ try self.extractOutline();
+ try self.computeViewBox();
+ try self.printSVG();
+ }
+};
+
+pub fn main() !void {
+ var file = try std.fs.cwd().createFile("out.svg", .{});
+ defer file.close();
+
+ var outline_printer = try OutlinePrinter.init(file);
+ defer outline_printer.deinit();
+ try outline_printer.run('a');
+}