2003-02-05 16:29:12 +08:00
|
|
|
/* The GIMP -- an image manipulation program
|
|
|
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
|
|
|
*
|
|
|
|
* GimpText
|
|
|
|
* Copyright (C) 2002-2003 Sven Neumann <sven@gimp.org>
|
|
|
|
*
|
|
|
|
* 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 <glib-object.h>
|
|
|
|
#include <pango/pangoft2.h>
|
|
|
|
|
|
|
|
#include "libgimpbase/gimpbase.h"
|
|
|
|
|
|
|
|
#include "text-types.h"
|
|
|
|
|
|
|
|
#include "base/pixel-region.h"
|
|
|
|
#include "base/tile-manager.h"
|
|
|
|
|
|
|
|
#include "core/gimpimage.h"
|
|
|
|
|
|
|
|
#include "gimptext.h"
|
|
|
|
#include "gimptextlayout.h"
|
|
|
|
|
|
|
|
|
|
|
|
struct _GimpTextLayout
|
|
|
|
{
|
|
|
|
GObject object;
|
|
|
|
|
|
|
|
GimpText *text;
|
|
|
|
PangoLayout *layout;
|
|
|
|
PangoRectangle extents;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct _GimpTextLayoutClass
|
|
|
|
{
|
|
|
|
GObjectClass parent_class;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
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_image_get_pango_context (GimpImage *image);
|
|
|
|
|
|
|
|
|
|
|
|
static GQuark gimp_text_context_quark = 0;
|
|
|
|
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;
|
2003-02-05 17:51:26 +08:00
|
|
|
PangoAlignment alignment = PANGO_ALIGN_LEFT;
|
2003-02-05 16:29:12 +08:00
|
|
|
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;
|
|
|
|
|
|
|
|
switch (text->font_size_unit)
|
|
|
|
{
|
|
|
|
case GIMP_UNIT_PIXEL:
|
|
|
|
size = PANGO_SCALE * text->font_size;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
gdouble xres, yres;
|
|
|
|
gdouble factor;
|
|
|
|
|
|
|
|
factor = gimp_unit_get_factor (text->font_size_unit);
|
|
|
|
g_return_val_if_fail (factor > 0.0, NULL);
|
|
|
|
|
|
|
|
gimp_image_get_resolution (image, &xres, &yres);
|
|
|
|
|
|
|
|
size = (gdouble) PANGO_SCALE * text->font_size * yres / factor;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2003-02-22 05:39:42 +08:00
|
|
|
pango_font_description_set_size (font_desc, MAX (1, size));
|
2003-02-05 16:29:12 +08:00
|
|
|
|
|
|
|
context = gimp_image_get_pango_context (image);
|
|
|
|
|
2003-02-25 02:54:24 +08:00
|
|
|
if (text->language)
|
|
|
|
pango_context_set_language (context,
|
|
|
|
pango_language_from_string (text->language));
|
|
|
|
|
2003-02-05 16:29:12 +08:00
|
|
|
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);
|
|
|
|
|
|
|
|
pango_layout_set_text (layout->layout, text->text, -1);
|
2003-02-05 17:51:26 +08:00
|
|
|
|
|
|
|
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 just doesn't work to do this */
|
|
|
|
alignment = PANGO_ALIGN_LEFT;
|
|
|
|
pango_layout_set_justify (layout->layout, TRUE);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
pango_layout_set_alignment (layout->layout, alignment);
|
|
|
|
|
|
|
|
pango_layout_set_width (layout->layout,
|
|
|
|
text->fixed_width > 0 ?
|
|
|
|
text->fixed_width * PANGO_SCALE : -1);
|
|
|
|
|
|
|
|
pango_layout_set_indent (layout->layout, text->indent * PANGO_SCALE);
|
|
|
|
pango_layout_set_spacing (layout->layout, text->line_spacing * PANGO_SCALE);
|
2003-02-05 16:29:12 +08:00
|
|
|
|
|
|
|
gimp_text_layout_position (layout);
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
TileManager *
|
2003-02-08 01:01:38 +08:00
|
|
|
gimp_text_layout_render (GimpTextLayout *layout,
|
|
|
|
gint width,
|
|
|
|
gint height)
|
2003-02-05 16:29:12 +08:00
|
|
|
{
|
|
|
|
TileManager *mask;
|
|
|
|
FT_Bitmap bitmap;
|
|
|
|
PixelRegion maskPR;
|
|
|
|
gint i;
|
|
|
|
gint x, y;
|
|
|
|
|
|
|
|
g_return_val_if_fail (GIMP_IS_TEXT_LAYOUT (layout), NULL);
|
|
|
|
|
|
|
|
gimp_text_layout_get_offsets (layout, &x, &y);
|
|
|
|
|
|
|
|
bitmap.width = width;
|
|
|
|
bitmap.rows = height;
|
|
|
|
bitmap.pitch = width;
|
|
|
|
if (bitmap.pitch & 3)
|
|
|
|
bitmap.pitch += 4 - (bitmap.pitch & 3);
|
|
|
|
|
|
|
|
bitmap.buffer = g_malloc0 (bitmap.rows * bitmap.pitch);
|
2003-02-08 01:01:38 +08:00
|
|
|
|
2003-02-05 16:29:12 +08:00
|
|
|
pango_ft2_render_layout (&bitmap, layout->layout, x, y);
|
|
|
|
|
|
|
|
mask = tile_manager_new (width, height, 1);
|
|
|
|
pixel_region_init (&maskPR, mask, 0, 0, width, height, TRUE);
|
|
|
|
|
|
|
|
for (i = 0; i < height; i++)
|
|
|
|
pixel_region_set_row (&maskPR,
|
|
|
|
0, i, width, bitmap.buffer + i * bitmap.pitch);
|
|
|
|
|
|
|
|
g_free (bitmap.buffer);
|
|
|
|
|
|
|
|
return mask;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gimp_text_layout_position (GimpTextLayout *layout)
|
|
|
|
{
|
2003-02-05 21:03:44 +08:00
|
|
|
PangoRectangle ink;
|
|
|
|
PangoRectangle logical;
|
|
|
|
gint x1, y1;
|
|
|
|
gint x2, y2;
|
2003-02-05 16:29:12 +08:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2003-02-05 22:49:48 +08:00
|
|
|
#ifdef VERBOSE
|
2003-02-05 16:29:12 +08:00
|
|
|
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);
|
2003-02-05 22:49:48 +08:00
|
|
|
#endif
|
2003-02-05 16:29:12 +08:00
|
|
|
|
2003-02-05 22:49:48 +08:00
|
|
|
if (ink.width < 1 || ink.height < 1)
|
|
|
|
return;
|
2003-02-05 16:29:12 +08:00
|
|
|
|
|
|
|
x1 = MIN (0, logical.x);
|
|
|
|
y1 = MIN (0, logical.y);
|
|
|
|
x2 = MAX (ink.x + ink.width, logical.x + logical.width);
|
|
|
|
y2 = MAX (ink.y + ink.height, logical.y + logical.height);
|
|
|
|
|
2003-02-05 22:49:48 +08:00
|
|
|
layout->extents.width = x2 - x1;
|
|
|
|
layout->extents.height = y2 - y1;
|
2003-02-05 21:03:44 +08:00
|
|
|
|
|
|
|
/* border should only be used by the compatibility API */
|
2003-02-05 22:49:48 +08:00
|
|
|
if (layout->text->border > 0)
|
2003-02-05 21:03:44 +08:00
|
|
|
{
|
2003-02-05 22:49:48 +08:00
|
|
|
gint border = layout->text->border;
|
2003-02-05 21:03:44 +08:00
|
|
|
|
2003-02-05 22:49:48 +08:00
|
|
|
layout->extents.x += border;
|
|
|
|
layout->extents.y += border;
|
|
|
|
layout->extents.width += 2 * border;
|
|
|
|
layout->extents.height += 2 * border;
|
2003-02-05 16:29:12 +08:00
|
|
|
}
|
|
|
|
|
2003-02-05 22:49:48 +08:00
|
|
|
#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
|
2003-02-05 16:29:12 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
detach_pango_context (GObject *image)
|
|
|
|
{
|
|
|
|
g_object_set_qdata (image, gimp_text_context_quark, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static PangoContext *
|
|
|
|
gimp_image_get_pango_context (GimpImage *image)
|
|
|
|
{
|
|
|
|
PangoContext *context;
|
|
|
|
|
|
|
|
if (!gimp_text_context_quark)
|
|
|
|
gimp_text_context_quark = g_quark_from_static_string ("pango-context");
|
|
|
|
|
|
|
|
context = (PangoContext *) g_object_get_qdata (G_OBJECT (image),
|
|
|
|
gimp_text_context_quark);
|
|
|
|
|
|
|
|
if (!context)
|
|
|
|
{
|
|
|
|
gdouble xres, yres;
|
|
|
|
|
|
|
|
gimp_image_get_resolution (image, &xres, &yres);
|
|
|
|
|
|
|
|
context = pango_ft2_get_context (xres, yres);
|
|
|
|
|
|
|
|
g_signal_connect_object (image, "resolution_changed",
|
|
|
|
G_CALLBACK (detach_pango_context),
|
|
|
|
context, 0);
|
|
|
|
|
|
|
|
g_object_set_qdata_full (G_OBJECT (image),
|
|
|
|
gimp_text_context_quark, context,
|
|
|
|
(GDestroyNotify) g_object_unref);
|
|
|
|
}
|
|
|
|
|
|
|
|
return g_object_ref (context);
|
|
|
|
}
|