1017 lines
		
	
	
		
			36 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1017 lines
		
	
	
		
			36 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 <cairo.h>
 | |
| #include <gegl.h>
 | |
| #include <gdk-pixbuf/gdk-pixbuf.h>
 | |
| #include <pango/pango.h>
 | |
| 
 | |
| #include "libpikabase/pikabase.h"
 | |
| #include "libpikamath/pikamath.h"
 | |
| #include "libpikacolor/pikacolor.h"
 | |
| #include "libpikaconfig/pikaconfig.h"
 | |
| 
 | |
| #include "text-types.h"
 | |
| 
 | |
| #include "pikafont.h"
 | |
| 
 | |
| #include "core/pika.h"
 | |
| #include "core/pika-memsize.h"
 | |
| #include "core/pika-utils.h"
 | |
| #include "core/pikacontainer.h"
 | |
| #include "core/pikadashpattern.h"
 | |
| #include "core/pikadatafactory.h"
 | |
| #include "core/pikastrokeoptions.h"
 | |
| #include "core/pikapattern.h"
 | |
| 
 | |
| #include "pikatext.h"
 | |
| 
 | |
| 
 | |
| enum
 | |
| {
 | |
|   PROP_0,
 | |
|   PROP_PIKA,
 | |
|   PROP_TEXT,
 | |
|   PROP_MARKUP,
 | |
|   PROP_FONT,
 | |
|   PROP_FONT_SIZE,
 | |
|   PROP_UNIT,
 | |
|   PROP_ANTIALIAS,
 | |
|   PROP_HINT_STYLE,
 | |
|   PROP_KERNING,
 | |
|   PROP_LANGUAGE,
 | |
|   PROP_BASE_DIR,
 | |
|   PROP_COLOR,
 | |
|   PROP_OUTLINE,
 | |
|   PROP_JUSTIFICATION,
 | |
|   PROP_INDENTATION,
 | |
|   PROP_LINE_SPACING,
 | |
|   PROP_LETTER_SPACING,
 | |
|   PROP_BOX_MODE,
 | |
|   PROP_BOX_WIDTH,
 | |
|   PROP_BOX_HEIGHT,
 | |
|   PROP_BOX_UNIT,
 | |
|   PROP_TRANSFORMATION,
 | |
|   PROP_OFFSET_X,
 | |
|   PROP_OFFSET_Y,
 | |
|   PROP_BORDER,
 | |
| 
 | |
|   PROP_OUTLINE_STYLE,       /* fill-options */
 | |
|   PROP_OUTLINE_FOREGROUND,  /* context */
 | |
|   PROP_OUTLINE_PATTERN,     /* context */
 | |
|   PROP_OUTLINE_WIDTH,       /* stroke-options */
 | |
|   PROP_OUTLINE_UNIT,
 | |
|   PROP_OUTLINE_CAP_STYLE,
 | |
|   PROP_OUTLINE_JOIN_STYLE,
 | |
|   PROP_OUTLINE_MITER_LIMIT,
 | |
|   PROP_OUTLINE_ANTIALIAS,   /* fill-options */
 | |
|   PROP_OUTLINE_DASH_OFFSET,
 | |
|   PROP_OUTLINE_DASH_INFO,
 | |
|   /* for backward compatibility */
 | |
|   PROP_HINTING
 | |
| };
 | |
| 
 | |
| enum
 | |
| {
 | |
|   CHANGED,
 | |
|   LAST_SIGNAL
 | |
| };
 | |
| 
 | |
| static void     pika_text_config_iface_init           (PikaConfigInterface *iface);
 | |
| static gboolean pika_text_serialize_property          (PikaConfig       *config,
 | |
|                                                        guint             property_id,
 | |
|                                                        const GValue     *value,
 | |
|                                                        GParamSpec       *pspec,
 | |
|                                                        PikaConfigWriter *writer);
 | |
| static gboolean pika_text_deserialize_property        (PikaConfig       *config,
 | |
|                                                        guint             property_id,
 | |
|                                                        GValue           *value,
 | |
|                                                        GParamSpec       *pspec,
 | |
|                                                        GScanner         *scanner,
 | |
|                                                        GTokenType       *expected);
 | |
| 
 | |
| static void     pika_text_finalize                    (GObject      *object);
 | |
| static void     pika_text_get_property                (GObject      *object,
 | |
|                                                        guint         property_id,
 | |
|                                                        GValue       *value,
 | |
|                                                        GParamSpec   *pspec);
 | |
| static void     pika_text_set_property                (GObject      *object,
 | |
|                                                        guint         property_id,
 | |
|                                                        const GValue *value,
 | |
|                                                        GParamSpec   *pspec);
 | |
| static void     pika_text_dispatch_properties_changed (GObject      *object,
 | |
|                                                        guint         n_pspecs,
 | |
|                                                        GParamSpec  **pspecs);
 | |
| static gint64   pika_text_get_memsize                 (PikaObject   *object,
 | |
|                                                        gint64       *gui_size);
 | |
| 
 | |
| 
 | |
| G_DEFINE_TYPE_WITH_CODE (PikaText, pika_text, PIKA_TYPE_OBJECT,
 | |
|                          G_IMPLEMENT_INTERFACE (PIKA_TYPE_CONFIG,
 | |
|                                                 pika_text_config_iface_init))
 | |
| 
 | |
| #define parent_class pika_text_parent_class
 | |
| 
 | |
| static guint text_signals[LAST_SIGNAL] = { 0 };
 | |
| 
 | |
| 
 | |
| static void
 | |
| pika_text_class_init (PikaTextClass *klass)
 | |
