summaryrefslogtreecommitdiff
path: root/pkg/macos/text/font_collection.zig
blob: 8b4a86a6f2c94ae389ea7ab9f3c1f20ea5febcb3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
const std = @import("std");
const Allocator = std.mem.Allocator;
const foundation = @import("../foundation.zig");
const text = @import("../text.zig");
const c = @import("c.zig").c;

pub const FontCollection = opaque {
    pub fn createFromAvailableFonts() Allocator.Error!*FontCollection {
        return @as(
            ?*FontCollection,
            @ptrFromInt(@intFromPtr(c.CTFontCollectionCreateFromAvailableFonts(null))),
        ) orelse Allocator.Error.OutOfMemory;
    }

    pub fn createWithFontDescriptors(descs: *foundation.Array) Allocator.Error!*FontCollection {
        return @as(
            ?*FontCollection,
            @ptrFromInt(@intFromPtr(c.CTFontCollectionCreateWithFontDescriptors(
                @ptrCast(descs),
                null,
            ))),
        ) orelse Allocator.Error.OutOfMemory;
    }

    pub fn release(self: *FontCollection) void {
        c.CFRelease(self);
    }

    pub fn createMatchingFontDescriptors(self: *FontCollection) *foundation.Array {
        const result = c.CTFontCollectionCreateMatchingFontDescriptors(@ptrCast(self));
        if (result) |ptr| return @ptrFromInt(@intFromPtr(ptr));

        // If we have no results, we create an empty array. This is not
        // exactly matching the Mac API. We can fix this later if we want
        // but I chose this to make it slightly more Zig-like at the cost
        // of some memory in the rare case.
        return foundation.Array.create(anyopaque, &[_]*const anyopaque{}) catch unreachable;
    }
};

fn debugDumpList(list: *foundation.Array) !void {
    var i: usize = 0;
    while (i < list.getCount()) : (i += 1) {
        const desc = list.getValueAtIndex(text.FontDescriptor, i);
        {
            var buf: [128]u8 = undefined;
            const name = desc.copyAttribute(.name);
            defer name.release();
            const cstr = name.cstring(&buf, .utf8).?;

            var family_buf: [128]u8 = undefined;
            const family = desc.copyAttribute(.family_name);
            defer family.release();
            const family_cstr = family.cstring(&family_buf, .utf8).?;

            var buf2: [128]u8 = undefined;
            const url = desc.copyAttribute(.url);
            defer url.release();
            const path = path: {
                const blank = try foundation.String.createWithBytes("", .utf8, false);
                defer blank.release();

                const path = url.copyPath() orelse break :path "<no path>";
                defer path.release();

                const decoded = try foundation.URL.createStringByReplacingPercentEscapes(
                    path,
                    blank,
                );
                defer decoded.release();

                break :path decoded.cstring(&buf2, .utf8) orelse
                    "<path cannot be converted to string>";
            };

            std.log.warn("i={d} name={s} family={s} path={s}", .{ i, cstr, family_cstr, path });
        }
    }
}

test "collection" {
    const testing = std.testing;

    const v = try FontCollection.createFromAvailableFonts();
    defer v.release();

    const list = v.createMatchingFontDescriptors();
    defer list.release();

    try testing.expect(list.getCount() > 0);
}

test "from descriptors" {
    const testing = std.testing;

    const name = try foundation.String.createWithBytes("AppleColorEmoji", .utf8, false);
    defer name.release();

    const desc = try text.FontDescriptor.createWithNameAndSize(name, 12);
    defer desc.release();

    var desc_arr = [_]*const text.FontDescriptor{desc};
    const arr = try foundation.Array.create(text.FontDescriptor, &desc_arr);
    defer arr.release();

    const v = try FontCollection.createWithFontDescriptors(arr);
    defer v.release();

    const list = v.createMatchingFontDescriptors();
    defer list.release();

    try testing.expect(list.getCount() > 0);

    // try debugDumpList(list);
}

test "from descriptors no match" {
    const testing = std.testing;

    const name = try foundation.String.createWithBytes("ThisShouldNeverExist", .utf8, false);
    defer name.release();

    const desc = try text.FontDescriptor.createWithNameAndSize(name, 12);
    defer desc.release();

    var desc_arr = [_]*const text.FontDescriptor{desc};
    const arr = try foundation.Array.create(text.FontDescriptor, &desc_arr);
    defer arr.release();

    const v = try FontCollection.createWithFontDescriptors(arr);
    defer v.release();

    const list = v.createMatchingFontDescriptors();
    defer list.release();

    try testing.expect(list.getCount() == 0);

    //try debugDumpList(list);
}