13#include <sigc++/scoped_connection.h>
20#include <cairomm/surface.h>
21#include <glibmm/ustring.h>
25#include <glibmm/keyfile.h>
26#include <glibmm/miscutils.h>
28#include <pango/pango-fontmap.h>
29#include <pangomm/fontdescription.h>
30#include <pangomm/fontmap.h>
31#include <sigc++/connection.h>
32#include <unordered_map>
35namespace filesystem = std::filesystem;
44 auto surface = Cairo::ImageSurface::create(Cairo::Surface::Format::ARGB32, 128, 64);
45 auto context = Cairo::Context::create(
surface);
46 auto layout = Pango::Layout::create(context);
47 const char* txt =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
48 layout->set_text(txt);
49 auto size = 22 * PANGO_SCALE;
50 if (caps_height > 0) {
54 layout->set_font_description(desc);
55 context->move_to(1, 1);
56 layout->show_in_cairo_context(context);
59 auto pixels =
surface->get_data();
64 for (
auto y = 0; y <
height; ++y) {
65 for (
auto x = 0; x <
width; ++x) {
76 auto surface = Cairo::ImageSurface::create(Cairo::Surface::Format::ARGB32, 1, 1);
77 auto context = Cairo::Context::create(
surface);
78 auto layout = Pango::Layout::create(context);
79 const char* txt =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
80 layout->set_text(txt);
81 desc.set_size(72 * PANGO_SCALE);
82 layout->set_font_description(desc);
84 Pango::Rectangle ink, rect;
85 layout->get_extents(ink, rect);
86 return static_cast<double>(ink.get_width()) / PANGO_SCALE / strlen(txt);
91Glib::ustring
get_full_font_name(Glib::RefPtr<Pango::FontFamily> ff, Glib::RefPtr<Pango::FontFace> face) {
94 auto family = ff->get_name();
95 auto face_name = face ? face->get_name() : Glib::ustring();
96 auto name = face_name.empty() ? family : family +
' ' + face_name;
104 static_cast<int>(desc.get_weight()) * 1'000'000 +
105 static_cast<int>(desc.get_style()) * 10'000 +
106 static_cast<int>(desc.get_stretch()) * 100 +
107 static_cast<int>(desc.get_variant());
113 auto na = a.
ff->get_name();
114 auto nb = b.
ff->get_name();
116 bool sans_a = a.
synthetic && a.
ff->get_name() ==
"Sans";
117 bool sans_b = b.
synthetic && b.
ff->get_name() ==
"Sans";
118 if (sans_a != sans_b) {
152 g_warning(
"Missing case in sort_fonts");
157Glib::ustring
get_fontspec(
const Glib::ustring& family,
const Glib::ustring& face,
const Glib::ustring& variations) {
158 if (variations.empty()) {
159 return face.empty() ? family : family +
", " + face;
162 auto desc = (face.empty() ? family : family +
", " + face) +
" " + variations;
167Glib::ustring
get_fontspec(
const Glib::ustring& family,
const Glib::ustring& face) {
172 Pango::FontDescription copy(desc);
173 copy.unset_fields(Pango::FontMask::FAMILY);
174 copy.unset_fields(Pango::FontMask::SIZE);
175 auto str = copy.to_string();
179Glib::ustring
get_inkscape_fontspec(
const Glib::RefPtr<Pango::FontFamily>& ff,
const Glib::RefPtr<Pango::FontFace>& face,
const Glib::ustring& variations) {
180 if (!ff)
return Glib::ustring();
185Pango::FontDescription
get_font_description(
const Glib::RefPtr<Pango::FontFamily>& ff,
const Glib::RefPtr<Pango::FontFace>& face) {
186 if (!face)
return Pango::FontDescription(
"sans serif");
188 auto desc = face->describe();
189 desc.unset_fields(Pango::FontMask::SIZE);
207 auto keyfile = Glib::KeyFile::create();
210 Glib::ustring
weight(
"weight");
211 Glib::ustring
width(
"width");
212 Glib::ustring family(
"family");
213 Glib::ustring fontflags(
"flags");
215 for (
auto&& font : fonts) {
217 auto group = desc.to_string();
219 if (font.monospaced) {
225 if (font.variable_font) {
228 if (font.synthetic) {
231 keyfile->set_double(group,
weight, font.weight);
232 keyfile->set_double(group,
width, font.width);
233 keyfile->set_integer(group, family, font.family_kind);
234 keyfile->set_integer(group, fontflags, flags);
238 keyfile->save_to_file(filename);
242 std::unordered_map<std::string, FontInfo> info;
245 auto keyfile = Glib::KeyFile::create();
249 bool exists = filesystem::exists(filesystem::u8path(filename));
251 bool exists = filesystem::exists(filesystem::path(filename));
254 if (exists && keyfile->load_from_file(filename)) {
256 auto ver = keyfile->get_double(
cache_header,
"version");
259 Glib::ustring
weight(
"weight");
260 Glib::ustring
width(
"width");
261 Glib::ustring family(
"family");
262 Glib::ustring fontflags(
"flags");
264 for (
auto&& group : keyfile->get_groups()) {
268 auto flags = keyfile->get_integer(group, fontflags);
282 font.
width = keyfile->get_double(group,
width);
283 font.
family_kind = keyfile->get_integer(group, family);
285 info[group.raw()] = font;
289 catch (Glib::Error &error) {
290 std::cerr << G_STRFUNC <<
": font cache not loaded - " << error.what() << std::endl;
297 std::vector<FontInfo> fonts;
302 auto result = std::make_shared<std::vector<FontInfo>>();
306 std::vector<FontInfo> empty;
307 progress.report_or_throw(0,
"", empty);
311 progress.throw_if_cancelled();
312 bool update_cache =
false;
315 for (
auto ff : families) {
316 bool synthetic_font =
false;
317#if PANGO_VERSION_CHECK(1,46,0)
318 auto default_face = ff->get_face();
319 if (default_face && default_face->is_synthesized()) {
320 synthetic_font =
true;
323 progress.report_or_throw(
counter / families.size(), ff->get_name(), empty);
324 std::vector<FontInfo> family;
325 auto faces = ff->list_faces();
326 std::set<std::string> styles;
327 for (
auto face : faces) {
330 if (!synthetic_font && face->is_synthesized())
continue;
332 auto desc = face->describe();
333 desc.unset_fields(Pango::FontMask::SIZE);
334 std::string
key = desc.to_string();
335 if (styles.count(
key))
continue;
344 auto it =
cache.find(desc.to_string().raw());
345 if (it ==
cache.end()) {
350 double caps_height = 0.0;
355 g_warning(
"Cannot load font %s",
key.c_str());
360 info.
oblique = font->is_oblique();
363 auto glyph = font->LoadGlyph(font->MapUnicodeChar(
'E'));
366 caps_height = glyph->bbox_exact.height();
371 g_warning(
"Error loading font %s",
key.c_str());
388 fonts.emplace_back(info);
389 family.emplace_back(info);
392 progress.report_or_throw(++
counter / families.size(),
"", family);
399 progress.report_or_throw(1,
"", empty);
405 auto at = fontspec.rfind(
'@');
406 if (at != Glib::ustring::npos && at > 0) {
408 while (at > 0 && fontspec[at - 1] ==
' ') at--;
410 return fontspec.substr(0, at);
417 i->gio_app()->signal_shutdown().connect([
this](){
434 sigc::scoped_connection con =
static_cast<sigc::connection
>(
_events.connect(fn));
Cairo::RefPtr< Cairo::ImageSurface > surface
std::unique_ptr< FontInstance > create_face(PangoFontDescription *descr)
std::vector< Glib::RefPtr< Pango::FontFamily > > get_font_families()
static InkscapeApplication * instance()
Singleton instance.
An interface for tasks to report progress and check for cancellation.
Async::Msg::Message< FontsPayload, double, Glib::ustring, std::vector< FontInfo > > MessageType
sigc::signal< void(const MessageType &)> _events
Inkscape::Async::OperationStream< FontsPayload, double, Glib::ustring, std::vector< FontInfo > > _loading
sigc::scoped_connection connect_to_fonts(std::function< void(const MessageType &)> fn)
sigc::scoped_connection _connection
static FontFactory & get(Args &&... args)
TODO: insert short description here.
The data describing a single loaded font.
const R * get_result(const Msg::Message< R, T... > &msg)
std::string profile_path()
Helper class to stream background task notifications as a series of messages.
const char cache_header[]
void save_font_cache(const std::vector< FontInfo > &fonts)
Glib::ustring get_inkscape_fontspec(const Glib::RefPtr< Pango::FontFamily > &ff, const Glib::RefPtr< Pango::FontFace > &face, const Glib::ustring &variations)
double calculate_font_weight(Pango::FontDescription &desc, double caps_height)
Glib::ustring get_fontspec_without_variants(const Glib::ustring &fontspec)
void sort_fonts_by_name(std::vector< FontInfo > &fonts, bool sans_first)
int get_font_style_order(const Pango::FontDescription &desc)
void sort_fonts(std::vector< FontInfo > &fonts, FontOrder order, bool sans_first)
Pango::FontDescription get_font_description(const Glib::RefPtr< Pango::FontFamily > &ff, const Glib::RefPtr< Pango::FontFace > &face)
Glib::ustring get_fontspec(const Glib::ustring &family, const Glib::ustring &face, const Glib::ustring &variations)
constexpr auto cache_version
Glib::ustring get_face_style(const Pango::FontDescription &desc)
std::vector< FontInfo > get_all_fonts()
Glib::ustring get_full_font_name(Glib::RefPtr< Pango::FontFamily > ff, Glib::RefPtr< Pango::FontFace > face)
double calculate_font_width(Pango::FontDescription &desc)
std::unordered_map< std::string, FontInfo > load_cached_font_info()
static cairo_user_data_key_t key
Inkscape::IO::Resource - simple resource API.
double sum(const double alpha[16], const double &x, const double &y)
unsigned short family_kind
Glib::RefPtr< Pango::FontFace > face
Glib::RefPtr< Pango::FontFamily > ff