| {
 | |
|   GObjectClass    *object_class      = G_OBJECT_CLASS (klass);
 | |
|   PikaObjectClass *pika_object_class = PIKA_OBJECT_CLASS (klass);
 | |
|   PikaRGB          black;
 | |
|   PikaRGB          gray;
 | |
|   PikaMatrix2      identity;
 | |
|   gchar           *language;
 | |
|   GParamSpec      *array_spec;
 | |
| 
 | |
|   text_signals[CHANGED] =
 | |
|     g_signal_new ("changed",
 | |
|                   G_TYPE_FROM_CLASS (klass),
 | |
|                   G_SIGNAL_RUN_FIRST,
 | |
|                   G_STRUCT_OFFSET (PikaTextClass, changed),
 | |
|                   NULL, NULL, NULL,
 | |
|                   G_TYPE_NONE, 0);
 | |
| 
 | |
|   object_class->finalize                    = pika_text_finalize;
 | |
|   object_class->get_property                = pika_text_get_property;
 | |
|   object_class->set_property                = pika_text_set_property;
 | |
|   object_class->dispatch_properties_changed = pika_text_dispatch_properties_changed;
 | |
| 
 | |
|   pika_object_class->get_memsize            = pika_text_get_memsize;
 | |
| 
 | |
|   pika_rgba_set (&black, 0.0, 0.0, 0.0, PIKA_OPACITY_OPAQUE);
 | |
|   pika_rgba_set (&gray, 0.75, 0.75, 0.75, PIKA_OPACITY_OPAQUE);
 | |
|   pika_matrix2_identity (&identity);
 | |
| 
 | |
|   PIKA_CONFIG_PROP_STRING (object_class, PROP_TEXT,
 | |
|                            "text",
 | |
|                            NULL, NULL,
 | |
|                            NULL,
 | |
|                            PIKA_PARAM_STATIC_STRINGS);
 | |
| 
 | |
|   PIKA_CONFIG_PROP_STRING (object_class, PROP_MARKUP,
 | |
|                            "markup",
 | |
|                            NULL, NULL,
 | |
|                            NULL,
 | |
|                            PIKA_PARAM_STATIC_STRINGS);
 | |
| 
 | |
|   PIKA_CONFIG_PROP_FONT (object_class, PROP_FONT,
 | |
|                          "font", NULL, NULL,
 | |
|                          PIKA_CONFIG_PARAM_FLAGS);
 | |
| 
 | |
|   PIKA_CONFIG_PROP_DOUBLE (object_class, PROP_FONT_SIZE,
 | |
|                            "font-size",
 | |
|                            NULL, NULL,
 | |
|                            0.0, 8192.0, 24.0,
 | |
|                            PIKA_PARAM_STATIC_STRINGS);
 | |
| 
 | |
|   /*  We use the name "font-size-unit" for backward compatibility.
 | |
|    *  The unit is also used for other sizes in the text object.
 | |
|    */
 | |
|   PIKA_CONFIG_PROP_UNIT (object_class, PROP_UNIT,
 | |
|                          "font-size-unit",
 | |
|                          NULL, NULL,
 | |
|                          TRUE, FALSE, PIKA_UNIT_PIXEL,
 | |
|                          PIKA_PARAM_STATIC_STRINGS);
 | |
| 
 | |
|   PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_ANTIALIAS,
 | |
|                             "antialias",
 | |
|                             NULL, NULL,
 | |
|                             TRUE,
 | |
|                             PIKA_PARAM_STATIC_STRINGS);
 | |
| 
 | |
|   PIKA_CONFIG_PROP_ENUM (object_class, PROP_HINT_STYLE,
 | |
|                          "hint-style",
 | |
|                          NULL, NULL,
 | |
|                          PIKA_TYPE_TEXT_HINT_STYLE,
 | |
|                          PIKA_TEXT_HINT_STYLE_MEDIUM,
 | |
|                          PIKA_PARAM_STATIC_STRINGS |
 | |
|                          PIKA_CONFIG_PARAM_DEFAULTS);
 | |
| 
 | |
|   PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_KERNING,
 | |
|                             "kerning",
 | |
|                             NULL, NULL,
 | |
|                             FALSE,
 | |
|                             PIKA_PARAM_STATIC_STRINGS |
 | |
|                             PIKA_CONFIG_PARAM_DEFAULTS);
 | |
| 
 | |
|   language = pika_get_default_language (NULL);
 | |
| 
 | |
|   PIKA_CONFIG_PROP_STRING (object_class, PROP_LANGUAGE,
 | |
|                            "language",
 | |
|                            NULL, NULL,
 | |
|                            language,
 | |
|                            PIKA_PARAM_STATIC_STRINGS);
 | |
| 
 | |
|   g_free (language);
 | |
| 
 | |
|   PIKA_CONFIG_PROP_ENUM (object_class, PROP_BASE_DIR,
 | |
|                          "base-direction",
 | |
|                          NULL, NULL,
 | |
|                          PIKA_TYPE_TEXT_DIRECTION,
 | |
|                          PIKA_TEXT_DIRECTION_LTR,
 | |
|                          PIKA_PARAM_STATIC_STRINGS);
 | |
| 
 | |
|   PIKA_CONFIG_PROP_RGB (object_class, PROP_COLOR,
 | |
|                         "color",
 | |
|                         NULL, NULL,
 | |
|                         FALSE, &black,
 | |
|                         PIKA_PARAM_STATIC_STRINGS);
 | |
| 
 | |
|   PIKA_CONFIG_PROP_ENUM (object_class, PROP_OUTLINE,
 | |
|                          "outline",
 | |
|                          NULL, NULL,
 | |
|                          PIKA_TYPE_TEXT_OUTLINE,
 | |
|                          PIKA_TEXT_OUTLINE_NONE,
 | |
|                          PIKA_PARAM_STATIC_STRINGS |
 | |
|                          PIKA_CONFIG_PARAM_DEFAULTS);
 | |
| 
 | |
|   PIKA_CONFIG_PROP_ENUM (object_class, PROP_JUSTIFICATION,
 | |
|                          "justify",
 | |
|                          NULL, NULL,
 | |
|                          PIKA_TYPE_TEXT_JUSTIFICATION,
 | |
|                          PIKA_TEXT_JUSTIFY_LEFT,
 | |
|                          PIKA_PARAM_STATIC_STRINGS);
 | |
| 
 | |
