1 /*
2 * Copyright © 2011 Google, Inc.
3 *
4 * This is part of HarfBuzz, a text shaping library.
5 *
6 * Permission is hereby granted, without written agreement and without
7 * license or royalty fees, to use, copy, modify, and distribute this
8 * software and its documentation for any purpose, provided that the
9 * above copyright notice and the following two paragraphs appear in
10 * all copies of this software.
11 *
12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16 * DAMAGE.
17 *
18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23 *
24 * Google Author(s): Behdad Esfahbod
25 */
26
27 #ifndef FACE_OPTIONS_HH
28 #define FACE_OPTIONS_HH
29
30 #include "options.hh"
31
32 #ifdef HAVE_FREETYPE
33 #include <hb-ft.h>
34 #endif
35 #ifdef HAVE_CORETEXT
36 #include <hb-coretext.h>
37 #endif
38
39 struct face_options_t
40 {
~face_options_tface_options_t41 ~face_options_t ()
42 {
43 g_free (face_loader);
44 g_free (font_file);
45 }
46
set_faceface_options_t47 void set_face (hb_face_t *face_)
48 { face = face_; }
49
50 void add_options (option_parser_t *parser);
51
52 void post_parse (GError **error);
53
54 static struct cache_t
55 {
~cache_tface_options_t::cache_t56 ~cache_t ()
57 {
58 g_free (font_path);
59 hb_face_destroy (face);
60 }
61
62 char *font_path = nullptr;
63 unsigned face_index = (unsigned) -1;
64 hb_face_t *face = nullptr;
65 } cache;
66
67 char *font_file = nullptr;
68 unsigned face_index = 0;
69 char *face_loader = nullptr;
70
71 hb_face_t *face = nullptr;
72 };
73
74
75 face_options_t::cache_t face_options_t::cache {};
76
77 static struct supported_face_loaders_t {
78 char name[9];
79 hb_face_t * (*func) (const char *font_file, unsigned face_index);
80 } supported_face_loaders[] =
81 {
82 {"ot", hb_face_create_from_file_or_fail},
83 #ifdef HAVE_FREETYPE
84 {"ft", hb_ft_face_create_from_file_or_fail},
85 #endif
86 #ifdef HAVE_CORETEXT
87 {"coretext", hb_coretext_face_create_from_file_or_fail},
88 #endif
89 };
90
91 void
post_parse(GError ** error)92 face_options_t::post_parse (GError **error)
93 {
94 if (!font_file)
95 {
96 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
97 "No font file set");
98 return;
99 }
100
101 assert (font_file);
102
103 const char *font_path = font_file;
104
105 if (0 == strcmp (font_path, "-"))
106 {
107 #if defined(_WIN32) || defined(__CYGWIN__)
108 setmode (fileno (stdin), O_BINARY);
109 font_path = "STDIN";
110 #else
111 font_path = "/dev/stdin";
112 #endif
113 }
114
115 hb_face_t * (*face_load) (const char *file_name, unsigned face_index) = nullptr;
116 if (!face_loader)
117 {
118 face_load = supported_face_loaders[0].func;
119 }
120 else
121 {
122 for (unsigned int i = 0; i < ARRAY_LENGTH (supported_face_loaders); i++)
123 if (0 == g_ascii_strcasecmp (face_loader, supported_face_loaders[i].name))
124 {
125 face_load = supported_face_loaders[i].func;
126 break;
127 }
128 if (!face_load)
129 {
130 GString *s = g_string_new (nullptr);
131 for (unsigned int i = 0; i < ARRAY_LENGTH (supported_face_loaders); i++)
132 {
133 if (i)
134 g_string_append_c (s, '/');
135 g_string_append (s, supported_face_loaders[i].name);
136 }
137 g_string_append_c (s, '\n');
138 char *p = g_string_free (s, FALSE);
139 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
140 "Unknown face loader `%s'; supported values are: %s; default is %s",
141 face_loader,
142 p,
143 supported_face_loaders[0].name);
144 free (p);
145 return;
146 }
147 }
148
149 if (!cache.font_path ||
150 0 != strcmp (cache.font_path, font_path) ||
151 cache.face_index != face_index)
152 {
153 hb_face_destroy (cache.face);
154 cache.face = face_load (font_path, face_index);
155 cache.face_index = face_index;
156
157 free ((char *) cache.font_path);
158 cache.font_path = g_strdup (font_path);
159
160 if (!cache.face)
161 {
162 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
163 "%s: Failed loading font face", font_path);
164 return;
165 }
166 }
167
168 face = cache.face;
169 }
170
171 void
add_options(option_parser_t * parser)172 face_options_t::add_options (option_parser_t *parser)
173 {
174 char *face_loaders_text = nullptr;
175 {
176 static_assert ((ARRAY_LENGTH_CONST (supported_face_loaders) > 0),
177 "No supported face-loaders found.");
178 GString *s = g_string_new (nullptr);
179 g_string_printf (s, "Set face loader to use (default: %s)\n\n Supported face loaders are: %s",
180 supported_face_loaders[0].name,
181 supported_face_loaders[0].name);
182 for (unsigned int i = 1; i < ARRAY_LENGTH (supported_face_loaders); i++)
183 {
184 g_string_append_c (s, '/');
185 g_string_append (s, supported_face_loaders[i].name);
186 }
187 face_loaders_text = g_string_free (s, FALSE);
188 parser->free_later (face_loaders_text);
189 }
190
191 GOptionEntry entries[] =
192 {
193 {"font-file", 0, 0, G_OPTION_ARG_STRING, &this->font_file, "Set font file-name", "filename"},
194 {"face-index", 'y', 0, G_OPTION_ARG_INT, &this->face_index, "Set face index (default: 0)", "index"},
195 {"face-loader", 0, 0, G_OPTION_ARG_STRING, &this->face_loader, face_loaders_text, "loader"},
196 {nullptr}
197 };
198 parser->add_group (entries,
199 "face",
200 "Font-face options:",
201 "Options for the font face",
202 this);
203 }
204
205 #endif
206