From aca36ba79b9045c63538d7a28cb443fba527b090 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 14 Sep 2022 14:26:42 -0700 Subject: [PATCH] fontconfig: fc-match --- pkg/fontconfig/common.zig | 16 +++++++ pkg/fontconfig/config.zig | 16 +------ pkg/fontconfig/lang_set.zig | 25 +++++++++++ pkg/fontconfig/main.zig | 5 +++ pkg/fontconfig/matrix.zig | 10 +++++ pkg/fontconfig/pattern.zig | 87 +++++++++++++++++++++++++++++++++++++ pkg/fontconfig/range.zig | 16 +++++++ pkg/fontconfig/test.zig | 14 +++++- pkg/fontconfig/value.zig | 57 ++++++++++++++++++++++++ 9 files changed, 231 insertions(+), 15 deletions(-) create mode 100644 pkg/fontconfig/common.zig create mode 100644 pkg/fontconfig/lang_set.zig create mode 100644 pkg/fontconfig/matrix.zig create mode 100644 pkg/fontconfig/range.zig create mode 100644 pkg/fontconfig/value.zig diff --git a/pkg/fontconfig/common.zig b/pkg/fontconfig/common.zig new file mode 100644 index 000000000..7a1dd6749 --- /dev/null +++ b/pkg/fontconfig/common.zig @@ -0,0 +1,16 @@ +const std = @import("std"); +const c = @import("c.zig"); + +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/config.zig b/pkg/fontconfig/config.zig index 596b4e873..e22aec3f3 100644 --- a/pkg/fontconfig/config.zig +++ b/pkg/fontconfig/config.zig @@ -4,6 +4,8 @@ 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; +const Result = @import("main.zig").Result; +const MatchKind = @import("main.zig").MatchKind; pub const Config = opaque { pub fn destroy(self: *Config) void { @@ -53,17 +55,3 @@ 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/lang_set.zig b/pkg/fontconfig/lang_set.zig new file mode 100644 index 000000000..113932571 --- /dev/null +++ b/pkg/fontconfig/lang_set.zig @@ -0,0 +1,25 @@ +const std = @import("std"); +const assert = std.debug.assert; +const c = @import("c.zig"); + +pub const LangSet = opaque { + pub fn create() *LangSet { + return @ptrCast(*LangSet, c.FcLangSetCreate()); + } + + pub fn destroy(self: *LangSet) void { + c.FcLangSetDestroy(self.cval()); + } + + pub inline fn cval(self: *LangSet) *c.struct__FcLangSet { + return @ptrCast( + *c.struct__FcLangSet, + self, + ); + } +}; + +test "create" { + var fs = LangSet.create(); + defer fs.destroy(); +} diff --git a/pkg/fontconfig/main.zig b/pkg/fontconfig/main.zig index 6531cfa95..12ee3206e 100644 --- a/pkg/fontconfig/main.zig +++ b/pkg/fontconfig/main.zig @@ -1,10 +1,15 @@ pub const c = @import("c.zig"); pub usingnamespace @import("init.zig"); pub usingnamespace @import("char_set.zig"); +pub usingnamespace @import("common.zig"); pub usingnamespace @import("config.zig"); pub usingnamespace @import("font_set.zig"); +pub usingnamespace @import("lang_set.zig"); +pub usingnamespace @import("matrix.zig"); pub usingnamespace @import("object_set.zig"); pub usingnamespace @import("pattern.zig"); +pub usingnamespace @import("range.zig"); +pub usingnamespace @import("value.zig"); test { @import("std").testing.refAllDecls(@This()); diff --git a/pkg/fontconfig/matrix.zig b/pkg/fontconfig/matrix.zig new file mode 100644 index 000000000..11a7275ef --- /dev/null +++ b/pkg/fontconfig/matrix.zig @@ -0,0 +1,10 @@ +const std = @import("std"); +const assert = std.debug.assert; +const c = @import("c.zig"); + +pub const Matrix = extern struct { + xx: f64, + xy: f64, + yx: f64, + yy: f64, +}; diff --git a/pkg/fontconfig/pattern.zig b/pkg/fontconfig/pattern.zig index 750c51be2..a223f7e6f 100644 --- a/pkg/fontconfig/pattern.zig +++ b/pkg/fontconfig/pattern.zig @@ -1,5 +1,9 @@ const std = @import("std"); +const assert = std.debug.assert; const c = @import("c.zig"); +const Result = @import("main.zig").Result; +const Value = @import("main.zig").Value; +const ValueBinding = @import("main.zig").ValueBinding; pub const Pattern = opaque { pub fn create() *Pattern { @@ -18,6 +22,10 @@ pub const Pattern = opaque { c.FcDefaultSubstitute(self.cval()); } + pub fn objectIterator(self: *Pattern) ObjectIterator { + return .{ .pat = self.cval(), .iter = null }; + } + pub fn print(self: *Pattern) void { c.FcPatternPrint(self.cval()); } @@ -25,6 +33,85 @@ pub const Pattern = opaque { pub inline fn cval(self: *Pattern) *c.struct__FcPattern { return @ptrCast(*c.struct__FcPattern, self); } + + pub const ObjectIterator = struct { + pat: *c.struct__FcPattern, + iter: ?c.struct__FcPatternIter, + + /// Move to the next object, returns true if there is another + /// object and false otherwise. If this is the first call, this + /// will be teh first object. + pub fn next(self: *ObjectIterator) bool { + // Null means our first iterator + if (self.iter == null) { + // If we have no objects, do not create iterator + if (c.FcPatternObjectCount(self.pat) == 0) return false; + + var iter: c.struct__FcPatternIter = undefined; + c.FcPatternIterStart( + self.pat, + &iter, + ); + assert(c.FcPatternIterIsValid(self.pat, &iter) == c.FcTrue); + self.iter = iter; + + // Return right away because the fontconfig iterator pattern + // is do/while. + return true; + } + + return c.FcPatternIterNext( + self.pat, + @ptrCast([*c]c.struct__FcPatternIter, &self.iter), + ) == c.FcTrue; + } + + pub fn object(self: *ObjectIterator) []const u8 { + return std.mem.sliceTo(c.FcPatternIterGetObject( + self.pat, + &self.iter.?, + ), 0); + } + + pub fn valueLen(self: *ObjectIterator) usize { + return @intCast(usize, c.FcPatternIterValueCount(self.pat, &self.iter.?)); + } + + pub fn valueIterator(self: *ObjectIterator) ValueIterator { + return .{ + .pat = self.pat, + .iter = &self.iter.?, + .max = c.FcPatternIterValueCount(self.pat, &self.iter.?), + }; + } + }; + + pub const ValueIterator = struct { + pat: *c.struct__FcPattern, + iter: *c.struct__FcPatternIter, + max: c_int, + id: c_int = 0, + + pub const Entry = struct { + result: Result, + value: Value, + binding: ValueBinding, + }; + + pub fn next(self: *ValueIterator) ?Entry { + if (self.id >= self.max) return null; + var value: c.struct__FcValue = undefined; + var binding: c.FcValueBinding = undefined; + const result = c.FcPatternIterGetValue(self.pat, self.iter, self.id, &value, &binding); + self.id += 1; + + return Entry{ + .result = @intToEnum(Result, result), + .binding = @intToEnum(ValueBinding, binding), + .value = Value.init(&value), + }; + } + }; }; test "create" { diff --git a/pkg/fontconfig/range.zig b/pkg/fontconfig/range.zig new file mode 100644 index 000000000..fd7addaa8 --- /dev/null +++ b/pkg/fontconfig/range.zig @@ -0,0 +1,16 @@ +const std = @import("std"); +const assert = std.debug.assert; +const c = @import("c.zig"); + +pub const Range = opaque { + pub fn destroy(self: *Range) void { + c.FcRangeDestroy(self.cval()); + } + + pub inline fn cval(self: *Range) *c.struct__FcRange { + return @ptrCast( + *c.struct__FcRange, + self, + ); + } +}; diff --git a/pkg/fontconfig/test.zig b/pkg/fontconfig/test.zig index 8af41f059..7f2f2d063 100644 --- a/pkg/fontconfig/test.zig +++ b/pkg/fontconfig/test.zig @@ -50,5 +50,17 @@ test "fc-match" { pat.destroy(); } - {} + { + for (fs.fonts()) |font| { + var it = font.objectIterator(); + while (it.next()) { + try testing.expect(it.object().len > 0); + try testing.expect(it.valueLen() > 0); + var value_it = it.valueIterator(); + while (value_it.next()) |entry| { + try testing.expect(entry.value != .unknown); + } + } + } + } } diff --git a/pkg/fontconfig/value.zig b/pkg/fontconfig/value.zig new file mode 100644 index 000000000..4103c7528 --- /dev/null +++ b/pkg/fontconfig/value.zig @@ -0,0 +1,57 @@ +const std = @import("std"); +const assert = std.debug.assert; +const c = @import("c.zig"); +const CharSet = @import("main.zig").CharSet; +const LangSet = @import("main.zig").LangSet; +const Matrix = @import("main.zig").Matrix; +const Range = @import("main.zig").Range; + +pub const Type = enum(c_int) { + unknown = c.FcTypeUnknown, + @"void" = c.FcTypeVoid, + integer = c.FcTypeInteger, + double = c.FcTypeDouble, + string = c.FcTypeString, + @"bool" = c.FcTypeBool, + matrix = c.FcTypeMatrix, + char_set = c.FcTypeCharSet, + ft_face = c.FcTypeFTFace, + lang_set = c.FcTypeLangSet, + range = c.FcTypeRange, +}; + +pub const Value = union(Type) { + unknown: void, + @"void": void, + integer: u32, + double: f64, + string: []const u8, + @"bool": bool, + matrix: *const Matrix, + char_set: *const CharSet, + ft_face: *anyopaque, + lang_set: *const LangSet, + range: *const Range, + + pub fn init(cvalue: *c.struct__FcValue) Value { + return switch (@intToEnum(Type, cvalue.@"type")) { + .unknown => .{ .unknown = {} }, + .@"void" => .{ .@"void" = {} }, + .string => .{ .string = std.mem.sliceTo(cvalue.u.s, 0) }, + .integer => .{ .integer = @intCast(u32, cvalue.u.i) }, + .double => .{ .double = cvalue.u.d }, + .@"bool" => .{ .@"bool" = cvalue.u.b == c.FcTrue }, + .matrix => .{ .matrix = @ptrCast(*const Matrix, cvalue.u.m) }, + .char_set => .{ .char_set = @ptrCast(*const CharSet, cvalue.u.c) }, + .ft_face => .{ .ft_face = @ptrCast(*anyopaque, cvalue.u.f) }, + .lang_set => .{ .lang_set = @ptrCast(*const LangSet, cvalue.u.l) }, + .range => .{ .range = @ptrCast(*const Range, cvalue.u.r) }, + }; + } +}; + +pub const ValueBinding = enum(c_int) { + weak = c.FcValueBindingWeak, + strong = c.FcValueBindingStrong, + same = c.FcValueBindingSame, +};