21# include "config_auto.h"
24#if (defined __CYGWIN__)
26# undef __STRICT_ANSI__
37#include "pango/pango.h"
38#include "pango/pangocairo.h"
39#include "pango/pangofc-font.h"
47# include <sys/param.h>
50#define DISABLE_HEAP_LEAK_CHECK
60std::string PangoFontInfo::fonts_dir_;
61std::string PangoFontInfo::cache_dir_;
63static PangoGlyph get_glyph(PangoFont *font, gunichar wc) {
64#if PANGO_VERSION_CHECK(1, 44, 0)
66 hb_font_t *hb_font = pango_font_get_hb_font(font);
68 hb_font_get_nominal_glyph(hb_font, wc, &glyph);
71 PangoGlyph glyph = pango_fc_font_get_glyph(PANGO_FC_FONT(font), wc);
83 tprintf(
"ERROR: Could not parse %s\n", desc.c_str());
88void PangoFontInfo::Clear() {
93 pango_font_description_free(desc_);
99 pango_font_description_free(desc_);
106 char *desc_str = pango_font_description_to_string(desc_);
107 std::string desc_name(desc_str);
117 if (fonts_dir_.empty()) {
126 if (!cache_dir_.empty()) {
129 const int MAX_FONTCONF_FILESIZE = 1024;
130 char fonts_conf_template[MAX_FONTCONF_FILESIZE];
131 cache_dir_ = cache_dir;
132 fonts_dir_ = fonts_dir;
133 snprintf(fonts_conf_template, MAX_FONTCONF_FILESIZE,
134 "<?xml version=\"1.0\"?>\n"
135 "<!DOCTYPE fontconfig SYSTEM \"fonts.dtd\">\n"
138 "<cachedir>%s</cachedir>\n"
139 "<config></config>\n"
141 fonts_dir, cache_dir);
142 std::string fonts_conf_file =
File::JoinPath(cache_dir,
"fonts.conf");
145 std::string env(
"FONTCONFIG_PATH=");
146 env.append(cache_dir);
147 _putenv(env.c_str());
148 _putenv(
"LANG=en_US.utf8");
150 setenv(
"FONTCONFIG_PATH", cache_dir,
true);
152 setenv(
"LANG",
"en_US.utf8",
true);
155 if (FcInitReinitialize() != FcTrue) {
156 tprintf(
"FcInitiReinitialize failed!!\n");
160 pango_cairo_font_map_set_default(
nullptr);
163static void ListFontFamilies(PangoFontFamily ***families,
int *n_families) {
165 PangoFontMap *font_map = pango_cairo_font_map_get_default();
167 pango_font_map_list_families(font_map, families, n_families);
170bool PangoFontInfo::ParseFontDescription(
const PangoFontDescription *desc) {
172 const char *family = pango_font_description_get_family(desc);
174 char *desc_str = pango_font_description_to_string(desc);
175 tprintf(
"WARNING: Could not parse family name from description: '%s'\n", desc_str);
179 family_name_ = std::string(family);
180 desc_ = pango_font_description_copy(desc);
183 font_size_ = pango_font_description_get_size(desc);
184 if (!pango_font_description_get_size_is_absolute(desc)) {
185 font_size_ /= PANGO_SCALE;
192 PangoFontDescription *desc = pango_font_description_from_string(name.c_str());
193 bool success = ParseFontDescription(desc);
194 pango_font_description_free(desc);
201PangoFont *PangoFontInfo::ToPangoFont()
const {
203 PangoFontMap *font_map = pango_cairo_font_map_get_default();
204 PangoContext *context = pango_context_new();
205 pango_cairo_context_set_resolution(context, resolution_);
206 pango_context_set_font_map(context, font_map);
207 PangoFont *font =
nullptr;
210 font = pango_font_map_load_font(font_map, context, desc_);
212 g_object_unref(context);
217 PangoFont *font = ToPangoFont();
218 if (font ==
nullptr) {
222 PangoCoverage *coverage = pango_font_get_coverage(font,
nullptr);
228 if (pango_coverage_get(coverage, *it) != PANGO_COVERAGE_EXACT) {
230 int len = it.get_utf8(tmp);
232 tlog(2,
"'%s' (U+%x) not covered by font\n", tmp, *it);
233 pango_coverage_unref(coverage);
234 g_object_unref(font);
238 pango_coverage_unref(coverage);
239 g_object_unref(font);
245static char *my_strnmove(
char *
dest,
const char *src,
size_t n) {
254 }
while (n && src[0]);
266 int num_dropped_chars = 0;
267 PangoFont *font = ToPangoFont();
268 if (font ==
nullptr) {
270 num_dropped_chars = utf8_text->length();
272 return num_dropped_chars;
274 PangoCoverage *coverage = pango_font_get_coverage(font,
nullptr);
278 char *out =
const_cast<char *
>(utf8_text->c_str());
283 if (!it.is_legal()) {
288 int utf8_len = it.utf8_len();
289 const char *utf8_char = it.utf8_data();
292 if (!
IsWhitespace(unicode) && !pango_is_zero_width(unicode) &&
293 pango_coverage_get(coverage, unicode) != PANGO_COVERAGE_EXACT) {
297 tlog(2,
"'%s' (U+%x) not covered by font\n", str, unicode);
303 my_strnmove(out, utf8_char, utf8_len);
306 pango_coverage_unref(coverage);
307 g_object_unref(font);
308 utf8_text->resize(out - utf8_text->c_str());
309 return num_dropped_chars;
313 int *x_advance)
const {
315 PangoFont *font = ToPangoFont();
320 int total_advance = 0;
328 PangoGlyph glyph_index = get_glyph(font, *it);
331 g_object_unref(font);
335 PangoRectangle ink_rect, logical_rect;
336 pango_font_get_glyph_extents(font, glyph_index, &ink_rect, &logical_rect);
337 pango_extents_to_pixels(&ink_rect,
nullptr);
338 pango_extents_to_pixels(&logical_rect,
nullptr);
340 int bearing = total_advance + PANGO_LBEARING(ink_rect);
341 if (it == it_begin || bearing < min_bearing) {
342 min_bearing = bearing;
344 total_advance += PANGO_RBEARING(logical_rect);
346 *x_bearing = min_bearing;
347 *x_advance = total_advance;
348 g_object_unref(font);
353 std::vector<std::string> graphemes;
358 std::vector<std::string> *graphemes)
const {
370 const char32 kDottedCircleGlyph = 9676;
371 bool bad_glyph =
false;
372 PangoFontMap *font_map = pango_cairo_font_map_get_default();
373 PangoContext *context = pango_context_new();
374 pango_context_set_font_map(context, font_map);
379 layout = pango_layout_new(context);
382 pango_layout_set_font_description(layout, desc_);
384 PangoFontDescription *desc = pango_font_description_from_string(
DescriptionName().c_str());
385 pango_layout_set_font_description(layout, desc);
386 pango_font_description_free(desc);
388 pango_layout_set_text(layout, utf8_word, len);
389 PangoLayoutIter *run_iter =
nullptr;
392 run_iter = pango_layout_get_iter(layout);
395 PangoLayoutRun *run = pango_layout_iter_get_run_readonly(run_iter);
397 tlog(2,
"Found end of line nullptr run marker\n");
400 PangoGlyph dotted_circle_glyph;
401 PangoFont *font = run->item->analysis.font;
403 dotted_circle_glyph = get_glyph(font, kDottedCircleGlyph);
406 PangoFontDescription *desc = pango_font_describe(font);
407 char *desc_str = pango_font_description_to_string(desc);
408 tlog(2,
"Desc of font in run: %s\n", desc_str);
410 pango_font_description_free(desc);
413 PangoGlyphItemIter cluster_iter;
414 gboolean have_cluster;
415 for (have_cluster = pango_glyph_item_iter_init_start(&cluster_iter, run, utf8_word);
416 have_cluster && !bad_glyph;
417 have_cluster = pango_glyph_item_iter_next_cluster(&cluster_iter)) {
418 const int start_byte_index = cluster_iter.start_index;
419 const int end_byte_index = cluster_iter.end_index;
420 int start_glyph_index = cluster_iter.start_glyph;
421 int end_glyph_index = cluster_iter.end_glyph;
422 std::string cluster_text =
423 std::string(utf8_word + start_byte_index, end_byte_index - start_byte_index);
425 graphemes->push_back(cluster_text);
428 tlog(2,
"Skipping whitespace\n");
432 printf(
"start_byte=%d end_byte=%d start_glyph=%d end_glyph=%d ", start_byte_index,
433 end_byte_index, start_glyph_index, end_glyph_index);
435 for (
int i = start_glyph_index, step = (end_glyph_index > start_glyph_index) ? 1 : -1;
436 !bad_glyph &&
i != end_glyph_index;
i += step) {
437 const bool unknown_glyph =
438 (cluster_iter.glyph_item->glyphs->glyphs[
i].glyph & PANGO_GLYPH_UNKNOWN_FLAG);
439 const bool illegal_glyph =
440 (cluster_iter.glyph_item->glyphs->glyphs[
i].glyph == dotted_circle_glyph);
441 bad_glyph = unknown_glyph || illegal_glyph;
443 printf(
"(%d=%d)", cluster_iter.glyph_item->glyphs->glyphs[
i].glyph, bad_glyph ? 1 : 0);
447 printf(
" '%s'\n", cluster_text.c_str());
450 tlog(1,
"Found illegal glyph!\n");
452 }
while (!bad_glyph && pango_layout_iter_next_run(run_iter));
454 pango_layout_iter_free(run_iter);
455 g_object_unref(context);
456 g_object_unref(layout);
457 if (bad_glyph && graphemes) {
464std::vector<std::string> FontUtils::available_fonts_;
481 std::string query_desc(input_query_desc);
482 PangoFontDescription *desc = pango_font_description_from_string(query_desc.c_str());
483 PangoFont *selected_font =
nullptr;
486 PangoFontMap *font_map = pango_cairo_font_map_get_default();
487 PangoContext *context = pango_context_new();
488 pango_context_set_font_map(context, font_map);
491 selected_font = pango_font_map_load_font(font_map, context, desc);
493 g_object_unref(context);
495 if (selected_font ==
nullptr) {
496 pango_font_description_free(desc);
497 tlog(4,
"** Font '%s' failed to load from font map!\n", input_query_desc);
500 PangoFontDescription *selected_desc = pango_font_describe(selected_font);
502 bool equal = pango_font_description_equal(desc, selected_desc);
503 tlog(3,
"query weight = %d \t selected weight =%d\n", pango_font_description_get_weight(desc),
504 pango_font_description_get_weight(selected_desc));
506 char *selected_desc_str = pango_font_description_to_string(selected_desc);
507 tlog(2,
"query_desc: '%s' Selected: '%s'\n", query_desc.c_str(), selected_desc_str);
508 if (!equal && best_match !=
nullptr) {
509 *best_match = selected_desc_str;
512 int len = best_match->size();
513 if (len > 2 && best_match->at(len - 1) ==
'0' && best_match->at(len - 2) ==
' ') {
514 *best_match = best_match->substr(0, len - 2);
517 g_free(selected_desc_str);
518 pango_font_description_free(selected_desc);
519 g_object_unref(selected_font);
520 pango_font_description_free(desc);
522 tlog(4,
"** Font '%s' failed pango_font_description_equal!\n", input_query_desc);
526static bool ShouldIgnoreFontFamilyName(
const char *query) {
527 static const char *kIgnoredFamilyNames[] = {
"Sans",
"Serif",
"Monospace",
nullptr};
528 const char **list = kIgnoredFamilyNames;
529 for (; *list !=
nullptr; ++list) {
530 if (!strcmp(*list, query)) {
540 if (!available_fonts_.empty()) {
541 return available_fonts_;
544 PangoFontFamily **families =
nullptr;
546 ListFontFamilies(&families, &n_families);
547 for (
int i = 0;
i < n_families; ++
i) {
548 const char *family_name = pango_font_family_get_name(families[
i]);
549 tlog(2,
"Listing family %s\n", family_name);
550 if (ShouldIgnoreFontFamilyName(family_name)) {
555 PangoFontFace **faces =
nullptr;
556 pango_font_family_list_faces(families[
i], &faces, &n_faces);
557 for (
int j = 0; j < n_faces; ++j) {
558 PangoFontDescription *desc = pango_font_face_describe(faces[j]);
559 char *desc_str = pango_font_description_to_string(desc);
561 if (!pango_font_face_is_synthesized(faces[j]) &&
IsAvailableFont(desc_str)) {
562 available_fonts_.emplace_back(desc_str);
564 pango_font_description_free(desc);
570 std::sort(available_fonts_.begin(), available_fonts_.end());
571 return available_fonts_;
578 const std::string &fontname,
int *raw_score, std::vector<bool> *ch_flags) {
581 tprintf(
"ERROR: Could not parse %s\n", fontname.c_str());
583 PangoFont *font = font_info.ToPangoFont();
584 PangoCoverage *coverage =
nullptr;
585 if (font !=
nullptr) {
586 coverage = pango_font_get_coverage(font,
nullptr);
590 ch_flags->reserve(ch_map.size());
594 for (
auto &&it : ch_map) {
597 (pango_coverage_get(coverage, it.first) == PANGO_COVERAGE_EXACT));
600 ok_chars += it.second;
603 ch_flags->push_back(covered);
606 pango_coverage_unref(coverage);
607 g_object_unref(font);
613 std::vector<std::pair<
const char *, std::vector<bool>>> *fonts) {
614 const double kMinOKFraction = 0.99;
617 const double kMinWeightedFraction = 0.99995;
620 std::vector<std::vector<bool>> font_flags;
621 std::vector<int> font_scores;
622 std::vector<int> raw_scores;
623 int most_ok_chars = 0;
624 int best_raw_score = 0;
626 for (
const auto &font_name : font_names) {
627 std::vector<bool> ch_flags;
629 int ok_chars =
FontScore(ch_map, font_name, &raw_score, &ch_flags);
630 most_ok_chars = std::max(ok_chars, most_ok_chars);
631 best_raw_score = std::max(raw_score, best_raw_score);
633 font_flags.push_back(ch_flags);
634 font_scores.push_back(ok_chars);
635 raw_scores.push_back(raw_score);
646 int least_good_enough =
static_cast<int>(most_ok_chars * kMinOKFraction);
647 int least_raw_enough =
static_cast<int>(best_raw_score * kMinOKFraction);
648 int override_enough =
static_cast<int>(most_ok_chars * kMinWeightedFraction);
650 std::string font_list;
651 for (
unsigned i = 0;
i < font_names.size(); ++
i) {
652 int score = font_scores[
i];
653 int raw_score = raw_scores[
i];
654 if ((score >= least_good_enough && raw_score >= least_raw_enough) || score >= override_enough) {
655 fonts->push_back(std::make_pair(font_names[
i].c_str(), font_flags[
i]));
656 tlog(1,
"OK font %s = %.4f%%, raw = %d = %.2f%%\n", font_names[
i].c_str(),
657 100.0 * score / most_ok_chars, raw_score, 100.0 * raw_score / best_raw_score);
658 font_list += font_names[
i];
660 }
else if (score >= least_good_enough || raw_score >= least_raw_enough) {
661 tlog(1,
"Runner-up font %s = %.4f%%, raw = %d = %.2f%%\n", font_names[
i].c_str(),
662 100.0 * score / most_ok_chars, raw_score, 100.0 * raw_score / best_raw_score);
670 std::vector<std::string> *graphemes) {
676 const std::vector<std::string> &all_fonts, std::string *font_name,
677 std::vector<std::string> *graphemes) {
684 for (
const auto &all_font : all_fonts) {
686 std::vector<std::string> found_graphemes;
691 graphemes->swap(found_graphemes);
694 *font_name = all_font;
705 available_fonts_.clear();
711 PangoFontMap *font_map = pango_cairo_font_map_get_default();
712 if (pango_cairo_font_map_get_font_type(
reinterpret_cast<PangoCairoFontMap *
>(font_map)) ==
713 CAIRO_FONT_TYPE_TOY) {
714 printf(
"Using CAIRO_FONT_TYPE_TOY.\n");
715 }
else if (pango_cairo_font_map_get_font_type(
reinterpret_cast<PangoCairoFontMap *
>(font_map)) ==
716 CAIRO_FONT_TYPE_FT) {
717 printf(
"Using CAIRO_FONT_TYPE_FT.\n");
718 }
else if (pango_cairo_font_map_get_font_type(
reinterpret_cast<PangoCairoFontMap *
>(font_map)) ==
719 CAIRO_FONT_TYPE_WIN32) {
720 printf(
"Using CAIRO_FONT_TYPE_WIN32.\n");
721 }
else if (pango_cairo_font_map_get_font_type(
reinterpret_cast<PangoCairoFontMap *
>(font_map)) ==
722 CAIRO_FONT_TYPE_QUARTZ) {
723 printf(
"Using CAIRO_FONT_TYPE_QUARTZ.\n");
724 }
else if (pango_cairo_font_map_get_font_type(
reinterpret_cast<PangoCairoFontMap *
>(font_map)) ==
725 CAIRO_FONT_TYPE_USER) {
726 printf(
"Using CAIRO_FONT_TYPE_USER.\n");
727 }
else if (!font_map) {
728 printf(
"Cannot create pango cairo font map!\n");
#define ASSERT_HOST_MSG(x,...)
#define DISABLE_HEAP_LEAK_CHECK
#define TLOG_IS_ON(level)
bool IsWhitespace(const char32 ch)
const int kDefaultResolution
void tprintf(const char *format,...)
bool IsUTF8Whitespace(const char *text)
static const_iterator begin(const char *utf8_str, int byte_length)
static const_iterator end(const char *utf8_str, int byte_length)
int DropUncoveredChars(std::string *utf8_text) const
bool CoversUTF8Text(const char *utf8_text, int byte_length) const
bool ParseFontDescriptionName(const std::string &name)
std::string DescriptionName() const
bool CanRenderString(const char *utf8_word, int len, std::vector< std::string > *graphemes) const
static void HardInitFontConfig(const char *fonts_dir, const char *cache_dir)
bool GetSpacingProperties(const std::string &utf8_char, int *x_bearing, int *x_advance) const
static void SoftInitFontConfig()
static int FontScore(const std::unordered_map< char32, int64_t > &ch_map, const std::string &fontname, int *raw_score, std::vector< bool > *ch_flags)
static std::string BestFonts(const std::unordered_map< char32, int64_t > &ch_map, std::vector< std::pair< const char *, std::vector< bool > > > *font_flag)
static bool SelectFont(const char *utf8_word, const int utf8_len, std::string *font_name, std::vector< std::string > *graphemes)
static bool IsAvailableFont(const char *font_desc)
static void PangoFontTypeInfo()
static const std::vector< std::string > & ListAvailableFonts()
static bool DeleteMatchingFiles(const char *pattern)
static std::string JoinPath(const std::string &prefix, const std::string &suffix)
static void WriteStringToFileOrDie(const std::string &str, const std::string &filename)