1#include <string>
2#include <algorithm>
3#include <mutex>
4#include <sstream>
5#include <map>
6#include <dirent.h>
7
8#ifdef HAVE_FONT_CONFIG
9#include <fontconfig/fontconfig.h>
10#endif
11
12#include "text_helper.hpp"
13#include "cast_c_array.hpp"
14
15namespace
16{
17 #ifdef HAVE_FONT_CONFIG
18 class FontConfig
19 {
20 public:
21 static
22 FcConfig*
23 get(void)
24 {
25 static FontConfig R;
26 return R.m_fc;
27 }
28
29 static
30 std::string
31 get_string(FcPattern *pattern, const char *label, std::string default_value = std::string())
32 {
33 FcChar8 *value(nullptr);
34 if (FcPatternGetString(pattern, label, 0, &value) == FcResultMatch)
35 {
36 return std::string((const char*)value);
37 }
38 else
39 {
40 return default_value;
41 }
42 }
43
44 static
45 int
46 get_int(FcPattern *pattern, const char *label, int default_value = 0)
47 {
48 int value(0);
49 if (FcPatternGetInteger(pattern, label, 0, &value) == FcResultMatch)
50 {
51 return value;
52 }
53 else
54 {
55 return default_value;
56 }
57 }
58
59 static
60 bool
61 get_bool(FcPattern *pattern, const char *label, bool default_value = false)
62 {
63 FcBool value(0);
64 if (FcPatternGetBool(pattern, label, 0, &value) == FcResultMatch)
65 {
66 return value;
67 }
68 else
69 {
70 return default_value;
71 }
72 }
73
74 static
75 fastuidraw::FontProperties
76 get_font_properties(FcPattern *pattern)
77 {
78 return fastuidraw::FontProperties()
79 .style(get_string(pattern, FC_STYLE).c_str())
80 .family(get_string(pattern, FC_FAMILY).c_str())
81 .foundry(get_string(pattern, FC_FOUNDRY).c_str())
82 .source_label(get_string(pattern, FC_FILE).c_str(),
83 get_int(pattern, FC_INDEX))
84 .bold(get_int(pattern, FC_WEIGHT) >= FC_WEIGHT_BOLD)
85 .italic(get_int(pattern, FC_SLANT) >= FC_SLANT_ITALIC);
86 }
87
88 private:
89 FontConfig(void)
90 {
91 m_fc = FcInitLoadConfigAndFonts();
92 }
93
94 ~FontConfig(void)
95 {
96 FcConfigDestroy(m_fc);
97 }
98
99 FcConfig* m_fc;
100 };
101 #endif
102 /* The purpose of the DaaBufferHolder is to -DELAY-
103 * the loading of data until the first time the data
104 * is requested.
105 */
106 class DataBufferLoader:public fastuidraw::reference_counted<DataBufferLoader>::concurrent
107 {
108 public:
109 explicit
110 DataBufferLoader(const std::string &pfilename):
111 m_filename(pfilename)
112 {}
113
114 fastuidraw::reference_counted_ptr<fastuidraw::DataBufferBase>
115 buffer(void)
116 {
117 fastuidraw::reference_counted_ptr<fastuidraw::DataBufferBase> R;
118
119 m_mutex.lock();
120 if (!m_buffer)
121 {
122 m_buffer = FASTUIDRAWnew fastuidraw::DataBuffer(m_filename.c_str());
123 }
124 R = m_buffer;
125 m_mutex.unlock();
126
127 return R;
128 }
129
130 private:
131 std::string m_filename;
132 std::mutex m_mutex;
133 fastuidraw::reference_counted_ptr<fastuidraw::DataBufferBase> m_buffer;
134 };
135
136 class FreeTypeFontGenerator:public fastuidraw::FontDatabase::FontGeneratorBase
137 {
138 public:
139 FreeTypeFontGenerator(fastuidraw::reference_counted_ptr<DataBufferLoader> buffer,
140 fastuidraw::reference_counted_ptr<fastuidraw::FreeTypeLib> lib,
141 int face_index,
142 const fastuidraw::FontProperties &props):
143 m_buffer(buffer),
144 m_lib(lib),
145 m_face_index(face_index),
146 m_props(props)
147 {}
148
149 virtual
150 fastuidraw::reference_counted_ptr<const fastuidraw::FontBase>
151 generate_font(void) const
152 {
153 fastuidraw::reference_counted_ptr<fastuidraw::FreeTypeFace::GeneratorBase> h;
154 fastuidraw::reference_counted_ptr<fastuidraw::DataBufferBase> buffer;
155 fastuidraw::reference_counted_ptr<const fastuidraw::FontBase> font;
156 buffer = m_buffer->buffer();
157 h = FASTUIDRAWnew fastuidraw::FreeTypeFace::GeneratorMemory(buffer, m_face_index);
158 font = FASTUIDRAWnew fastuidraw::FontFreeType(h, m_props, m_lib);
159 return font;
160 }
161
162 virtual
163 const fastuidraw::FontProperties&
164 font_properties(void) const
165 {
166 return m_props;
167 }
168
169 private:
170 fastuidraw::reference_counted_ptr<DataBufferLoader> m_buffer;
171 fastuidraw::reference_counted_ptr<fastuidraw::FreeTypeLib> m_lib;
172 int m_face_index;
173 fastuidraw::FontProperties m_props;
174 };
175
176 void
177 preprocess_text(std::string &text)
178 {
179 /* we want to change '\t' into 4 spaces
180 */
181 std::string v;
182 v.reserve(text.size() + 4 * std::count(text.begin(), text.end(), '\t'));
183 for(std::string::const_iterator iter = text.begin(); iter != text.end(); ++iter)
184 {
185 if (*iter != '\t')
186 {
187 v.push_back(*iter);
188 }
189 else
190 {
191 v.push_back(' ');
192 }
193 }
194 text.swap(v);
195 }
196
197 void
198 add_fonts_from_file(const std::string &filename,
199 fastuidraw::reference_counted_ptr<fastuidraw::FreeTypeLib> lib,
200 fastuidraw::reference_counted_ptr<fastuidraw::FontDatabase> font_database)
201 {
202 FT_Error error_code;
203 FT_Face face(nullptr);
204
205 lib->lock();
206 error_code = FT_New_Face(lib->lib(), filename.c_str(), 0, &face);
207 lib->unlock();
208
209 if (error_code == 0 && face != nullptr && (face->face_flags & FT_FACE_FLAG_SCALABLE) != 0)
210 {
211 fastuidraw::reference_counted_ptr<DataBufferLoader> buffer_loader;
212
213 buffer_loader = FASTUIDRAWnew DataBufferLoader(filename);
214 for(unsigned int i = 0, endi = face->num_faces; i < endi; ++i)
215 {
216 fastuidraw::reference_counted_ptr<const fastuidraw::FontDatabase::FontGeneratorBase> h;
217 enum fastuidraw::return_code R;
218 fastuidraw::FontProperties props;
219 if (i != 0)
220 {
221 lib->lock();
222 FT_Done_Face(face);
223 FT_New_Face(lib->lib(), filename.c_str(), i, &face);
224 lib->unlock();
225 }
226 fastuidraw::FontFreeType::compute_font_properties_from_face(face, props);
227 props.source_label(filename.c_str(), i);
228
229 h = FASTUIDRAWnew FreeTypeFontGenerator(buffer_loader, lib, i, props);
230 R = font_database->add_font_generator(h);
231
232 if (R != fastuidraw::routine_success)
233 {
234 std::cout << "Vanilla warning: unable to add font " << h->font_properties()
235 << " because it was already marked as added\n";
236 }
237 else
238 {
239 // std::cout << "Vanilla add font: " << props << "\n";
240 }
241 }
242 }
243
244 lib->lock();
245 if (face != nullptr)
246 {
247 FT_Done_Face(face);
248 }
249 lib->unlock();
250 }
251
252}
253
254/////////////////////////////
255// GlyphSetGenerator methods
256GlyphSetGenerator::
257GlyphSetGenerator(fastuidraw::GlyphRenderer r,
258 fastuidraw::reference_counted_ptr<const fastuidraw::FontBase> f,
259 std::vector<fastuidraw::Glyph> &dst):
260 m_render(r),
261 m_font(f)
262{
263 dst.resize(f->number_glyphs());
264 m_dst = fastuidraw::c_array<fastuidraw::Glyph>(&dst[0], dst.size());
265 SDL_AtomicSet(&m_counter, 0);
266}
267
268int
269GlyphSetGenerator::
270execute(void *ptr)
271{
272 unsigned int idx, K;
273 GlyphSetGenerator *p(static_cast<GlyphSetGenerator*>(ptr));
274
275 for(idx = SDL_AtomicAdd(&p->m_counter, 1), K = 0;
276 idx < p->m_dst.size();
277 idx = SDL_AtomicAdd(&p->m_counter, 1), ++K)
278 {
279 p->m_dst[idx] = fastuidraw::Glyph::create_glyph(p->m_render, p->m_font, idx);
280 }
281 return K;
282}
283
284void
285GlyphSetGenerator::
286generate(unsigned int num_threads,
287 fastuidraw::GlyphRenderer r,
288 fastuidraw::reference_counted_ptr<const fastuidraw::FontBase> f,
289 std::vector<fastuidraw::Glyph> &dst,
290 fastuidraw::GlyphCache &glyph_cache,
291 std::vector<int> &cnts)
292{
293 GlyphSetGenerator generator(r, f, dst);
294 std::vector<SDL_Thread*> threads;
295
296 cnts.clear();
297 cnts.resize(fastuidraw::t_max(1u, num_threads), 0);
298
299 if (num_threads < 2)
300 {
301 cnts[0] = execute(&generator);
302 }
303 else
304 {
305 for(int i = 0; i < num_threads; ++i)
306 {
307 threads.push_back(SDL_CreateThread(execute, "", &generator));
308 }
309
310 for(int i = 0; i < num_threads; ++i)
311 {
312 SDL_WaitThread(threads[i], &cnts[i]);
313 }
314 }
315
316 for(fastuidraw::Glyph glyph : dst)
317 {
318 if (glyph.valid())
319 {
320 enum fastuidraw::return_code R;
321
322 R = glyph_cache.add_glyph(glyph, false);
323 FASTUIDRAWassert(R == fastuidraw::routine_success);
324 FASTUIDRAWunused(R);
325 }
326 }
327}
328
329//////////////////////////////
330// global methods
331void
332create_formatted_text(fastuidraw::GlyphSequence &out_sequence,
333 const std::vector<uint32_t> &glyph_codes,
334 const fastuidraw::FontBase *font,
335 const fastuidraw::vec2 &shift_by)
336{
337 fastuidraw::vec2 pen(shift_by);
338 float format_size(out_sequence.format_size());
339 fastuidraw::GlyphMetrics layout;
340 float ratio;
341
342 for(uint32_t glyph_code : glyph_codes)
343 {
344 layout = out_sequence.glyph_cache().fetch_glyph_metrics(font, glyph_code);
345 if (layout.valid())
346 {
347 out_sequence.add_glyph(fastuidraw::GlyphSource(glyph_code, font), pen);
348
349 ratio = format_size / layout.units_per_EM();
350 pen.x() += ratio * layout.advance().x();
351 }
352 }
353}
354
355template<typename T>
356static
357void
358create_formatted_textT(T &out_sequence,
359 enum fastuidraw::Painter::screen_orientation orientation,
360 std::istream &istr,
361 const fastuidraw::FontBase *font,
362 fastuidraw::reference_counted_ptr<fastuidraw::FontDatabase> font_database,
363 const fastuidraw::vec2 &starting_place)
364{
365 std::streampos current_position, end_position;
366 float format_size(out_sequence.format_size());
367 unsigned int loc(0);
368 fastuidraw::vec2 pen(starting_place);
369 std::string line, original_line;
370 bool first_line(true);
371 float pen_y_advance, ratio;
372
373 current_position = istr.tellg();
374 istr.seekg(0, std::ios::end);
375 end_position = istr.tellg();
376 istr.seekg(current_position, std::ios::beg);
377
378 ratio = format_size / font->metrics().units_per_EM();
379 pen_y_advance = ratio * font->metrics().height();
380
381 std::vector<fastuidraw::GlyphSource> glyph_sources;
382 std::vector<fastuidraw::vec2> sub_p;
383 std::vector<fastuidraw::GlyphMetrics> metrics;
384
385 while(getline(istr, line))
386 {
387 fastuidraw::c_array<uint32_t> sub_ch;
388 fastuidraw::c_array<fastuidraw::range_type<float> > sub_extents;
389 bool empty_line;
390
391 empty_line = true;
392
393 original_line = line;
394 preprocess_text(line);
395
396 sub_p.resize(line.length());
397 glyph_sources.resize(line.length());
398 metrics.resize(line.length());
399
400 font_database->create_glyph_sequence(font, line.begin(), line.end(), glyph_sources.begin());
401 out_sequence.glyph_cache().fetch_glyph_metrics(cast_c_array(glyph_sources), cast_c_array(metrics));
402 for(unsigned int i = 0, endi = glyph_sources.size(); i < endi; ++i)
403 {
404 sub_p[i] = pen;
405 if (glyph_sources[i].m_font)
406 {
407 empty_line = false;
408 pen.x() += ratio * metrics[i].advance().x();
409 }
410 }
411
412 if (orientation == fastuidraw::Painter::y_increases_downwards)
413 {
414 pen.y() += pen_y_advance;
415 }
416 else
417 {
418 pen.y() -= pen_y_advance;
419 }
420
421 pen.x() = starting_place.x();
422 loc += line.length();
423 first_line = false;
424
425 out_sequence.add_glyphs(const_cast_c_array(glyph_sources),
426 const_cast_c_array(sub_p));
427 }
428}
429
430void
431create_formatted_text(fastuidraw::GlyphSequence &out_sequence,
432 enum fastuidraw::Painter::screen_orientation orientation,
433 std::istream &istr,
434 const fastuidraw::FontBase *font,
435 fastuidraw::reference_counted_ptr<fastuidraw::FontDatabase> font_database,
436 const fastuidraw::vec2 &starting_place)
437{
438 create_formatted_textT(out_sequence, orientation, istr, font, font_database, starting_place);
439}
440
441void
442create_formatted_text(fastuidraw::GlyphRun &out_sequence,
443 enum fastuidraw::Painter::screen_orientation orientation,
444 std::istream &istr,
445 const fastuidraw::FontBase *font,
446 fastuidraw::reference_counted_ptr<fastuidraw::FontDatabase> font_database,
447 const fastuidraw::vec2 &starting_place)
448{
449 create_formatted_textT(out_sequence, orientation, istr, font, font_database, starting_place);
450}
451
452void
453add_fonts_from_path(const std::string &filename,
454 fastuidraw::reference_counted_ptr<fastuidraw::FreeTypeLib> lib,
455 fastuidraw::reference_counted_ptr<fastuidraw::FontDatabase> font_database)
456{
457 DIR *dir;
458 struct dirent *entry;
459
460 dir = opendir(filename.c_str());
461 if (!dir)
462 {
463 add_fonts_from_file(filename, lib, font_database);
464 return;
465 }
466
467 for(entry = readdir(dir); entry != nullptr; entry = readdir(dir))
468 {
469 std::string file;
470 file = entry->d_name;
471 if (file != ".." && file != ".")
472 {
473 if (filename.empty() || filename.back() != '/')
474 {
475 add_fonts_from_path(filename + "/" + file, lib, font_database);
476 }
477 else
478 {
479 add_fonts_from_path(filename + file, lib, font_database);
480 }
481 }
482 }
483 closedir(dir);
484}
485
486fastuidraw::c_string
487default_font(void)
488{
489 #ifdef _WIN32
490 {
491 return "C:/Windows/Fonts/arial.ttf";
492 }
493 #elif defined(__APPLE__)
494 {
495 return "/Library/Fonts/Arial.ttf";
496 }
497 #else
498 {
499 return "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf";
500 }
501 #endif
502}
503
504fastuidraw::c_string
505default_font_path(void)
506{
507 #ifdef _WIN32
508 {
509 return "C:/Windows/Fonts";
510 }
511 #elif defined(__APPLE__)
512 {
513 return "/Library/Fonts/";
514 }
515 #else
516 {
517 return "/usr/share/fonts/";
518 }
519 #endif
520}
521
522void
523add_fonts_from_font_config(fastuidraw::reference_counted_ptr<fastuidraw::FreeTypeLib> lib,
524 fastuidraw::reference_counted_ptr<fastuidraw::FontDatabase> font_database)
525{
526 #ifdef HAVE_FONT_CONFIG
527 {
528 FcConfig *config = FontConfig::get();
529 FcObjectSet *object_set;
530 FcFontSet *font_set;
531 FcPattern* pattern;
532 std::map<std::string, fastuidraw::reference_counted_ptr<DataBufferLoader> > buffer_loaders;
533
534 object_set = FcObjectSetBuild(FC_FOUNDRY, FC_FAMILY, FC_STYLE, FC_WEIGHT,
535 FC_SLANT, FC_SCALABLE, FC_FILE, FC_INDEX,
536 FC_LANG, nullptr);
537 pattern = FcPatternCreate();
538 FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
539 font_set = FcFontList(config, pattern, object_set);
540
541 for (int i = 0; i < font_set->nfont; ++i)
542 {
543 std::string filename;
544
545 filename = FontConfig::get_string(font_set->fonts[i], FC_FILE);
546 if (filename != "")
547 {
548 fastuidraw::reference_counted_ptr<DataBufferLoader> b;
549 fastuidraw::reference_counted_ptr<const fastuidraw::FontDatabase::FontGeneratorBase> g;
550 std::map<std::string, fastuidraw::reference_counted_ptr<DataBufferLoader> >::const_iterator iter;
551 int face_index;
552 enum fastuidraw::return_code R;
553 fastuidraw::FontProperties props(FontConfig::get_font_properties(font_set->fonts[i]));
554
555 iter = buffer_loaders.find(filename);
556 if (iter == buffer_loaders.end())
557 {
558 b = FASTUIDRAWnew DataBufferLoader(filename);
559 buffer_loaders[filename] = b;
560 }
561 else
562 {
563 b = iter->second;
564 }
565
566 face_index = FontConfig::get_int(font_set->fonts[i], FC_INDEX);
567 g = FASTUIDRAWnew FreeTypeFontGenerator(b, lib, face_index, props);
568
569 R = font_database->add_font_generator(g);
570 if (R != fastuidraw::routine_success)
571 {
572 std::cout << "FontConfig Warning: unable to add font " << props
573 << " because it was already marked as added\n";
574 }
575 else
576 {
577 // std::cout << "FontConfig add font: " << props << "\n";
578 }
579 }
580 }
581 FcFontSetDestroy(font_set);
582 FcPatternDestroy(pattern);
583 FcObjectSetDestroy(object_set);
584 }
585 #endif
586}
587
588fastuidraw::reference_counted_ptr<const fastuidraw::FontBase>
589select_font_font_config(int weight, int slant,
590 fastuidraw::c_string style,
591 fastuidraw::c_string family,
592 fastuidraw::c_string foundry,
593 const std::set<std::string> &langs,
594 fastuidraw::reference_counted_ptr<fastuidraw::FreeTypeLib> lib,
595 fastuidraw::reference_counted_ptr<fastuidraw::FontDatabase> font_database)
596{
597 #ifdef HAVE_FONT_CONFIG
598 {
599 FcConfig *config = FontConfig::get();
600 FcPattern* pattern;
601 FcLangSet* lang_set(nullptr);
602
603 pattern = FcPatternCreate();
604 if (weight >= 0)
605 {
606 FcPatternAddInteger(pattern, FC_WEIGHT, weight);
607 }
608
609 if (slant >= 0)
610 {
611 FcPatternAddInteger(pattern, FC_SLANT, slant);
612 }
613
614 if (style)
615 {
616 FcPatternAddString(pattern, FC_STYLE, (const FcChar8*)style);
617 }
618
619 if (family)
620 {
621 FcPatternAddString(pattern, FC_FAMILY, (const FcChar8*)family);
622 }
623
624 if (foundry)
625 {
626 FcPatternAddString(pattern, FC_FOUNDRY, (const FcChar8*)foundry);
627 }
628
629 for (const std::string &lang : langs)
630 {
631 if (!lang_set)
632 {
633 lang_set = FcLangSetCreate();
634 }
635 FcLangSetAdd(lang_set, (const FcChar8*)lang.c_str());
636 }
637
638 if (lang_set)
639 {
640 FcPatternAddLangSet(pattern, FC_LANG, lang_set);
641 }
642
643 FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
644
645 FcConfigSubstitute(config, pattern, FcMatchPattern);
646 FcDefaultSubstitute(pattern);
647
648 FcResult r;
649 FcPattern *font_pattern = FcFontMatch(config, pattern, &r);
650 fastuidraw::reference_counted_ptr<const fastuidraw::FontBase> font;
651
652 if (font_pattern)
653 {
654 FcChar8* filename(nullptr);
655 if (FcPatternGetString(font_pattern, FC_FILE, 0, &filename) == FcResultMatch)
656 {
657 int face_index;
658
659 face_index = FontConfig::get_int(font_pattern, FC_INDEX);
660 font = font_database->fetch_font((fastuidraw::c_string)filename, face_index);
661 if (!font)
662 {
663 fastuidraw::FontProperties props;
664 fastuidraw::reference_counted_ptr<FreeTypeFontGenerator> gen;
665 fastuidraw::reference_counted_ptr<DataBufferLoader> buffer_loader;
666
667 props = FontConfig::get_font_properties(font_pattern);
668 buffer_loader = FASTUIDRAWnew DataBufferLoader(std::string((const char*)filename));
669 gen = FASTUIDRAWnew FreeTypeFontGenerator(buffer_loader, lib, face_index, props);
670
671 font = font_database->fetch_or_generate_font(gen);
672 }
673 }
674 FcPatternDestroy(font_pattern);
675 }
676 FcPatternDestroy(pattern);
677 if (lang_set)
678 {
679 FcLangSetDestroy(lang_set);
680 }
681 return font;
682 }
683 #else
684 {
685 fastuidraw::FontProperties props;
686 uint32_t flags(0u);
687
688 if (foundry)
689 {
690 props.foundry(foundry);
691 }
692
693 if (family)
694 {
695 props.family(family);
696 }
697
698 if (style)
699 {
700 props.style(style);
701 }
702 else
703 {
704 flags |= fastuidraw::FontDatabase::ignore_style;
705 }
706
707 if (weight >=0 && slant >= 0)
708 {
709 props
710 .bold(weight >= 200)
711 .italic(slant >= 100);
712 }
713 else
714 {
715 flags |= fastuidraw::FontDatabase::ignore_bold_italic;
716 }
717
718 return font_database->fetch_font(props, flags);
719 }
720 #endif
721}
722