812 lines
23 KiB
C
812 lines
23 KiB
C
/* PIKA - Photo and Image Kooker Application
|
|
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
|
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
|
*
|
|
* Original copyright, applying to most contents (license remains unchanged):
|
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
|
*
|
|
* PikaText
|
|
* 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 "libpikabase/pikabase.h"
|
|
#include "libpikacolor/pikacolor.h"
|
|
#include "libpikamath/pikamath.h"
|
|
|
|
#include "text-types.h"
|
|
|
|
#include "core/pikaerror.h"
|
|
|
|
#include "pikafont.h"
|
|
|
|
#include "pikatext.h"
|
|
#include "pikatextlayout.h"
|
|
|
|
#include "pika-intl.h"
|
|
|
|
struct _PikaTextLayout
|
|
{
|
|
GObject object;
|
|
|
|
PikaText *text;
|
|
gdouble xres;
|
|
gdouble yres;
|
|
PangoLayout *layout;
|
|
PangoRectangle extents;
|
|
};
|
|
|
|
|
|
static void pika_text_layout_finalize (GObject *object);
|
|
|
|
static void pika_text_layout_position (PikaTextLayout *layout);
|
|
static void pika_text_layout_set_markup (PikaTextLayout *layout,
|
|
GError **error);
|
|
|
|
static PangoContext * pika_text_get_pango_context (PikaText *text,
|
|
gdouble xres,
|
|
gdouble yres);
|
|
|
|
|
|
G_DEFINE_TYPE (PikaTextLayout, pika_text_layout, G_TYPE_OBJECT)
|
|
|
|
#define parent_class pika_text_layout_parent_class
|
|
|
|
|
|
static void
|
|
pika_text_layout_class_init (PikaTextLayoutClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->finalize = pika_text_layout_finalize;
|
|
}
|
|
|
|
static void
|
|
pika_text_layout_init (PikaTextLayout *layout)
|
|
{
|
|
layout->text = NULL;
|
|
layout->layout = NULL;
|
|
}
|
|
|
|
static void
|
|
pika_text_layout_finalize (GObject *object)
|
|
{
|
|
PikaTextLayout *layout = PIKA_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);
|
|
}
|
|
|
|
|
|
PikaTextLayout *
|
|
pika_text_layout_new (PikaText *text,
|
|
gdouble xres,
|
|
gdouble yres,
|
|
GError **error)
|
|
{
|
|
PikaTextLayout *layout;
|
|
PangoContext *context;
|
|
PangoFontDescription *font_desc;
|
|
PangoAlignment alignment = PANGO_ALIGN_LEFT;
|
|
gint size;
|
|
|
|
g_return_val_if_fail (PIKA_IS_TEXT (text), NULL);
|
|
|
|
font_desc = pango_font_description_from_string (pika_font_get_lookup_name (text->font));
|
|
g_return_val_if_fail (font_desc != NULL, NULL);
|
|
|
|
size = pango_units_from_double (pika_units_to_points (text->font_size,
|
|
text->unit,
|
|
yres));
|
|
|
|
pango_font_description_set_size (font_desc, MAX (1, size));
|
|
|
|
context = pika_text_get_pango_context (text, xres, yres);
|
|
|
|
layout = g_object_new (PIKA_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);
|
|
|
|
pika_text_layout_set_markup (layout, error);
|
|
|
|
switch (text->justify)
|
|
{
|
|
case PIKA_TEXT_JUSTIFY_LEFT:
|
|
alignment = PANGO_ALIGN_LEFT;
|
|
break;
|
|
case PIKA_TEXT_JUSTIFY_RIGHT:
|
|
alignment = PANGO_ALIGN_RIGHT;
|
|
break;
|
|
case PIKA_TEXT_JUSTIFY_CENTER:
|
|
alignment = PANGO_ALIGN_CENTER;
|
|
break;
|
|
case PIKA_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 PIKA_TEXT_BOX_DYNAMIC:
|
|
break;
|
|
case PIKA_TEXT_BOX_FIXED:
|
|
if (! PANGO_GRAVITY_IS_VERTICAL (pango_context_get_base_gravity (context)))
|
|
pango_layout_set_width (layout->layout,
|
|
pango_units_from_double
|
|
(pika_units_to_pixels (text->box_width,
|
|
text->box_unit,
|
|
xres)));
|
|
else
|
|
pango_layout_set_width (layout->layout,
|
|
pango_units_from_double
|
|
(pika_units_to_pixels (text->box_height,
|
|
text->box_unit,
|
|
yres)));
|
|
break;
|
|
}
|
|
|
|
pango_layout_set_indent (layout->layout,
|
|
pango_units_from_double
|
|
(pika_units_to_pixels (text->indent,
|
|
text->unit,
|
|
xres)));
|
|
pango_layout_set_spacing (layout->layout,
|
|
pango_units_from_double
|
|
(pika_units_to_pixels (text->line_spacing,
|
|
text->unit,
|
|
yres)));
|
|
|
|
pika_text_layout_position (layout);
|
|
|
|
switch (text->box_mode)
|
|
{
|
|
case PIKA_TEXT_BOX_DYNAMIC:
|
|
break;
|
|
case PIKA_TEXT_BOX_FIXED:
|
|
layout->extents.width = ceil (pika_units_to_pixels (text->box_width,
|
|
text->box_unit,
|
|
xres));
|
|
layout->extents.height = ceil (pika_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
|
|
pika_text_layout_get_size (PikaTextLayout *layout,
|
|
gint *width,
|
|
gint *height)
|
|
{
|
|
g_return_val_if_fail (PIKA_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
|
|
pika_text_layout_get_offsets (PikaTextLayout *layout,
|
|
gint *x,
|
|
gint *y)
|
|
{
|
|
g_return_if_fail (PIKA_IS_TEXT_LAYOUT (layout));
|
|
|
|
if (x)
|
|
*x = layout->extents.x;
|
|
|
|
if (y)
|
|
*y = layout->extents.y;
|
|
}
|
|
|
|
void
|
|
pika_text_layout_get_resolution (PikaTextLayout *layout,
|
|
gdouble *xres,
|
|
gdouble *yres)
|
|
{
|
|
g_return_if_fail (PIKA_IS_TEXT_LAYOUT (layout));
|
|
|
|
if (xres)
|
|
*xres = layout->xres;
|
|
|
|
if (yres)
|
|
*yres = layout->yres;
|
|
}
|
|
|
|
PikaText *
|
|
pika_text_layout_get_text (PikaTextLayout *layout)
|
|
{
|
|
g_return_val_if_fail (PIKA_IS_TEXT_LAYOUT (layout), NULL);
|
|
|
|
return layout->text;
|
|
}
|
|
|
|
PangoLayout *
|
|
pika_text_layout_get_pango_layout (PikaTextLayout *layout)
|
|
{
|
|
g_return_val_if_fail (PIKA_IS_TEXT_LAYOUT (layout), NULL);
|
|
|
|
return layout->layout;
|
|
}
|
|
|
|
void
|
|
pika_text_layout_get_transform (PikaTextLayout *layout,
|
|
cairo_matrix_t *matrix)
|
|
{
|
|
PikaText *text;
|
|
gdouble xres;
|
|
gdouble yres;
|
|
gdouble norm;
|
|
|
|
g_return_if_fail (PIKA_IS_TEXT_LAYOUT (layout));
|
|
g_return_if_fail (matrix != NULL);
|
|
|
|
text = pika_text_layout_get_text (layout);
|
|
|
|
pika_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
|
|
pika_text_layout_transform_rect (PikaTextLayout *layout,
|
|
PangoRectangle *rect)
|
|
{
|
|
cairo_matrix_t matrix;
|
|
gdouble x, y;
|
|
gdouble width, height;
|
|
|
|
g_return_if_fail (PIKA_IS_TEXT_LAYOUT (layout));
|
|
g_return_if_fail (rect != NULL);
|
|
|
|
x = rect->x;
|
|
y = rect->y;
|
|
width = rect->width;
|
|
height = rect->height;
|
|
|
|
pika_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
|
|
pika_text_layout_transform_point (PikaTextLayout *layout,
|
|
gdouble *x,
|
|
gdouble *y)
|
|
{
|
|
cairo_matrix_t matrix;
|
|
gdouble _x = 0.0;
|
|
gdouble _y = 0.0;
|
|
|
|
g_return_if_fail (PIKA_IS_TEXT_LAYOUT (layout));
|
|
|
|
if (x) _x = *x;
|
|
if (y) _y = *y;
|
|
|
|
pika_text_layout_get_transform (layout, &matrix);
|
|
|
|
cairo_matrix_transform_point (&matrix, &_x, &_y);
|
|
|
|
if (x) *x = _x;
|
|
if (y) *y = _y;
|
|
}
|
|
|
|
void
|
|
pika_text_layout_transform_distance (PikaTextLayout *layout,
|
|
gdouble *x,
|
|
gdouble *y)
|
|
{
|
|
cairo_matrix_t matrix;
|
|
gdouble _x = 0.0;
|
|
gdouble _y = 0.0;
|
|
|
|
g_return_if_fail (PIKA_IS_TEXT_LAYOUT (layout));
|
|
|
|
if (x) _x = *x;
|
|
if (y) _y = *y;
|
|
|
|
pika_text_layout_get_transform (layout, &matrix);
|
|
|
|
cairo_matrix_transform_distance (&matrix, &_x, &_y);
|
|
|
|
if (x) *x = _x;
|
|
if (y) *y = _y;
|
|
}
|
|
|
|
void
|
|
pika_text_layout_untransform_rect (PikaTextLayout *layout,
|
|
PangoRectangle *rect)
|
|
{
|
|
cairo_matrix_t matrix;
|
|
gdouble x, y;
|
|
gdouble width, height;
|
|
|
|
g_return_if_fail (PIKA_IS_TEXT_LAYOUT (layout));
|
|
g_return_if_fail (rect != NULL);
|
|
|
|
x = rect->x;
|
|
y = rect->y;
|
|
width = rect->width;
|
|
height = rect->height;
|
|
|
|
pika_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
|
|
pika_text_layout_untransform_point (PikaTextLayout *layout,
|
|
gdouble *x,
|
|
gdouble *y)
|
|
{
|
|
cairo_matrix_t matrix;
|
|
gdouble _x = 0.0;
|
|
gdouble _y = 0.0;
|
|
|
|
g_return_if_fail (PIKA_IS_TEXT_LAYOUT (layout));
|
|
|
|
if (x) _x = *x;
|
|
if (y) _y = *y;
|
|
|
|
pika_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
|
|
pika_text_layout_untransform_distance (PikaTextLayout *layout,
|
|
gdouble *x,
|
|
gdouble *y)
|
|
{
|
|
cairo_matrix_t matrix;
|
|
gdouble _x = 0.0;
|
|
gdouble _y = 0.0;
|
|
|
|
g_return_if_fail (PIKA_IS_TEXT_LAYOUT (layout));
|
|
|
|
if (x) _x = *x;
|
|
if (y) _y = *y;
|
|
|
|
pika_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
|
|
pika_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 *
|
|
pika_text_layout_apply_tags (PikaTextLayout *layout,
|
|
const gchar *markup)
|
|
{
|
|
PikaText *text = layout->text;
|
|
gchar *result;
|
|
|
|
{
|
|
guchar r, g, b;
|
|
|
|
pika_rgb_get_uchar (&text->color, &r, &g, &b);
|
|
|
|
result = g_strdup_printf ("<span color=\"#%02x%02x%02x\">%s</span>",
|
|
r, g, b, markup);
|
|
}
|
|
/* Updating font 'locl' (if supported) with 'lang' feature tag */
|
|
if (text->language)
|
|
{
|
|
gchar *tmp = g_strdup_printf ("<span lang=\"%s\">%s</span>",
|
|
text->language,
|
|
result);
|
|
g_free (result);
|
|
result = tmp;
|
|
}
|
|
|
|
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
|
|
pika_text_layout_set_markup (PikaTextLayout *layout,
|
|
GError **error)
|
|
{
|
|
PikaText *text = layout->text;
|
|
gchar *open_tag = NULL;
|
|
gchar *content = NULL;
|
|
gchar *close_tag = NULL;
|
|
gchar *tagged;
|
|
gchar *markup;
|
|
|
|
if (text->markup)
|
|
{
|
|
if (! pika_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 = pika_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 PIKA 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 PIKA 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, PIKA_ERROR, PIKA_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
|
|
pika_text_layout_position (PikaTextLayout *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);
|
|
PikaTextDirection base_dir = layout->text->base_dir;
|
|
gint width;
|
|
|
|
pango_layout_get_pixel_size (layout->layout, &width, NULL);
|
|
|
|
if ((base_dir == PIKA_TEXT_DIRECTION_LTR && align == PANGO_ALIGN_RIGHT) ||
|
|
(base_dir == PIKA_TEXT_DIRECTION_RTL && align == PANGO_ALIGN_RIGHT) ||
|
|
(base_dir == PIKA_TEXT_DIRECTION_TTB_RTL && align == PANGO_ALIGN_RIGHT) ||
|
|
(base_dir == PIKA_TEXT_DIRECTION_TTB_RTL_UPRIGHT && align == PANGO_ALIGN_RIGHT) ||
|
|
(base_dir == PIKA_TEXT_DIRECTION_TTB_LTR && align == PANGO_ALIGN_LEFT) ||
|
|
(base_dir == PIKA_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 *
|
|
pika_text_get_font_options (PikaText *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 PIKA_TEXT_HINT_STYLE_NONE:
|
|
cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
|
|
break;
|
|
|
|
case PIKA_TEXT_HINT_STYLE_SLIGHT:
|
|
cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_SLIGHT);
|
|
break;
|
|
|
|
case PIKA_TEXT_HINT_STYLE_MEDIUM:
|
|
cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_MEDIUM);
|
|
break;
|
|
|
|
case PIKA_TEXT_HINT_STYLE_FULL:
|
|
cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_FULL);
|
|
break;
|
|
}
|
|
|
|
return options;
|
|
}
|
|
|
|
static PangoContext *
|
|
pika_text_get_pango_context (PikaText *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 = pika_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 PIKA_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 PIKA_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 PIKA_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 PIKA_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 PIKA_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 PIKA_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;
|
|
}
|