|   PIKA_CONFIG_PROP_DOUBLE (object_class, PROP_INDENTATION,
 | |
|                            "indent",
 | |
|                            NULL, NULL,
 | |
|                            -8192.0, 8192.0, 0.0,
 | |
|                            PIKA_PARAM_STATIC_STRINGS |
 | |
|                            PIKA_CONFIG_PARAM_DEFAULTS);
 | |
| 
 | |
|   PIKA_CONFIG_PROP_DOUBLE (object_class, PROP_LINE_SPACING,
 | |
|                            "line-spacing",
 | |
|                            NULL, NULL,
 | |
|                            -8192.0, 8192.0, 0.0,
 | |
|                            PIKA_PARAM_STATIC_STRINGS |
 | |
|                            PIKA_CONFIG_PARAM_DEFAULTS);
 | |
| 
 | |
|   PIKA_CONFIG_PROP_DOUBLE (object_class, PROP_LETTER_SPACING,
 | |
|                            "letter-spacing",
 | |
|                            NULL, NULL,
 | |
|                            -8192.0, 8192.0, 0.0,
 | |
|                            PIKA_PARAM_STATIC_STRINGS |
 | |
|                            PIKA_CONFIG_PARAM_DEFAULTS);
 | |
| 
 | |
|   PIKA_CONFIG_PROP_ENUM (object_class, PROP_BOX_MODE,
 | |
|                          "box-mode",
 | |
|                          NULL, NULL,
 | |
|                          PIKA_TYPE_TEXT_BOX_MODE,
 | |
|                          PIKA_TEXT_BOX_DYNAMIC,
 | |
|                          PIKA_PARAM_STATIC_STRINGS);
 | |
| 
 | |
|   PIKA_CONFIG_PROP_DOUBLE (object_class, PROP_BOX_WIDTH,
 | |
|                            "box-width",
 | |
|                            NULL, NULL,
 | |
|                            0.0, PIKA_MAX_IMAGE_SIZE, 0.0,
 | |
|                            PIKA_PARAM_STATIC_STRINGS |
 | |
|                            PIKA_CONFIG_PARAM_DEFAULTS);
 | |
| 
 | |
|   PIKA_CONFIG_PROP_DOUBLE (object_class, PROP_BOX_HEIGHT,
 | |
|                            "box-height",
 | |
|                            NULL, NULL,
 | |
|                            0.0, PIKA_MAX_IMAGE_SIZE, 0.0,
 | |
|                            PIKA_PARAM_STATIC_STRINGS |
 | |
|                            PIKA_CONFIG_PARAM_DEFAULTS);
 | |
| 
 | |
|   PIKA_CONFIG_PROP_UNIT (object_class, PROP_BOX_UNIT,
 | |
|                          "box-unit",
 | |
|                          NULL, NULL,
 | |
|                          TRUE, FALSE, PIKA_UNIT_PIXEL,
 | |
|                          PIKA_PARAM_STATIC_STRINGS);
 | |
| 
 | |
|   PIKA_CONFIG_PROP_MATRIX2 (object_class, PROP_TRANSFORMATION,
 | |
|                             "transformation",
 | |
|                             NULL, NULL,
 | |
|                             &identity,
 | |
|                             PIKA_PARAM_STATIC_STRINGS |
 | |
|                             PIKA_CONFIG_PARAM_DEFAULTS);
 | |
| 
 | |
|   PIKA_CONFIG_PROP_DOUBLE (object_class, PROP_OFFSET_X,
 | |
|                            "offset-x",
 | |
|                            NULL, NULL,
 | |
|                            -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
 | |
|                            PIKA_PARAM_STATIC_STRINGS |
 | |
|                            PIKA_CONFIG_PARAM_DEFAULTS);
 | |
| 
 | |
|   PIKA_CONFIG_PROP_DOUBLE (object_class, PROP_OFFSET_Y,
 | |
|                            "offset-y",
 | |
|                            NULL, NULL,
 | |
|                            -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
 | |
|                            PIKA_PARAM_STATIC_STRINGS |
 | |
|                            PIKA_CONFIG_PARAM_DEFAULTS);
 | |
| 
 | |
|   /*  border does only exist to implement the old text API  */
 | |
|   g_object_class_install_property (object_class, PROP_BORDER,
 | |
|                                    g_param_spec_int ("border", NULL, NULL,
 | |
|                                                      0, PIKA_MAX_IMAGE_SIZE, 0,
 | |
|                                                      G_PARAM_CONSTRUCT |
 | |
|                                                      PIKA_PARAM_WRITABLE));
 | |
| 
 | |
|    PIKA_CONFIG_PROP_ENUM (object_class, PROP_OUTLINE_STYLE,
 | |
|                           "outline-custom-style", NULL, NULL,
 | |
|                           PIKA_TYPE_CUSTOM_STYLE,
 | |
|                           PIKA_CUSTOM_STYLE_SOLID_COLOR,
 | |
|                           PIKA_PARAM_STATIC_STRINGS);
 | |
|    PIKA_CONFIG_PROP_OBJECT (object_class, PROP_OUTLINE_PATTERN,
 | |
|                             "outline-pattern", NULL, NULL,
 | |
|                             PIKA_TYPE_PATTERN,
 | |
|                             PIKA_PARAM_STATIC_STRINGS);
 | |
|    PIKA_CONFIG_PROP_RGB (object_class, PROP_OUTLINE_FOREGROUND,
 | |
|                          "outline-foreground", NULL, NULL,
 | |
|                          FALSE, &gray,
 | |
|                          PIKA_PARAM_STATIC_STRINGS);
 | |
|    PIKA_CONFIG_PROP_DOUBLE (object_class, PROP_OUTLINE_WIDTH,
 | |
|                             "outline-width", NULL, NULL,
 | |
|                             0.0, 8192.0, 4.0,
 | |
|                             PIKA_PARAM_STATIC_STRINGS |
 | |
|                             PIKA_CONFIG_PARAM_DEFAULTS);
 | |
|    PIKA_CONFIG_PROP_ENUM (object_class, PROP_OUTLINE_CAP_STYLE,
 | |
|                           "outline-cap-style", NULL, NULL,
 | |
|                           PIKA_TYPE_CAP_STYLE, PIKA_CAP_BUTT,
 | |
|                           PIKA_PARAM_STATIC_STRINGS);
 | |
