1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * GimpText
5  * Copyright (C) 2002-2003  Sven Neumann <sven@gimp.org>
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  */
20 
21 #include "config.h"
22 
23 #include <string.h>
24 
25 #include <cairo.h>
26 #include <gegl.h>
27 #include <gdk-pixbuf/gdk-pixbuf.h>
28 #include <pango/pango.h>
29 
30 #include "libgimpbase/gimpbase.h"
31 #include "libgimpmath/gimpmath.h"
32 #include "libgimpcolor/gimpcolor.h"
33 #include "libgimpconfig/gimpconfig.h"
34 
35 #include "text-types.h"
36 
37 #include "core/gimp-memsize.h"
38 #include "core/gimp-utils.h"
39 #include "core/gimpmarshal.h"
40 #include "core/gimpstrokeoptions.h"
41 
42 #include "gimptext.h"
43 
44 
45 enum
46 {
47   PROP_0,
48   PROP_TEXT,
49   PROP_MARKUP,
50   PROP_FONT,
51   PROP_FONT_SIZE,
52   PROP_UNIT,
53   PROP_ANTIALIAS,
54   PROP_HINT_STYLE,
55   PROP_KERNING,
56   PROP_LANGUAGE,
57   PROP_BASE_DIR,
58   PROP_COLOR,
59   PROP_OUTLINE,
60   PROP_JUSTIFICATION,
61   PROP_INDENTATION,
62   PROP_LINE_SPACING,
63   PROP_LETTER_SPACING,
64   PROP_BOX_MODE,
65   PROP_BOX_WIDTH,
66   PROP_BOX_HEIGHT,
67   PROP_BOX_UNIT,
68   PROP_TRANSFORMATION,
69   PROP_OFFSET_X,
70   PROP_OFFSET_Y,
71   PROP_BORDER,
72   /* for backward compatibility */
73   PROP_HINTING
74 };
75 
76 enum
77 {
78   CHANGED,
79   LAST_SIGNAL
80 };
81 
82 
83 static void     gimp_text_finalize                    (GObject      *object);
84 static void     gimp_text_get_property                (GObject      *object,
85                                                        guint         property_id,
86                                                        GValue       *value,
87                                                        GParamSpec   *pspec);
88 static void     gimp_text_set_property                (GObject      *object,
89                                                        guint         property_id,
90                                                        const GValue *value,
91                                                        GParamSpec   *pspec);
92 static void     gimp_text_dispatch_properties_changed (GObject      *object,
93                                                        guint         n_pspecs,
94                                                        GParamSpec  **pspecs);
95 static gint64   gimp_text_get_memsize                 (GimpObject   *object,
96                                                        gint64       *gui_size);
97 
98 
99 G_DEFINE_TYPE_WITH_CODE (GimpText, gimp_text, GIMP_TYPE_OBJECT,
100                          G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG, NULL))
101 
102 #define parent_class gimp_text_parent_class
103 
104 static guint text_signals[LAST_SIGNAL] = { 0 };
105 
106 
107 static void
gimp_text_class_init(GimpTextClass * klass)108 gimp_text_class_init (GimpTextClass *klass)
109 {
110   GObjectClass    *object_class      = G_OBJECT_CLASS (klass);
111   GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass);
112   GimpRGB          black;
113   GimpMatrix2      identity;
114   gchar           *language;
115 
116   text_signals[CHANGED] =
117     g_signal_new ("changed",
118                   G_TYPE_FROM_CLASS (klass),
119                   G_SIGNAL_RUN_FIRST,
120                   G_STRUCT_OFFSET (GimpTextClass, changed),
121                   NULL, NULL,
122                   gimp_marshal_VOID__VOID,
123                   G_TYPE_NONE, 0);
124 
125   object_class->finalize                    = gimp_text_finalize;
126   object_class->get_property                = gimp_text_get_property;
127   object_class->set_property                = gimp_text_set_property;
128   object_class->dispatch_properties_changed = gimp_text_dispatch_properties_changed;
129 
130   gimp_object_class->get_memsize            = gimp_text_get_memsize;
131 
132   gimp_rgba_set (&black, 0.0, 0.0, 0.0, GIMP_OPACITY_OPAQUE);
133   gimp_matrix2_identity (&identity);
134 
135   GIMP_CONFIG_PROP_STRING (object_class, PROP_TEXT,
136                            "text",
137                            NULL, NULL,
138                            NULL,
139                            GIMP_PARAM_STATIC_STRINGS);
140 
141   GIMP_CONFIG_PROP_STRING (object_class, PROP_MARKUP,
142                            "markup",
143                            NULL, NULL,
144                            NULL,
145                            GIMP_PARAM_STATIC_STRINGS);
146 
147   GIMP_CONFIG_PROP_STRING (object_class, PROP_FONT,
148                            "font",
149                            NULL, NULL,
150                            "Sans-serif",
151                            GIMP_PARAM_STATIC_STRINGS);
152 
153   GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_FONT_SIZE,
154                            "font-size",
155                            NULL, NULL,
156                            0.0, 8192.0, 24.0,
157                            GIMP_PARAM_STATIC_STRINGS);
158 
159   /*  We use the name "font-size-unit" for backward compatibility.
160    *  The unit is also used for other sizes in the text object.
161    */
162   GIMP_CONFIG_PROP_UNIT (object_class, PROP_UNIT,
163                          "font-size-unit",
164                          NULL, NULL,
165                          TRUE, FALSE, GIMP_UNIT_PIXEL,
166                          GIMP_PARAM_STATIC_STRINGS);
167 
168   GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_ANTIALIAS,
169                             "antialias",
170                             NULL, NULL,
171                             TRUE,
172                             GIMP_PARAM_STATIC_STRINGS);
173 
174   GIMP_CONFIG_PROP_ENUM (object_class, PROP_HINT_STYLE,
175                          "hint-style",
176                          NULL, NULL,
177                          GIMP_TYPE_TEXT_HINT_STYLE,
178                          GIMP_TEXT_HINT_STYLE_MEDIUM,
179                          GIMP_PARAM_STATIC_STRINGS |
180                          GIMP_CONFIG_PARAM_DEFAULTS);
181 
182   GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_KERNING,
183                             "kerning",
184                             NULL, NULL,
185                             FALSE,
186                             GIMP_PARAM_STATIC_STRINGS |
187                             GIMP_CONFIG_PARAM_DEFAULTS);
188 
189   language = gimp_get_default_language (NULL);
190 
191   GIMP_CONFIG_PROP_STRING (object_class, PROP_LANGUAGE,
192                            "language",
193                            NULL, NULL,
194                            language,
195                            GIMP_PARAM_STATIC_STRINGS);
196 
197   g_free (language);
198 
199   GIMP_CONFIG_PROP_ENUM (object_class, PROP_BASE_DIR,
200                          "base-direction",
201                          NULL, NULL,
202                          GIMP_TYPE_TEXT_DIRECTION,
203                          GIMP_TEXT_DIRECTION_LTR,
204                          GIMP_PARAM_STATIC_STRINGS);
205 
206   GIMP_CONFIG_PROP_RGB (object_class, PROP_COLOR,
207                         "color",
208                         NULL, NULL,
209                         FALSE, &black,
210                         GIMP_PARAM_STATIC_STRINGS);
211 
212   GIMP_CONFIG_PROP_ENUM (object_class, PROP_OUTLINE,
213                          "outline",
214                          NULL, NULL,
215                          GIMP_TYPE_TEXT_OUTLINE,
216                          GIMP_TEXT_OUTLINE_NONE,
217                          GIMP_PARAM_STATIC_STRINGS |
218                          GIMP_CONFIG_PARAM_DEFAULTS);
219 
220   GIMP_CONFIG_PROP_ENUM (object_class, PROP_JUSTIFICATION,
221                          "justify",
222                          NULL, NULL,
223                          GIMP_TYPE_TEXT_JUSTIFICATION,
224                          GIMP_TEXT_JUSTIFY_LEFT,
225                          GIMP_PARAM_STATIC_STRINGS);
226 
227   GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_INDENTATION,
228                            "indent",
229                            NULL, NULL,
230                            -8192.0, 8192.0, 0.0,
231                            GIMP_PARAM_STATIC_STRINGS |
232                            GIMP_CONFIG_PARAM_DEFAULTS);
233 
234   GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_LINE_SPACING,
235                            "line-spacing",
236                            NULL, NULL,
237                            -8192.0, 8192.0, 0.0,
238                            GIMP_PARAM_STATIC_STRINGS |
239                            GIMP_CONFIG_PARAM_DEFAULTS);
240 
241   GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_LETTER_SPACING,
242                            "letter-spacing",
243                            NULL, NULL,
244                            -8192.0, 8192.0, 0.0,
245                            GIMP_PARAM_STATIC_STRINGS |
246                            GIMP_CONFIG_PARAM_DEFAULTS);
247 
248   GIMP_CONFIG_PROP_ENUM (object_class, PROP_BOX_MODE,
249                          "box-mode",
250                          NULL, NULL,
251                          GIMP_TYPE_TEXT_BOX_MODE,
252                          GIMP_TEXT_BOX_DYNAMIC,
253                          GIMP_PARAM_STATIC_STRINGS);
254 
255   GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_BOX_WIDTH,
256                            "box-width",
257                            NULL, NULL,
258                            0.0, GIMP_MAX_IMAGE_SIZE, 0.0,
259                            GIMP_PARAM_STATIC_STRINGS |
260                            GIMP_CONFIG_PARAM_DEFAULTS);
261 
262   GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_BOX_HEIGHT,
263                            "box-height",
264                            NULL, NULL,
265                            0.0, GIMP_MAX_IMAGE_SIZE, 0.0,
266                            GIMP_PARAM_STATIC_STRINGS |
267                            GIMP_CONFIG_PARAM_DEFAULTS);
268 
269   GIMP_CONFIG_PROP_UNIT (object_class, PROP_BOX_UNIT,
270                          "box-unit",
271                          NULL, NULL,
272                          TRUE, FALSE, GIMP_UNIT_PIXEL,
273                          GIMP_PARAM_STATIC_STRINGS);
274 
275   GIMP_CONFIG_PROP_MATRIX2 (object_class, PROP_TRANSFORMATION,
276                             "transformation",
277                             NULL, NULL,
278                             &identity,
279                             GIMP_PARAM_STATIC_STRINGS |
280                             GIMP_CONFIG_PARAM_DEFAULTS);
281 
282   GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_OFFSET_X,
283                            "offset-x",
284                            NULL, NULL,
285                            -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
286                            GIMP_PARAM_STATIC_STRINGS |
287                            GIMP_CONFIG_PARAM_DEFAULTS);
288 
289   GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_OFFSET_Y,
290                            "offset-y",
291                            NULL, NULL,
292                            -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
293                            GIMP_PARAM_STATIC_STRINGS |
294                            GIMP_CONFIG_PARAM_DEFAULTS);
295 
296   /*  border does only exist to implement the old text API  */
297   g_object_class_install_property (object_class, PROP_BORDER,
298                                    g_param_spec_int ("border", NULL, NULL,
299                                                      0, GIMP_MAX_IMAGE_SIZE, 0,
300                                                      G_PARAM_CONSTRUCT |
301                                                      GIMP_PARAM_WRITABLE));
302 
303   /*  the old hinting options have been replaced by 'hint-style'  */
304   GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_HINTING,
305                             "hinting",
306                             NULL, NULL,
307                             TRUE,
308                             GIMP_PARAM_STATIC_STRINGS);
309 }
310 
311 static void
gimp_text_init(GimpText * text)312 gimp_text_init (GimpText *text)
313 {
314 }
315 
316 static void
gimp_text_finalize(GObject * object)317 gimp_text_finalize (GObject *object)
318 {
319   GimpText *text = GIMP_TEXT (object);
320 
321   g_clear_pointer (&text->text,     g_free);
322   g_clear_pointer (&text->markup,   g_free);
323   g_clear_pointer (&text->font,     g_free);
324   g_clear_pointer (&text->language, g_free);
325 
326   G_OBJECT_CLASS (parent_class)->finalize (object);
327 }
328 
329 static void
gimp_text_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)330 gimp_text_get_property (GObject      *object,
331                         guint         property_id,
332                         GValue       *value,
333                         GParamSpec   *pspec)
334 {
335   GimpText *text = GIMP_TEXT (object);
336 
337   switch (property_id)
338     {
339     case PROP_TEXT:
340       g_value_set_string (value, text->text);
341       break;
342     case PROP_MARKUP:
343       g_value_set_string (value, text->markup);
344       break;
345     case PROP_FONT:
346       g_value_set_string (value, text->font);
347       break;
348     case PROP_FONT_SIZE:
349       g_value_set_double (value, text->font_size);
350       break;
351     case PROP_UNIT:
352       g_value_set_int (value, text->unit);
353       break;
354     case PROP_ANTIALIAS:
355       g_value_set_boolean (value, text->antialias);
356       break;
357     case PROP_HINT_STYLE:
358       g_value_set_enum (value, text->hint_style);
359       break;
360     case PROP_KERNING:
361       g_value_set_boolean (value, text->kerning);
362       break;
363     case PROP_BASE_DIR:
364       g_value_set_enum (value, text->base_dir);
365       break;
366     case PROP_LANGUAGE:
367       g_value_set_string (value, text->language);
368       break;
369     case PROP_COLOR:
370       g_value_set_boxed (value, &text->color);
371       break;
372     case PROP_OUTLINE:
373       g_value_set_enum (value, text->outline);
374       break;
375     case PROP_JUSTIFICATION:
376       g_value_set_enum (value, text->justify);
377       break;
378     case PROP_INDENTATION:
379       g_value_set_double (value, text->indent);
380       break;
381     case PROP_LINE_SPACING:
382       g_value_set_double (value, text->line_spacing);
383       break;
384     case PROP_LETTER_SPACING:
385       g_value_set_double (value, text->letter_spacing);
386       break;
387     case PROP_BOX_MODE:
388       g_value_set_enum (value, text->box_mode);
389       break;
390     case PROP_BOX_WIDTH:
391       g_value_set_double (value, text->box_width);
392       break;
393     case PROP_BOX_HEIGHT:
394       g_value_set_double (value, text->box_height);
395       break;
396     case PROP_BOX_UNIT:
397       g_value_set_int (value, text->box_unit);
398       break;
399     case PROP_TRANSFORMATION:
400       g_value_set_boxed (value, &text->transformation);
401       break;
402     case PROP_OFFSET_X:
403       g_value_set_double (value, text->offset_x);
404       break;
405     case PROP_OFFSET_Y:
406       g_value_set_double (value, text->offset_y);
407       break;
408     case PROP_HINTING:
409       g_value_set_boolean (value,
410                            text->hint_style != GIMP_TEXT_HINT_STYLE_NONE);
411       break;
412     default:
413       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
414       break;
415     }
416 }
417 
418 static void
gimp_text_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)419 gimp_text_set_property (GObject      *object,
420                         guint         property_id,
421                         const GValue *value,
422                         GParamSpec   *pspec)
423 {
424   GimpText    *text = GIMP_TEXT (object);
425   GimpRGB     *color;
426   GimpMatrix2 *matrix;
427 
428   switch (property_id)
429     {
430     case PROP_TEXT:
431       g_free (text->text);
432       text->text = g_value_dup_string (value);
433       if (text->text && text->markup)
434         {
435           g_clear_pointer (&text->markup, g_free);
436           g_object_notify (object, "markup");
437         }
438       break;
439     case PROP_MARKUP:
440       g_free (text->markup);
441       text->markup = g_value_dup_string (value);
442       if (text->markup && text->text)
443         {
444           g_clear_pointer (&text->text, g_free);
445           g_object_notify (object, "text");
446         }
447       break;
448     case PROP_FONT:
449       {
450         const gchar *font = g_value_get_string (value);
451 
452         g_free (text->font);
453 
454         if (font)
455           {
456             gsize len = strlen (font);
457 
458             if (g_str_has_suffix (font, " Not-Rotated"))
459               len -= strlen ( " Not-Rotated");
460 
461             text->font = g_strndup (font, len);
462           }
463         else
464           {
465             text->font = NULL;
466           }
467       }
468       break;
469     case PROP_FONT_SIZE:
470       text->font_size = g_value_get_double (value);
471       break;
472     case PROP_UNIT:
473       text->unit = g_value_get_int (value);
474       break;
475     case PROP_ANTIALIAS:
476       text->antialias = g_value_get_boolean (value);
477       break;
478     case PROP_HINT_STYLE:
479       text->hint_style = g_value_get_enum (value);
480       break;
481     case PROP_KERNING:
482       text->kerning = g_value_get_boolean (value);
483       break;
484     case PROP_LANGUAGE:
485       g_free (text->language);
486       text->language = g_value_dup_string (value);
487       break;
488     case PROP_BASE_DIR:
489       text->base_dir = g_value_get_enum (value);
490       break;
491     case PROP_COLOR:
492       color = g_value_get_boxed (value);
493       text->color = *color;
494       break;
495     case PROP_OUTLINE:
496       text->outline = g_value_get_enum (value);
497       break;
498     case PROP_JUSTIFICATION:
499       text->justify = g_value_get_enum (value);
500       break;
501     case PROP_INDENTATION:
502       text->indent = g_value_get_double (value);
503       break;
504     case PROP_LINE_SPACING:
505       text->line_spacing = g_value_get_double (value);
506       break;
507     case PROP_LETTER_SPACING:
508       text->letter_spacing = g_value_get_double (value);
509       break;
510     case PROP_BOX_MODE:
511       text->box_mode = g_value_get_enum (value);
512       break;
513     case PROP_BOX_WIDTH:
514       text->box_width = g_value_get_double (value);
515       break;
516     case PROP_BOX_HEIGHT:
517       text->box_height = g_value_get_double (value);
518       break;
519     case PROP_BOX_UNIT:
520       text->box_unit = g_value_get_int (value);
521       break;
522     case PROP_TRANSFORMATION:
523       matrix = g_value_get_boxed (value);
524       text->transformation = *matrix;
525       break;
526     case PROP_OFFSET_X:
527       text->offset_x = g_value_get_double (value);
528       break;
529     case PROP_OFFSET_Y:
530       text->offset_y = g_value_get_double (value);
531       break;
532     case PROP_BORDER:
533       text->border = g_value_get_int (value);
534       break;
535     case PROP_HINTING:
536       /* interpret "hinting" only if "hint-style" has its default
537        * value, so we don't overwrite a serialized new hint-style with
538        * a compat "hinting" that is only there for old GIMP versions
539        */
540       if (text->hint_style == GIMP_TEXT_HINT_STYLE_MEDIUM)
541         text->hint_style = (g_value_get_boolean (value) ?
542                             GIMP_TEXT_HINT_STYLE_MEDIUM :
543                             GIMP_TEXT_HINT_STYLE_NONE);
544       break;
545     default:
546       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
547       break;
548     }
549 }
550 
551 static void
gimp_text_dispatch_properties_changed(GObject * object,guint n_pspecs,GParamSpec ** pspecs)552 gimp_text_dispatch_properties_changed (GObject     *object,
553                                        guint        n_pspecs,
554                                        GParamSpec **pspecs)
555 {
556   G_OBJECT_CLASS (parent_class)->dispatch_properties_changed (object,
557                                                               n_pspecs, pspecs);
558 
559   g_signal_emit (object, text_signals[CHANGED], 0);
560 }
561 
562 static gint64
gimp_text_get_memsize(GimpObject * object,gint64 * gui_size)563 gimp_text_get_memsize (GimpObject *object,
564                        gint64     *gui_size)
565 {
566   GimpText *text    = GIMP_TEXT (object);
567   gint64    memsize = 0;
568 
569   memsize += gimp_string_get_memsize (text->text);
570   memsize += gimp_string_get_memsize (text->markup);
571   memsize += gimp_string_get_memsize (text->font);
572   memsize += gimp_string_get_memsize (text->language);
573 
574   return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
575                                                                   gui_size);
576 }
577 
578 void
gimp_text_get_transformation(GimpText * text,GimpMatrix3 * matrix)579 gimp_text_get_transformation (GimpText    *text,
580                               GimpMatrix3 *matrix)
581 {
582   g_return_if_fail (GIMP_IS_TEXT (text));
583   g_return_if_fail (matrix != NULL);
584 
585   matrix->coeff[0][0] = text->transformation.coeff[0][0];
586   matrix->coeff[0][1] = text->transformation.coeff[0][1];
587   matrix->coeff[0][2] = text->offset_x;
588 
589   matrix->coeff[1][0] = text->transformation.coeff[1][0];
590   matrix->coeff[1][1] = text->transformation.coeff[1][1];
591   matrix->coeff[1][2] = text->offset_y;
592 
593   matrix->coeff[2][0] = 0.0;
594   matrix->coeff[2][1] = 0.0;
595   matrix->coeff[2][2] = 1.0;
596 }
597