diff --git a/pkg/fontconfig/char_set.zig b/pkg/fontconfig/char_set.zig new file mode 100644 index 000000000..5ab7cd201 --- /dev/null +++ b/pkg/fontconfig/char_set.zig @@ -0,0 +1,25 @@ +const std = @import("std"); +const assert = std.debug.assert; +const c = @import("c.zig"); + +pub const CharSet = opaque { + pub fn create() *CharSet { + return @ptrCast(*CharSet, c.FcCharSetCreate()); + } + + pub fn destroy(self: *CharSet) void { + c.FcCharSetDestroy(self.cval()); + } + + pub inline fn cval(self: *CharSet) *c.struct__FcCharSet { + return @ptrCast( + *c.struct__FcCharSet, + self, + ); + } +}; + +test "create" { + var fs = CharSet.create(); + defer fs.destroy(); +} diff --git a/pkg/fontconfig/config.zig b/pkg/fontconfig/config.zig index ae20762f0..596b4e873 100644 --- a/pkg/fontconfig/config.zig +++ b/pkg/fontconfig/config.zig @@ -1,5 +1,6 @@ const std = @import("std"); const c = @import("c.zig"); +const CharSet = @import("char_set.zig").CharSet; const FontSet = @import("font_set.zig").FontSet; const ObjectSet = @import("object_set.zig").ObjectSet; const Pattern = @import("pattern.zig").Pattern; @@ -9,11 +10,60 @@ pub const Config = opaque { c.FcConfigDestroy(@ptrCast(*c.struct__FcConfig, self)); } - pub fn list(self: *Config, pat: *Pattern, os: *ObjectSet) *FontSet { + pub fn fontList(self: *Config, pat: *Pattern, os: *ObjectSet) *FontSet { return @ptrCast(*FontSet, c.FcFontList(self.cval(), pat.cval(), os.cval())); } + pub fn fontSort( + self: *Config, + pat: *Pattern, + trim: bool, + charset: ?[*]*CharSet, + ) FontSortResult { + var result: FontSortResult = undefined; + result.fs = @ptrCast(*FontSet, c.FcFontSort( + self.cval(), + pat.cval(), + if (trim) c.FcTrue else c.FcFalse, + @ptrCast([*c]?*c.struct__FcCharSet, charset), + @ptrCast([*c]c_uint, &result.result), + )); + + return result; + } + + pub fn fontRenderPrepare(self: *Config, pat: *Pattern, font: *Pattern) *Pattern { + return @ptrCast(*Pattern, c.FcFontRenderPrepare(self.cval(), pat.cval(), font.cval())); + } + + pub fn substituteWithPat(self: *Config, pat: *Pattern, kind: MatchKind) bool { + return c.FcConfigSubstitute( + self.cval(), + pat.cval(), + @enumToInt(kind), + ) == c.FcTrue; + } + pub inline fn cval(self: *Config) *c.struct__FcConfig { return @ptrCast(*c.struct__FcConfig, self); } }; + +pub const FontSortResult = struct { + result: Result, + fs: *FontSet, +}; + +pub const Result = enum(c_uint) { + match = c.FcResultMatch, + no_match = c.FcResultNoMatch, + type_mismatch = c.FcResultTypeMismatch, + no_id = c.FcResultNoId, + out_of_memory = c.FcResultOutOfMemory, +}; + +pub const MatchKind = enum(c_uint) { + pattern = c.FcMatchPattern, + font = c.FcMatchFont, + scan = c.FcMatchScan, +}; diff --git a/pkg/fontconfig/font_set.zig b/pkg/fontconfig/font_set.zig index 68a66d2e8..0651d275c 100644 --- a/pkg/fontconfig/font_set.zig +++ b/pkg/fontconfig/font_set.zig @@ -12,13 +12,21 @@ pub const FontSet = opaque { c.FcFontSetDestroy(self.cval()); } - pub fn len(self: *FontSet) u32 { - return @intCast(u32, self.cval().nfont); + pub fn fonts(self: *FontSet) []*Pattern { + const empty: [0]*Pattern = undefined; + const s = self.cval(); + if (s.fonts == null) return ∅ + const ptr = @ptrCast([*]*Pattern, @alignCast(@alignOf(*Pattern), s.fonts)); + const len = @intCast(usize, s.nfont); + return ptr[0..len]; } - pub fn get(self: *FontSet, idx: usize) *Pattern { - assert(idx < self.len()); - return @ptrCast(*Pattern, self.cval().fonts[idx]); + pub fn add(self: *FontSet, pat: *Pattern) bool { + return c.FcFontSetAdd(self.cval(), pat.cval()) == c.FcTrue; + } + + pub fn print(self: *FontSet) void { + c.FcFontSetPrint(self.cval()); } pub inline fn cval(self: *FontSet) *c.struct__FcFontSet { @@ -35,5 +43,5 @@ test "create" { var fs = FontSet.create(); defer fs.destroy(); - try testing.expectEqual(@as(u32, 0), fs.len()); + try testing.expectEqual(@as(usize, 0), fs.fonts().len); } diff --git a/pkg/fontconfig/main.zig b/pkg/fontconfig/main.zig index 1b1777b04..6531cfa95 100644 --- a/pkg/fontconfig/main.zig +++ b/pkg/fontconfig/main.zig @@ -1,5 +1,6 @@ pub const c = @import("c.zig"); pub usingnamespace @import("init.zig"); +pub usingnamespace @import("char_set.zig"); pub usingnamespace @import("config.zig"); pub usingnamespace @import("font_set.zig"); pub usingnamespace @import("object_set.zig"); diff --git a/pkg/fontconfig/pattern.zig b/pkg/fontconfig/pattern.zig index ddb031bce..750c51be2 100644 --- a/pkg/fontconfig/pattern.zig +++ b/pkg/fontconfig/pattern.zig @@ -14,6 +14,10 @@ pub const Pattern = opaque { c.FcPatternDestroy(self.cval()); } + pub fn defaultSubstitute(self: *Pattern) void { + c.FcDefaultSubstitute(self.cval()); + } + pub fn print(self: *Pattern) void { c.FcPatternPrint(self.cval()); } @@ -31,4 +35,6 @@ test "create" { test "name parse" { var pat = Pattern.parse(":monospace"); defer pat.destroy(); + + pat.defaultSubstitute(); } diff --git a/pkg/fontconfig/test.zig b/pkg/fontconfig/test.zig index fb7b36993..8af41f059 100644 --- a/pkg/fontconfig/test.zig +++ b/pkg/fontconfig/test.zig @@ -13,10 +13,42 @@ test "fc-list" { var os = fontconfig.ObjectSet.create(); defer os.destroy(); - var fs = cfg.list(pat, os); + var fs = cfg.fontList(pat, os); defer fs.destroy(); // Note: this is environmental, but in general we expect all our // testing environments to have at least one font. - try testing.expect(fs.len() > 0); + try testing.expect(fs.fonts().len > 0); +} + +test "fc-match" { + const testing = std.testing; + + var cfg = fontconfig.initLoadConfigAndFonts(); + defer cfg.destroy(); + + var pat = fontconfig.Pattern.create(); + errdefer pat.destroy(); + try testing.expect(cfg.substituteWithPat(pat, .pattern)); + pat.defaultSubstitute(); + + const result = cfg.fontSort(pat, false, null); + errdefer result.fs.destroy(); + + var fs = fontconfig.FontSet.create(); + defer fs.destroy(); + defer for (fs.fonts()) |font| font.destroy(); + + { + const fonts = result.fs.fonts(); + try testing.expect(fonts.len > 0); + for (fonts) |font| { + var pat_prep = cfg.fontRenderPrepare(pat, font); + try testing.expect(fs.add(pat_prep)); + } + result.fs.destroy(); + pat.destroy(); + } + + {} }