|    PIKA_CONFIG_PROP_ENUM (object_class, PROP_OUTLINE_JOIN_STYLE,
 | |
|                           "outline-join-style", NULL, NULL,
 | |
|                           PIKA_TYPE_JOIN_STYLE, PIKA_JOIN_MITER,
 | |
|                           PIKA_PARAM_STATIC_STRINGS);
 | |
|    PIKA_CONFIG_PROP_DOUBLE (object_class, PROP_OUTLINE_MITER_LIMIT,
 | |
|                             "outline-miter-limit",
 | |
|                             NULL, NULL,
 | |
|                             0.0, 100.0, 10.0,
 | |
|                             PIKA_PARAM_STATIC_STRINGS);
 | |
|    PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_OUTLINE_ANTIALIAS,
 | |
|                              "outline-antialias", NULL, NULL,
 | |
|                              TRUE,
 | |
|                              PIKA_PARAM_STATIC_STRINGS);
 | |
|    PIKA_CONFIG_PROP_DOUBLE (object_class, PROP_OUTLINE_DASH_OFFSET,
 | |
|                             "outline-dash-offset", NULL, NULL,
 | |
|                             0.0, 2000.0, 0.0,
 | |
|                             PIKA_PARAM_STATIC_STRINGS);
 | |
| 
 | |
|    array_spec = g_param_spec_double ("outline-dash-length", NULL, NULL,
 | |
|                                      0.0, 2000.0, 1.0, PIKA_PARAM_READWRITE);
 | |
|    g_object_class_install_property (object_class, PROP_OUTLINE_DASH_INFO,
 | |
|                                     pika_param_spec_value_array ("outline-dash-info",
 | |
|                                                                  NULL, NULL,
 | |
|                                                                  array_spec,
 | |
|                                                                  PIKA_PARAM_STATIC_STRINGS |
 | |
|                                                                  PIKA_CONFIG_PARAM_FLAGS));
 | |
| 
 | |
|   /*  the old hinting options have been replaced by 'hint-style'  */
 | |
|   PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_HINTING,
 | |
|                             "hinting",
 | |
|                             NULL, NULL,
 | |
|                             TRUE,
 | |
|                             PIKA_PARAM_STATIC_STRINGS);
 | |
| 
 | |
|   g_object_class_install_property (object_class, PROP_PIKA,
 | |
|                                    g_param_spec_object ("pika", NULL, NULL,
 | |
|                                                         PIKA_TYPE_PIKA,
 | |
|                                                         PIKA_PARAM_READWRITE |
 | |
|                                                         G_PARAM_CONSTRUCT_ONLY));
 | |
| 
 | |
| }
 | |
| 
 | |
| static void
 | |
| pika_text_init (PikaText *text)
 | |
| {
 | |
| }
 | |
| 
 | |
| static void
 | |
| pika_text_config_iface_init (PikaConfigInterface *iface)
 | |
| {
 | |
|   iface->serialize_property   = pika_text_serialize_property;
 | |
|   iface->deserialize_property = pika_text_deserialize_property;
 | |
| }
 | |
| 
 | |
| static void
 | |
| pika_text_finalize (GObject *object)
 | |
| {
 | |
|   PikaText *text = PIKA_TEXT (object);
 | |
| 
 | |
|   g_clear_pointer (&text->text,     g_free);
 | |
|   g_clear_pointer (&text->markup,   g_free);
 | |
|   g_clear_pointer (&text->language, g_free);
 | |
|   g_clear_object (&text->font);
 | |
| 
 | |
|   G_OBJECT_CLASS (parent_class)->finalize (object);
 | |
| }
 | |
| 
 | |
| static void
 | |
| pika_text_get_property (GObject      *object,
 | |
|                         guint         property_id,
 | |
|                         GValue       *value,
 | |
|                         GParamSpec   *pspec)
 | |
