/src/serenity/Userland/Libraries/LibGfx/Font/FontDatabase.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #include <AK/DeprecatedFlyString.h> |
8 | | #include <AK/FlyString.h> |
9 | | #include <AK/LexicalPath.h> |
10 | | #include <AK/Queue.h> |
11 | | #include <AK/QuickSort.h> |
12 | | #include <LibCore/Resource.h> |
13 | | #include <LibFileSystem/FileSystem.h> |
14 | | #include <LibGfx/Font/Font.h> |
15 | | #include <LibGfx/Font/FontDatabase.h> |
16 | | #include <LibGfx/Font/OpenType/Font.h> |
17 | | #include <LibGfx/Font/Typeface.h> |
18 | | #include <LibGfx/Font/WOFF/Font.h> |
19 | | |
20 | | namespace Gfx { |
21 | | |
22 | | FontDatabase& FontDatabase::the() |
23 | 0 | { |
24 | 0 | static FontDatabase s_the; |
25 | 0 | return s_the; |
26 | 0 | } |
27 | | |
28 | | static RefPtr<Font> s_default_font; |
29 | | static ByteString s_default_font_query; |
30 | | |
31 | | static RefPtr<Font> s_window_title_font; |
32 | | static ByteString s_window_title_font_query; |
33 | | |
34 | | static RefPtr<Font> s_fixed_width_font; |
35 | | static ByteString s_fixed_width_font_query; |
36 | | |
37 | | void FontDatabase::set_default_font_query(ByteString query) |
38 | 0 | { |
39 | 0 | if (s_default_font_query == query) |
40 | 0 | return; |
41 | 0 | s_default_font_query = move(query); |
42 | 0 | s_default_font = nullptr; |
43 | 0 | } |
44 | | |
45 | | ByteString FontDatabase::default_font_query() |
46 | 0 | { |
47 | 0 | return s_default_font_query; |
48 | 0 | } |
49 | | |
50 | | void FontDatabase::set_window_title_font_query(ByteString query) |
51 | 0 | { |
52 | 0 | if (s_window_title_font_query == query) |
53 | 0 | return; |
54 | 0 | s_window_title_font_query = move(query); |
55 | 0 | s_window_title_font = nullptr; |
56 | 0 | } |
57 | | |
58 | | ByteString FontDatabase::window_title_font_query() |
59 | 0 | { |
60 | 0 | return s_window_title_font_query; |
61 | 0 | } |
62 | | |
63 | | Font& FontDatabase::default_font() |
64 | 0 | { |
65 | 0 | if (!s_default_font) { |
66 | 0 | VERIFY(!s_default_font_query.is_empty()); |
67 | 0 | s_default_font = FontDatabase::the().get_by_name(s_default_font_query); |
68 | 0 | VERIFY(s_default_font); |
69 | 0 | } |
70 | 0 | return *s_default_font; |
71 | 0 | } |
72 | | |
73 | | Font& FontDatabase::window_title_font() |
74 | 0 | { |
75 | 0 | if (!s_window_title_font) { |
76 | 0 | VERIFY(!s_window_title_font_query.is_empty()); |
77 | 0 | s_window_title_font = FontDatabase::the().get_by_name(s_window_title_font_query); |
78 | 0 | VERIFY(s_window_title_font); |
79 | 0 | } |
80 | 0 | return *s_window_title_font; |
81 | 0 | } |
82 | | |
83 | | void FontDatabase::set_fixed_width_font_query(ByteString query) |
84 | 0 | { |
85 | 0 | if (s_fixed_width_font_query == query) |
86 | 0 | return; |
87 | 0 | s_fixed_width_font_query = move(query); |
88 | 0 | s_fixed_width_font = nullptr; |
89 | 0 | } |
90 | | |
91 | | ByteString FontDatabase::fixed_width_font_query() |
92 | 0 | { |
93 | 0 | return s_fixed_width_font_query; |
94 | 0 | } |
95 | | |
96 | | Font& FontDatabase::default_fixed_width_font() |
97 | 0 | { |
98 | 0 | if (!s_fixed_width_font) { |
99 | 0 | VERIFY(!s_fixed_width_font_query.is_empty()); |
100 | 0 | s_fixed_width_font = FontDatabase::the().get_by_name(s_fixed_width_font_query); |
101 | 0 | VERIFY(s_fixed_width_font); |
102 | 0 | } |
103 | 0 | return *s_fixed_width_font; |
104 | 0 | } |
105 | | |
106 | | struct FontDatabase::Private { |
107 | | HashMap<ByteString, NonnullRefPtr<Gfx::Font>, CaseInsensitiveStringTraits> full_name_to_font_map; |
108 | | HashMap<FlyString, Vector<NonnullRefPtr<Typeface>>, AK::ASCIICaseInsensitiveFlyStringTraits> typefaces; |
109 | | }; |
110 | | |
111 | | void FontDatabase::load_all_fonts_from_uri(StringView uri) |
112 | 0 | { |
113 | 0 | auto root_or_error = Core::Resource::load_from_uri(uri); |
114 | 0 | if (root_or_error.is_error()) { |
115 | 0 | if (root_or_error.error().is_errno() && root_or_error.error().code() == ENOENT) { |
116 | 0 | return; |
117 | 0 | } |
118 | 0 | dbgln("FontDatabase::load_all_fonts_from_uri('{}'): {}", uri, root_or_error.error()); |
119 | 0 | return; |
120 | 0 | } |
121 | 0 | auto root = root_or_error.release_value(); |
122 | |
|
123 | 0 | root->for_each_descendant_file([this](Core::Resource const& resource) -> IterationDecision { |
124 | 0 | auto uri = resource.uri(); |
125 | 0 | auto path = LexicalPath(uri.bytes_as_string_view()); |
126 | 0 | if (path.has_extension(".font"sv)) { |
127 | 0 | if (auto font_or_error = Gfx::BitmapFont::try_load_from_resource(resource); !font_or_error.is_error()) { |
128 | 0 | auto font = font_or_error.release_value(); |
129 | 0 | m_private->full_name_to_font_map.set(font->qualified_name().to_byte_string(), *font); |
130 | 0 | auto typeface = get_or_create_typeface(font->family(), font->variant()); |
131 | 0 | typeface->add_bitmap_font(font); |
132 | 0 | } |
133 | 0 | } else if (path.has_extension(".ttf"sv)) { |
134 | | // FIXME: What about .otf |
135 | 0 | if (auto font_or_error = OpenType::Font::try_load_from_resource(resource); !font_or_error.is_error()) { |
136 | 0 | auto font = font_or_error.release_value(); |
137 | 0 | auto typeface = get_or_create_typeface(font->family(), font->variant()); |
138 | 0 | typeface->set_vector_font(move(font)); |
139 | 0 | } |
140 | 0 | } else if (path.has_extension(".woff"sv)) { |
141 | 0 | if (auto font_or_error = WOFF::Font::try_load_from_resource(resource); !font_or_error.is_error()) { |
142 | 0 | auto font = font_or_error.release_value(); |
143 | 0 | auto typeface = get_or_create_typeface(font->family(), font->variant()); |
144 | 0 | typeface->set_vector_font(move(font)); |
145 | 0 | } |
146 | 0 | } |
147 | 0 | return IterationDecision::Continue; |
148 | 0 | }); |
149 | 0 | } |
150 | | |
151 | | FontDatabase::FontDatabase() |
152 | 0 | : m_private(make<Private>()) |
153 | 0 | { |
154 | 0 | load_all_fonts_from_uri("resource://fonts"sv); |
155 | 0 | } |
156 | | |
157 | | void FontDatabase::for_each_font(Function<void(Gfx::Font const&)> callback) |
158 | 0 | { |
159 | 0 | Vector<RefPtr<Gfx::Font>> fonts; |
160 | 0 | fonts.ensure_capacity(m_private->full_name_to_font_map.size()); |
161 | 0 | for (auto& it : m_private->full_name_to_font_map) |
162 | 0 | fonts.append(it.value); |
163 | 0 | quick_sort(fonts, [](auto& a, auto& b) { return a->qualified_name() < b->qualified_name(); }); |
164 | 0 | for (auto& font : fonts) |
165 | 0 | callback(*font); |
166 | 0 | } |
167 | | |
168 | | void FontDatabase::for_each_fixed_width_font(Function<void(Gfx::Font const&)> callback) |
169 | 0 | { |
170 | 0 | Vector<RefPtr<Gfx::Font>> fonts; |
171 | 0 | fonts.ensure_capacity(m_private->full_name_to_font_map.size()); |
172 | 0 | for (auto& it : m_private->full_name_to_font_map) { |
173 | 0 | if (it.value->is_fixed_width()) |
174 | 0 | fonts.append(it.value); |
175 | 0 | } |
176 | 0 | quick_sort(fonts, [](auto& a, auto& b) { return a->qualified_name() < b->qualified_name(); }); |
177 | 0 | for (auto& font : fonts) |
178 | 0 | callback(*font); |
179 | 0 | } |
180 | | |
181 | | RefPtr<Gfx::Font> FontDatabase::get_by_name(StringView name) |
182 | 0 | { |
183 | 0 | auto it = m_private->full_name_to_font_map.find(name); |
184 | 0 | if (it == m_private->full_name_to_font_map.end()) { |
185 | 0 | auto parts = name.split_view(" "sv); |
186 | 0 | if (parts.size() >= 4) { |
187 | 0 | auto slope = parts.take_last().to_number<int>().value_or(0); |
188 | 0 | auto weight = parts.take_last().to_number<int>().value_or(0); |
189 | 0 | auto size = parts.take_last().to_number<int>().value_or(0); |
190 | 0 | auto family = MUST(String::join(' ', parts)); |
191 | 0 | return get(family, size, weight, Gfx::FontWidth::Normal, slope); |
192 | 0 | } |
193 | 0 | dbgln("Font lookup failed: '{}'", name); |
194 | 0 | return nullptr; |
195 | 0 | } |
196 | 0 | return it->value; |
197 | 0 | } |
198 | | |
199 | | RefPtr<Gfx::Font> FontDatabase::get(FlyString const& family, float point_size, unsigned weight, unsigned width, unsigned slope, Font::AllowInexactSizeMatch allow_inexact_size_match) |
200 | 0 | { |
201 | 0 | auto it = m_private->typefaces.find(family); |
202 | 0 | if (it == m_private->typefaces.end()) |
203 | 0 | return nullptr; |
204 | 0 | for (auto const& typeface : it->value) { |
205 | 0 | if (typeface->weight() == weight && typeface->width() == width && typeface->slope() == slope) |
206 | 0 | return typeface->get_font(point_size, allow_inexact_size_match); |
207 | 0 | } |
208 | 0 | return nullptr; |
209 | 0 | } |
210 | | |
211 | | RefPtr<Gfx::Font> FontDatabase::get(FlyString const& family, FlyString const& variant, float point_size, Font::AllowInexactSizeMatch allow_inexact_size_match) |
212 | 0 | { |
213 | 0 | auto it = m_private->typefaces.find(family); |
214 | 0 | if (it == m_private->typefaces.end()) |
215 | 0 | return nullptr; |
216 | 0 | for (auto const& typeface : it->value) { |
217 | 0 | if (typeface->variant() == variant) |
218 | 0 | return typeface->get_font(point_size, allow_inexact_size_match); |
219 | 0 | } |
220 | 0 | return nullptr; |
221 | 0 | } |
222 | | |
223 | | RefPtr<Typeface> FontDatabase::get_or_create_typeface(FlyString const& family, FlyString const& variant) |
224 | 0 | { |
225 | 0 | auto it = m_private->typefaces.find(family); |
226 | 0 | if (it != m_private->typefaces.end()) { |
227 | 0 | for (auto const& typeface : it->value) { |
228 | 0 | if (typeface->variant() == variant) |
229 | 0 | return typeface; |
230 | 0 | } |
231 | 0 | } |
232 | 0 | auto typeface = adopt_ref(*new Typeface(family, variant)); |
233 | 0 | m_private->typefaces.ensure(family).append(typeface); |
234 | 0 | return typeface; |
235 | 0 | } |
236 | | |
237 | | void FontDatabase::for_each_typeface(Function<void(Typeface const&)> callback) |
238 | 0 | { |
239 | 0 | for (auto const& it : m_private->typefaces) { |
240 | 0 | for (auto const& jt : it.value) { |
241 | 0 | callback(*jt); |
242 | 0 | } |
243 | 0 | } |
244 | 0 | } |
245 | | |
246 | | void FontDatabase::for_each_typeface_with_family_name(FlyString const& family_name, Function<void(Typeface const&)> callback) |
247 | 0 | { |
248 | 0 | auto it = m_private->typefaces.find(family_name); |
249 | 0 | if (it == m_private->typefaces.end()) |
250 | 0 | return; |
251 | 0 | for (auto const& typeface : it->value) |
252 | 0 | callback(*typeface); |
253 | 0 | } |
254 | | |
255 | | } |