… apply to GIMP GUI not text layer rendering in image. Reviewer note: this is the theoretical fix, but it won't work right now because Cairo explicitly bypasses grayscale antialiasing when system set subpixel one. Still let's push this first patch, but the issue will be actually fixed when Cairo will merge my MR too: https://gitlab.freedesktop.org/cairo/cairo/-/merge_requests/114
796 lines
22 KiB
C
796 lines
22 KiB
C
/* GIMP - The GNU 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 3 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, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <string.h>
|
|
|
|
#include <gegl.h>
|
|
#include <gdk-pixbuf/gdk-pixbuf.h>
|
|
#include <pango/pangocairo.h>
|
|
|
|
#include "libgimpbase/gimpbase.h"
|
|
#include "libgimpcolor/gimpcolor.h"
|
|
#include "libgimpmath/gimpmath.h"
|
|
|
|
#include "text-types.h"
|
|
|
|
#include "core/gimperror.h"
|
|
|
|
#include "gimptext.h"
|
|
#include "gimptextlayout.h"
|
|
|
|
#include "gimp-intl.h"
|
|
|
|
struct _GimpTextLayout
|
|
{
|
|
GObject object;
|
|
|
|
GimpText *text;
|
|
gdouble xres;
|
|
gdouble yres;
|
|
PangoLayout *layout;
|
|
PangoRectangle extents;
|
|
};
|
|
|
|
|
|
static void gimp_text_layout_finalize (GObject *object);
|
|
|
|
static void gimp_text_layout_position (GimpTextLayout *layout);
|
|
static void gimp_text_layout_set_markup (GimpTextLayout *layout,
|
|
GError **error);
|
|
|
|
static PangoContext * gimp_text_get_pango_context (GimpText *text,
|
|
gdouble xres,
|
|
gdouble yres);
|
|
|
|
|
|
G_DEFINE_TYPE (GimpTextLayout, gimp_text_layout, G_TYPE_OBJECT)
|
|
|
|
#define parent_class gimp_text_layout_parent_class
|
|
|
|
|
|
static void
|
|
gimp_text_layout_class_init (GimpTextLayoutClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (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 = 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,
|
|
gdouble xres,
|
|
gdouble yres,
|
|
GError **error)
|
|
{
|
|
GimpTextLayout *layout;
|
|
PangoContext *context;
|
|
PangoFontDescription *font_desc;
|
|
PangoAlignment alignment = PANGO_ALIGN_LEFT;
|
|
gint size;
|
|
|
|
g_return_val_if_fail (GIMP_IS_TEXT (text), NULL);
|
|
|
|
font_desc = pango_font_description_from_string (text->font);
|
|
g_return_val_if_fail (font_desc != NULL, NULL);
|
|
|
|
size = pango_units_from_double (gimp_units_to_points (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);
|
|
layout->xres = xres;
|
|
layout->yres = yres;
|
|
|
|
pango_layout_set_wrap (layout->layout, PANGO_WRAP_WORD_CHAR);
|
|
|
|
pango_layout_set_font_description (layout->layout, font_desc);
|
|
pango_font_description_free (font_desc);
|
|
|
|
gimp_text_layout_set_markup (layout, error);
|
|
|
|
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:
|
|
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:
|
|
if (! PANGO_GRAVITY_IS_VERTICAL (pango_context_get_base_gravity (context)))
|
|
pango_layout_set_width (layout->layout,
|
|
pango_units_from_double
|
|
(gimp_units_to_pixels (text->box_width,
|
|
text->box_unit,
|
|
xres)));
|
|
else
|
|
pango_layout_set_width (layout->layout,
|
|
pango_units_from_double
|
|
(gimp_units_to_pixels (text->box_height,
|
|
text->box_unit,
|
|
yres)));
|
|
break;
|
|
}
|
|
|
|
pango_layout_set_indent (layout->layout,
|
|
pango_units_from_double
|
|
(gimp_units_to_pixels (text->indent,
|
|
text->unit,
|
|
xres)));
|
|
pango_layout_set_spacing (layout->layout,
|
|
pango_units_from_double
|
|
(gimp_units_to_pixels (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.width = ceil (gimp_units_to_pixels (text->box_width,
|
|
text->box_unit,
|
|
xres));
|
|
layout->extents.height = ceil (gimp_units_to_pixels (text->box_height,
|
|
text->box_unit,
|
|
yres));
|
|
|
|
/* #define VERBOSE */
|
|
|
|
#ifdef VERBOSE
|
|
g_printerr ("extents set to %d x %d\n",
|
|
layout->extents.width, layout->extents.height);
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
g_object_unref (context);
|
|
|
|
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;
|
|
}
|
|
|
|
void
|
|
gimp_text_layout_get_resolution (GimpTextLayout *layout,
|
|
gdouble *xres,
|
|
gdouble *yres)
|
|
{
|
|
g_return_if_fail (GIMP_IS_TEXT_LAYOUT (layout));
|
|
|
|
if (xres)
|
|
*xres = layout->xres;
|
|
|
|
if (yres)
|
|
*yres = layout->yres;
|
|
}
|
|
|
|
GimpText *
|
|
gimp_text_layout_get_text (GimpTextLayout *layout)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_TEXT_LAYOUT (layout), NULL);
|
|
|
|
return layout->text;
|
|
}
|
|
|
|
PangoLayout *
|
|
gimp_text_layout_get_pango_layout (GimpTextLayout *layout)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_TEXT_LAYOUT (layout), NULL);
|
|
|
|
return layout->layout;
|
|
}
|
|
|
|
void
|
|
gimp_text_layout_get_transform (GimpTextLayout *layout,
|
|
cairo_matrix_t *matrix)
|
|
{
|
|
GimpText *text;
|
|
gdouble xres;
|
|
gdouble yres;
|
|
gdouble norm;
|
|
|
|
g_return_if_fail (GIMP_IS_TEXT_LAYOUT (layout));
|
|
g_return_if_fail (matrix != NULL);
|
|
|
|
text = gimp_text_layout_get_text (layout);
|
|
|
|
gimp_text_layout_get_resolution (layout, &xres, &yres);
|
|
|
|
norm = 1.0 / yres * xres;
|
|
|
|
matrix->xx = text->transformation.coeff[0][0] * norm;
|
|
matrix->xy = text->transformation.coeff[0][1] * 1.0;
|
|
matrix->yx = text->transformation.coeff[1][0] * norm;
|
|
matrix->yy = text->transformation.coeff[1][1] * 1.0;
|
|
matrix->x0 = 0;
|
|
matrix->y0 = 0;
|
|
}
|
|
|
|
void
|
|
gimp_text_layout_transform_rect (GimpTextLayout *layout,
|
|
PangoRectangle *rect)
|
|
{
|
|
cairo_matrix_t matrix;
|
|
gdouble x, y;
|
|
gdouble width, height;
|
|
|
|
g_return_if_fail (GIMP_IS_TEXT_LAYOUT (layout));
|
|
g_return_if_fail (rect != NULL);
|
|
|
|
x = rect->x;
|
|
y = rect->y;
|
|
width = rect->width;
|
|
height = rect->height;
|
|
|
|
gimp_text_layout_get_transform (layout, &matrix);
|
|
|
|
cairo_matrix_transform_point (&matrix, &x, &y);
|
|
cairo_matrix_transform_distance (&matrix, &width, &height);
|
|
|
|
rect->x = ROUND (x);
|
|
rect->y = ROUND (y);
|
|
rect->width = ROUND (width);
|
|
rect->height = ROUND (height);
|
|
}
|
|
|
|
void
|
|
gimp_text_layout_transform_point (GimpTextLayout *layout,
|
|
gdouble *x,
|
|
gdouble *y)
|
|
{
|
|
cairo_matrix_t matrix;
|
|
gdouble _x = 0.0;
|
|
gdouble _y = 0.0;
|
|
|
|
g_return_if_fail (GIMP_IS_TEXT_LAYOUT (layout));
|
|
|
|
if (x) _x = *x;
|
|
if (y) _y = *y;
|
|
|
|
gimp_text_layout_get_transform (layout, &matrix);
|
|
|
|
cairo_matrix_transform_point (&matrix, &_x, &_y);
|
|
|
|
if (x) *x = _x;
|
|
if (y) *y = _y;
|
|
}
|
|
|
|
void
|
|
gimp_text_layout_transform_distance (GimpTextLayout *layout,
|
|
gdouble *x,
|
|
gdouble *y)
|
|
{
|
|
cairo_matrix_t matrix;
|
|
gdouble _x = 0.0;
|
|
gdouble _y = 0.0;
|
|
|
|
g_return_if_fail (GIMP_IS_TEXT_LAYOUT (layout));
|
|
|
|
if (x) _x = *x;
|
|
if (y) _y = *y;
|
|
|
|
gimp_text_layout_get_transform (layout, &matrix);
|
|
|
|
cairo_matrix_transform_distance (&matrix, &_x, &_y);
|
|
|
|
if (x) *x = _x;
|
|
if (y) *y = _y;
|
|
}
|
|
|
|
void
|
|
gimp_text_layout_untransform_rect (GimpTextLayout *layout,
|
|
PangoRectangle *rect)
|
|
{
|
|
cairo_matrix_t matrix;
|
|
gdouble x, y;
|
|
gdouble width, height;
|
|
|
|
g_return_if_fail (GIMP_IS_TEXT_LAYOUT (layout));
|
|
g_return_if_fail (rect != NULL);
|
|
|
|
x = rect->x;
|
|
y = rect->y;
|
|
width = rect->width;
|
|
height = rect->height;
|
|
|
|
gimp_text_layout_get_transform (layout, &matrix);
|
|
|
|
if (cairo_matrix_invert (&matrix) == CAIRO_STATUS_SUCCESS)
|
|
{
|
|
cairo_matrix_transform_point (&matrix, &x, &y);
|
|
cairo_matrix_transform_distance (&matrix, &width, &height);
|
|
|
|
rect->x = ROUND (x);
|
|
rect->y = ROUND (y);
|
|
rect->width = ROUND (width);
|
|
rect->height = ROUND (height);
|
|
}
|
|
}
|
|
|
|
void
|
|
gimp_text_layout_untransform_point (GimpTextLayout *layout,
|
|
gdouble *x,
|
|
gdouble *y)
|
|
{
|
|
cairo_matrix_t matrix;
|
|
gdouble _x = 0.0;
|
|
gdouble _y = 0.0;
|
|
|
|
g_return_if_fail (GIMP_IS_TEXT_LAYOUT (layout));
|
|
|
|
if (x) _x = *x;
|
|
if (y) _y = *y;
|
|
|
|
gimp_text_layout_get_transform (layout, &matrix);
|
|
|
|
if (cairo_matrix_invert (&matrix) == CAIRO_STATUS_SUCCESS)
|
|
{
|
|
cairo_matrix_transform_point (&matrix, &_x, &_y);
|
|
|
|
if (x) *x = _x;
|
|
if (y) *y = _y;
|
|
}
|
|
}
|
|
|
|
void
|
|
gimp_text_layout_untransform_distance (GimpTextLayout *layout,
|
|
gdouble *x,
|
|
gdouble *y)
|
|
{
|
|
cairo_matrix_t matrix;
|
|
gdouble _x = 0.0;
|
|
gdouble _y = 0.0;
|
|
|
|
g_return_if_fail (GIMP_IS_TEXT_LAYOUT (layout));
|
|
|
|
if (x) _x = *x;
|
|
if (y) _y = *y;
|
|
|
|
gimp_text_layout_get_transform (layout, &matrix);
|
|
|
|
if (cairo_matrix_invert (&matrix) == CAIRO_STATUS_SUCCESS)
|
|
{
|
|
cairo_matrix_transform_distance (&matrix, &_x, &_y);
|
|
|
|
if (x) *x = _x;
|
|
if (y) *y = _y;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
gimp_text_layout_split_markup (const gchar *markup,
|
|
gchar **open_tag,
|
|
gchar **content,
|
|
gchar **close_tag)
|
|
{
|
|
gchar *p_open;
|
|
gchar *p_close;
|
|
|
|
p_open = strstr (markup, "<markup>");
|
|
if (! p_open)
|
|
return FALSE;
|
|
|
|
*open_tag = g_strndup (markup, p_open - markup + strlen ("<markup>"));
|
|
|
|
p_close = g_strrstr (markup, "</markup>");
|
|
if (! p_close)
|
|
{
|
|
g_free (*open_tag);
|
|
return FALSE;
|
|
}
|
|
|
|
*close_tag = g_strdup (p_close);
|
|
|
|
if (p_open + strlen ("<markup>") < p_close)
|
|
{
|
|
*content = g_strndup (p_open + strlen ("<markup>"),
|
|
p_close - p_open - strlen ("<markup>"));
|
|
}
|
|
else
|
|
{
|
|
*content = g_strdup ("");
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gchar *
|
|
gimp_text_layout_apply_tags (GimpTextLayout *layout,
|
|
const gchar *markup)
|
|
{
|
|
GimpText *text = layout->text;
|
|
gchar *result;
|
|
|
|
{
|
|
guchar r, g, b;
|
|
|
|
gimp_rgb_get_uchar (&text->color, &r, &g, &b);
|
|
|
|
result = g_strdup_printf ("<span color=\"#%02x%02x%02x\">%s</span>",
|
|
r, g, b, markup);
|
|
}
|
|
|
|
if (fabs (text->letter_spacing) > 0.1)
|
|
{
|
|
gchar *tmp = g_strdup_printf ("<span letter_spacing=\"%d\">%s</span>",
|
|
(gint) (text->letter_spacing * PANGO_SCALE),
|
|
result);
|
|
g_free (result);
|
|
result = tmp;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static void
|
|
gimp_text_layout_set_markup (GimpTextLayout *layout,
|
|
GError **error)
|
|
{
|
|
GimpText *text = layout->text;
|
|
gchar *open_tag = NULL;
|
|
gchar *content = NULL;
|
|
gchar *close_tag = NULL;
|
|
gchar *tagged;
|
|
gchar *markup;
|
|
|
|
if (text->markup)
|
|
{
|
|
if (! gimp_text_layout_split_markup (text->markup,
|
|
&open_tag, &content, &close_tag))
|
|
{
|
|
open_tag = g_strdup ("<markup>");
|
|
content = g_strdup ("");
|
|
close_tag = g_strdup ("</markup>");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
open_tag = g_strdup ("<markup>");
|
|
close_tag = g_strdup ("</markup>");
|
|
|
|
if (text->text)
|
|
content = g_markup_escape_text (text->text, -1);
|
|
else
|
|
content = g_strdup ("");
|
|
}
|
|
|
|
tagged = gimp_text_layout_apply_tags (layout, content);
|
|
|
|
g_free (content);
|
|
|
|
markup = g_strconcat (open_tag, tagged, close_tag, NULL);
|
|
|
|
g_free (open_tag);
|
|
g_free (tagged);
|
|
g_free (close_tag);
|
|
|
|
if (pango_parse_markup (markup, -1, 0, NULL, NULL, NULL, error) == FALSE)
|
|
{
|
|
if (error && *error &&
|
|
(*error)->domain == G_MARKUP_ERROR &&
|
|
(*error)->code == G_MARKUP_ERROR_INVALID_CONTENT)
|
|
{
|
|
/* Errors from pango lib are not accurate enough.
|
|
* Other possible error codes are: G_MARKUP_ERROR_UNKNOWN_ELEMENT
|
|
* and G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, which likely indicate a bug
|
|
* in GIMP code or a pango library version issue.
|
|
* G_MARKUP_ERROR_INVALID_CONTENT on the other hand likely indicates
|
|
* size/color/style/weight/variant/etc. value issue. Font size is the
|
|
* only free text in GIMP GUI so we assume that must be it.
|
|
* Also we output a custom message because pango's error->message is
|
|
* too technical (telling of <span> tags, not using user's font size
|
|
* unit, and such). */
|
|
g_error_free (*error);
|
|
*error = NULL;
|
|
g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
|
|
_("The new text layout cannot be generated. "
|
|
"Most likely the font size is too big."));
|
|
}
|
|
}
|
|
else
|
|
pango_layout_set_markup (layout->layout, markup, -1);
|
|
|
|
g_free (markup);
|
|
}
|
|
|
|
static void
|
|
gimp_text_layout_position (GimpTextLayout *layout)
|
|
{
|
|
PangoRectangle ink;
|
|
PangoRectangle logical;
|
|
PangoContext *context;
|
|
gint x1, y1;
|
|
gint x2, y2;
|
|
|
|
layout->extents.x = 0;
|
|
layout->extents.y = 0;
|
|
layout->extents.width = 0;
|
|
layout->extents.height = 0;
|
|
|
|
pango_layout_get_pixel_extents (layout->layout, &ink, &logical);
|
|
|
|
ink.width = ceil ((gdouble) ink.width * layout->xres / layout->yres);
|
|
logical.width = ceil ((gdouble) logical.width * layout->xres / layout->yres);
|
|
context = pango_layout_get_context (layout->layout);
|
|
|
|
#ifdef VERBOSE
|
|
g_printerr ("ink rect: %d x %d @ %d, %d\n",
|
|
ink.width, ink.height, ink.x, ink.y);
|
|
g_printerr ("logical rect: %d x %d @ %d, %d\n",
|
|
logical.width, logical.height, logical.x, logical.y);
|
|
#endif
|
|
|
|
if (ink.width < 1 || ink.height < 1)
|
|
{
|
|
layout->extents.width = 1;
|
|
layout->extents.height = logical.height;
|
|
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 the width of the layout is > 0, then the text-box is FIXED and
|
|
* the layout position should be offset if the alignment is centered
|
|
* or right-aligned, also adjust for RTL text direction.
|
|
*/
|
|
if (pango_layout_get_width (layout->layout) > 0)
|
|
{
|
|
PangoAlignment align = pango_layout_get_alignment (layout->layout);
|
|
GimpTextDirection base_dir = layout->text->base_dir;
|
|
gint width;
|
|
|
|
pango_layout_get_pixel_size (layout->layout, &width, NULL);
|
|
|
|
if ((base_dir == GIMP_TEXT_DIRECTION_LTR && align == PANGO_ALIGN_RIGHT) ||
|
|
(base_dir == GIMP_TEXT_DIRECTION_RTL && align == PANGO_ALIGN_LEFT) ||
|
|
(base_dir == GIMP_TEXT_DIRECTION_TTB_RTL && align == PANGO_ALIGN_RIGHT) ||
|
|
(base_dir == GIMP_TEXT_DIRECTION_TTB_RTL_UPRIGHT && align == PANGO_ALIGN_RIGHT) ||
|
|
(base_dir == GIMP_TEXT_DIRECTION_TTB_LTR && align == PANGO_ALIGN_LEFT) ||
|
|
(base_dir == GIMP_TEXT_DIRECTION_TTB_LTR_UPRIGHT && align == PANGO_ALIGN_LEFT))
|
|
{
|
|
layout->extents.x +=
|
|
PANGO_PIXELS (pango_layout_get_width (layout->layout)) - width;
|
|
}
|
|
else if (align == PANGO_ALIGN_CENTER)
|
|
{
|
|
layout->extents.x +=
|
|
(PANGO_PIXELS (pango_layout_get_width (layout->layout)) - width) / 2;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
if (PANGO_GRAVITY_IS_VERTICAL (pango_context_get_base_gravity (context)))
|
|
{
|
|
gint temp;
|
|
|
|
temp = layout->extents.y;
|
|
layout->extents.y = layout->extents.x;
|
|
layout->extents.x = temp;
|
|
|
|
temp = layout->extents.height;
|
|
layout->extents.height = layout->extents.width;
|
|
layout->extents.width = temp;
|
|
}
|
|
|
|
#ifdef VERBOSE
|
|
g_printerr ("layout extents: %d x %d @ %d, %d\n",
|
|
layout->extents.width, layout->extents.height,
|
|
layout->extents.x, layout->extents.y);
|
|
#endif
|
|
}
|
|
|
|
static cairo_font_options_t *
|
|
gimp_text_get_font_options (GimpText *text)
|
|
{
|
|
cairo_font_options_t *options = cairo_font_options_create ();
|
|
|
|
cairo_font_options_set_antialias (options, (text->antialias ?
|
|
CAIRO_ANTIALIAS_GRAY :
|
|
CAIRO_ANTIALIAS_NONE));
|
|
|
|
switch (text->hint_style)
|
|
{
|
|
case GIMP_TEXT_HINT_STYLE_NONE:
|
|
cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
|
|
break;
|
|
|
|
case GIMP_TEXT_HINT_STYLE_SLIGHT:
|
|
cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_SLIGHT);
|
|
break;
|
|
|
|
case GIMP_TEXT_HINT_STYLE_MEDIUM:
|
|
cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_MEDIUM);
|
|
break;
|
|
|
|
case GIMP_TEXT_HINT_STYLE_FULL:
|
|
cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_FULL);
|
|
break;
|
|
}
|
|
|
|
return options;
|
|
}
|
|
|
|
static PangoContext *
|
|
gimp_text_get_pango_context (GimpText *text,
|
|
gdouble xres,
|
|
gdouble yres)
|
|
{
|
|
PangoContext *context;
|
|
PangoFontMap *fontmap;
|
|
cairo_font_options_t *options;
|
|
|
|
fontmap = pango_cairo_font_map_new_for_font_type (CAIRO_FONT_TYPE_FT);
|
|
if (! fontmap)
|
|
g_error ("You are using a Pango that has been built against a cairo "
|
|
"that lacks the Freetype font backend");
|
|
|
|
pango_cairo_font_map_set_resolution (PANGO_CAIRO_FONT_MAP (fontmap), yres);
|
|
|
|
context = pango_font_map_create_context (fontmap);
|
|
g_object_unref (fontmap);
|
|
|
|
options = gimp_text_get_font_options (text);
|
|
pango_cairo_context_set_font_options (context, options);
|
|
cairo_font_options_destroy (options);
|
|
|
|
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);
|
|
pango_context_set_gravity_hint (context, PANGO_GRAVITY_HINT_NATURAL);
|
|
pango_context_set_base_gravity (context, PANGO_GRAVITY_SOUTH);
|
|
break;
|
|
|
|
case GIMP_TEXT_DIRECTION_RTL:
|
|
pango_context_set_base_dir (context, PANGO_DIRECTION_RTL);
|
|
pango_context_set_gravity_hint (context, PANGO_GRAVITY_HINT_NATURAL);
|
|
pango_context_set_base_gravity (context, PANGO_GRAVITY_SOUTH);
|
|
break;
|
|
|
|
case GIMP_TEXT_DIRECTION_TTB_RTL:
|
|
pango_context_set_base_dir (context, PANGO_DIRECTION_LTR);
|
|
pango_context_set_gravity_hint (context, PANGO_GRAVITY_HINT_LINE);
|
|
pango_context_set_base_gravity (context, PANGO_GRAVITY_EAST);
|
|
break;
|
|
|
|
case GIMP_TEXT_DIRECTION_TTB_RTL_UPRIGHT:
|
|
pango_context_set_base_dir (context, PANGO_DIRECTION_LTR);
|
|
pango_context_set_gravity_hint (context, PANGO_GRAVITY_HINT_STRONG);
|
|
pango_context_set_base_gravity (context, PANGO_GRAVITY_EAST);
|
|
break;
|
|
|
|
case GIMP_TEXT_DIRECTION_TTB_LTR:
|
|
pango_context_set_base_dir (context, PANGO_DIRECTION_LTR);
|
|
pango_context_set_gravity_hint (context, PANGO_GRAVITY_HINT_LINE);
|
|
pango_context_set_base_gravity (context, PANGO_GRAVITY_WEST);
|
|
break;
|
|
|
|
case GIMP_TEXT_DIRECTION_TTB_LTR_UPRIGHT:
|
|
pango_context_set_base_dir (context, PANGO_DIRECTION_LTR);
|
|
pango_context_set_gravity_hint (context, PANGO_GRAVITY_HINT_STRONG);
|
|
pango_context_set_base_gravity (context, PANGO_GRAVITY_WEST);
|
|
break;
|
|
}
|
|
|
|
return context;
|
|
}
|