| {
 | |
|   PikaText *text = PIKA_TEXT (object);
 | |
| 
 | |
|   switch (property_id)
 | |
|     {
 | |
|     case PROP_TEXT:
 | |
|       g_value_set_string (value, text->text);
 | |
|       break;
 | |
|     case PROP_MARKUP:
 | |
|       g_value_set_string (value, text->markup);
 | |
|       break;
 | |
|     case PROP_FONT:
 | |
|       g_value_set_object (value, text->font);
 | |
|       break;
 | |
|     case PROP_FONT_SIZE:
 | |
|       g_value_set_double (value, text->font_size);
 | |
|       break;
 | |
|     case PROP_UNIT:
 | |
|       g_value_set_int (value, text->unit);
 | |
|       break;
 | |
|     case PROP_ANTIALIAS:
 | |
|       g_value_set_boolean (value, text->antialias);
 | |
|       break;
 | |
|     case PROP_HINT_STYLE:
 | |
|       g_value_set_enum (value, text->hint_style);
 | |
|       break;
 | |
|     case PROP_KERNING:
 | |
|       g_value_set_boolean (value, text->kerning);
 | |
|       break;
 | |
|     case PROP_BASE_DIR:
 | |
|       g_value_set_enum (value, text->base_dir);
 | |
|       break;
 | |
|     case PROP_LANGUAGE:
 | |
|       g_value_set_string (value, text->language);
 | |
|       break;
 | |
|     case PROP_COLOR:
 | |
|       g_value_set_boxed (value, &text->color);
 | |
|       break;
 | |
|     case PROP_OUTLINE:
 | |
|       g_value_set_enum (value, text->outline);
 | |
|       break;
 | |
|     case PROP_JUSTIFICATION:
 | |
|       g_value_set_enum (value, text->justify);
 | |
|       break;
 | |
|     case PROP_INDENTATION:
 | |
|       g_value_set_double (value, text->indent);
 | |
|       break;
 | |
|     case PROP_LINE_SPACING:
 | |
|       g_value_set_double (value, text->line_spacing);
 | |
|       break;
 | |
|     case PROP_LETTER_SPACING:
 | |
|       g_value_set_double (value, text->letter_spacing);
 | |
|       break;
 | |
|     case PROP_BOX_MODE:
 | |
|       g_value_set_enum (value, text->box_mode);
 | |
|       break;
 | |
|     case PROP_BOX_WIDTH:
 | |
|       g_value_set_double (value, text->box_width);
 | |
|       break;
 | |
|     case PROP_BOX_HEIGHT:
 | |
|       g_value_set_double (value, text->box_height);
 | |
|       break;
 | |
|     case PROP_BOX_UNIT:
 | |
|       g_value_set_int (value, text->box_unit);
 | |
|       break;
 | |
|     case PROP_TRANSFORMATION:
 | |
|       g_value_set_boxed (value, &text->transformation);
 | |
|       break;
 | |
|     case PROP_OFFSET_X:
 | |
|       g_value_set_double (value, text->offset_x);
 | |
|       break;
 | |
|     case PROP_OFFSET_Y:
 | |
|       g_value_set_double (value, text->offset_y);
 | |
|       break;
 | |
|     case PROP_OUTLINE_STYLE:
 | |
|       g_value_set_enum (value, text->outline_style);
 | |
|       break;
 | |
|     case PROP_OUTLINE_FOREGROUND:
 | |
|       g_value_set_boxed (value, &text->outline_foreground);
 | |
|       break;
 | |
|     case PROP_OUTLINE_PATTERN:
 | |
|       g_value_set_object (value, text->outline_pattern);
 | |
|       break;
 | |
|     case PROP_OUTLINE_WIDTH:
 | |
|       g_value_set_double (value, text->outline_width);
 | |
|       break;
 | |
|     case PROP_OUTLINE_CAP_STYLE:
 | |
|       g_value_set_enum (value, text->outline_cap_style);
 | |
|       break;
 | |
|     case PROP_OUTLINE_JOIN_STYLE:
 | |
|       g_value_set_enum (value, text->outline_join_style);
 | |
|       break;
 | |
|     case PROP_OUTLINE_MITER_LIMIT:
 | |
|       g_value_set_double (value, text->outline_miter_limit);
 | |
|       break;
 | |
|     case PROP_OUTLINE_ANTIALIAS:
 | |
|       g_value_set_boolean (value, text->outline_antialias);
 | |
|       break;
 | |
|     case PROP_OUTLINE_DASH_OFFSET:
 | |
|       g_value_set_double (value, text->outline_dash_offset);
 | |
|       break;
 | |
|     case PROP_OUTLINE_DASH_INFO:
 | |
|       {
 | |
|         PikaValueArray *value_array;
 | |
| 
 | |
|         value_array = pika_dash_pattern_to_value_array (text->outline_dash_info);
 | |
|         g_value_take_boxed (value, value_array);
 | |
|       }
 | |
|       break;
 | |
|     case PROP_HINTING:
 | |
|       g_value_set_boolean (value,
 | |
|                            text->hint_style != PIKA_TEXT_HINT_STYLE_NONE);
 | |
|       break;
 | |
|     case PROP_PIKA:
 | |
|       g_value_set_object (value, text->pika);
 | |
|       break;
 | |
|     default:
 | |
|       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 | |
|       break;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void
 | |
| pika_text_set_property (GObject      *object,
 | |
|                         guint         property_id,
 | |
|                         const GValue *value,
 | |
|                         GParamSpec   *pspec)
 | |
| {
 | |
|   PikaText    *text = PIKA_TEXT (object);
 | |
|   PikaRGB     *color;
 | |
|   PikaMatrix2 *matrix;
 | |
| 
 | |
|   switch (property_id)
 | |
|     {
 | |
|     case PROP_TEXT:
 | |
|       g_free (text->text);
 | |
|       text->text = g_value_dup_string (value);
 | |
|       if (text->text && text->markup)
 | |
|         {
 | |
|           g_clear_pointer (&text->markup, g_free);
 | |
|           g_object_notify (object, "markup");
 | |
|         }
 | |
|       break;
 | |
|     case PROP_MARKUP:
 | |
|       g_free (text->markup);
 | |
|       text->markup = g_value_dup_string (value);
 | |
|       if (text->markup && text->text)
 | |
|         {
 | |
|           g_clear_pointer (&text->text, g_free);
 | |
|           g_object_notify (object, "text");
 | |
|         }
 | |
|       break;
 | |
|     case PROP_FONT:
 | |
|       {
 | |
|         PikaFont *font = g_value_get_object (value);
 | |
| 
 | |
|         if (font != text->font)
 | |
|           g_set_object (&text->font, font);
 | |
|       }
 | |
|       break;
 | |
|     case PROP_FONT_SIZE:
 | |
|       text->font_size = g_value_get_double (value);
 | |
|       break;
 | |
|     case PROP_UNIT:
 | |
|       text->unit = g_value_get_int (value);
 | |
|       break;
 | |
|     case PROP_ANTIALIAS:
 | |
|       text->antialias = g_value_get_boolean (value);
 | |
|       break;
 | |
|     case PROP_HINT_STYLE:
 | |
|       text->hint_style = g_value_get_enum (value);
 | |
|       break;
 | |
|     case PROP_KERNING:
 | |
|       text->kerning = g_value_get_boolean (value);
 | |
|       break;
 | |
|     case PROP_LANGUAGE:
 | |
|       g_free (text->language);
 | |
|       text->language = g_value_dup_string (value);
 | |
|       break;
 | |
|     case PROP_BASE_DIR:
 | |
|       text->base_dir = g_value_get_enum (value);
 | |
|       break;
 | |
|     case PROP_COLOR:
 | |
|       color = g_value_get_boxed (value);
 | |
|       text->color = *color;
 | |
|       break;
 | |
|     case PROP_OUTLINE:
 | |
|       text->outline = g_value_get_enum (value);
 | |
|       break;
 | |
|     case PROP_JUSTIFICATION:
 | |
|       text->justify = g_value_get_enum (value);
 | |
|       break;
 | |
|     case PROP_INDENTATION:
 | |
|       text->indent = g_value_get_double (value);
 | |
|       break;
 | |
|     case PROP_LINE_SPACING:
 | |
|       text->line_spacing = g_value_get_double (value);
 | |
|       break;
 | |
|     case PROP_LETTER_SPACING:
 | |
|       text->letter_spacing = g_value_get_double (value);
 | |
|       break;
 | |
|     case PROP_BOX_MODE:
 | |
|       text->box_mode = g_value_get_enum (value);
 | |
|       break;
 | |
|     case PROP_BOX_WIDTH:
 | |
|       text->box_width = g_value_get_double (value);
 | |
|       break;
 | |
|     case PROP_BOX_HEIGHT:
 | |
|       text->box_height = g_value_get_double (value);
 | |
|       break;
 | |
|     case PROP_BOX_UNIT:
 | |
|       text->box_unit = g_value_get_int (value);
 | |
|       break;
 | |
|     case PROP_TRANSFORMATION:
 | |
|       matrix = g_value_get_boxed (value);
 | |
|       text->transformation = *matrix;
 | |
|       break;
 | |
|     case PROP_OFFSET_X:
 | |
|       text->offset_x = g_value_get_double (value);
 | |
|       break;
 | |
|     case PROP_OFFSET_Y:
 | |
|       text->offset_y = g_value_get_double (value);
 | |
|       break;
 | |
|     case PROP_OUTLINE_STYLE:
 | |
|       text->outline_style = g_value_get_enum (value);
 | |
|       break;
 | |
|     case PROP_OUTLINE_FOREGROUND:
 | |
|       color                    = g_value_get_boxed (value);
 | |
|       text->outline_foreground = *color;
 | |
|       break;
 | |
|     case PROP_OUTLINE_PATTERN:
 | |
|       {
 | |
|         PikaPattern *pattern = g_value_get_object (value);
 | |
| 
 | |
|         if (text->outline_pattern != pattern)
 | |
|           {
 | |
|             if (text->outline_pattern)
 | |
|               g_object_unref (text->outline_pattern);
 | |
| 
 | |
|             text->outline_pattern = pattern ? g_object_ref (pattern) : pattern;
 | |
|           }
 | |
|         break;
 | |
|       }
 | |
|     case PROP_OUTLINE_WIDTH:
 | |
|       text->outline_width = g_value_get_double (value);
 | |
|       break;
 | |
|     case PROP_OUTLINE_CAP_STYLE:
 | |
|       text->outline_cap_style = g_value_get_enum (value);
 | |
|       break;
 | |
|     case PROP_OUTLINE_JOIN_STYLE:
 | |
|       text->outline_join_style = g_value_get_enum (value);
 | |
|       break;
 | |
|     case PROP_OUTLINE_MITER_LIMIT:
 | |
|       text->outline_miter_limit = g_value_get_double (value);
 | |
|       break;
 | |
|     case PROP_OUTLINE_ANTIALIAS:
 | |
|       text->outline_antialias = g_value_get_boolean (value);
 | |
|       break;
 | |
|     case PROP_OUTLINE_DASH_OFFSET:
 | |
|       text->outline_dash_offset = g_value_get_double (value);
 | |
|       break;
 | |
|     case PROP_OUTLINE_DASH_INFO:
 | |
|       {
 | |
|         PikaValueArray *value_array = g_value_get_boxed (value);
 | |
|         text->outline_dash_info = pika_dash_pattern_from_value_array (value_array);
 | |
|       }
 | |
|       break;
 | |
|     case PROP_BORDER:
 | |
|       text->border = g_value_get_int (value);
 | |
|       break;
 | |
|     case PROP_HINTING:
 | |
|       /* interpret "hinting" only if "hint-style" has its default
 | |
|        * value, so we don't overwrite a serialized new hint-style with
 | |
|        * a compat "hinting" that is only there for old PIKA versions
 | |
|        */
 | |
|       if (text->hint_style == PIKA_TEXT_HINT_STYLE_MEDIUM)
 | |
|         text->hint_style = (g_value_get_boolean (value) ?
 | |
|                             PIKA_TEXT_HINT_STYLE_MEDIUM :
 | |
|                             PIKA_TEXT_HINT_STYLE_NONE);
 | |
|       break;
 | |
|     case PROP_PIKA:
 | |
|       text->pika = g_value_get_object (value);
 | |
|       break;
 | |
|     default:
 | |
|       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 | |
|       break;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void
 | |
| pika_text_dispatch_properties_changed (GObject     *object,
 | |
|                                        guint        n_pspecs,
 | |
|                                        GParamSpec **pspecs)
 | |
| {
 | |
|   G_OBJECT_CLASS (parent_class)->dispatch_properties_changed (object,
 | |
|                                                               n_pspecs, pspecs);
 | |
| 
 | |
|   g_signal_emit (object, text_signals[CHANGED], 0);
 | |
| }
 | |
| 
 | |
| static gint64
 | |
| pika_text_get_memsize (PikaObject *object,
 | |
|                        gint64     *gui_size)
 | |
| {
 | |
|   PikaText *text    = PIKA_TEXT (object);
 | |
|   gint64    memsize = 0;
 | |
| 
 | |
|   memsize += pika_string_get_memsize (text->text);
 | |
|   memsize += pika_string_get_memsize (text->markup);
 | |
|   memsize += pika_string_get_memsize (text->language);
 | |
| 
 | |
|   return memsize + PIKA_OBJECT_CLASS (parent_class)->get_memsize (object,
 | |
|                                                                   gui_size);
 | |
| }
 | |
| 
 | |
| void
 | |
| pika_text_get_transformation (PikaText    *text,
 | |
|                               PikaMatrix3 *matrix)
 | |
| {
 | |
|   g_return_if_fail (PIKA_IS_TEXT (text));
 | |
|   g_return_if_fail (matrix != NULL);
 | |
| 
 | |
|   matrix->coeff[0][0] = text->transformation.coeff[0][0];
 | |
|   matrix->coeff[0][1] = text->transformation.coeff[0][1];
 | |
|   matrix->coeff[0][2] = text->offset_x;
 | |
| 
 | |
|   matrix->coeff[1][0] = text->transformation.coeff[1][0];
 | |
|   matrix->coeff[1][1] = text->transformation.coeff[1][1];
 | |
|   matrix->coeff[1][2] = text->offset_y;
 | |
| 
 | |
|   matrix->coeff[2][0] = 0.0;
 | |
|   matrix->coeff[2][1] = 0.0;
 | |
|   matrix->coeff[2][2] = 1.0;
 | |
| }
 | |
| 
 | |
| static gboolean
 | |
| pika_text_serialize_property (PikaConfig       *config,
 | |
|                               guint             property_id,
 | |
|                               const GValue     *value,
 | |
|                               GParamSpec       *pspec,
 | |
|                               PikaConfigWriter *writer)
 | |
| {
 | |
|   if (property_id == PROP_OUTLINE_PATTERN)
 | |
|     {
 | |
|       PikaObject *serialize_obj = g_value_get_object (value);
 | |
| 
 | |
|       pika_config_writer_open (writer, pspec->name);
 | |
| 
 | |
|       if (serialize_obj)
 | |
|         pika_config_writer_string (writer, pika_object_get_name (serialize_obj));
 | |
|       else
 | |
|         pika_config_writer_print (writer, "NULL", 4);
 | |
| 
 | |
|       pika_config_writer_close (writer);
 | |
| 
 | |
|       return TRUE;
 | |
|     }
 | |
|   else if (property_id == PROP_MARKUP)
 | |
|     {
 | |
|       gchar         *markup = (gchar*)g_value_get_string (value);
 | |
|       GRegex        *regex;
 | |
|       PikaText      *text;
 | |
|       PikaContainer *container;
 | |
|       PangoAttrList *attr_list;
 | |
|       PikaFont      *font;
 | |
|       guint          length;
 | |
|       GSList        *list   = NULL;
 | |
|       GSList        *fonts  = NULL;
 | |
| 
 | |
|       g_return_val_if_fail (PIKA_IS_TEXT (config), FALSE);
 | |
| 
 | |
|       if (markup == NULL)
 | |
|         return FALSE;
 | |
| 
 | |
|       text      = PIKA_TEXT (config);
 | |
|       container = pika_data_factory_get_container (text->pika->font_factory);
 | |
| 
 | |
|       /*lookupname format is "pikafont%d" we keep only the "font%d" part
 | |
|        * this is to avoid problems when deserializing
 | |
|        * e.g. if  there are 2 fonts with lookupname pikafont17 and pikafont23
 | |
|        * we might replace the first with a font whose lookupname is pikafont23,
 | |
|        * and we might replace the original pikafont23 with say pikafont29
 | |
|        * this means that all occurences of pikafont23 turned into pikafont29
 | |
|        */
 | |
|       regex  = g_regex_new ("\"pikafont(\\d+)\"", 0, 0, NULL);
 | |
|       markup = g_regex_replace (regex, markup, -1, 0, "\"font\\1\"", 0, NULL);
 | |
| 
 | |
|       pika_config_writer_open   (writer, "markup");
 | |
|       pika_config_writer_string (writer, markup);
 | |
| 
 | |
|       pango_parse_markup (markup, -1, 0, &attr_list, NULL, NULL, NULL);
 | |
| 
 | |
|       list   = pango_attr_list_get_attributes (attr_list);
 | |
|       length = g_slist_length (list);
 | |
| 
 | |
|       for (guint i = 0; i < length; ++i)
 | |
|         {
 | |
|           PangoAttrFontDesc *attr_font_desc = pango_attribute_as_font_desc ((PangoAttribute*)g_slist_nth_data (list, i));
 | |
| 
 | |
|           if (attr_font_desc != NULL)
 | |
|             {
 | |
|               gchar *altered_font_name = pango_font_description_to_string (attr_font_desc->desc);
 | |
|               gchar *font_name         = g_strdup_printf ("pika%s", altered_font_name);
 | |
| 
 | |
|               if (g_slist_find_custom (fonts, (gconstpointer) font_name, (GCompareFunc) g_strcmp0) == NULL)
 | |
|                 {
 | |
|                   fonts = g_slist_prepend (fonts, (gpointer) font_name);
 | |
| 
 | |
|                   font = PIKA_FONT (pika_container_search (container,
 | |
|                                                            (PikaContainerSearchFunc) pika_font_match_by_lookup_name,
 | |
|                                                            (gpointer) font_name));
 | |
| 
 | |
|                   pika_config_writer_open   (writer, "markupfont");
 | |
|                   /*lookupname format is "font%d" we keep only the "font%d" (see the above comment)*/
 | |
|                   pika_config_writer_string (writer, font_name+4);
 | |
| 
 | |
|                   pika_config_writer_open   (writer, "font");
 | |
|                   PIKA_CONFIG_GET_IFACE     (PIKA_CONFIG (font))->serialize (PIKA_CONFIG (font),
 | |
|                                              writer,
 | |
|                                              NULL);
 | |
|                   pika_config_writer_close  (writer);
 | |
|                   pika_config_writer_close  (writer);
 | |
|                 }
 | |
| 
 | |
|               else
 | |
|                 {
 | |
|                   g_free (font_name);
 | |
|                 }
 | |
|               g_free (altered_font_name);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|       pika_config_writer_close  (writer);
 | |
| 
 | |
|       g_slist_free_full (fonts, (GDestroyNotify) g_free);
 | |
|       g_slist_free_full (list,  (GDestroyNotify) pango_attribute_destroy);
 | |
|       pango_attr_list_unref (attr_list);
 | |
|       g_free (markup);
 | |
|       g_regex_unref (regex);
 | |
| 
 | |
|       return TRUE;
 | |
|     }
 | |
| 
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| static gboolean
 | |
| pika_text_deserialize_property (PikaConfig *object,
 | |
|                                 guint       property_id,
 | |
|                                 GValue     *value,
 | |
|                                 GParamSpec *pspec,
 | |
|                                 GScanner   *scanner,
 | |
|                                 GTokenType *expected)
 | |
| {
 | |
|   if (property_id == PROP_OUTLINE_PATTERN)
 | |
|     {
 | |
|       gchar *object_name;
 | |
| 
 | |
|       if (pika_scanner_parse_identifier (scanner, "NULL"))
 | |
|         {
 | |
|           g_value_set_object (value, NULL);
 | |
|         }
 | |
|       else if (pika_scanner_parse_string (scanner, &object_name))
 | |
|         {
 | |
|           PikaText      *text = PIKA_TEXT (object);
 | |
|           PikaContainer *container;
 | |
|           PikaObject    *deserialize_obj;
 | |
| 
 | |
|           if (! object_name)
 | |
|             object_name = g_strdup ("");
 | |
| 
 | |
|           container = pika_data_factory_get_container (text->pika->pattern_factory);
 | |
| 
 | |
|           deserialize_obj = pika_container_get_child_by_name (container,
 | |
|                                                               object_name);
 | |
| 
 | |
|           g_value_set_object (value, deserialize_obj);
 | |
| 
 | |
|           g_free (object_name);
 | |
|         }
 | |
|       else
 | |
|         {
 | |
|           *expected = G_TOKEN_STRING;
 | |
|         }
 | |
| 
 | |
|       return TRUE;
 | |
|     }
 | |
|   else if (property_id == PROP_OUTLINE_DASH_INFO)
 | |
|     {
 | |
|       if (pika_scanner_parse_identifier (scanner, "NULL"))
 | |
|         {
 | |
|           g_value_take_boxed (value, NULL);
 | |
|           return TRUE;
 | |
|         }
 | |
|     }
 | |
|   else if (property_id == PROP_MARKUP)
 | |
|     {
 | |
|       gchar    *markup       = NULL;
 | |
|       GString  *markup_str;
 | |
|       PikaFont *dummy_object = g_object_new (PIKA_TYPE_FONT, NULL);
 | |
| 
 | |
|       pika_scanner_parse_string (scanner, &markup);
 | |
| 
 | |
|       markup_str = g_string_new (markup);
 | |
| 
 | |
|       /* This is for backward compatibility with older xcf files.*/
 | |
|       if (g_scanner_peek_next_token (scanner) == G_TOKEN_STRING)
 | |
|         {
 | |
|           while (g_scanner_peek_next_token (scanner) == G_TOKEN_STRING)
 | |
|             {
 | |
|               gchar    *markup_fontname = NULL;
 | |
|               gchar    *replaced_markup;
 | |
|               gchar    *new_markup;
 | |
|               PikaFont *font;
 | |
| 
 | |
|               pika_scanner_parse_string (scanner, &markup_fontname);
 | |
| 
 | |
|               font = PIKA_FONT (PIKA_CONFIG_GET_IFACE (dummy_object)->deserialize_create (PIKA_TYPE_FONT,
 | |
|                                                                                           scanner,
 | |
|                                                                                           -1,
 | |
|                                                                                           NULL));
 | |
|               replaced_markup = g_markup_printf_escaped (" font=\"%s\"", markup_fontname);
 | |
|               new_markup      = g_strdup_printf (" pikafont=\"%s\"", pika_font_get_lookup_name (font));
 | |
|               g_string_replace (markup_str, replaced_markup, new_markup, 0);
 | |
| 
 | |
|               g_free (markup_fontname);
 | |
|               g_free (replaced_markup);
 | |
|               g_free (new_markup);
 | |
|               g_object_unref (font);
 | |
|             }
 | |
|           /* We avoid the edge case when actual fonts are called "pikafont%d"
 | |
|            * and their replacement name chains to each other by marking already
 | |
|            * processed font as "pikafont=". Then we clean up this fake attribute
 | |
|            * name in the end.
 | |
|            * This is not a problem with the new format as font are stored as
 | |
|            * "font%d" (see comment in pika_text_serialize_property()).
 | |
|            */
 | |
|           g_string_replace (markup_str, " pikafont=\"", " font=\"", 0);
 | |
|         }
 | |
|       else
 | |
|         {
 | |
|           while (g_scanner_peek_next_token (scanner) == G_TOKEN_LEFT_PAREN)
 | |
|             {
 | |
|               gchar    *lookupname = NULL;
 | |
|               gchar    *replaced_markup;
 | |
|               gchar    *new_markup;
 | |
|               PikaFont *font;
 | |
| 
 | |
|               g_scanner_get_next_token  (scanner); /* ( */
 | |
|               g_scanner_get_next_token  (scanner); /* "lookupname" */
 | |
|               pika_scanner_parse_string (scanner, &lookupname);
 | |
| 
 | |
|               g_scanner_get_next_token  (scanner); /* ) */
 | |
|               g_scanner_get_next_token  (scanner); /* font */
 | |
| 
 | |
|               font = PIKA_FONT (PIKA_CONFIG_GET_IFACE (dummy_object)->deserialize_create (PIKA_TYPE_FONT,
 | |
|                                                                                           scanner,
 | |
|                                                                                           -1,
 | |
|                                                                                           NULL));
 | |
|               g_scanner_get_next_token  (scanner); /* ) */
 | |
|               g_scanner_get_next_token  (scanner); /* ) */
 | |
| 
 | |
|               replaced_markup = g_markup_printf_escaped (" font=\"%s\"", lookupname);
 | |
|               new_markup      = g_strdup_printf (" font=\"%s\"", pika_font_get_lookup_name (font));
 | |
|               g_string_replace (markup_str, replaced_markup, new_markup, 0);
 | |
| 
 | |
|               g_free (lookupname);
 | |
|               g_free (replaced_markup);
 | |
|               g_free (new_markup);
 | |
|               g_object_unref (font);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|       g_value_set_string (value, markup_str->str);
 | |
| 
 | |
|       g_object_unref (dummy_object);
 | |
|       g_string_free  (markup_str, TRUE);
 | |
|       g_free (markup);
 | |
| 
 | |
|       return TRUE;
 | |
|     }
 | |
| 
 | |
|   return FALSE;
 | |
| }
 |