| /* Pango/Font rendering |
| * |
| * Demonstrates various aspects of font rendering. |
| */ |
| |
| #include <gtk/gtk.h> |
| |
| static GtkWidget *window = NULL; |
| static GtkWidget *font_button = NULL; |
| static GtkWidget *entry = NULL; |
| static GtkWidget *image = NULL; |
| static GtkWidget *hinting = NULL; |
| static GtkWidget *hint_metrics = NULL; |
| static GtkWidget *up_button = NULL; |
| static GtkWidget *down_button = NULL; |
| static GtkWidget *text_radio = NULL; |
| static GtkWidget *show_grid = NULL; |
| static GtkWidget *show_extents = NULL; |
| |
| static PangoContext *context; |
| |
| static int scale = 10; |
| |
| static void |
| on_destroy (gpointer data) |
| { |
| window = NULL; |
| } |
| |
| static void |
| update_image (void) |
| { |
| const char *text; |
| PangoFontDescription *desc; |
| PangoLayout *layout; |
| PangoRectangle ink, pink, logical; |
| int baseline; |
| cairo_surface_t *surface; |
| cairo_t *cr; |
| GdkPixbuf *pixbuf; |
| GdkPixbuf *pixbuf2; |
| const char *hint; |
| cairo_font_options_t *fopt; |
| cairo_hint_style_t hintstyle; |
| cairo_hint_metrics_t hintmetrics; |
| int i; |
| |
| if (!context) |
| context = gtk_widget_create_pango_context (image); |
| |
| text = gtk_editable_get_text (GTK_EDITABLE (entry)); |
| desc = gtk_font_chooser_get_font_desc (GTK_FONT_CHOOSER (font_button)); |
| |
| fopt = cairo_font_options_copy (pango_cairo_context_get_font_options (context)); |
| |
| hint = gtk_combo_box_get_active_id (GTK_COMBO_BOX (hinting)); |
| if (strcmp (hint, "none") == 0) |
| hintstyle = CAIRO_HINT_STYLE_NONE; |
| else if (strcmp (hint, "slight") == 0) |
| hintstyle = CAIRO_HINT_STYLE_SLIGHT; |
| else if (strcmp (hint, "medium") == 0) |
| hintstyle = CAIRO_HINT_STYLE_MEDIUM; |
| else if (strcmp (hint, "full") == 0) |
| hintstyle = CAIRO_HINT_STYLE_FULL; |
| else |
| hintstyle = CAIRO_HINT_STYLE_DEFAULT; |
| cairo_font_options_set_hint_style (fopt, hintstyle); |
| |
| if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (hint_metrics))) |
| hintmetrics = CAIRO_HINT_METRICS_ON; |
| else |
| hintmetrics = CAIRO_HINT_METRICS_OFF; |
| cairo_font_options_set_hint_metrics (fopt, hintmetrics); |
| |
| pango_cairo_context_set_font_options (context, fopt); |
| cairo_font_options_destroy (fopt); |
| pango_context_changed (context); |
| |
| if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (text_radio))) |
| { |
| layout = pango_layout_new (context); |
| pango_layout_set_font_description (layout, desc); |
| pango_layout_set_text (layout, text, -1); |
| pango_layout_get_extents (layout, &ink, &logical); |
| pink = ink; |
| baseline = pango_layout_get_baseline (layout); |
| |
| pango_extents_to_pixels (&ink, NULL); |
| |
| surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, ink.width + 20, ink.height + 20); |
| cr = cairo_create (surface); |
| cairo_set_source_rgb (cr, 1, 1, 1); |
| cairo_paint (cr); |
| |
| cairo_set_source_rgb (cr, 0, 0, 0); |
| cairo_move_to (cr, 10, 10); |
| pango_cairo_show_layout (cr, layout); |
| |
| cairo_destroy (cr); |
| g_object_unref (layout); |
| |
| pixbuf = gdk_pixbuf_get_from_surface (surface, 0, 0, cairo_image_surface_get_width (surface), cairo_image_surface_get_height (surface)); |
| pixbuf2 = gdk_pixbuf_scale_simple (pixbuf, gdk_pixbuf_get_width (pixbuf) * scale, gdk_pixbuf_get_height (pixbuf) * scale, GDK_INTERP_NEAREST); |
| |
| g_object_unref (pixbuf); |
| cairo_surface_destroy (surface); |
| |
| surface = cairo_image_surface_create_for_data (gdk_pixbuf_get_pixels (pixbuf2), |
| CAIRO_FORMAT_ARGB32, |
| gdk_pixbuf_get_width (pixbuf2), |
| gdk_pixbuf_get_height (pixbuf2), |
| gdk_pixbuf_get_rowstride (pixbuf2)); |
| |
| cr = cairo_create (surface); |
| cairo_set_line_width (cr, 1); |
| |
| if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (show_grid))) |
| { |
| cairo_set_source_rgba (cr, 0.2, 0, 0, 0.2); |
| for (i = 1; i < ink.height + 20; i++) |
| { |
| cairo_move_to (cr, 0, scale * i - 0.5); |
| cairo_line_to (cr, scale * (ink.width + 20), scale * i - 0.5); |
| cairo_stroke (cr); |
| } |
| for (i = 1; i < ink.width + 20; i++) |
| { |
| cairo_move_to (cr, scale * i - 0.5, 0); |
| cairo_line_to (cr, scale * i - 0.5, scale * (ink.height + 20)); |
| cairo_stroke (cr); |
| } |
| } |
| |
| if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (show_extents))) |
| { |
| cairo_set_source_rgba (cr, 0, 0, 1, 1); |
| |
| cairo_rectangle (cr, |
| scale * (10 + pango_units_to_double (logical.x)) - 0.5, |
| scale * (10 + pango_units_to_double (logical.y)) - 0.5, |
| scale * pango_units_to_double (logical.width) + 1, |
| scale * pango_units_to_double (logical.height) + 1); |
| cairo_stroke (cr); |
| cairo_move_to (cr, scale * (10 + pango_units_to_double (logical.x)) - 0.5, |
| scale * (10 + pango_units_to_double (baseline)) - 0.5); |
| cairo_line_to (cr, scale * (10 + pango_units_to_double (logical.x + logical.width)) + 1, |
| scale * (10 + pango_units_to_double (baseline)) - 0.5); |
| cairo_stroke (cr); |
| cairo_set_source_rgba (cr, 1, 0, 0, 1); |
| cairo_rectangle (cr, |
| scale * (10 + pango_units_to_double (pink.x)) + 0.5, |
| scale * (10 + pango_units_to_double (pink.y)) + 0.5, |
| scale * pango_units_to_double (pink.width) - 1, |
| scale * pango_units_to_double (pink.height) - 1); |
| cairo_stroke (cr); |
| } |
| |
| cairo_surface_destroy (surface); |
| cairo_destroy (cr); |
| } |
| else |
| { |
| PangoLayoutIter *iter; |
| PangoLayoutRun *run; |
| PangoGlyphInfo *g; |
| int i, j; |
| |
| layout = pango_layout_new (context); |
| pango_layout_set_font_description (layout, desc); |
| pango_layout_set_text (layout, "aaaa", -1); |
| pango_layout_get_extents (layout, &ink, &logical); |
| pango_extents_to_pixels (&logical, NULL); |
| |
| surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, logical.width * 3 / 2, 4*logical.height); |
| cr = cairo_create (surface); |
| cairo_set_source_rgb (cr, 1, 1, 1); |
| cairo_paint (cr); |
| |
| iter = pango_layout_get_iter (layout); |
| run = pango_layout_iter_get_run (iter); |
| |
| cairo_set_source_rgb (cr, 0, 0, 0); |
| for (i = 0; i < 4; i++) |
| { |
| g = &(run->glyphs->glyphs[i]); |
| g->geometry.width = PANGO_UNITS_ROUND (g->geometry.width * 3 / 2); |
| } |
| |
| for (j = 0; j < 4; j++) |
| { |
| for (i = 0; i < 4; i++) |
| { |
| g = &(run->glyphs->glyphs[i]); |
| g->geometry.x_offset = i * (PANGO_SCALE / 4); |
| g->geometry.y_offset = j * (PANGO_SCALE / 4); |
| } |
| |
| cairo_move_to (cr, 0, j * logical.height); |
| pango_cairo_show_layout (cr, layout); |
| } |
| |
| cairo_destroy (cr); |
| pango_layout_iter_free (iter); |
| g_object_unref (layout); |
| |
| pixbuf = gdk_pixbuf_get_from_surface (surface, 0, 0, cairo_image_surface_get_width (surface), cairo_image_surface_get_height (surface)); |
| pixbuf2 = gdk_pixbuf_scale_simple (pixbuf, gdk_pixbuf_get_width (pixbuf) * scale, gdk_pixbuf_get_height (pixbuf) * scale, GDK_INTERP_NEAREST); |
| g_object_unref (pixbuf); |
| cairo_surface_destroy (surface); |
| } |
| |
| |
| gtk_picture_set_pixbuf (GTK_PICTURE (image), pixbuf2); |
| |
| g_object_unref (pixbuf2); |
| |
| pango_font_description_free (desc); |
| } |
| |
| static void |
| update_buttons (void) |
| { |
| gtk_widget_set_sensitive (up_button, scale < 32); |
| gtk_widget_set_sensitive (down_button, scale > 1); |
| } |
| |
| static void |
| scale_up (void) |
| { |
| scale += 1; |
| update_buttons (); |
| update_image (); |
| } |
| |
| static void |
| scale_down (void) |
| { |
| scale -= 1; |
| update_buttons (); |
| update_image (); |
| } |
| |
| GtkWidget * |
| do_fontrendering (GtkWidget *do_widget) |
| { |
| if (!window) |
| { |
| GtkBuilder *builder; |
| |
| builder = gtk_builder_new_from_resource ("/fontrendering/fontrendering.ui"); |
| window = GTK_WIDGET (gtk_builder_get_object (builder, "window")); |
| gtk_window_set_display (GTK_WINDOW (window), |
| gtk_widget_get_display (do_widget)); |
| g_signal_connect (window, "destroy", |
| G_CALLBACK (on_destroy), NULL); |
| g_object_set_data_full (G_OBJECT (window), "builder", builder, g_object_unref); |
| font_button = GTK_WIDGET (gtk_builder_get_object (builder, "font_button")); |
| up_button = GTK_WIDGET (gtk_builder_get_object (builder, "up_button")); |
| down_button = GTK_WIDGET (gtk_builder_get_object (builder, "down_button")); |
| entry = GTK_WIDGET (gtk_builder_get_object (builder, "entry")); |
| image = GTK_WIDGET (gtk_builder_get_object (builder, "image")); |
| hinting = GTK_WIDGET (gtk_builder_get_object (builder, "hinting")); |
| hint_metrics = GTK_WIDGET (gtk_builder_get_object (builder, "hint_metrics")); |
| text_radio = GTK_WIDGET (gtk_builder_get_object (builder, "text_radio")); |
| show_grid = GTK_WIDGET (gtk_builder_get_object (builder, "show_grid")); |
| show_extents = GTK_WIDGET (gtk_builder_get_object (builder, "show_extents")); |
| |
| g_signal_connect (up_button, "clicked", G_CALLBACK (scale_up), NULL); |
| g_signal_connect (down_button, "clicked", G_CALLBACK (scale_down), NULL); |
| g_signal_connect (entry, "notify::text", G_CALLBACK (update_image), NULL); |
| g_signal_connect (font_button, "notify::font-desc", G_CALLBACK (update_image), NULL); |
| g_signal_connect (hinting, "notify::active", G_CALLBACK (update_image), NULL); |
| g_signal_connect (hint_metrics, "notify::active", G_CALLBACK (update_image), NULL); |
| g_signal_connect (text_radio, "notify::active", G_CALLBACK (update_image), NULL); |
| g_signal_connect (show_grid, "notify::active", G_CALLBACK (update_image), NULL); |
| g_signal_connect (show_extents, "notify::active", G_CALLBACK (update_image), NULL); |
| |
| update_image (); |
| } |
| |
| if (!gtk_widget_get_visible (window)) |
| gtk_widget_show (window); |
| else |
| gtk_widget_destroy (window); |
| |
| return window; |
| } |