/* The GIMP -- an image manipulation program * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * GimpText * Copyright (C) 2002-2003 Sven Neumann * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "config.h" #include #include #include "text-types.h" #include "core/gimpimage.h" #include "core/gimpunit.h" #include "gimptext.h" #include "gimptext-private.h" #include "gimptextlayout.h" static void gimp_text_layout_class_init (GimpTextLayoutClass *klass); static void gimp_text_layout_init (GimpTextLayout *layout); static void gimp_text_layout_finalize (GObject *object); static void gimp_text_layout_position (GimpTextLayout *layout); static PangoContext * gimp_text_get_pango_context (GimpText *text, gdouble xres, gdouble yres); static gint gimp_text_layout_pixel_size (Gimp *gimp, gdouble value, GimpUnit unit, gdouble res); static gint gimp_text_layout_point_size (Gimp *gimp, gdouble value, GimpUnit unit, gdouble res); static GObjectClass * parent_class = NULL; GType gimp_text_layout_get_type (void) { static GType layout_type = 0; if (! layout_type) { static const GTypeInfo layout_info = { sizeof (GimpTextLayoutClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) gimp_text_layout_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (GimpTextLayout), 0, /* n_preallocs */ (GInstanceInitFunc) gimp_text_layout_init, }; layout_type = g_type_register_static (G_TYPE_OBJECT, "GimpTextLayout", &layout_info, 0); } return layout_type; } static void gimp_text_layout_class_init (GimpTextLayoutClass *klass) { GObjectClass *object_class; object_class = G_OBJECT_CLASS (klass); parent_class = g_type_class_peek_parent (klass); object_class->finalize = gimp_text_layout_finalize; } static void gimp_text_layout_init (GimpTextLayout *layout) { layout->text = NULL; layout->layout = NULL; } static void gimp_text_layout_finalize (GObject *object) { GimpTextLayout *layout; layout = GIMP_TEXT_LAYOUT (object); if (layout->text) { g_object_unref (layout->text); layout->text = NULL; } if (layout->layout) { g_object_unref (layout->layout); layout->layout = NULL; } G_OBJECT_CLASS (parent_class)->finalize (object); } GimpTextLayout * gimp_text_layout_new (GimpText *text, GimpImage *image) { GimpTextLayout *layout; PangoContext *context; PangoFontDescription *font_desc; PangoAlignment alignment = PANGO_ALIGN_LEFT; gdouble xres, yres; gint size; g_return_val_if_fail (GIMP_IS_TEXT (text), NULL); g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); font_desc = pango_font_description_from_string (text->font); g_return_val_if_fail (font_desc != NULL, NULL); if (!font_desc) return NULL; gimp_image_get_resolution (image, &xres, &yres); size = gimp_text_layout_point_size (image->gimp, text->font_size, text->unit, yres); pango_font_description_set_size (font_desc, MAX (1, size)); context = gimp_text_get_pango_context (text, xres, yres); layout = g_object_new (GIMP_TYPE_TEXT_LAYOUT, NULL); layout->text = g_object_ref (text); layout->layout = pango_layout_new (context); g_object_unref (context); pango_layout_set_font_description (layout->layout, font_desc); pango_font_description_free (font_desc); if (text->text) pango_layout_set_text (layout->layout, text->text, -1); else pango_layout_set_text (layout->layout, NULL, 0); switch (text->justify) { case GIMP_TEXT_JUSTIFY_LEFT: alignment = PANGO_ALIGN_LEFT; break; case GIMP_TEXT_JUSTIFY_RIGHT: alignment = PANGO_ALIGN_RIGHT; break; case GIMP_TEXT_JUSTIFY_CENTER: alignment = PANGO_ALIGN_CENTER; break; case GIMP_TEXT_JUSTIFY_FILL: /* FIXME: This doesn't work since the implementation is missing at the Pango level. */ alignment = PANGO_ALIGN_LEFT; pango_layout_set_justify (layout->layout, TRUE); break; } pango_layout_set_alignment (layout->layout, alignment); switch (text->box_mode) { case GIMP_TEXT_BOX_DYNAMIC: break; case GIMP_TEXT_BOX_FIXED: pango_layout_set_width (layout->layout, gimp_text_layout_pixel_size (image->gimp, text->box_width, text->box_unit, xres)); break; } pango_layout_set_indent (layout->layout, gimp_text_layout_pixel_size (image->gimp, text->indent, text->unit, xres)); pango_layout_set_spacing (layout->layout, gimp_text_layout_pixel_size (image->gimp, text->line_spacing, text->unit, yres)); gimp_text_layout_position (layout); switch (text->box_mode) { case GIMP_TEXT_BOX_DYNAMIC: break; case GIMP_TEXT_BOX_FIXED: layout->extents.height = PANGO_PIXELS (gimp_text_layout_pixel_size (image->gimp, text->box_height, text->box_unit, yres)); break; } return layout; } gboolean gimp_text_layout_get_size (GimpTextLayout *layout, gint *width, gint *height) { g_return_val_if_fail (GIMP_IS_TEXT_LAYOUT (layout), FALSE); if (width) *width = layout->extents.width; if (height) *height = layout->extents.height; return (layout->extents.width > 0 && layout->extents.height > 0); } void gimp_text_layout_get_offsets (GimpTextLayout *layout, gint *x, gint *y) { g_return_if_fail (GIMP_IS_TEXT_LAYOUT (layout)); if (x) *x = layout->extents.x; if (y) *y = layout->extents.y; } static void gimp_text_layout_position (GimpTextLayout *layout) { PangoRectangle ink; PangoRectangle logical; gint x1, y1; gint x2, y2; layout->extents.x = 0; layout->extents.x = 0; layout->extents.width = 0; layout->extents.height = 0; pango_layout_get_pixel_extents (layout->layout, &ink, &logical); #ifdef VERBOSE g_print ("ink rect: %d x %d @ %d, %d\n", ink.width, ink.height, ink.x, ink.y); g_print ("logical rect: %d x %d @ %d, %d\n", logical.width, logical.height, logical.x, logical.y); #endif if (ink.width < 1 || ink.height < 1) return; x1 = MIN (ink.x, logical.x); y1 = MIN (ink.y, logical.y); x2 = MAX (ink.x + ink.width, logical.x + logical.width); y2 = MAX (ink.y + ink.height, logical.y + logical.height); layout->extents.x = - x1; layout->extents.y = - y1; layout->extents.width = x2 - x1; layout->extents.height = y2 - y1; if (layout->text->border > 0) { gint border = layout->text->border; layout->extents.x += border; layout->extents.y += border; layout->extents.width += 2 * border; layout->extents.height += 2 * border; } #ifdef VERBOSE g_print ("layout extents: %d x %d @ %d, %d\n", layout->extents.width, layout->extents.height, layout->extents.x, layout->extents.y); #endif } static void gimp_text_ft2_subst_func (FcPattern *pattern, gpointer data) { GimpText *text = GIMP_TEXT (data); FcPatternAddBool (pattern, FC_HINTING, text->hinting); FcPatternAddBool (pattern, FC_AUTOHINT, text->autohint); FcPatternAddBool (pattern, FC_ANTIALIAS, text->antialias); } static PangoContext * gimp_text_get_pango_context (GimpText *text, gdouble xres, gdouble yres) { PangoContext *context; PangoFT2FontMap *fontmap; fontmap = PANGO_FT2_FONT_MAP (pango_ft2_font_map_new ()); pango_ft2_font_map_set_resolution (fontmap, xres, yres); pango_ft2_font_map_set_default_substitute (fontmap, gimp_text_ft2_subst_func, g_object_ref (text), (GDestroyNotify) g_object_unref); context = pango_ft2_font_map_create_context (fontmap); g_object_unref (fontmap); /* Workaround for bug #143542 (PangoFT2Fontmap leak), * see also bug #148997 (Text layer rendering leaks font file descriptor): * * Calling pango_ft2_font_map_substitute_changed() causes the * font_map cache to be flushed, thereby removing the circular * reference that causes the leak. */ g_object_weak_ref (G_OBJECT (context), (GWeakNotify) pango_ft2_font_map_substitute_changed, fontmap); if (text->language) pango_context_set_language (context, pango_language_from_string (text->language)); switch (text->base_dir) { case GIMP_TEXT_DIRECTION_LTR: pango_context_set_base_dir (context, PANGO_DIRECTION_LTR); break; case GIMP_TEXT_DIRECTION_RTL: pango_context_set_base_dir (context, PANGO_DIRECTION_RTL); break; } return context; } static gint gimp_text_layout_pixel_size (Gimp *gimp, gdouble value, GimpUnit unit, gdouble res) { gdouble factor; switch (unit) { case GIMP_UNIT_PIXEL: return PANGO_SCALE * value; default: factor = _gimp_unit_get_factor (gimp, unit); g_return_val_if_fail (factor > 0.0, 0); return PANGO_SCALE * value * res / factor; } } static gint gimp_text_layout_point_size (Gimp *gimp, gdouble value, GimpUnit unit, gdouble res) { gdouble factor; switch (unit) { case GIMP_UNIT_POINT: return PANGO_SCALE * value; case GIMP_UNIT_PIXEL: g_return_val_if_fail (res > 0.0, 0); return (PANGO_SCALE * value * _gimp_unit_get_factor (gimp, GIMP_UNIT_POINT) / res); default: factor = _gimp_unit_get_factor (gimp, unit); g_return_val_if_fail (factor > 0.0, 0); return (PANGO_SCALE * value * _gimp_unit_get_factor (gimp, GIMP_UNIT_POINT) / factor); } }