| File: | font-viewer/sushi-font-widget.c |
| Warning: | line 507, column 5 2nd function call argument is an uninitialized value |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* | |||
| 2 | * Copyright (C) 2011 Red Hat, Inc. | |||
| 3 | * | |||
| 4 | * This program is free software; you can redistribute it and/or | |||
| 5 | * modify it under the terms of the GNU General Public License as | |||
| 6 | * published by the Free Software Foundation; either version 2 of the | |||
| 7 | * License, or (at your option) any later version. | |||
| 8 | * | |||
| 9 | * This program is distributed in the hope that it will be useful, but | |||
| 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
| 12 | * General Public License for more details. | |||
| 13 | * | |||
| 14 | * You should have received a copy of the GNU General Public License | |||
| 15 | * along with this program; if not, write to the Free Software | |||
| 16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA | |||
| 17 | * 02111-1307, USA. | |||
| 18 | * | |||
| 19 | * The Sushi project hereby grant permission for non-gpl compatible GStreamer | |||
| 20 | * plugins to be used and distributed together with GStreamer and Sushi. This | |||
| 21 | * permission is above and beyond the permissions granted by the GPL license | |||
| 22 | * Sushi is covered by. | |||
| 23 | * | |||
| 24 | * Authors: Cosimo Cecchi <cosimoc@redhat.com> | |||
| 25 | * | |||
| 26 | */ | |||
| 27 | ||||
| 28 | #include "sushi-font-widget.h" | |||
| 29 | #include "sushi-font-loader.h" | |||
| 30 | ||||
| 31 | #include <math.h> | |||
| 32 | ||||
| 33 | enum { | |||
| 34 | PROP_URI = 1, | |||
| 35 | PROP_FACE_INDEX, | |||
| 36 | NUM_PROPERTIES | |||
| 37 | }; | |||
| 38 | ||||
| 39 | enum { | |||
| 40 | LOADED, | |||
| 41 | ERROR, | |||
| 42 | NUM_SIGNALS | |||
| 43 | }; | |||
| 44 | ||||
| 45 | struct _SushiFontWidgetPrivate { | |||
| 46 | gchar *uri; | |||
| 47 | gint face_index; | |||
| 48 | ||||
| 49 | FT_Library library; | |||
| 50 | FT_Face face; | |||
| 51 | gchar *face_contents; | |||
| 52 | ||||
| 53 | const gchar *lowercase_text; | |||
| 54 | const gchar *uppercase_text; | |||
| 55 | const gchar *punctuation_text; | |||
| 56 | ||||
| 57 | gchar *sample_string; | |||
| 58 | ||||
| 59 | gchar *font_name; | |||
| 60 | }; | |||
| 61 | ||||
| 62 | static GParamSpec *properties[NUM_PROPERTIES] = { NULL((void*)0), }; | |||
| 63 | static guint signals[NUM_SIGNALS] = { 0, }; | |||
| 64 | ||||
| 65 | G_DEFINE_TYPE_WITH_PRIVATE (SushiFontWidget, sushi_font_widget, CTK_TYPE_DRAWING_AREA)static void sushi_font_widget_init (SushiFontWidget *self); static void sushi_font_widget_class_init (SushiFontWidgetClass *klass ); static GType sushi_font_widget_get_type_once (void); static gpointer sushi_font_widget_parent_class = ((void*)0); static gint SushiFontWidget_private_offset; static void sushi_font_widget_class_intern_init (gpointer klass) { sushi_font_widget_parent_class = g_type_class_peek_parent (klass); if (SushiFontWidget_private_offset != 0) g_type_class_adjust_private_offset (klass, &SushiFontWidget_private_offset); sushi_font_widget_class_init ((SushiFontWidgetClass*) klass); } __attribute__ ((__unused__ )) static inline gpointer sushi_font_widget_get_instance_private (SushiFontWidget *self) { return (((gpointer) ((guint8*) (self ) + (glong) (SushiFontWidget_private_offset)))); } GType sushi_font_widget_get_type (void) { static GType static_g_define_type_id = 0; if ((__extension__ ({ _Static_assert (sizeof *(&static_g_define_type_id) == sizeof (gpointer), "Expression evaluates to false"); (void) ( 0 ? (gpointer) * (&static_g_define_type_id) : ((void*)0)) ; (!(__extension__ ({ _Static_assert (sizeof *(&static_g_define_type_id ) == sizeof (gpointer), "Expression evaluates to false"); __typeof__ (*(&static_g_define_type_id)) gapg_temp_newval; __typeof__ ((&static_g_define_type_id)) gapg_temp_atomic = (&static_g_define_type_id ); __atomic_load (gapg_temp_atomic, &gapg_temp_newval, 5) ; gapg_temp_newval; })) && g_once_init_enter_pointer ( &static_g_define_type_id)); })) ) { GType g_define_type_id = sushi_font_widget_get_type_once (); (__extension__ ({ _Static_assert (sizeof *(&static_g_define_type_id) == sizeof (gpointer) , "Expression evaluates to false"); 0 ? (void) (*(&static_g_define_type_id ) = (g_define_type_id)) : (void) 0; g_once_init_leave_pointer ((&static_g_define_type_id), (gpointer) (guintptr) (g_define_type_id )); })) ; } return static_g_define_type_id; } __attribute__ ( (__noinline__)) static GType sushi_font_widget_get_type_once ( void) { GType g_define_type_id = g_type_register_static_simple ((ctk_drawing_area_get_type ()), g_intern_static_string ("SushiFontWidget" ), sizeof (SushiFontWidgetClass), (GClassInitFunc)(void (*)(void )) sushi_font_widget_class_intern_init, sizeof (SushiFontWidget ), (GInstanceInitFunc)(void (*)(void)) sushi_font_widget_init , (GTypeFlags) 0); { {{ SushiFontWidget_private_offset = g_type_add_instance_private (g_define_type_id, sizeof (SushiFontWidgetPrivate)); };} } return g_define_type_id; }; | |||
| 66 | ||||
| 67 | #define SURFACE_SIZE4 4 | |||
| 68 | #define SECTION_SPACING16 16 | |||
| 69 | #define LINE_SPACING2 2 | |||
| 70 | ||||
| 71 | static const gchar lowercase_text_stock[] = "abcdefghijklmnopqrstuvwxyz"; | |||
| 72 | static const gchar uppercase_text_stock[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; | |||
| 73 | static const gchar punctuation_text_stock[] = "0123456789.:,;(*!?')"; | |||
| 74 | ||||
| 75 | /* adapted from gnome-utils:font-viewer/font-view.c | |||
| 76 | * | |||
| 77 | * Copyright (C) 2002-2003 James Henstridge <james@daa.com.au> | |||
| 78 | * Copyright (C) 2010 Cosimo Cecchi <cosimoc@gnome.org> | |||
| 79 | * | |||
| 80 | * License: GPLv2+ | |||
| 81 | */ | |||
| 82 | static void | |||
| 83 | draw_string (SushiFontWidget *self, | |||
| 84 | cairo_t *cr, | |||
| 85 | CtkBorder padding, | |||
| 86 | const gchar *text, | |||
| 87 | gint *pos_y) | |||
| 88 | { | |||
| 89 | cairo_font_extents_t font_extents; | |||
| 90 | cairo_text_extents_t extents; | |||
| 91 | CtkTextDirection text_dir; | |||
| 92 | gint pos_x; | |||
| 93 | ||||
| 94 | text_dir = ctk_widget_get_direction (CTK_WIDGET (self)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((self)), ((ctk_widget_get_type ()))))))); | |||
| 95 | ||||
| 96 | cairo_font_extents (cr, &font_extents); | |||
| 97 | cairo_text_extents (cr, text, &extents); | |||
| 98 | ||||
| 99 | if (pos_y != NULL((void*)0)) | |||
| 100 | *pos_y += font_extents.ascent + font_extents.descent + | |||
| 101 | extents.y_advance + LINE_SPACING2 / 2; | |||
| 102 | if (text_dir == CTK_TEXT_DIR_LTR) | |||
| 103 | pos_x = padding.left; | |||
| 104 | else { | |||
| 105 | pos_x = ctk_widget_get_allocated_width (CTK_WIDGET (self)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((self)), ((ctk_widget_get_type ()))))))) - | |||
| 106 | extents.x_advance - padding.right; | |||
| 107 | } | |||
| 108 | ||||
| 109 | cairo_move_to (cr, pos_x, *pos_y); | |||
| 110 | cairo_show_text (cr, text); | |||
| 111 | ||||
| 112 | *pos_y += LINE_SPACING2 / 2; | |||
| 113 | } | |||
| 114 | ||||
| 115 | static gboolean | |||
| 116 | check_font_contain_text (FT_Face face, | |||
| 117 | const gchar *text) | |||
| 118 | { | |||
| 119 | gunichar *string; | |||
| 120 | glong len, idx, map; | |||
| 121 | FT_CharMap charmap; | |||
| 122 | gboolean retval = FALSE(0); | |||
| 123 | ||||
| 124 | string = g_utf8_to_ucs4_fast (text, -1, &len); | |||
| 125 | ||||
| 126 | for (map = 0; map < face->num_charmaps; map++) { | |||
| 127 | charmap = face->charmaps[map]; | |||
| 128 | FT_Set_Charmap (face, charmap); | |||
| 129 | ||||
| 130 | retval = TRUE(!(0)); | |||
| 131 | ||||
| 132 | for (idx = 0; idx < len; idx++) { | |||
| 133 | gunichar c = string[idx]; | |||
| 134 | ||||
| 135 | if (!FT_Get_Char_Index (face, c)) { | |||
| 136 | retval = FALSE(0); | |||
| 137 | break; | |||
| 138 | } | |||
| 139 | } | |||
| 140 | ||||
| 141 | if (retval) | |||
| 142 | break; | |||
| 143 | } | |||
| 144 | ||||
| 145 | g_free (string); | |||
| 146 | ||||
| 147 | return retval; | |||
| 148 | } | |||
| 149 | ||||
| 150 | static gchar * | |||
| 151 | build_charlist_for_face (FT_Face face, | |||
| 152 | gint *length) | |||
| 153 | { | |||
| 154 | GString *string; | |||
| 155 | gulong c; | |||
| 156 | guint glyph; | |||
| 157 | gint total_chars = 0; | |||
| 158 | ||||
| 159 | string = g_string_new (NULL((void*)0)); | |||
| 160 | ||||
| 161 | c = FT_Get_First_Char (face, &glyph); | |||
| 162 | ||||
| 163 | while (glyph != 0) { | |||
| 164 | g_string_append_unichar (string, (gunichar) c); | |||
| 165 | c = FT_Get_Next_Char (face, c, &glyph); | |||
| 166 | total_chars++; | |||
| 167 | } | |||
| 168 | ||||
| 169 | if (length) | |||
| 170 | *length = total_chars; | |||
| 171 | ||||
| 172 | return g_string_free (string, FALSE)(__builtin_constant_p ((0)) ? (((0)) ? (g_string_free) ((string ), ((0))) : g_string_free_and_steal (string)) : (g_string_free ) ((string), ((0)))); | |||
| 173 | } | |||
| 174 | ||||
| 175 | static gchar * | |||
| 176 | random_string_from_available_chars (FT_Face face, | |||
| 177 | gint n_chars) | |||
| 178 | { | |||
| 179 | gchar *chars; | |||
| 180 | gint idx, rand, total_chars; | |||
| 181 | GString *retval; | |||
| 182 | gchar *ptr, *end; | |||
| 183 | ||||
| 184 | idx = 0; | |||
| 185 | chars = build_charlist_for_face (face, &total_chars); | |||
| 186 | ||||
| 187 | if (total_chars == 0) | |||
| 188 | return NULL((void*)0); | |||
| 189 | ||||
| 190 | retval = g_string_new (NULL((void*)0)); | |||
| 191 | ||||
| 192 | while (idx < n_chars) { | |||
| 193 | rand = g_random_int_range (0, total_chars); | |||
| 194 | ||||
| 195 | ptr = g_utf8_offset_to_pointer (chars, rand); | |||
| 196 | end = g_utf8_find_next_char (ptr, NULL((void*)0)); | |||
| 197 | ||||
| 198 | g_string_append_len (retval, ptr, end - ptr)g_string_append_len_inline (retval, ptr, end - ptr); | |||
| 199 | idx++; | |||
| 200 | } | |||
| 201 | ||||
| 202 | return g_string_free (retval, FALSE)(__builtin_constant_p ((0)) ? (((0)) ? (g_string_free) ((retval ), ((0))) : g_string_free_and_steal (retval)) : (g_string_free ) ((retval), ((0)))); | |||
| 203 | } | |||
| 204 | ||||
| 205 | static gboolean | |||
| 206 | set_pango_sample_string (SushiFontWidget *self) | |||
| 207 | { | |||
| 208 | const gchar *sample_string; | |||
| 209 | gboolean retval = FALSE(0); | |||
| 210 | ||||
| 211 | sample_string = pango_language_get_sample_string (pango_language_from_string (NULL((void*)0))); | |||
| 212 | if (check_font_contain_text (self->priv->face, sample_string)) | |||
| 213 | retval = TRUE(!(0)); | |||
| 214 | ||||
| 215 | if (!retval) { | |||
| 216 | sample_string = pango_language_get_sample_string (pango_language_from_string ("C")); | |||
| 217 | if (check_font_contain_text (self->priv->face, sample_string)) | |||
| 218 | retval = TRUE(!(0)); | |||
| 219 | } | |||
| 220 | ||||
| 221 | if (retval) { | |||
| 222 | g_free (self->priv->sample_string); | |||
| 223 | self->priv->sample_string = g_strdup (sample_string)g_strdup_inline (sample_string); | |||
| 224 | } | |||
| 225 | ||||
| 226 | return retval; | |||
| 227 | } | |||
| 228 | ||||
| 229 | static void | |||
| 230 | build_strings_for_face (SushiFontWidget *self) | |||
| 231 | { | |||
| 232 | /* if we don't have lowercase/uppercase/punctuation text in the face, | |||
| 233 | * we omit it directly, and render a random text below. | |||
| 234 | */ | |||
| 235 | if (check_font_contain_text (self->priv->face, lowercase_text_stock)) | |||
| 236 | self->priv->lowercase_text = lowercase_text_stock; | |||
| 237 | else | |||
| 238 | self->priv->lowercase_text = NULL((void*)0); | |||
| 239 | ||||
| 240 | if (check_font_contain_text (self->priv->face, uppercase_text_stock)) | |||
| 241 | self->priv->uppercase_text = uppercase_text_stock; | |||
| 242 | else | |||
| 243 | self->priv->uppercase_text = NULL((void*)0); | |||
| 244 | ||||
| 245 | if (check_font_contain_text (self->priv->face, punctuation_text_stock)) | |||
| 246 | self->priv->punctuation_text = punctuation_text_stock; | |||
| 247 | else | |||
| 248 | self->priv->punctuation_text = NULL((void*)0); | |||
| 249 | ||||
| 250 | if (!set_pango_sample_string (self)) | |||
| 251 | self->priv->sample_string = random_string_from_available_chars (self->priv->face, 36); | |||
| 252 | ||||
| 253 | g_free (self->priv->font_name); | |||
| 254 | self->priv->font_name = NULL((void*)0); | |||
| 255 | ||||
| 256 | if (self->priv->face->family_name != NULL((void*)0)) { | |||
| 257 | gchar *font_name = | |||
| 258 | g_strconcat (self->priv->face->family_name, " ", | |||
| 259 | self->priv->face->style_name, NULL((void*)0)); | |||
| 260 | ||||
| 261 | if (check_font_contain_text (self->priv->face, font_name)) | |||
| 262 | self->priv->font_name = font_name; | |||
| 263 | else | |||
| 264 | g_free (font_name); | |||
| 265 | } | |||
| 266 | } | |||
| 267 | ||||
| 268 | static gint * | |||
| 269 | build_sizes_table (FT_Face face, | |||
| 270 | gint *n_sizes, | |||
| 271 | gint *alpha_size, | |||
| 272 | gint *title_size) | |||
| 273 | { | |||
| 274 | gint *sizes = NULL((void*)0); | |||
| 275 | gint i; | |||
| 276 | ||||
| 277 | /* work out what sizes to render */ | |||
| 278 | if (FT_IS_SCALABLE (face)( !!( (face)->face_flags & ( 1L << 0 ) ) )) { | |||
| 279 | *n_sizes = 14; | |||
| 280 | sizes = g_new (gint, *n_sizes)((gint *) g_malloc_n ((*n_sizes), sizeof (gint))); | |||
| 281 | sizes[0] = 8; | |||
| 282 | sizes[1] = 10; | |||
| 283 | sizes[2] = 12; | |||
| 284 | sizes[3] = 18; | |||
| 285 | sizes[4] = 24; | |||
| 286 | sizes[5] = 36; | |||
| 287 | sizes[6] = 48; | |||
| 288 | sizes[7] = 72; | |||
| 289 | sizes[8] = 96; | |||
| 290 | sizes[9] = 120; | |||
| 291 | sizes[10] = 144; | |||
| 292 | sizes[11] = 168; | |||
| 293 | sizes[12] = 192; | |||
| 294 | sizes[13] = 216; | |||
| 295 | ||||
| 296 | *alpha_size = 24; | |||
| 297 | *title_size = 48; | |||
| 298 | } else { | |||
| 299 | gint alpha_diff = G_MAXINT2147483647; | |||
| 300 | gint title_diff = G_MAXINT2147483647; | |||
| 301 | ||||
| 302 | /* use fixed sizes */ | |||
| 303 | *n_sizes = face->num_fixed_sizes; | |||
| 304 | sizes = g_new (gint, *n_sizes)((gint *) g_malloc_n ((*n_sizes), sizeof (gint))); | |||
| 305 | *alpha_size = 0; | |||
| 306 | ||||
| 307 | for (i = 0; i < face->num_fixed_sizes; i++) { | |||
| 308 | sizes[i] = face->available_sizes[i].height; | |||
| 309 | ||||
| 310 | if ((gint) (abs (sizes[i] - 24)) < alpha_diff) { | |||
| 311 | alpha_diff = (gint) abs (sizes[i] - 24); | |||
| 312 | *alpha_size = sizes[i]; | |||
| 313 | } | |||
| 314 | if ((gint) (abs (sizes[i] - 24)) < title_diff) { | |||
| 315 | title_diff = (gint) abs (sizes[i] - 24); | |||
| 316 | *title_size = sizes[i]; | |||
| 317 | } | |||
| 318 | } | |||
| 319 | } | |||
| 320 | ||||
| 321 | return sizes; | |||
| 322 | } | |||
| 323 | ||||
| 324 | static void | |||
| 325 | sushi_font_widget_size_request (CtkWidget *drawing_area, | |||
| 326 | gint *width, | |||
| 327 | gint *height, | |||
| 328 | gint *min_height) | |||
| 329 | { | |||
| 330 | SushiFontWidget *self = SUSHI_FONT_WIDGET (drawing_area)((((SushiFontWidget*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((drawing_area)), ((sushi_font_widget_get_type ())))))); | |||
| 331 | SushiFontWidgetPrivate *priv = self->priv; | |||
| 332 | gint i, pixmap_width, pixmap_height; | |||
| 333 | cairo_text_extents_t extents; | |||
| 334 | cairo_font_extents_t font_extents; | |||
| 335 | cairo_font_face_t *font; | |||
| 336 | gint *sizes = NULL((void*)0), n_sizes, alpha_size, title_size; | |||
| 337 | cairo_t *cr; | |||
| 338 | cairo_surface_t *surface; | |||
| 339 | FT_Face face = priv->face; | |||
| 340 | CtkStyleContext *context; | |||
| 341 | CtkStateFlags state; | |||
| 342 | CtkBorder padding; | |||
| 343 | ||||
| 344 | if (face == NULL((void*)0)) { | |||
| 345 | if (width != NULL((void*)0)) | |||
| 346 | *width = 1; | |||
| 347 | if (height != NULL((void*)0)) | |||
| 348 | *height = 1; | |||
| 349 | if (min_height != NULL((void*)0)) | |||
| 350 | *min_height = 1; | |||
| 351 | ||||
| 352 | return; | |||
| 353 | } | |||
| 354 | ||||
| 355 | if (min_height != NULL((void*)0)) | |||
| 356 | *min_height = -1; | |||
| 357 | ||||
| 358 | surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, | |||
| 359 | SURFACE_SIZE4, SURFACE_SIZE4); | |||
| 360 | cr = cairo_create (surface); | |||
| 361 | context = ctk_widget_get_style_context (drawing_area); | |||
| 362 | state = ctk_style_context_get_state (context); | |||
| 363 | ctk_style_context_get_padding (context, state, &padding); | |||
| 364 | ||||
| 365 | sizes = build_sizes_table (face, &n_sizes, &alpha_size, &title_size); | |||
| 366 | ||||
| 367 | /* calculate size of pixmap to use */ | |||
| 368 | pixmap_width = padding.left + padding.right; | |||
| 369 | pixmap_height = padding.top + padding.bottom; | |||
| 370 | ||||
| 371 | font = cairo_ft_font_face_create_for_ft_face (face, 0); | |||
| 372 | cairo_set_font_face (cr, font); | |||
| 373 | cairo_font_face_destroy (font); | |||
| 374 | ||||
| 375 | if (self->priv->font_name != NULL((void*)0)) { | |||
| 376 | cairo_set_font_size (cr, title_size); | |||
| 377 | cairo_font_extents (cr, &font_extents); | |||
| 378 | cairo_text_extents (cr, self->priv->font_name, &extents); | |||
| 379 | pixmap_height += font_extents.ascent + font_extents.descent + | |||
| 380 | extents.y_advance + LINE_SPACING2; | |||
| 381 | pixmap_width = MAX (pixmap_width, extents.width + padding.left + padding.right)(((pixmap_width) > (extents.width + padding.left + padding .right)) ? (pixmap_width) : (extents.width + padding.left + padding .right)); | |||
| 382 | } | |||
| 383 | ||||
| 384 | pixmap_height += SECTION_SPACING16 / 2; | |||
| 385 | cairo_set_font_size (cr, alpha_size); | |||
| 386 | cairo_font_extents (cr, &font_extents); | |||
| 387 | ||||
| 388 | if (self->priv->lowercase_text != NULL((void*)0)) { | |||
| 389 | cairo_text_extents (cr, self->priv->lowercase_text, &extents); | |||
| 390 | pixmap_height += font_extents.ascent + font_extents.descent + | |||
| 391 | extents.y_advance + LINE_SPACING2; | |||
| 392 | pixmap_width = MAX (pixmap_width, extents.width + padding.left + padding.right)(((pixmap_width) > (extents.width + padding.left + padding .right)) ? (pixmap_width) : (extents.width + padding.left + padding .right)); | |||
| 393 | } | |||
| 394 | ||||
| 395 | if (self->priv->uppercase_text != NULL((void*)0)) { | |||
| 396 | cairo_text_extents (cr, self->priv->uppercase_text, &extents); | |||
| 397 | pixmap_height += font_extents.ascent + font_extents.descent + | |||
| 398 | extents.y_advance + LINE_SPACING2; | |||
| 399 | pixmap_width = MAX (pixmap_width, extents.width + padding.left + padding.right)(((pixmap_width) > (extents.width + padding.left + padding .right)) ? (pixmap_width) : (extents.width + padding.left + padding .right)); | |||
| 400 | } | |||
| 401 | ||||
| 402 | if (self->priv->punctuation_text != NULL((void*)0)) { | |||
| 403 | cairo_text_extents (cr, self->priv->punctuation_text, &extents); | |||
| 404 | pixmap_height += font_extents.ascent + font_extents.descent + | |||
| 405 | extents.y_advance + LINE_SPACING2; | |||
| 406 | pixmap_width = MAX (pixmap_width, extents.width + padding.left + padding.right)(((pixmap_width) > (extents.width + padding.left + padding .right)) ? (pixmap_width) : (extents.width + padding.left + padding .right)); | |||
| 407 | } | |||
| 408 | ||||
| 409 | if (self->priv->sample_string != NULL((void*)0)) { | |||
| 410 | pixmap_height += SECTION_SPACING16; | |||
| 411 | ||||
| 412 | for (i = 0; i < n_sizes; i++) { | |||
| 413 | cairo_set_font_size (cr, sizes[i]); | |||
| 414 | cairo_font_extents (cr, &font_extents); | |||
| 415 | cairo_text_extents (cr, self->priv->sample_string, &extents); | |||
| 416 | pixmap_height += font_extents.ascent + font_extents.descent + | |||
| 417 | extents.y_advance + LINE_SPACING2; | |||
| 418 | pixmap_width = MAX (pixmap_width, extents.width + padding.left + padding.right)(((pixmap_width) > (extents.width + padding.left + padding .right)) ? (pixmap_width) : (extents.width + padding.left + padding .right)); | |||
| 419 | ||||
| 420 | if ((i == 7) && (min_height != NULL((void*)0))) | |||
| 421 | *min_height = pixmap_height; | |||
| 422 | } | |||
| 423 | } | |||
| 424 | ||||
| 425 | pixmap_height += padding.bottom + SECTION_SPACING16; | |||
| 426 | ||||
| 427 | if (min_height != NULL((void*)0) && *min_height == -1) | |||
| 428 | *min_height = pixmap_height; | |||
| 429 | ||||
| 430 | if (width != NULL((void*)0)) | |||
| 431 | *width = pixmap_width; | |||
| 432 | ||||
| 433 | if (height != NULL((void*)0)) | |||
| 434 | *height = pixmap_height; | |||
| 435 | ||||
| 436 | cairo_destroy (cr); | |||
| 437 | cairo_surface_destroy (surface); | |||
| 438 | g_free (sizes); | |||
| 439 | } | |||
| 440 | ||||
| 441 | static void | |||
| 442 | sushi_font_widget_get_preferred_width (CtkWidget *drawing_area, | |||
| 443 | gint *minimum_width, | |||
| 444 | gint *natural_width) | |||
| 445 | { | |||
| 446 | gint width; | |||
| 447 | ||||
| 448 | sushi_font_widget_size_request (drawing_area, &width, NULL((void*)0), NULL((void*)0)); | |||
| 449 | ||||
| 450 | *minimum_width = *natural_width = width; | |||
| 451 | } | |||
| 452 | ||||
| 453 | static void | |||
| 454 | sushi_font_widget_get_preferred_height (CtkWidget *drawing_area, | |||
| 455 | gint *minimum_height, | |||
| 456 | gint *natural_height) | |||
| 457 | { | |||
| 458 | gint height, min_height; | |||
| 459 | ||||
| 460 | sushi_font_widget_size_request (drawing_area, NULL((void*)0), &height, &min_height); | |||
| 461 | ||||
| 462 | *minimum_height = min_height; | |||
| 463 | *natural_height = height; | |||
| 464 | } | |||
| 465 | ||||
| 466 | static gboolean | |||
| 467 | sushi_font_widget_draw (CtkWidget *drawing_area, | |||
| 468 | cairo_t *cr) | |||
| 469 | { | |||
| 470 | SushiFontWidget *self = SUSHI_FONT_WIDGET (drawing_area)((((SushiFontWidget*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((drawing_area)), ((sushi_font_widget_get_type ())))))); | |||
| 471 | SushiFontWidgetPrivate *priv = self->priv; | |||
| 472 | gint *sizes = NULL((void*)0), n_sizes, alpha_size, title_size, pos_y = 0, i; | |||
| ||||
| 473 | cairo_font_face_t *font; | |||
| 474 | FT_Face face = priv->face; | |||
| 475 | CtkStyleContext *context; | |||
| 476 | CdkRGBA color; | |||
| 477 | CtkBorder padding; | |||
| 478 | CtkStateFlags state; | |||
| 479 | gint allocated_width, allocated_height; | |||
| 480 | ||||
| 481 | if (face == NULL((void*)0)) | |||
| 482 | goto end; | |||
| 483 | ||||
| 484 | context = ctk_widget_get_style_context (drawing_area); | |||
| 485 | state = ctk_style_context_get_state (context); | |||
| 486 | ||||
| 487 | allocated_width = ctk_widget_get_allocated_width (drawing_area); | |||
| 488 | allocated_height = ctk_widget_get_allocated_height (drawing_area); | |||
| 489 | ||||
| 490 | ctk_render_background (context, cr, | |||
| 491 | 0, 0, allocated_width, allocated_height); | |||
| 492 | ||||
| 493 | ctk_style_context_get_color (context, state, &color); | |||
| 494 | ctk_style_context_get_padding (context, state, &padding); | |||
| 495 | ||||
| 496 | cdk_cairo_set_source_rgba (cr, &color); | |||
| 497 | ||||
| 498 | sizes = build_sizes_table (face, &n_sizes, &alpha_size, &title_size); | |||
| 499 | ||||
| 500 | font = cairo_ft_font_face_create_for_ft_face (face, 0); | |||
| 501 | cairo_set_font_face (cr, font); | |||
| 502 | cairo_font_face_destroy (font); | |||
| 503 | ||||
| 504 | /* draw text */ | |||
| 505 | ||||
| 506 | if (self->priv->font_name != NULL((void*)0)) { | |||
| 507 | cairo_set_font_size (cr, title_size); | |||
| ||||
| 508 | draw_string (self, cr, padding, self->priv->font_name, &pos_y); | |||
| 509 | } | |||
| 510 | ||||
| 511 | if (pos_y > allocated_height) | |||
| 512 | goto end; | |||
| 513 | ||||
| 514 | pos_y += SECTION_SPACING16 / 2; | |||
| 515 | cairo_set_font_size (cr, alpha_size); | |||
| 516 | ||||
| 517 | if (self->priv->lowercase_text != NULL((void*)0)) | |||
| 518 | draw_string (self, cr, padding, self->priv->lowercase_text, &pos_y); | |||
| 519 | if (pos_y > allocated_height) | |||
| 520 | goto end; | |||
| 521 | ||||
| 522 | if (self->priv->uppercase_text != NULL((void*)0)) | |||
| 523 | draw_string (self, cr, padding, self->priv->uppercase_text, &pos_y); | |||
| 524 | if (pos_y > allocated_height) | |||
| 525 | goto end; | |||
| 526 | ||||
| 527 | if (self->priv->punctuation_text != NULL((void*)0)) | |||
| 528 | draw_string (self, cr, padding, self->priv->punctuation_text, &pos_y); | |||
| 529 | if (pos_y > allocated_height) | |||
| 530 | goto end; | |||
| 531 | ||||
| 532 | pos_y += SECTION_SPACING16; | |||
| 533 | ||||
| 534 | for (i = 0; i < n_sizes; i++) { | |||
| 535 | cairo_set_font_size (cr, sizes[i]); | |||
| 536 | draw_string (self, cr, padding, self->priv->sample_string, &pos_y); | |||
| 537 | if (pos_y > allocated_height) | |||
| 538 | break; | |||
| 539 | } | |||
| 540 | ||||
| 541 | end: | |||
| 542 | g_free (sizes); | |||
| 543 | ||||
| 544 | return FALSE(0); | |||
| 545 | } | |||
| 546 | ||||
| 547 | static void | |||
| 548 | font_face_async_ready_cb (GObject *object, | |||
| 549 | GAsyncResult *result, | |||
| 550 | gpointer user_data) | |||
| 551 | { | |||
| 552 | SushiFontWidget *self = user_data; | |||
| 553 | GError *error = NULL((void*)0); | |||
| 554 | ||||
| 555 | self->priv->face = | |||
| 556 | sushi_new_ft_face_from_uri_finish (result, | |||
| 557 | &self->priv->face_contents, | |||
| 558 | &error); | |||
| 559 | ||||
| 560 | if (error != NULL((void*)0)) { | |||
| 561 | g_signal_emit (self, signals[ERROR], 0, error->message); | |||
| 562 | g_print ("Can't load the font face: %s\n", error->message); | |||
| 563 | g_error_free (error); | |||
| 564 | ||||
| 565 | return; | |||
| 566 | } | |||
| 567 | ||||
| 568 | build_strings_for_face (self); | |||
| 569 | ||||
| 570 | ctk_widget_queue_resize (CTK_WIDGET (self)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((self)), ((ctk_widget_get_type ()))))))); | |||
| 571 | g_signal_emit (self, signals[LOADED], 0); | |||
| 572 | } | |||
| 573 | ||||
| 574 | void | |||
| 575 | sushi_font_widget_load (SushiFontWidget *self) | |||
| 576 | { | |||
| 577 | sushi_new_ft_face_from_uri_async (self->priv->library, | |||
| 578 | self->priv->uri, | |||
| 579 | self->priv->face_index, | |||
| 580 | font_face_async_ready_cb, | |||
| 581 | self); | |||
| 582 | } | |||
| 583 | ||||
| 584 | static void | |||
| 585 | sushi_font_widget_init (SushiFontWidget *self) | |||
| 586 | { | |||
| 587 | FT_Error err; | |||
| 588 | ||||
| 589 | self->priv = sushi_font_widget_get_instance_private (self); | |||
| 590 | ||||
| 591 | self->priv->face = NULL((void*)0); | |||
| 592 | err = FT_Init_FreeType (&self->priv->library); | |||
| 593 | ||||
| 594 | if (err != FT_Err_Ok) | |||
| 595 | g_error ("Unable to initialize FreeType"); | |||
| 596 | ||||
| 597 | ctk_style_context_add_class (ctk_widget_get_style_context (CTK_WIDGET (self)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((self)), ((ctk_widget_get_type ()))))))), | |||
| 598 | CTK_STYLE_CLASS_VIEW"view"); | |||
| 599 | } | |||
| 600 | ||||
| 601 | static void | |||
| 602 | sushi_font_widget_get_property (GObject *object, | |||
| 603 | guint prop_id, | |||
| 604 | GValue *value, | |||
| 605 | GParamSpec *pspec) | |||
| 606 | { | |||
| 607 | SushiFontWidget *self = SUSHI_FONT_WIDGET (object)((((SushiFontWidget*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((object)), ((sushi_font_widget_get_type ())))))); | |||
| 608 | ||||
| 609 | switch (prop_id) { | |||
| 610 | case PROP_URI: | |||
| 611 | g_value_set_string (value, self->priv->uri); | |||
| 612 | break; | |||
| 613 | case PROP_FACE_INDEX: | |||
| 614 | g_value_set_int (value, self->priv->face_index); | |||
| 615 | break; | |||
| 616 | default: | |||
| 617 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec)do { GObject *_glib__object = (GObject*) ((object)); GParamSpec *_glib__pspec = (GParamSpec*) ((pspec)); guint _glib__property_id = ((prop_id)); g_warning ("%s:%d: invalid %s id %u for \"%s\" of type '%s' in '%s'" , "sushi-font-widget.c", 617, ("property"), _glib__property_id , _glib__pspec->name, g_type_name ((((((GTypeClass*) (((GTypeInstance *) (_glib__pspec))->g_class))->g_type)))), (g_type_name ((((((GTypeClass*) (((GTypeInstance*) (_glib__object))->g_class ))->g_type)))))); } while (0); | |||
| 618 | break; | |||
| 619 | } | |||
| 620 | } | |||
| 621 | ||||
| 622 | static void | |||
| 623 | sushi_font_widget_set_property (GObject *object, | |||
| 624 | guint prop_id, | |||
| 625 | const GValue *value, | |||
| 626 | GParamSpec *pspec) | |||
| 627 | { | |||
| 628 | SushiFontWidget *self = SUSHI_FONT_WIDGET (object)((((SushiFontWidget*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((object)), ((sushi_font_widget_get_type ())))))); | |||
| 629 | ||||
| 630 | switch (prop_id) { | |||
| 631 | case PROP_URI: | |||
| 632 | self->priv->uri = g_value_dup_string (value); | |||
| 633 | break; | |||
| 634 | case PROP_FACE_INDEX: | |||
| 635 | self->priv->face_index = g_value_get_int (value); | |||
| 636 | break; | |||
| 637 | default: | |||
| 638 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec)do { GObject *_glib__object = (GObject*) ((object)); GParamSpec *_glib__pspec = (GParamSpec*) ((pspec)); guint _glib__property_id = ((prop_id)); g_warning ("%s:%d: invalid %s id %u for \"%s\" of type '%s' in '%s'" , "sushi-font-widget.c", 638, ("property"), _glib__property_id , _glib__pspec->name, g_type_name ((((((GTypeClass*) (((GTypeInstance *) (_glib__pspec))->g_class))->g_type)))), (g_type_name ((((((GTypeClass*) (((GTypeInstance*) (_glib__object))->g_class ))->g_type)))))); } while (0); | |||
| 639 | break; | |||
| 640 | } | |||
| 641 | } | |||
| 642 | ||||
| 643 | static void | |||
| 644 | sushi_font_widget_finalize (GObject *object) | |||
| 645 | { | |||
| 646 | SushiFontWidget *self = SUSHI_FONT_WIDGET (object)((((SushiFontWidget*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((object)), ((sushi_font_widget_get_type ())))))); | |||
| 647 | ||||
| 648 | g_free (self->priv->uri); | |||
| 649 | ||||
| 650 | if (self->priv->face != NULL((void*)0)) { | |||
| 651 | FT_Done_Face (self->priv->face); | |||
| 652 | self->priv->face = NULL((void*)0); | |||
| 653 | } | |||
| 654 | ||||
| 655 | g_free (self->priv->font_name); | |||
| 656 | g_free (self->priv->sample_string); | |||
| 657 | g_free (self->priv->face_contents); | |||
| 658 | ||||
| 659 | if (self->priv->library != NULL((void*)0)) { | |||
| 660 | FT_Done_FreeType (self->priv->library); | |||
| 661 | self->priv->library = NULL((void*)0); | |||
| 662 | } | |||
| 663 | ||||
| 664 | G_OBJECT_CLASS (sushi_font_widget_parent_class)((((GObjectClass*) (void *) g_type_check_class_cast ((GTypeClass *) ((sushi_font_widget_parent_class)), (((GType) ((20) << (2))))))))->finalize (object); | |||
| 665 | } | |||
| 666 | ||||
| 667 | static void | |||
| 668 | sushi_font_widget_constructed (GObject *object) | |||
| 669 | { | |||
| 670 | SushiFontWidget *self = SUSHI_FONT_WIDGET (object)((((SushiFontWidget*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((object)), ((sushi_font_widget_get_type ())))))); | |||
| 671 | ||||
| 672 | sushi_font_widget_load (self); | |||
| 673 | ||||
| 674 | G_OBJECT_CLASS (sushi_font_widget_parent_class)((((GObjectClass*) (void *) g_type_check_class_cast ((GTypeClass *) ((sushi_font_widget_parent_class)), (((GType) ((20) << (2))))))))->constructed (object); | |||
| 675 | } | |||
| 676 | ||||
| 677 | static void | |||
| 678 | sushi_font_widget_class_init (SushiFontWidgetClass *klass) | |||
| 679 | { | |||
| 680 | GObjectClass *oclass = G_OBJECT_CLASS (klass)((((GObjectClass*) (void *) g_type_check_class_cast ((GTypeClass *) ((klass)), (((GType) ((20) << (2)))))))); | |||
| 681 | CtkWidgetClass *wclass = CTK_WIDGET_CLASS (klass)((((CtkWidgetClass*) (void *) g_type_check_class_cast ((GTypeClass *) ((klass)), ((ctk_widget_get_type ())))))); | |||
| 682 | ||||
| 683 | oclass->finalize = sushi_font_widget_finalize; | |||
| 684 | oclass->set_property = sushi_font_widget_set_property; | |||
| 685 | oclass->get_property = sushi_font_widget_get_property; | |||
| 686 | oclass->constructed = sushi_font_widget_constructed; | |||
| 687 | ||||
| 688 | wclass->draw = sushi_font_widget_draw; | |||
| 689 | wclass->get_preferred_width = sushi_font_widget_get_preferred_width; | |||
| 690 | wclass->get_preferred_height = sushi_font_widget_get_preferred_height; | |||
| 691 | ||||
| 692 | properties[PROP_URI] = | |||
| 693 | g_param_spec_string ("uri", | |||
| 694 | "Uri", "Uri", | |||
| 695 | NULL((void*)0), G_PARAM_READWRITE | G_PARAM_CONSTRUCT); | |||
| 696 | properties[PROP_FACE_INDEX] = | |||
| 697 | g_param_spec_int ("face-index", | |||
| 698 | "Face index", "Face index", | |||
| 699 | 0, G_MAXINT2147483647, | |||
| 700 | 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT); | |||
| 701 | ||||
| 702 | signals[LOADED] = | |||
| 703 | g_signal_new ("loaded", | |||
| 704 | G_TYPE_FROM_CLASS (klass)(((GTypeClass*) (klass))->g_type), | |||
| 705 | G_SIGNAL_RUN_FIRST, | |||
| 706 | 0, NULL((void*)0), NULL((void*)0), | |||
| 707 | g_cclosure_marshal_VOID__VOID, | |||
| 708 | G_TYPE_NONE((GType) ((1) << (2))), 0); | |||
| 709 | signals[ERROR] = | |||
| 710 | g_signal_new ("error", | |||
| 711 | G_TYPE_FROM_CLASS (klass)(((GTypeClass*) (klass))->g_type), | |||
| 712 | G_SIGNAL_RUN_FIRST, | |||
| 713 | 0, NULL((void*)0), NULL((void*)0), | |||
| 714 | g_cclosure_marshal_VOID__STRING, | |||
| 715 | G_TYPE_NONE((GType) ((1) << (2))), 1, G_TYPE_STRING((GType) ((16) << (2)))); | |||
| 716 | ||||
| 717 | g_object_class_install_properties (oclass, NUM_PROPERTIES, properties); | |||
| 718 | } | |||
| 719 | ||||
| 720 | SushiFontWidget * | |||
| 721 | sushi_font_widget_new (const gchar *uri, gint face_index) | |||
| 722 | { | |||
| 723 | return g_object_new (SUSHI_TYPE_FONT_WIDGET(sushi_font_widget_get_type ()), | |||
| 724 | "uri", uri, | |||
| 725 | "face-index", face_index, | |||
| 726 | NULL((void*)0)); | |||
| 727 | } | |||
| 728 | ||||
| 729 | /** | |||
| 730 | * sushi_font_widget_get_ft_face: (skip) | |||
| 731 | * | |||
| 732 | */ | |||
| 733 | FT_Face | |||
| 734 | sushi_font_widget_get_ft_face (SushiFontWidget *self) | |||
| 735 | { | |||
| 736 | return self->priv->face; | |||
| 737 | } | |||
| 738 | ||||
| 739 | const gchar * | |||
| 740 | sushi_font_widget_get_uri (SushiFontWidget *self) | |||
| 741 | { | |||
| 742 | return self->priv->uri; | |||
| 743 | } | |||
| 744 |