1 /* dzl-pango.c
2  *
3  * Copyright (C) 2014-2017 Christian Hergert <christian@hergert.me>
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #define G_LOG_DOMAIN "dzl-pango"
20 
21 #include "config.h"
22 
23 #include <gdk/gdk.h>
24 #include <glib/gstdio.h>
25 #define _USE_MATH_DEFINES
26 #include <math.h>
27 
28 #include "util/dzl-pango.h"
29 
30 #define FONT_FAMILY  "font-family"
31 #define FONT_STYLE   "font-style"
32 #define FONT_VARIANT "font-variant"
33 #define FONT_STRETCH "font-stretch"
34 #define FONT_WEIGHT  "font-weight"
35 #define FONT_SIZE    "font-size"
36 
37 /**
38  * SECTION:dzl-pango
39  * @title: Pango Utilities
40  * @short_description: Utilities to use with the Pango text layout library
41  */
42 
43 /**
44  * dzl_pango_font_description_to_css:
45  *
46  * This function will generate CSS suitable for Gtk's CSS engine
47  * based on the properties of the #PangoFontDescription.
48  *
49  * Returns: (transfer full): A newly allocated string containing the
50  *    CSS describing the font description.
51  */
52 gchar *
dzl_pango_font_description_to_css(const PangoFontDescription * font_desc)53 dzl_pango_font_description_to_css (const PangoFontDescription *font_desc)
54 {
55   PangoFontMask mask;
56   GString *str;
57 
58 #define ADD_KEYVAL(key,fmt) \
59   g_string_append(str,key":"fmt";")
60 #define ADD_KEYVAL_PRINTF(key,fmt,...) \
61   g_string_append_printf(str,key":"fmt";", __VA_ARGS__)
62 
63   g_return_val_if_fail (font_desc, NULL);
64 
65   str = g_string_new (NULL);
66 
67   mask = pango_font_description_get_set_fields (font_desc);
68 
69   if ((mask & PANGO_FONT_MASK_FAMILY) != 0)
70     {
71       const gchar *family;
72 
73       family = pango_font_description_get_family (font_desc);
74       ADD_KEYVAL_PRINTF (FONT_FAMILY, "\"%s\"", family);
75     }
76 
77   if ((mask & PANGO_FONT_MASK_STYLE) != 0)
78     {
79       PangoStyle style;
80 
81       style = pango_font_description_get_style (font_desc);
82 
83       switch (style)
84         {
85         case PANGO_STYLE_NORMAL:
86           ADD_KEYVAL (FONT_STYLE, "normal");
87           break;
88 
89         case PANGO_STYLE_OBLIQUE:
90           ADD_KEYVAL (FONT_STYLE, "oblique");
91           break;
92 
93         case PANGO_STYLE_ITALIC:
94           ADD_KEYVAL (FONT_STYLE, "italic");
95           break;
96 
97         default:
98           break;
99         }
100     }
101 
102   if ((mask & PANGO_FONT_MASK_VARIANT) != 0)
103     {
104       PangoVariant variant;
105 
106       variant = pango_font_description_get_variant (font_desc);
107 
108       switch (variant)
109         {
110         case PANGO_VARIANT_NORMAL:
111           ADD_KEYVAL (FONT_VARIANT, "normal");
112           break;
113 
114         case PANGO_VARIANT_SMALL_CAPS:
115           ADD_KEYVAL (FONT_VARIANT, "small-caps");
116           break;
117 
118         default:
119           break;
120         }
121     }
122 
123   if ((mask & PANGO_FONT_MASK_WEIGHT))
124     {
125       gint weight;
126 
127       weight = pango_font_description_get_weight (font_desc);
128 
129       /*
130        * WORKAROUND:
131        *
132        * font-weight with numbers does not appear to be working as expected
133        * right now. So for the common (bold/normal), let's just use the string
134        * and let gtk warn for the other values, which shouldn't really be
135        * used for this.
136        */
137 
138       switch (weight)
139         {
140         case PANGO_WEIGHT_SEMILIGHT:
141           /*
142            * 350 is not actually a valid css font-weight, so we will just round
143            * up to 400.
144            */
145         case PANGO_WEIGHT_NORMAL:
146           ADD_KEYVAL (FONT_WEIGHT, "normal");
147           break;
148 
149         case PANGO_WEIGHT_BOLD:
150           ADD_KEYVAL (FONT_WEIGHT, "bold");
151           break;
152 
153         case PANGO_WEIGHT_THIN:
154         case PANGO_WEIGHT_ULTRALIGHT:
155         case PANGO_WEIGHT_LIGHT:
156         case PANGO_WEIGHT_BOOK:
157         case PANGO_WEIGHT_MEDIUM:
158         case PANGO_WEIGHT_SEMIBOLD:
159         case PANGO_WEIGHT_ULTRABOLD:
160         case PANGO_WEIGHT_HEAVY:
161         case PANGO_WEIGHT_ULTRAHEAVY:
162         default:
163           /* round to nearest hundred */
164           weight = round (weight / 100.0) * 100;
165           ADD_KEYVAL_PRINTF ("font-weight", "%d", weight);
166           break;
167         }
168     }
169 
170 #ifndef GDK_WINDOWING_QUARTZ
171   /*
172    * We seem to get "Condensed" for fonts on the Quartz backend,
173    * which is rather annoying as it results in us always hitting
174    * fallback (stretch) paths. So let's cheat and just disable
175    * stretch support for now on Quartz.
176    */
177   if ((mask & PANGO_FONT_MASK_STRETCH))
178     {
179       switch (pango_font_description_get_stretch (font_desc))
180         {
181         case PANGO_STRETCH_ULTRA_CONDENSED:
182           ADD_KEYVAL (FONT_STRETCH, "ultra-condensed");
183           break;
184 
185         case PANGO_STRETCH_EXTRA_CONDENSED:
186           ADD_KEYVAL (FONT_STRETCH, "extra-condensed");
187           break;
188 
189         case PANGO_STRETCH_CONDENSED:
190           ADD_KEYVAL (FONT_STRETCH, "condensed");
191           break;
192 
193         case PANGO_STRETCH_SEMI_CONDENSED:
194           ADD_KEYVAL (FONT_STRETCH, "semi-condensed");
195           break;
196 
197         case PANGO_STRETCH_NORMAL:
198           ADD_KEYVAL (FONT_STRETCH, "normal");
199           break;
200 
201         case PANGO_STRETCH_SEMI_EXPANDED:
202           ADD_KEYVAL (FONT_STRETCH, "semi-expanded");
203           break;
204 
205         case PANGO_STRETCH_EXPANDED:
206           ADD_KEYVAL (FONT_STRETCH, "expanded");
207           break;
208 
209         case PANGO_STRETCH_EXTRA_EXPANDED:
210           ADD_KEYVAL (FONT_STRETCH, "extra-expanded");
211           break;
212 
213         case PANGO_STRETCH_ULTRA_EXPANDED:
214           ADD_KEYVAL (FONT_STRETCH, "ultra-expanded");
215           break;
216 
217         default:
218           break;
219         }
220     }
221 #endif
222 
223   if ((mask & PANGO_FONT_MASK_SIZE))
224     {
225       gint font_size;
226 
227       font_size = pango_font_description_get_size (font_desc) / PANGO_SCALE;
228       ADD_KEYVAL_PRINTF ("font-size", "%dpt", font_size);
229     }
230 
231   return g_string_free (str, FALSE);
232 
233 #undef ADD_KEYVAL
234 #undef ADD_KEYVAL_PRINTF
235 }
236