summaryrefslogtreecommitdiffstats
path: root/app/text
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--app/text/Makefile.am80
-rw-r--r--app/text/Makefile.in1031
-rw-r--r--app/text/gimpfont.c820
-rw-r--r--app/text/gimpfont.h45
-rw-r--r--app/text/gimpfontfactory.c677
-rw-r--r--app/text/gimpfontfactory.h58
-rw-r--r--app/text/gimptext-compat.c207
-rw-r--r--app/text/gimptext-compat.h45
-rw-r--r--app/text/gimptext-parasite.c204
-rw-r--r--app/text/gimptext-parasite.h34
-rw-r--r--app/text/gimptext-vectors.c255
-rw-r--r--app/text/gimptext-vectors.h29
-rw-r--r--app/text/gimptext-xlfd.c301
-rw-r--r--app/text/gimptext-xlfd.h36
-rw-r--r--app/text/gimptext.c596
-rw-r--r--app/text/gimptext.h83
-rw-r--r--app/text/gimptextlayer-transform.c201
-rw-r--r--app/text/gimptextlayer-transform.h53
-rw-r--r--app/text/gimptextlayer-xcf.c220
-rw-r--r--app/text/gimptextlayer-xcf.h34
-rw-r--r--app/text/gimptextlayer.c861
-rw-r--r--app/text/gimptextlayer.h78
-rw-r--r--app/text/gimptextlayout-render.c77
-rw-r--r--app/text/gimptextlayout-render.h31
-rw-r--r--app/text/gimptextlayout.c805
-rw-r--r--app/text/gimptextlayout.h79
-rw-r--r--app/text/gimptextundo.c307
-rw-r--r--app/text/gimptextundo.h55
-rw-r--r--app/text/text-enums.c73
-rw-r--r--app/text/text-enums.h45
-rw-r--r--app/text/text-types.h38
31 files changed, 7458 insertions, 0 deletions
diff --git a/app/text/Makefile.am b/app/text/Makefile.am
new file mode 100644
index 0000000..de7616e
--- /dev/null
+++ b/app/text/Makefile.am
@@ -0,0 +1,80 @@
+## Process this file with automake to produce Makefile.in
+
+AM_CPPFLAGS = \
+ -DG_LOG_DOMAIN=\"Gimp-Text\" \
+ -I$(top_builddir) \
+ -I$(top_srcdir) \
+ -I$(top_builddir)/app \
+ -I$(top_srcdir)/app \
+ $(GEGL_CFLAGS) \
+ $(PANGOCAIRO_CFLAGS) \
+ $(HARFBUZZ_CFLAGS) \
+ $(GDK_PIXBUF_CFLAGS) \
+ -I$(includedir)
+
+noinst_LIBRARIES = libapptext.a
+
+libapptext_a_sources = \
+ text-types.h \
+ text-enums.h \
+ gimpfont.c \
+ gimpfont.h \
+ gimpfontfactory.c \
+ gimpfontfactory.h \
+ gimptext.c \
+ gimptext.h \
+ gimptext-compat.c \
+ gimptext-compat.h \
+ gimptext-parasite.c \
+ gimptext-parasite.h \
+ gimptext-vectors.c \
+ gimptext-vectors.h \
+ gimptext-xlfd.c \
+ gimptext-xlfd.h \
+ gimptextlayer.c \
+ gimptextlayer.h \
+ gimptextlayer-transform.c \
+ gimptextlayer-transform.h \
+ gimptextlayer-xcf.c \
+ gimptextlayer-xcf.h \
+ gimptextlayout.c \
+ gimptextlayout.h \
+ gimptextlayout-render.c \
+ gimptextlayout-render.h \
+ gimptextundo.c \
+ gimptextundo.h
+
+libapptext_a_built_sources = text-enums.c
+
+libapptext_a_SOURCES = $(libapptext_a_built_sources) $(libapptext_a_sources)
+
+#
+# rules to generate built sources
+#
+# setup autogeneration dependencies
+gen_sources = xgen-tec
+CLEANFILES = $(gen_sources)
+
+xgen-tec: $(srcdir)/text-enums.h $(GIMP_MKENUMS) Makefile.am
+ $(AM_V_GEN) $(GIMP_MKENUMS) \
+ --fhead "#include \"config.h\"\n#include <gio/gio.h>\n#include \"libgimpbase/gimpbase.h\"\n#include \"text-enums.h\"\n#include \"gimp-intl.h\"" \
+ --fprod "\n/* enumerations from \"@basename@\" */" \
+ --vhead "GType\n@enum_name@_get_type (void)\n{\n static const G@Type@Value values[] =\n {" \
+ --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \
+ --vtail " { 0, NULL, NULL }\n };\n" \
+ --dhead " static const Gimp@Type@Desc descs[] =\n {" \
+ --dprod " { @VALUENAME@, @valuedesc@, @valuehelp@ },@if ('@valueabbrev@' ne 'NULL')@\n /* Translators: this is an abbreviated version of @valueudesc@.\n Keep it short. */\n { @VALUENAME@, @valueabbrev@, NULL },@endif@" \
+ --dtail " { 0, NULL, NULL }\n };\n\n static GType type = 0;\n\n if (G_UNLIKELY (! type))\n {\n type = g_@type@_register_static (\"@EnumName@\", values);\n gimp_type_set_translation_context (type, \"@enumnick@\");\n gimp_@type@_set_value_descriptions (type, descs);\n }\n\n return type;\n}\n" \
+ $< > $@
+
+# copy the generated enum file back to the source directory only if it's
+# changed; otherwise, only update its timestamp, so that the recipe isn't
+# executed again on the next build, however, allow this to (harmlessly) fail,
+# to support building from a read-only source tree.
+$(srcdir)/text-enums.c: xgen-tec
+ $(AM_V_GEN) if ! cmp -s $< $@; then \
+ cp $< $@; \
+ else \
+ touch $@ 2> /dev/null \
+ || true; \
+ fi
diff --git a/app/text/Makefile.in b/app/text/Makefile.in
new file mode 100644
index 0000000..7541048
--- /dev/null
+++ b/app/text/Makefile.in
@@ -0,0 +1,1031 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = app/text
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LIBRARIES = $(noinst_LIBRARIES)
+ARFLAGS = cru
+AM_V_AR = $(am__v_AR_@AM_V@)
+am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@)
+am__v_AR_0 = @echo " AR " $@;
+am__v_AR_1 =
+libapptext_a_AR = $(AR) $(ARFLAGS)
+libapptext_a_LIBADD =
+am__objects_1 = text-enums.$(OBJEXT)
+am__objects_2 = gimpfont.$(OBJEXT) gimpfontfactory.$(OBJEXT) \
+ gimptext.$(OBJEXT) gimptext-compat.$(OBJEXT) \
+ gimptext-parasite.$(OBJEXT) gimptext-vectors.$(OBJEXT) \
+ gimptext-xlfd.$(OBJEXT) gimptextlayer.$(OBJEXT) \
+ gimptextlayer-transform.$(OBJEXT) gimptextlayer-xcf.$(OBJEXT) \
+ gimptextlayout.$(OBJEXT) gimptextlayout-render.$(OBJEXT) \
+ gimptextundo.$(OBJEXT)
+am_libapptext_a_OBJECTS = $(am__objects_1) $(am__objects_2)
+libapptext_a_OBJECTS = $(am_libapptext_a_OBJECTS)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/gimpfont.Po \
+ ./$(DEPDIR)/gimpfontfactory.Po ./$(DEPDIR)/gimptext-compat.Po \
+ ./$(DEPDIR)/gimptext-parasite.Po \
+ ./$(DEPDIR)/gimptext-vectors.Po ./$(DEPDIR)/gimptext-xlfd.Po \
+ ./$(DEPDIR)/gimptext.Po ./$(DEPDIR)/gimptextlayer-transform.Po \
+ ./$(DEPDIR)/gimptextlayer-xcf.Po ./$(DEPDIR)/gimptextlayer.Po \
+ ./$(DEPDIR)/gimptextlayout-render.Po \
+ ./$(DEPDIR)/gimptextlayout.Po ./$(DEPDIR)/gimptextundo.Po \
+ ./$(DEPDIR)/text-enums.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libapptext_a_SOURCES)
+DIST_SOURCES = $(libapptext_a_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AM_CPPFLAGS = \
+ -DG_LOG_DOMAIN=\"Gimp-Text\" \
+ -I$(top_builddir) \
+ -I$(top_srcdir) \
+ -I$(top_builddir)/app \
+ -I$(top_srcdir)/app \
+ $(GEGL_CFLAGS) \
+ $(PANGOCAIRO_CFLAGS) \
+ $(HARFBUZZ_CFLAGS) \
+ $(GDK_PIXBUF_CFLAGS) \
+ -I$(includedir)
+
+noinst_LIBRARIES = libapptext.a
+libapptext_a_sources = \
+ text-types.h \
+ text-enums.h \
+ gimpfont.c \
+ gimpfont.h \
+ gimpfontfactory.c \
+ gimpfontfactory.h \
+ gimptext.c \
+ gimptext.h \
+ gimptext-compat.c \
+ gimptext-compat.h \
+ gimptext-parasite.c \
+ gimptext-parasite.h \
+ gimptext-vectors.c \
+ gimptext-vectors.h \
+ gimptext-xlfd.c \
+ gimptext-xlfd.h \
+ gimptextlayer.c \
+ gimptextlayer.h \
+ gimptextlayer-transform.c \
+ gimptextlayer-transform.h \
+ gimptextlayer-xcf.c \
+ gimptextlayer-xcf.h \
+ gimptextlayout.c \
+ gimptextlayout.h \
+ gimptextlayout-render.c \
+ gimptextlayout-render.h \
+ gimptextundo.c \
+ gimptextundo.h
+
+libapptext_a_built_sources = text-enums.c
+libapptext_a_SOURCES = $(libapptext_a_built_sources) $(libapptext_a_sources)
+
+#
+# rules to generate built sources
+#
+# setup autogeneration dependencies
+gen_sources = xgen-tec
+CLEANFILES = $(gen_sources)
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu app/text/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu app/text/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLIBRARIES:
+ -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
+
+libapptext.a: $(libapptext_a_OBJECTS) $(libapptext_a_DEPENDENCIES) $(EXTRA_libapptext_a_DEPENDENCIES)
+ $(AM_V_at)-rm -f libapptext.a
+ $(AM_V_AR)$(libapptext_a_AR) libapptext.a $(libapptext_a_OBJECTS) $(libapptext_a_LIBADD)
+ $(AM_V_at)$(RANLIB) libapptext.a
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpfont.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpfontfactory.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptext-compat.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptext-parasite.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptext-vectors.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptext-xlfd.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptext.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptextlayer-transform.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptextlayer-xcf.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptextlayer.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptextlayout-render.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptextlayout.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptextundo.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/text-enums.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LIBRARIES)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/gimpfont.Po
+ -rm -f ./$(DEPDIR)/gimpfontfactory.Po
+ -rm -f ./$(DEPDIR)/gimptext-compat.Po
+ -rm -f ./$(DEPDIR)/gimptext-parasite.Po
+ -rm -f ./$(DEPDIR)/gimptext-vectors.Po
+ -rm -f ./$(DEPDIR)/gimptext-xlfd.Po
+ -rm -f ./$(DEPDIR)/gimptext.Po
+ -rm -f ./$(DEPDIR)/gimptextlayer-transform.Po
+ -rm -f ./$(DEPDIR)/gimptextlayer-xcf.Po
+ -rm -f ./$(DEPDIR)/gimptextlayer.Po
+ -rm -f ./$(DEPDIR)/gimptextlayout-render.Po
+ -rm -f ./$(DEPDIR)/gimptextlayout.Po
+ -rm -f ./$(DEPDIR)/gimptextundo.Po
+ -rm -f ./$(DEPDIR)/text-enums.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/gimpfont.Po
+ -rm -f ./$(DEPDIR)/gimpfontfactory.Po
+ -rm -f ./$(DEPDIR)/gimptext-compat.Po
+ -rm -f ./$(DEPDIR)/gimptext-parasite.Po
+ -rm -f ./$(DEPDIR)/gimptext-vectors.Po
+ -rm -f ./$(DEPDIR)/gimptext-xlfd.Po
+ -rm -f ./$(DEPDIR)/gimptext.Po
+ -rm -f ./$(DEPDIR)/gimptextlayer-transform.Po
+ -rm -f ./$(DEPDIR)/gimptextlayer-xcf.Po
+ -rm -f ./$(DEPDIR)/gimptextlayer.Po
+ -rm -f ./$(DEPDIR)/gimptextlayout-render.Po
+ -rm -f ./$(DEPDIR)/gimptextlayout.Po
+ -rm -f ./$(DEPDIR)/gimptextundo.Po
+ -rm -f ./$(DEPDIR)/text-enums.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+xgen-tec: $(srcdir)/text-enums.h $(GIMP_MKENUMS) Makefile.am
+ $(AM_V_GEN) $(GIMP_MKENUMS) \
+ --fhead "#include \"config.h\"\n#include <gio/gio.h>\n#include \"libgimpbase/gimpbase.h\"\n#include \"text-enums.h\"\n#include \"gimp-intl.h\"" \
+ --fprod "\n/* enumerations from \"@basename@\" */" \
+ --vhead "GType\n@enum_name@_get_type (void)\n{\n static const G@Type@Value values[] =\n {" \
+ --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \
+ --vtail " { 0, NULL, NULL }\n };\n" \
+ --dhead " static const Gimp@Type@Desc descs[] =\n {" \
+ --dprod " { @VALUENAME@, @valuedesc@, @valuehelp@ },@if ('@valueabbrev@' ne 'NULL')@\n /* Translators: this is an abbreviated version of @valueudesc@.\n Keep it short. */\n { @VALUENAME@, @valueabbrev@, NULL },@endif@" \
+ --dtail " { 0, NULL, NULL }\n };\n\n static GType type = 0;\n\n if (G_UNLIKELY (! type))\n {\n type = g_@type@_register_static (\"@EnumName@\", values);\n gimp_type_set_translation_context (type, \"@enumnick@\");\n gimp_@type@_set_value_descriptions (type, descs);\n }\n\n return type;\n}\n" \
+ $< > $@
+
+# copy the generated enum file back to the source directory only if it's
+# changed; otherwise, only update its timestamp, so that the recipe isn't
+# executed again on the next build, however, allow this to (harmlessly) fail,
+# to support building from a read-only source tree.
+$(srcdir)/text-enums.c: xgen-tec
+ $(AM_V_GEN) if ! cmp -s $< $@; then \
+ cp $< $@; \
+ else \
+ touch $@ 2> /dev/null \
+ || true; \
+ fi
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/app/text/gimpfont.c b/app/text/gimpfont.c
new file mode 100644
index 0000000..148275d
--- /dev/null
+++ b/app/text/gimpfont.c
@@ -0,0 +1,820 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpfont.c
+ * Copyright (C) 2003 Michael Natterer <mitch@gimp.org>
+ * 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 <gdk-pixbuf/gdk-pixbuf.h>
+#include <gegl.h>
+
+#include <pango/pangocairo.h>
+
+#include <hb.h>
+#include <hb-ot.h>
+#include <hb-ft.h>
+
+#define PANGO_ENABLE_ENGINE 1 /* Argh */
+#include <pango/pango-ot.h>
+
+#include <ft2build.h>
+#include FT_TRUETYPE_TABLES_H
+
+#include "text-types.h"
+
+#include "core/gimptempbuf.h"
+
+#include "gimpfont.h"
+
+#include "gimp-intl.h"
+
+
+/* This is a so-called pangram; it's supposed to
+ contain all characters found in the alphabet. */
+#define GIMP_TEXT_PANGRAM N_("Pack my box with\nfive dozen liquor jugs.")
+
+#define GIMP_FONT_POPUP_SIZE (PANGO_SCALE * 30)
+
+#define DEBUGPRINT(x) /* g_print x */
+
+enum
+{
+ PROP_0,
+ PROP_PANGO_CONTEXT
+};
+
+
+struct _GimpFont
+{
+ GimpData parent_instance;
+
+ PangoContext *pango_context;
+
+ PangoLayout *popup_layout;
+ gint popup_width;
+ gint popup_height;
+};
+
+struct _GimpFontClass
+{
+ GimpDataClass parent_class;
+};
+
+
+static void gimp_font_constructed (GObject *object);
+static void gimp_font_finalize (GObject *object);
+static void gimp_font_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_font_get_preview_size (GimpViewable *viewable,
+ gint size,
+ gboolean popup,
+ gboolean dot_for_dot,
+ gint *width,
+ gint *height);
+static gboolean gimp_font_get_popup_size (GimpViewable *viewable,
+ gint width,
+ gint height,
+ gboolean dot_for_dot,
+ gint *popup_width,
+ gint *popup_height);
+static GimpTempBuf * gimp_font_get_new_preview (GimpViewable *viewable,
+ GimpContext *context,
+ gint width,
+ gint height);
+
+static const gchar * gimp_font_get_sample_string (PangoContext *context,
+ PangoFontDescription *font_desc);
+
+
+G_DEFINE_TYPE (GimpFont, gimp_font, GIMP_TYPE_DATA)
+
+#define parent_class gimp_font_parent_class
+
+
+static void
+gimp_font_class_init (GimpFontClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass);
+
+ object_class->constructed = gimp_font_constructed;
+ object_class->finalize = gimp_font_finalize;
+ object_class->set_property = gimp_font_set_property;
+
+ viewable_class->get_preview_size = gimp_font_get_preview_size;
+ viewable_class->get_popup_size = gimp_font_get_popup_size;
+ viewable_class->get_new_preview = gimp_font_get_new_preview;
+
+ viewable_class->default_icon_name = "gtk-select-font";
+
+ g_object_class_install_property (object_class, PROP_PANGO_CONTEXT,
+ g_param_spec_object ("pango-context",
+ NULL, NULL,
+ PANGO_TYPE_CONTEXT,
+ GIMP_PARAM_WRITABLE));
+}
+
+static void
+gimp_font_init (GimpFont *font)
+{
+}
+
+static void
+gimp_font_constructed (GObject *object)
+{
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+
+ gimp_data_make_internal (GIMP_DATA (object),
+ gimp_object_get_name (object));
+}
+
+static void
+gimp_font_finalize (GObject *object)
+{
+ GimpFont *font = GIMP_FONT (object);
+
+ g_clear_object (&font->pango_context);
+ g_clear_object (&font->popup_layout);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gimp_font_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpFont *font = GIMP_FONT (object);
+
+ switch (property_id)
+ {
+ case PROP_PANGO_CONTEXT:
+ if (font->pango_context)
+ g_object_unref (font->pango_context);
+ font->pango_context = g_value_dup_object (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_font_get_preview_size (GimpViewable *viewable,
+ gint size,
+ gboolean popup,
+ gboolean dot_for_dot,
+ gint *width,
+ gint *height)
+{
+ *width = size;
+ *height = size;
+}
+
+static gboolean
+gimp_font_get_popup_size (GimpViewable *viewable,
+ gint width,
+ gint height,
+ gboolean dot_for_dot,
+ gint *popup_width,
+ gint *popup_height)
+{
+ GimpFont *font = GIMP_FONT (viewable);
+ PangoFontDescription *font_desc;
+ PangoRectangle ink;
+ PangoRectangle logical;
+ const gchar *name;
+
+ if (! font->pango_context)
+ return FALSE;
+
+ name = gimp_object_get_name (font);
+
+ font_desc = pango_font_description_from_string (name);
+ g_return_val_if_fail (font_desc != NULL, FALSE);
+
+ pango_font_description_set_size (font_desc, GIMP_FONT_POPUP_SIZE);
+
+ if (font->popup_layout)
+ g_object_unref (font->popup_layout);
+
+ font->popup_layout = pango_layout_new (font->pango_context);
+ pango_layout_set_font_description (font->popup_layout, font_desc);
+ pango_font_description_free (font_desc);
+
+ pango_layout_set_text (font->popup_layout, gettext (GIMP_TEXT_PANGRAM), -1);
+ pango_layout_get_pixel_extents (font->popup_layout, &ink, &logical);
+
+ *popup_width = MAX (ink.width, logical.width) + 6;
+ *popup_height = MAX (ink.height, logical.height) + 6;
+
+ *popup_width = cairo_format_stride_for_width (CAIRO_FORMAT_A8, *popup_width);
+
+ font->popup_width = *popup_width;
+ font->popup_height = *popup_height;
+
+ return TRUE;
+}
+
+static GimpTempBuf *
+gimp_font_get_new_preview (GimpViewable *viewable,
+ GimpContext *context,
+ gint width,
+ gint height)
+{
+ GimpFont *font = GIMP_FONT (viewable);
+ PangoLayout *layout;
+ PangoRectangle ink;
+ PangoRectangle logical;
+ gint layout_width;
+ gint layout_height;
+ gint layout_x;
+ gint layout_y;
+ GimpTempBuf *temp_buf;
+ cairo_t *cr;
+ cairo_surface_t *surface;
+
+ if (! font->pango_context)
+ return NULL;
+
+ if (! font->popup_layout ||
+ font->popup_width != width || font->popup_height != height)
+ {
+ PangoFontDescription *font_desc;
+ const gchar *name;
+
+ name = gimp_object_get_name (font);
+
+ DEBUGPRINT (("%s: ", name));
+
+ font_desc = pango_font_description_from_string (name);
+ g_return_val_if_fail (font_desc != NULL, NULL);
+
+ pango_font_description_set_size (font_desc,
+ PANGO_SCALE * height * 2.0 / 3.0);
+
+ layout = pango_layout_new (font->pango_context);
+
+ pango_layout_set_font_description (layout, font_desc);
+ pango_layout_set_text (layout,
+ gimp_font_get_sample_string (font->pango_context,
+ font_desc),
+ -1);
+
+ pango_font_description_free (font_desc);
+ }
+ else
+ {
+ layout = g_object_ref (font->popup_layout);
+ }
+
+ width = cairo_format_stride_for_width (CAIRO_FORMAT_A8, width);
+
+ temp_buf = gimp_temp_buf_new (width, height, babl_format ("Y' u8"));
+ memset (gimp_temp_buf_get_data (temp_buf), 255, width * height);
+
+ surface = cairo_image_surface_create_for_data (gimp_temp_buf_get_data (temp_buf),
+ CAIRO_FORMAT_A8,
+ width, height, width);
+
+ pango_layout_get_pixel_extents (layout, &ink, &logical);
+
+ layout_width = MAX (ink.width, logical.width);
+ layout_height = MAX (ink.height, logical.height);
+
+ layout_x = (width - layout_width) / 2;
+ layout_y = (height - layout_height) / 2;
+
+ if (ink.x < logical.x)
+ layout_x += logical.x - ink.x;
+
+ if (ink.y < logical.y)
+ layout_y += logical.y - ink.y;
+
+ cr = cairo_create (surface);
+
+ cairo_translate (cr, layout_x, layout_y);
+ cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
+ pango_cairo_show_layout (cr, layout);
+
+ cairo_destroy (cr);
+ cairo_surface_destroy (surface);
+
+ g_object_unref (layout);
+
+ return temp_buf;
+}
+
+GimpData *
+gimp_font_get_standard (void)
+{
+ static GimpData *standard_font = NULL;
+
+ if (! standard_font)
+ {
+ standard_font = g_object_new (GIMP_TYPE_FONT,
+ "name", "",
+ NULL);
+
+ gimp_data_clean (standard_font);
+ gimp_data_make_internal (standard_font, "gimp-font-standard");
+
+ g_object_add_weak_pointer (G_OBJECT (standard_font),
+ (gpointer *) &standard_font);
+ }
+
+ return standard_font;
+}
+
+
+static inline gboolean
+gimp_font_covers_string (PangoFcFont *font,
+ const gchar *sample)
+{
+ const gchar *p;
+
+ for (p = sample; *p; p = g_utf8_next_char (p))
+ {
+ if (! pango_fc_font_has_char (font, g_utf8_get_char (p)))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* This function was picked up from Pango's pango-ot-info.c. Until there
+ * is a better way to get the tag, we use this.
+ */
+static hb_tag_t
+get_hb_table_type (PangoOTTableType table_type)
+{
+ switch (table_type)
+ {
+ case PANGO_OT_TABLE_GSUB:
+ return HB_OT_TAG_GSUB;
+ case PANGO_OT_TABLE_GPOS:
+ return HB_OT_TAG_GPOS;
+ default:
+ return HB_TAG_NONE;
+ }
+}
+
+/* Guess a suitable short sample string for the font. */
+static const gchar *
+gimp_font_get_sample_string (PangoContext *context,
+ PangoFontDescription *font_desc)
+{
+ PangoFont *font;
+ hb_face_t *hb_face;
+ FT_Face face;
+ TT_OS2 *os2;
+ PangoOTTableType tt;
+ gint i;
+
+ /* This is a table of scripts and corresponding short sample strings
+ * to be used instead of the Latin sample string Aa. The script
+ * codes are as in ISO15924 (see
+ * https://www.unicode.org/iso15924/iso15924-codes.html), but in
+ * lower case. The Unicode subrange bit numbers, as used in TrueType
+ * so-called OS/2 tables, are from
+ * https://www.microsoft.com/typography/otspec/os2.htm#ur .
+ *
+ * The table is mostly ordered by Unicode order. But as there are
+ * fonts that support several of these scripts, the ordering is
+ * be modified so that the script which such a font is more likely
+ * to be actually designed for comes first and matches.
+ *
+ * These sample strings are mostly just guesswork as for their
+ * usefulness. Usually they contain what I assume is the first
+ * letter in the corresponding alphabet, or two first letters if the
+ * first one happens to look too "trivial" to be recognizable by
+ * itself.
+ *
+ * This table is used to determine the primary script a font has
+ * been designed for.
+ *
+ * Very useful link: https://www.wazu.jp/index.html
+ */
+ static const struct
+ {
+ const gchar script[4];
+ gint bit;
+ const gchar *sample;
+ } scripts[] = {
+ /* Han is first because fonts that support it presumably are primarily
+ * designed for it.
+ */
+ {
+ "hani", /* Han Ideographic */
+ 59,
+ "\346\260\270" /* U+6C38 "forever". Ed Trager says
+ * this is a "pan-stroke" often used
+ * in teaching Chinese calligraphy. It
+ * contains the eight basic Chinese
+ * stroke forms.
+ */
+ },
+ {
+ "copt", /* Coptic */
+ 7,
+ "\316\221\316\261" /* U+0410 GREEK CAPITAL LETTER ALPHA
+ U+0430 GREEK SMALL LETTER ALPHA
+ */
+ },
+ {
+ "grek", /* Greek */
+ 7,
+ "\316\221\316\261" /* U+0410 GREEK CAPITAL LETTER ALPHA
+ U+0430 GREEK SMALL LETTER ALPHA
+ */
+ },
+ {
+ "cyrl", /* Cyrillic */
+ 9,
+ "\320\220\325\260" /* U+0410 CYRILLIC CAPITAL LETTER A
+ U+0430 CYRILLIC SMALL LETTER A
+ */
+ },
+ {
+ "armn", /* Armenian */
+ 10,
+ "\324\261\325\241" /* U+0531 ARMENIAN CAPITAL LETTER AYB
+ U+0561 ARMENIAN SMALL LETTER AYB
+ */
+ },
+ {
+ "hebr", /* Hebrew */
+ 11,
+ "\327\220" /* U+05D0 HEBREW LETTER ALEF */
+ },
+ {
+ "arab", /* Arabic */
+ 13,
+ "\330\247\330\250" /* U+0627 ARABIC LETTER ALEF
+ * U+0628 ARABIC LETTER BEH
+ */
+ },
+ {
+ "syrc", /* Syriac */
+ 71,
+ "\334\220\334\222" /* U+0710 SYRIAC LETTER ALAPH
+ * U+0712 SYRIAC LETTER BETH
+ */
+ },
+ {
+ "thaa", /* Thaana */
+ 72,
+ "\336\200\336\201" /* U+0780 THAANA LETTER HAA
+ * U+0781 THAANA LETTER SHAVIYANI
+ */
+ },
+ /* Should really use some better sample strings for the complex
+ * scripts. Something that shows how well the font handles the
+ * complex processing for each script.
+ */
+ {
+ "deva", /* Devanagari */
+ 15,
+ "\340\244\205" /* U+0905 DEVANAGARI LETTER A*/
+ },
+ {
+ "beng", /* Bengali */
+ 16,
+ "\340\246\205" /* U+0985 BENGALI LETTER A */
+ },
+ {
+ "guru", /* Gurmukhi */
+ 17,
+ "\340\250\205" /* U+0A05 GURMUKHI LETTER A */
+ },
+ {
+ "gujr", /* Gujarati */
+ 18,
+ "\340\252\205" /* U+0A85 GUJARATI LETTER A */
+ },
+ {
+ "orya", /* Oriya */
+ 19,
+ "\340\254\205" /* U+0B05 ORIYA LETTER A */
+ },
+ {
+ "taml", /* Tamil */
+ 20,
+ "\340\256\205" /* U+0B85 TAMIL LETTER A */
+ },
+ {
+ "telu", /* Telugu */
+ 21,
+ "\340\260\205" /* U+0C05 TELUGU LETTER A */
+ },
+ {
+ "knda", /* Kannada */
+ 22,
+ "\340\262\205" /* U+0C85 KANNADA LETTER A */
+ },
+ {
+ "mylm", /* Malayalam */
+ 23,
+ "\340\264\205" /* U+0D05 MALAYALAM LETTER A */
+ },
+ {
+ "sinh", /* Sinhala */
+ 73,
+ "\340\266\205" /* U+0D85 SINHALA LETTER AYANNA */
+ },
+ {
+ "thai", /* Thai */
+ 24,
+ "\340\270\201\340\270\264"/* U+0E01 THAI CHARACTER KO KAI
+ * U+0E34 THAI CHARACTER SARA I
+ */
+ },
+ {
+ "laoo", /* Lao */
+ 25,
+ "\340\272\201\340\272\264"/* U+0E81 LAO LETTER KO
+ * U+0EB4 LAO VOWEL SIGN I
+ */
+ },
+ {
+ "tibt", /* Tibetan */
+ 70,
+ "\340\274\200" /* U+0F00 TIBETAN SYLLABLE OM */
+ },
+ {
+ "mymr", /* Myanmar */
+ 74,
+ "\341\200\200" /* U+1000 MYANMAR LETTER KA */
+ },
+ {
+ "geor", /* Georgian */
+ 26,
+ "\341\202\240\341\203\200" /* U+10A0 GEORGIAN CAPITAL LETTER AN
+ * U+10D0 GEORGIAN LETTER AN
+ */
+ },
+ {
+ "hang", /* Hangul */
+ 28,
+ "\341\204\200\341\204\201"/* U+1100 HANGUL CHOSEONG KIYEOK
+ * U+1101 HANGUL CHOSEONG SSANGKIYEOK
+ */
+ },
+ {
+ "ethi", /* Ethiopic */
+ 75,
+ "\341\210\200" /* U+1200 ETHIOPIC SYLLABLE HA */
+ },
+ {
+ "cher", /* Cherokee */
+ 76,
+ "\341\216\243" /* U+13A3 CHEROKEE LETTER O */
+ },
+ {
+ "cans", /* Unified Canadian Aboriginal Syllabics */
+ 77,
+ "\341\220\201" /* U+1401 CANADIAN SYLLABICS E */
+ },
+ {
+ "ogam", /* Ogham */
+ 78,
+ "\341\232\201" /* U+1681 OGHAM LETTER BEITH */
+ },
+ {
+ "runr", /* Runic */
+ 79,
+ "\341\232\240" /* U+16A0 RUNIC LETTER FEHU FEOH FE F */
+ },
+ {
+ "tglg", /* Tagalog */
+ 84,
+ "\341\234\200" /* U+1700 TAGALOG LETTER A */
+ },
+ {
+ "hano", /* Hanunoo */
+ -1,
+ "\341\234\240" /* U+1720 HANUNOO LETTER A */
+ },
+ {
+ "buhd", /* Buhid */
+ -1,
+ "\341\235\200" /* U+1740 BUHID LETTER A */
+ },
+ {
+ "tagb", /* Tagbanwa */
+ -1,
+ "\341\235\240" /* U+1760 TAGBANWA LETTER A */
+ },
+ {
+ "khmr", /* Khmer */
+ 80,
+ "\341\236\201\341\237\222\341\236\211\341\236\273\341\237\206"
+ /* U+1781 KHMER LETTER KHA
+ * U+17D2 KHMER LETTER SIGN COENG
+ * U+1789 KHMER LETTER NYO
+ * U+17BB KHMER VOWEL SIGN U
+ * U+17C6 KHMER SIGN NIKAHIT
+ * A common word meaning "I" that contains
+ * lots of complex processing.
+ */
+ },
+ {
+ "mong", /* Mongolian */
+ 81,
+ "\341\240\240" /* U+1820 MONGOLIAN LETTER A */
+ },
+ {
+ "limb", /* Limbu */
+ -1,
+ "\341\244\201" /* U+1901 LIMBU LETTER KA */
+ },
+ {
+ "tale", /* Tai Le */
+ -1,
+ "\341\245\220" /* U+1950 TAI LE LETTER KA */
+ },
+ {
+ "latn", /* Latin */
+ 0,
+ "Aa"
+ }
+ };
+
+ gint ot_alts[4];
+ gint n_ot_alts = 0;
+ gint sr_alts[20];
+ gint n_sr_alts = 0;
+
+ font = pango_context_load_font (context, font_desc);
+
+ g_return_val_if_fail (PANGO_IS_FC_FONT (font), "Aa");
+
+ face = pango_fc_font_lock_face (PANGO_FC_FONT (font));
+ g_return_val_if_fail (face != NULL, "Aa");
+ hb_face = hb_ft_face_create (face, NULL);
+
+ /* First check what script(s), if any, the font has GSUB or GPOS
+ * OpenType layout tables for.
+ */
+ for (tt = PANGO_OT_TABLE_GSUB;
+ n_ot_alts < G_N_ELEMENTS (ot_alts) && tt <= PANGO_OT_TABLE_GPOS;
+ tt++)
+ {
+ hb_tag_t tag;
+ unsigned int count;
+ PangoOTTag *slist;
+
+ tag = get_hb_table_type (tt);
+ count = hb_ot_layout_table_get_script_tags (hb_face, tag, 0, NULL, NULL);
+ slist = g_new (PangoOTTag, count + 1);
+ hb_ot_layout_table_get_script_tags (hb_face, tag, 0, &count, slist);
+ slist[count] = 0;
+
+ for (i = 0;
+ n_ot_alts < G_N_ELEMENTS (ot_alts) && i < G_N_ELEMENTS (scripts);
+ i++)
+ {
+ gint j, k;
+
+ for (k = 0; k < n_ot_alts; k++)
+ if (ot_alts[k] == i)
+ break;
+
+ if (k == n_ot_alts)
+ {
+ for (j = 0;
+ n_ot_alts < G_N_ELEMENTS (ot_alts) && slist[j];
+ j++)
+ {
+#define TAG(s) FT_MAKE_TAG (s[0], s[1], s[2], s[3])
+
+ if (slist[j] == TAG (scripts[i].script) &&
+ gimp_font_covers_string (PANGO_FC_FONT (font),
+ scripts[i].sample))
+ {
+ ot_alts[n_ot_alts++] = i;
+ DEBUGPRINT (("%.4s ", scripts[i].script));
+ }
+#undef TAG
+ }
+ }
+ }
+
+ g_free (slist);
+ }
+
+ hb_face_destroy (hb_face);
+
+ DEBUGPRINT (("; OS/2: "));
+
+ /* Next check the OS/2 table for Unicode ranges the font claims
+ * to cover.
+ */
+
+ os2 = FT_Get_Sfnt_Table (face, ft_sfnt_os2);
+
+ if (os2)
+ {
+ for (i = 0;
+ n_sr_alts < G_N_ELEMENTS (sr_alts) && i < G_N_ELEMENTS (scripts);
+ i++)
+ {
+ if (scripts[i].bit >= 0 &&
+ (&os2->ulUnicodeRange1)[scripts[i].bit/32] & (1 << (scripts[i].bit % 32)) &&
+ gimp_font_covers_string (PANGO_FC_FONT (font),
+ scripts[i].sample))
+ {
+ sr_alts[n_sr_alts++] = i;
+ DEBUGPRINT (("%.4s ", scripts[i].script));
+ }
+ }
+ }
+
+ pango_fc_font_unlock_face (PANGO_FC_FONT (font));
+
+ g_object_unref (font);
+
+ if (n_ot_alts > 2)
+ {
+ /* The font has OpenType tables for several scripts. If it
+ * support Basic Latin as well, use Aa.
+ */
+ gint i;
+
+ for (i = 0; i < n_sr_alts; i++)
+ if (scripts[sr_alts[i]].bit == 0)
+ {
+ DEBUGPRINT (("=> several OT, also latin, use Aa\n"));
+ return "Aa";
+ }
+ }
+
+ if (n_ot_alts > 0 && n_sr_alts >= n_ot_alts + 3)
+ {
+ /* At least one script with an OpenType table, but many more
+ * subranges than such scripts. If it supports Basic Latin,
+ * use Aa, else the highest priority subrange.
+ */
+ gint i;
+
+ for (i = 0; i < n_sr_alts; i++)
+ if (scripts[sr_alts[i]].bit == 0)
+ {
+ DEBUGPRINT (("=> several SR, also latin, use Aa\n"));
+ return "Aa";
+ }
+
+ DEBUGPRINT (("=> several SR, use %.4s\n", scripts[sr_alts[0]].script));
+ return scripts[sr_alts[0]].sample;
+ }
+
+ if (n_ot_alts > 0)
+ {
+ /* OpenType tables for at least one script, use the
+ * highest priority one
+ */
+ DEBUGPRINT (("=> at least one OT, use %.4s\n",
+ scripts[sr_alts[0]].script));
+ return scripts[ot_alts[0]].sample;
+ }
+
+ if (n_sr_alts > 0)
+ {
+ /* Use the highest priority subrange. This means that a
+ * font that supports Greek, Cyrillic and Latin (quite
+ * common), will get the Greek sample string. That is
+ * capital and lowercase alpha, which looks like capital A
+ * and lowercase alpha, so it's actually quite nice, and
+ * doesn't give a too strong impression that the font would
+ * be for Greek only.
+ */
+ DEBUGPRINT (("=> at least one SR, use %.4s\n",
+ scripts[sr_alts[0]].script));
+ return scripts[sr_alts[0]].sample;
+ }
+
+ /* Final fallback */
+ DEBUGPRINT (("=> fallback, use Aa\n"));
+ return "Aa";
+}
diff --git a/app/text/gimpfont.h b/app/text/gimpfont.h
new file mode 100644
index 0000000..1b6a632
--- /dev/null
+++ b/app/text/gimpfont.h
@@ -0,0 +1,45 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpfont.h
+ * Copyright (C) 2003 Michael Natterer <mitch@gimp.org>
+ * 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/>.
+ */
+
+#ifndef __GIMP_FONT_H__
+#define __GIMP_FONT_H__
+
+
+#include "core/gimpdata.h"
+
+
+#define GIMP_TYPE_FONT (gimp_font_get_type ())
+#define GIMP_FONT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_FONT, GimpFont))
+#define GIMP_FONT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_FONT, GimpFontClass))
+#define GIMP_IS_FONT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_FONT))
+#define GIMP_IS_FONT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_FONT))
+#define GIMP_FONT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_FONT, GimpFontClass))
+
+
+typedef struct _GimpFontClass GimpFontClass;
+
+
+GType gimp_font_get_type (void) G_GNUC_CONST;
+
+GimpData * gimp_font_get_standard (void);
+
+
+#endif /* __GIMP_FONT_H__ */
diff --git a/app/text/gimpfontfactory.c b/app/text/gimpfontfactory.c
new file mode 100644
index 0000000..ead75d5
--- /dev/null
+++ b/app/text/gimpfontfactory.c
@@ -0,0 +1,677 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpfontfactory.c
+ * Copyright (C) 2003-2018 Michael Natterer <mitch@gimp.org>
+ *
+ * Partly based on code Copyright (C) Sven Neumann <sven@gimp.org>
+ * Manish Singh <yosh@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 <gdk-pixbuf/gdk-pixbuf.h>
+#include <pango/pangocairo.h>
+#include <pango/pangofc-fontmap.h>
+#include <gegl.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpconfig/gimpconfig.h"
+
+#include "text-types.h"
+
+#include "core/gimp.h"
+#include "core/gimp-parallel.h"
+#include "core/gimpasync.h"
+#include "core/gimpasyncset.h"
+#include "core/gimpcancelable.h"
+#include "core/gimpcontainer.h"
+
+#include "gimpfont.h"
+#include "gimpfontfactory.h"
+
+#include "gimp-intl.h"
+
+
+/* Use fontconfig directly for speed. We can use the pango stuff when/if
+ * fontconfig/pango get more efficient.
+ */
+#define USE_FONTCONFIG_DIRECTLY
+
+#ifdef USE_FONTCONFIG_DIRECTLY
+#include <fontconfig/fontconfig.h>
+#endif
+
+#define CONF_FNAME "fonts.conf"
+
+
+struct _GimpFontFactoryPrivate
+{
+ gpointer foo; /* can't have an empty struct */
+};
+
+#define GET_PRIVATE(obj) (((GimpFontFactory *) (obj))->priv)
+
+
+static void gimp_font_factory_data_init (GimpDataFactory *factory,
+ GimpContext *context);
+static void gimp_font_factory_data_refresh (GimpDataFactory *factory,
+ GimpContext *context);
+static void gimp_font_factory_data_save (GimpDataFactory *factory);
+static void gimp_font_factory_data_cancel (GimpDataFactory *factory);
+static GimpData * gimp_font_factory_data_duplicate (GimpDataFactory *factory,
+ GimpData *data);
+static gboolean gimp_font_factory_data_delete (GimpDataFactory *factory,
+ GimpData *data,
+ gboolean delete_from_disk,
+ GError **error);
+
+static void gimp_font_factory_load (GimpFontFactory *factory,
+ GError **error);
+static gboolean gimp_font_factory_load_fonts_conf (FcConfig *config,
+ GFile *fonts_conf);
+static void gimp_font_factory_add_directories (FcConfig *config,
+ GList *path,
+ GError **error);
+static void gimp_font_factory_recursive_add_fontdir
+ (FcConfig *config,
+ GFile *file,
+ GError **error);
+static void gimp_font_factory_load_names (GimpContainer *container,
+ PangoFontMap *fontmap,
+ PangoContext *context);
+
+
+G_DEFINE_TYPE_WITH_PRIVATE (GimpFontFactory, gimp_font_factory,
+ GIMP_TYPE_DATA_FACTORY)
+
+#define parent_class gimp_font_factory_parent_class
+
+
+static void
+gimp_font_factory_class_init (GimpFontFactoryClass *klass)
+{
+ GimpDataFactoryClass *factory_class = GIMP_DATA_FACTORY_CLASS (klass);
+
+ factory_class->data_init = gimp_font_factory_data_init;
+ factory_class->data_refresh = gimp_font_factory_data_refresh;
+ factory_class->data_save = gimp_font_factory_data_save;
+ factory_class->data_cancel = gimp_font_factory_data_cancel;
+ factory_class->data_duplicate = gimp_font_factory_data_duplicate;
+ factory_class->data_delete = gimp_font_factory_data_delete;
+}
+
+static void
+gimp_font_factory_init (GimpFontFactory *factory)
+{
+ factory->priv = gimp_font_factory_get_instance_private (factory);
+}
+
+static void
+gimp_font_factory_data_init (GimpDataFactory *factory,
+ GimpContext *context)
+{
+ GError *error = NULL;
+
+ gimp_font_factory_load (GIMP_FONT_FACTORY (factory), &error);
+
+ if (error)
+ {
+ gimp_message_literal (gimp_data_factory_get_gimp (factory), NULL,
+ GIMP_MESSAGE_INFO,
+ error->message);
+ g_error_free (error);
+ }
+}
+
+static void
+gimp_font_factory_data_refresh (GimpDataFactory *factory,
+ GimpContext *context)
+{
+ GError *error = NULL;
+
+ gimp_font_factory_load (GIMP_FONT_FACTORY (factory), &error);
+
+ if (error)
+ {
+ gimp_message_literal (gimp_data_factory_get_gimp (factory), NULL,
+ GIMP_MESSAGE_INFO,
+ error->message);
+ g_error_free (error);
+ }
+}
+
+static void
+gimp_font_factory_data_save (GimpDataFactory *factory)
+{
+ /* this is not "saving" but this functions is called at the right
+ * time at exit to reset the config
+ */
+
+ /* if font loading is in progress in another thread, do nothing. calling
+ * FcInitReinitialize() while loading takes place is unsafe.
+ */
+ if (! gimp_async_set_is_empty (gimp_data_factory_get_async_set (factory)))
+ return;
+
+ /* Reinit the library with defaults. */
+ FcInitReinitialize ();
+}
+
+static void
+gimp_font_factory_data_cancel (GimpDataFactory *factory)
+{
+ GimpAsyncSet *async_set = gimp_data_factory_get_async_set (factory);
+
+ /* we can't really cancel font loading, so we just clear the async set and
+ * return without waiting for loading to finish. we also cancel the async
+ * set beforehand, as a way to signal to
+ * gimp_font_factory_load_async_callback() that loading was canceled and the
+ * factory might be dead, and that it should just do nothing.
+ */
+ gimp_cancelable_cancel (GIMP_CANCELABLE (async_set));
+ gimp_async_set_clear (async_set);
+}
+
+static GimpData *
+gimp_font_factory_data_duplicate (GimpDataFactory *factory,
+ GimpData *data)
+{
+ return NULL;
+}
+
+static gboolean
+gimp_font_factory_data_delete (GimpDataFactory *factory,
+ GimpData *data,
+ gboolean delete_from_disk,
+ GError **error)
+{
+ return TRUE;
+}
+
+
+/* public functions */
+
+GimpDataFactory *
+gimp_font_factory_new (Gimp *gimp,
+ const gchar *path_property_name)
+{
+ g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);
+ g_return_val_if_fail (path_property_name != NULL, NULL);
+
+ return g_object_new (GIMP_TYPE_FONT_FACTORY,
+ "gimp", gimp,
+ "data-type", GIMP_TYPE_FONT,
+ "path-property-name", path_property_name,
+ "get-standard-func", gimp_font_get_standard,
+ NULL);
+}
+
+
+/* private functions */
+
+static void
+gimp_font_factory_load_async (GimpAsync *async,
+ FcConfig *config)
+{
+ if (FcConfigBuildFonts (config))
+ {
+ gimp_async_finish (async, config);
+ }
+ else
+ {
+ FcConfigDestroy (config);
+
+ gimp_async_abort (async);
+ }
+}
+
+static void
+gimp_font_factory_load_async_callback (GimpAsync *async,
+ GimpFontFactory *factory)
+{
+ GimpContainer *container;
+
+ /* the operation was canceled and the factory might be dead (see
+ * gimp_font_factory_data_cancel()). bail.
+ */
+ if (gimp_async_is_canceled (async))
+ return;
+
+ container = gimp_data_factory_get_container (GIMP_DATA_FACTORY (factory));
+
+ if (gimp_async_is_finished (async))
+ {
+ FcConfig *config = gimp_async_get_result (async);
+ PangoFontMap *fontmap;
+ PangoContext *context;
+
+ FcConfigSetCurrent (config);
+
+ 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),
+ 72.0 /* FIXME */);
+ context = pango_font_map_create_context (fontmap);
+ g_object_unref (fontmap);
+
+ gimp_font_factory_load_names (container, PANGO_FONT_MAP (fontmap), context);
+ g_object_unref (context);
+ FcConfigDestroy (config);
+ }
+
+ gimp_container_thaw (container);
+}
+
+static void
+gimp_font_factory_load (GimpFontFactory *factory,
+ GError **error)
+{
+ GimpContainer *container;
+ Gimp *gimp;
+ GimpAsyncSet *async_set;
+ FcConfig *config;
+ GFile *fonts_conf;
+ GList *path;
+ GimpAsync *async;
+
+ async_set = gimp_data_factory_get_async_set (GIMP_DATA_FACTORY (factory));
+
+ if (! gimp_async_set_is_empty (async_set))
+ {
+ /* font loading is already in progress */
+ return;
+ }
+
+ container = gimp_data_factory_get_container (GIMP_DATA_FACTORY (factory));
+
+ gimp = gimp_data_factory_get_gimp (GIMP_DATA_FACTORY (factory));
+
+ if (gimp->be_verbose)
+ g_print ("Loading fonts\n");
+
+ config = FcInitLoadConfig ();
+
+ if (! config)
+ return;
+
+ fonts_conf = gimp_directory_file (CONF_FNAME, NULL);
+ if (! gimp_font_factory_load_fonts_conf (config, fonts_conf))
+ g_printerr ("%s: failed to read '%s'.\n",
+ G_STRFUNC, g_file_peek_path (fonts_conf));
+ g_object_unref (fonts_conf);
+
+ fonts_conf = gimp_sysconf_directory_file (CONF_FNAME, NULL);
+ if (! gimp_font_factory_load_fonts_conf (config, fonts_conf))
+ g_printerr ("%s: failed to read '%s'.\n",
+ G_STRFUNC, g_file_peek_path (fonts_conf));
+ g_object_unref (fonts_conf);
+
+ path = gimp_data_factory_get_data_path (GIMP_DATA_FACTORY (factory));
+ if (! path)
+ return;
+
+ gimp_container_freeze (container);
+ gimp_container_clear (container);
+
+ gimp_font_factory_add_directories (config, path, error);
+ g_list_free_full (path, (GDestroyNotify) g_object_unref);
+
+ /* We perform font cache initialization in a separate thread, so
+ * in the case a cache rebuild is to be done it will not block
+ * the UI.
+ */
+ async = gimp_parallel_run_async_independent_full (
+ +10,
+ (GimpRunAsyncFunc) gimp_font_factory_load_async,
+ config);
+
+ gimp_async_add_callback_for_object (
+ async,
+ (GimpAsyncCallback) gimp_font_factory_load_async_callback,
+ factory,
+ factory);
+
+ gimp_async_set_add (async_set, async);
+
+ g_object_unref (async);
+}
+
+static gboolean
+gimp_font_factory_load_fonts_conf (FcConfig *config,
+ GFile *fonts_conf)
+{
+ gchar *path = g_file_get_path (fonts_conf);
+ gboolean ret = TRUE;
+
+ if (! FcConfigParseAndLoad (config, (const guchar *) path, FcFalse))
+ ret = FALSE;
+
+ g_free (path);
+
+ return ret;
+}
+
+static void
+gimp_font_factory_add_directories (FcConfig *config,
+ GList *path,
+ GError **error)
+{
+ GList *list;
+
+ for (list = path; list; list = list->next)
+ {
+ /* The configured directories must exist or be created. */
+ g_file_make_directory_with_parents (list->data, NULL, NULL);
+
+ /* Do not use FcConfigAppFontAddDir(). Instead use
+ * FcConfigAppFontAddFile() with our own recursive loop.
+ * Otherwise, when some fonts fail to load (e.g. permission
+ * issues), we end up in weird situations where the fonts are in
+ * the list, but are unusable and output many errors.
+ * See bug 748553.
+ */
+ gimp_font_factory_recursive_add_fontdir (config, list->data, error);
+ }
+
+ if (error && *error)
+ {
+ gchar *font_list = g_strdup ((*error)->message);
+
+ g_clear_error (error);
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Some fonts failed to load:\n%s"), font_list);
+ g_free (font_list);
+ }
+}
+
+static void
+gimp_font_factory_recursive_add_fontdir (FcConfig *config,
+ GFile *file,
+ GError **error)
+{
+ GFileEnumerator *enumerator;
+
+ g_return_if_fail (config != NULL);
+
+ enumerator = g_file_enumerate_children (file,
+ G_FILE_ATTRIBUTE_STANDARD_NAME ","
+ G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN ","
+ G_FILE_ATTRIBUTE_STANDARD_TYPE ","
+ G_FILE_ATTRIBUTE_TIME_MODIFIED,
+ G_FILE_QUERY_INFO_NONE,
+ NULL, NULL);
+ if (enumerator)
+ {
+ GFileInfo *info;
+
+ while ((info = g_file_enumerator_next_file (enumerator, NULL, NULL)))
+ {
+ GFileType file_type;
+ GFile *child;
+
+ if (g_file_info_get_is_hidden (info))
+ {
+ g_object_unref (info);
+ continue;
+ }
+
+ file_type = g_file_info_get_file_type (info);
+ child = g_file_enumerator_get_child (enumerator, info);
+
+ if (file_type == G_FILE_TYPE_DIRECTORY)
+ {
+ gimp_font_factory_recursive_add_fontdir (config, child, error);
+ }
+ else if (file_type == G_FILE_TYPE_REGULAR)
+ {
+ gchar *path = g_file_get_path (child);
+#ifdef G_OS_WIN32
+ gchar *tmp = g_win32_locale_filename_from_utf8 (path);
+
+ g_free (path);
+ /* XXX: g_win32_locale_filename_from_utf8() may return
+ * NULL. So we need to check that path is not NULL before
+ * trying to load with fontconfig.
+ */
+ path = tmp;
+#endif
+
+ if (! path ||
+ FcFalse == FcConfigAppFontAddFile (config, (const FcChar8 *) path))
+ {
+ g_printerr ("%s: adding font file '%s' failed.\n",
+ G_STRFUNC, path);
+ if (error)
+ {
+ if (*error)
+ {
+ gchar *current_message = g_strdup ((*error)->message);
+
+ g_clear_error (error);
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ "%s\n- %s", current_message, path);
+ g_free (current_message);
+ }
+ else
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ "- %s", path);
+ }
+ }
+ }
+
+ g_free (path);
+ }
+
+ g_object_unref (child);
+ g_object_unref (info);
+ }
+
+ g_object_unref (enumerator);
+ }
+ else
+ {
+ if (error)
+ {
+ gchar *path = g_file_get_path (file);
+
+ if (*error)
+ {
+ gchar *current_message = g_strdup ((*error)->message);
+
+ g_clear_error (error);
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ "%s\n- %s%s", current_message, path,
+ G_DIR_SEPARATOR_S);
+ g_free (current_message);
+ }
+ else
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ "- %s%s", path, G_DIR_SEPARATOR_S);
+ }
+
+ g_free (path);
+ }
+ }
+}
+
+static void
+gimp_font_factory_add_font (GimpContainer *container,
+ PangoContext *context,
+ PangoFontDescription *desc)
+{
+ gchar *name;
+
+ if (! desc)
+ return;
+
+ name = pango_font_description_to_string (desc);
+
+ /* It doesn't look like pango_font_description_to_string() could ever
+ * return NULL. But just to be double sure and avoid a segfault, I
+ * check before validating the string.
+ */
+ if (name && strlen (name) > 0 &&
+ g_utf8_validate (name, -1, NULL))
+ {
+ GimpFont *font;
+
+ font = g_object_new (GIMP_TYPE_FONT,
+ "name", name,
+ "pango-context", context,
+ NULL);
+
+ gimp_container_add (container, GIMP_OBJECT (font));
+ g_object_unref (font);
+ }
+
+ g_free (name);
+}
+
+#ifdef USE_FONTCONFIG_DIRECTLY
+/* We're really chummy here with the implementation. Oh well. */
+
+/* This is copied straight from make_alias_description in pango, plus
+ * the gimp_font_list_add_font bits.
+ */
+static void
+gimp_font_factory_make_alias (GimpContainer *container,
+ PangoContext *context,
+ const gchar *family,
+ gboolean bold,
+ gboolean italic)
+{
+ PangoFontDescription *desc = pango_font_description_new ();
+
+ pango_font_description_set_family (desc, family);
+ pango_font_description_set_style (desc,
+ italic ?
+ PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL);
+ pango_font_description_set_variant (desc, PANGO_VARIANT_NORMAL);
+ pango_font_description_set_weight (desc,
+ bold ?
+ PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL);
+ pango_font_description_set_stretch (desc, PANGO_STRETCH_NORMAL);
+
+ gimp_font_factory_add_font (container, context, desc);
+
+ pango_font_description_free (desc);
+}
+
+static void
+gimp_font_factory_load_aliases (GimpContainer *container,
+ PangoContext *context)
+{
+ const gchar *families[] = { "Sans-serif", "Serif", "Monospace" };
+ gint i;
+
+ for (i = 0; i < 3; i++)
+ {
+ gimp_font_factory_make_alias (container, context, families[i],
+ FALSE, FALSE);
+ gimp_font_factory_make_alias (container, context, families[i],
+ TRUE, FALSE);
+ gimp_font_factory_make_alias (container, context, families[i],
+ FALSE, TRUE);
+ gimp_font_factory_make_alias (container, context, families[i],
+ TRUE, TRUE);
+ }
+}
+
+static void
+gimp_font_factory_load_names (GimpContainer *container,
+ PangoFontMap *fontmap,
+ PangoContext *context)
+{
+ FcObjectSet *os;
+ FcPattern *pat;
+ FcFontSet *fontset;
+ gint i;
+
+ os = FcObjectSetBuild (FC_FAMILY, FC_STYLE,
+ FC_SLANT, FC_WEIGHT, FC_WIDTH,
+ NULL);
+ g_return_if_fail (os);
+
+ pat = FcPatternCreate ();
+ if (! pat)
+ {
+ FcObjectSetDestroy (os);
+ g_critical ("%s: FcPatternCreate() returned NULL.", G_STRFUNC);
+ return;
+ }
+
+ fontset = FcFontList (NULL, pat, os);
+
+ FcPatternDestroy (pat);
+ FcObjectSetDestroy (os);
+
+ g_return_if_fail (fontset);
+
+ for (i = 0; i < fontset->nfont; i++)
+ {
+ PangoFontDescription *desc;
+
+ desc = pango_fc_font_description_from_pattern (fontset->fonts[i], FALSE);
+ gimp_font_factory_add_font (container, context, desc);
+ pango_font_description_free (desc);
+ }
+
+ /* only create aliases if there is at least one font available */
+ if (fontset->nfont > 0)
+ gimp_font_factory_load_aliases (container, context);
+
+ FcFontSetDestroy (fontset);
+}
+
+#else /* ! USE_FONTCONFIG_DIRECTLY */
+
+static void
+gimp_font_factory_load_names (GimpContainer *container,
+ PangoFontMap *fontmap,
+ PangoContext *context)
+{
+ PangoFontFamily **families;
+ PangoFontFace **faces;
+ gint n_families;
+ gint n_faces;
+ gint i, j;
+
+ pango_font_map_list_families (fontmap, &families, &n_families);
+
+ for (i = 0; i < n_families; i++)
+ {
+ pango_font_family_list_faces (families[i], &faces, &n_faces);
+
+ for (j = 0; j < n_faces; j++)
+ {
+ PangoFontDescription *desc;
+
+ desc = pango_font_face_describe (faces[j]);
+ gimp_font_factory_add_font (container, context, desc);
+ pango_font_description_free (desc);
+ }
+ }
+
+ g_free (families);
+}
+
+#endif /* USE_FONTCONFIG_DIRECTLY */
diff --git a/app/text/gimpfontfactory.h b/app/text/gimpfontfactory.h
new file mode 100644
index 0000000..4c52041
--- /dev/null
+++ b/app/text/gimpfontfactory.h
@@ -0,0 +1,58 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpfontfactory.h
+ * Copyright (C) 2018 Michael Natterer <mitch@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/>.
+ */
+
+#ifndef __GIMP_FONT_FACTORY_H__
+#define __GIMP_FONT_FACTORY_H__
+
+
+#include "core/gimpdatafactory.h"
+
+
+#define GIMP_TYPE_FONT_FACTORY (gimp_font_factory_get_type ())
+#define GIMP_FONT_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_FONT_FACTORY, GimpFontFactory))
+#define GIMP_FONT_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_FONT_FACTORY, GimpFontFactoryClass))
+#define GIMP_IS_FONT_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_FONT_FACTORY))
+#define GIMP_IS_FONT_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_FONT_FACTORY))
+#define GIMP_FONT_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_FONT_FACTORY, GimpFontFactoryClass))
+
+
+typedef struct _GimpFontFactoryPrivate GimpFontFactoryPrivate;
+typedef struct _GimpFontFactoryClass GimpFontFactoryClass;
+
+struct _GimpFontFactory
+{
+ GimpDataFactory parent_instance;
+
+ GimpFontFactoryPrivate *priv;
+};
+
+struct _GimpFontFactoryClass
+{
+ GimpDataFactoryClass parent_class;
+};
+
+
+GType gimp_font_factory_get_type (void) G_GNUC_CONST;
+
+GimpDataFactory * gimp_font_factory_new (Gimp *gimp,
+ const gchar *path_property_name);
+
+
+#endif /* __GIMP_FONT_FACTORY_H__ */
diff --git a/app/text/gimptext-compat.c b/app/text/gimptext-compat.c
new file mode 100644
index 0000000..cb71f3e
--- /dev/null
+++ b/app/text/gimptext-compat.c
@@ -0,0 +1,207 @@
+/* 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 <gegl.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <pango/pangocairo.h>
+
+#include "libgimpcolor/gimpcolor.h"
+
+#include "text-types.h"
+
+#include "core/gimp.h"
+#include "core/gimpchannel.h"
+#include "core/gimpcontext.h"
+#include "core/gimpdatafactory.h"
+#include "core/gimpimage.h"
+#include "core/gimpdrawable.h"
+#include "core/gimpimage.h"
+#include "core/gimpimage-undo.h"
+#include "core/gimplayer-floating-selection.h"
+
+#include "gimptext.h"
+#include "gimptext-compat.h"
+#include "gimptextlayer.h"
+
+#include "gimp-intl.h"
+
+
+GimpLayer *
+text_render (GimpImage *image,
+ GimpDrawable *drawable,
+ GimpContext *context,
+ gint text_x,
+ gint text_y,
+ const gchar *fontname,
+ const gchar *text,
+ gint border,
+ gboolean antialias)
+{
+ PangoFontDescription *desc;
+ GimpText *gtext;
+ GimpLayer *layer;
+ GimpRGB color;
+ gchar *font;
+ gdouble size;
+
+ g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
+ g_return_val_if_fail (drawable == NULL || GIMP_IS_DRAWABLE (drawable), NULL);
+ g_return_val_if_fail (drawable == NULL ||
+ gimp_item_is_attached (GIMP_ITEM (drawable)), NULL);
+ g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
+ g_return_val_if_fail (fontname != NULL, NULL);
+ g_return_val_if_fail (text != NULL, NULL);
+
+ if (! gimp_data_factory_data_wait (image->gimp->font_factory))
+ return NULL;
+
+ if (border < 0)
+ border = 0;
+
+ desc = pango_font_description_from_string (fontname);
+ size = PANGO_PIXELS (pango_font_description_get_size (desc));
+
+ pango_font_description_unset_fields (desc, PANGO_FONT_MASK_SIZE);
+ font = pango_font_description_to_string (desc);
+
+ pango_font_description_free (desc);
+
+ gimp_context_get_foreground (context, &color);
+
+ gtext = g_object_new (GIMP_TYPE_TEXT,
+ "text", text,
+ "font", font,
+ "font-size", size,
+ "antialias", antialias,
+ "border", border,
+ "color", &color,
+ NULL);
+
+ g_free (font);
+
+ layer = gimp_text_layer_new (image, gtext);
+
+ g_object_unref (gtext);
+
+ if (!layer)
+ return NULL;
+
+ /* Start a group undo */
+ gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_TEXT,
+ _("Add Text Layer"));
+
+ /* Set the layer offsets */
+ gimp_item_set_offset (GIMP_ITEM (layer), text_x, text_y);
+
+ /* If there is a selection mask clear it--
+ * this might not always be desired, but in general,
+ * it seems like the correct behavior.
+ */
+ if (! gimp_channel_is_empty (gimp_image_get_mask (image)))
+ gimp_channel_clear (gimp_image_get_mask (image), NULL, TRUE);
+
+ if (drawable == NULL)
+ {
+ /* If the drawable is NULL, create a new layer */
+ gimp_image_add_layer (image, layer, NULL, -1, TRUE);
+ }
+ else
+ {
+ /* Otherwise, instantiate the text as the new floating selection */
+ floating_sel_attach (layer, drawable);
+ }
+
+ /* end the group undo */
+ gimp_image_undo_group_end (image);
+
+ return layer;
+}
+
+gboolean
+text_get_extents (Gimp *gimp,
+ const gchar *fontname,
+ const gchar *text,
+ gint *width,
+ gint *height,
+ gint *ascent,
+ gint *descent)
+{
+ PangoFontDescription *font_desc;
+ PangoContext *context;
+ PangoLayout *layout;
+ PangoFontMap *fontmap;
+ PangoRectangle rect;
+
+ g_return_val_if_fail (GIMP_IS_GIMP (gimp), FALSE);
+ g_return_val_if_fail (fontname != NULL, FALSE);
+ g_return_val_if_fail (text != NULL, FALSE);
+
+ if (! gimp_data_factory_data_wait (gimp->font_factory))
+ return FALSE;
+
+ 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),
+ 72.0); /* FIXME: resolution */
+ context = pango_font_map_create_context (fontmap);
+ g_object_unref (fontmap);
+
+ layout = pango_layout_new (context);
+ g_object_unref (context);
+
+ font_desc = pango_font_description_from_string (fontname);
+ pango_layout_set_font_description (layout, font_desc);
+ pango_font_description_free (font_desc);
+
+ pango_layout_set_text (layout, text, -1);
+
+ pango_layout_get_pixel_extents (layout, NULL, &rect);
+
+ if (width)
+ *width = rect.width;
+ if (height)
+ *height = rect.height;
+
+ if (ascent || descent)
+ {
+ PangoLayoutIter *iter;
+ PangoLayoutLine *line;
+
+ iter = pango_layout_get_iter (layout);
+ line = pango_layout_iter_get_line_readonly (iter);
+ pango_layout_iter_free (iter);
+
+ pango_layout_line_get_pixel_extents (line, NULL, &rect);
+
+ if (ascent)
+ *ascent = PANGO_ASCENT (rect);
+ if (descent)
+ *descent = - PANGO_DESCENT (rect);
+ }
+
+ g_object_unref (layout);
+
+ return TRUE;
+}
diff --git a/app/text/gimptext-compat.h b/app/text/gimptext-compat.h
new file mode 100644
index 0000000..2e178f0
--- /dev/null
+++ b/app/text/gimptext-compat.h
@@ -0,0 +1,45 @@
+/* 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/>.
+ */
+
+#ifndef __GIMP_TEXT_COMPAT_H__
+#define __GIMP_TEXT_COMPAT_H__
+
+
+/* convenience functions that provide the 1.2 API, only used by the PDB */
+
+GimpLayer * text_render (GimpImage *image,
+ GimpDrawable *drawable,
+ GimpContext *context,
+ gint text_x,
+ gint text_y,
+ const gchar *fontname,
+ const gchar *text,
+ gint border,
+ gboolean antialias);
+gboolean text_get_extents (Gimp *gimp,
+ const gchar *fontname,
+ const gchar *text,
+ gint *width,
+ gint *height,
+ gint *ascent,
+ gint *descent);
+
+
+#endif /* __GIMP_TEXT_COMPAT_H__ */
diff --git a/app/text/gimptext-parasite.c b/app/text/gimptext-parasite.c
new file mode 100644
index 0000000..790a5b1
--- /dev/null
+++ b/app/text/gimptext-parasite.c
@@ -0,0 +1,204 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GimpText
+ * Copyright (C) 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 <stdlib.h>
+
+#include <cairo.h>
+#include <gegl.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpconfig/gimpconfig.h"
+
+#include "text-types.h"
+
+#include "core/gimperror.h"
+
+#include "gimptext.h"
+#include "gimptext-parasite.h"
+#include "gimptext-xlfd.h"
+
+#include "gimp-intl.h"
+
+
+/****************************************/
+/* The native GimpTextLayer parasite. */
+/****************************************/
+
+const gchar *
+gimp_text_parasite_name (void)
+{
+ return "gimp-text-layer";
+}
+
+GimpParasite *
+gimp_text_to_parasite (const GimpText *text)
+{
+ GimpParasite *parasite;
+ gchar *str;
+
+ g_return_val_if_fail (GIMP_IS_TEXT (text), NULL);
+
+ str = gimp_config_serialize_to_string (GIMP_CONFIG (text), NULL);
+ g_return_val_if_fail (str != NULL, NULL);
+
+ parasite = gimp_parasite_new (gimp_text_parasite_name (),
+ GIMP_PARASITE_PERSISTENT,
+ strlen (str) + 1, str);
+ g_free (str);
+
+ return parasite;
+}
+
+GimpText *
+gimp_text_from_parasite (const GimpParasite *parasite,
+ GError **error)
+{
+ GimpText *text;
+ const gchar *str;
+
+ g_return_val_if_fail (parasite != NULL, NULL);
+ g_return_val_if_fail (strcmp (gimp_parasite_name (parasite),
+ gimp_text_parasite_name ()) == 0, NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ str = gimp_parasite_data (parasite);
+
+ text = g_object_new (GIMP_TYPE_TEXT, NULL);
+
+ if (str != NULL)
+ {
+ gimp_config_deserialize_string (GIMP_CONFIG (text),
+ str,
+ gimp_parasite_data_size (parasite),
+ NULL,
+ error);
+ }
+ else
+ {
+ g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
+ _("Empty text parasite"));
+ }
+
+ return text;
+}
+
+
+/****************************************************************/
+/* Compatibility to plug-in GDynText 1.4.4 and later versions */
+/* GDynText was written by Marco Lamberto <lm@geocities.com> */
+/****************************************************************/
+
+const gchar *
+gimp_text_gdyntext_parasite_name (void)
+{
+ return "plug_in_gdyntext/data";
+}
+
+enum
+{
+ TEXT = 0,
+ ANTIALIAS = 1,
+ ALIGNMENT = 2,
+ ROTATION = 3,
+ LINE_SPACING = 4,
+ COLOR = 5,
+ LAYER_ALIGNMENT = 6,
+ XLFD = 7,
+ NUM_PARAMS
+};
+
+GimpText *
+gimp_text_from_gdyntext_parasite (const GimpParasite *parasite)
+{
+ GimpText *retval = NULL;
+ GimpTextJustification justify;
+ const gchar *str;
+ gchar *text = NULL;
+ gchar **params;
+ gboolean antialias;
+ gdouble spacing;
+ GimpRGB rgb;
+ glong color;
+ gint i;
+
+ g_return_val_if_fail (parasite != NULL, NULL);
+ g_return_val_if_fail (strcmp (gimp_parasite_name (parasite),
+ gimp_text_gdyntext_parasite_name ()) == 0,
+ NULL);
+
+ str = gimp_parasite_data (parasite);
+ g_return_val_if_fail (str != NULL, NULL);
+
+ if (! g_str_has_prefix (str, "GDT10{")) /* magic value */
+ return NULL;
+
+ params = g_strsplit (str + strlen ("GDT10{"), "}{", -1);
+
+ /* first check that we have the required number of parameters */
+ for (i = 0; i < NUM_PARAMS; i++)
+ if (!params[i])
+ goto cleanup;
+
+ text = g_strcompress (params[TEXT]);
+
+ if (! g_utf8_validate (text, -1, NULL))
+ {
+ gchar *tmp = gimp_any_to_utf8 (text, -1, NULL);
+
+ g_free (text);
+ text = tmp;
+ }
+
+ antialias = atoi (params[ANTIALIAS]) ? TRUE : FALSE;
+
+ switch (atoi (params[ALIGNMENT]))
+ {
+ default:
+ case 0: justify = GIMP_TEXT_JUSTIFY_LEFT; break;
+ case 1: justify = GIMP_TEXT_JUSTIFY_CENTER; break;
+ case 2: justify = GIMP_TEXT_JUSTIFY_RIGHT; break;
+ }
+
+ spacing = g_strtod (params[LINE_SPACING], NULL);
+
+ color = strtol (params[COLOR], NULL, 16);
+ gimp_rgba_set_uchar (&rgb, color >> 16, color >> 8, color, 255);
+
+ retval = g_object_new (GIMP_TYPE_TEXT,
+ "text", text,
+ "antialias", antialias,
+ "justify", justify,
+ "line-spacing", spacing,
+ "color", &rgb,
+ NULL);
+
+ gimp_text_set_font_from_xlfd (GIMP_TEXT (retval), params[XLFD]);
+
+ cleanup:
+ g_free (text);
+ g_strfreev (params);
+
+ return retval;
+}
diff --git a/app/text/gimptext-parasite.h b/app/text/gimptext-parasite.h
new file mode 100644
index 0000000..d834309
--- /dev/null
+++ b/app/text/gimptext-parasite.h
@@ -0,0 +1,34 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GimpText
+ * Copyright (C) 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/>.
+ */
+
+#ifndef __GIMP_TEXT_PARASITE_H__
+#define __GIMP_TEXT_PARASITE_H__
+
+
+const gchar * gimp_text_parasite_name (void) G_GNUC_CONST;
+GimpParasite * gimp_text_to_parasite (const GimpText *text);
+GimpText * gimp_text_from_parasite (const GimpParasite *parasite,
+ GError **error);
+
+const gchar * gimp_text_gdyntext_parasite_name (void) G_GNUC_CONST;
+GimpText * gimp_text_from_gdyntext_parasite (const GimpParasite *parasite);
+
+
+#endif /* __GIMP_TEXT_PARASITE_H__ */
diff --git a/app/text/gimptext-vectors.c b/app/text/gimptext-vectors.c
new file mode 100644
index 0000000..3a6901e
--- /dev/null
+++ b/app/text/gimptext-vectors.c
@@ -0,0 +1,255 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GimpText-vectors
+ * Copyright (C) 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 <gdk-pixbuf/gdk-pixbuf.h>
+#include <pango/pangocairo.h>
+#include <gegl.h>
+
+#include "text-types.h"
+
+#include "core/gimp.h"
+#include "core/gimpimage.h"
+
+#include "vectors/gimpbezierstroke.h"
+#include "vectors/gimpvectors.h"
+#include "vectors/gimpanchor.h"
+
+#include "gimptext.h"
+#include "gimptext-vectors.h"
+#include "gimptextlayout.h"
+#include "gimptextlayout-render.h"
+
+
+typedef struct
+{
+ GimpVectors *vectors;
+ GimpStroke *stroke;
+ GimpAnchor *anchor;
+} RenderContext;
+
+
+static void gimp_text_render_vectors (cairo_t *cr,
+ RenderContext *context);
+
+
+GimpVectors *
+gimp_text_vectors_new (GimpImage *image,
+ GimpText *text)
+{
+ GimpVectors *vectors;
+ RenderContext context = { NULL, };
+
+ g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
+ g_return_val_if_fail (GIMP_IS_TEXT (text), NULL);
+
+ vectors = gimp_vectors_new (image, NULL);
+
+ if (text->text || text->markup)
+ {
+ GimpTextLayout *layout;
+ cairo_surface_t *surface;
+ cairo_t *cr;
+ gdouble xres;
+ gdouble yres;
+ GError *error = NULL;
+
+ if (text->text)
+ gimp_object_set_name_safe (GIMP_OBJECT (vectors), text->text);
+
+ context.vectors = vectors;
+
+ surface = cairo_recording_surface_create (CAIRO_CONTENT_ALPHA, NULL);
+ cr = cairo_create (surface);
+
+ gimp_image_get_resolution (image, &xres, &yres);
+
+ layout = gimp_text_layout_new (text, xres, yres, &error);
+ if (error)
+ {
+ gimp_message_literal (image->gimp, NULL, GIMP_MESSAGE_ERROR, error->message);
+ g_error_free (error);
+ }
+ gimp_text_layout_render (layout, cr, text->base_dir, TRUE);
+ g_object_unref (layout);
+
+ gimp_text_render_vectors (cr, &context);
+
+ cairo_destroy (cr);
+ cairo_surface_destroy (surface);
+
+ if (context.stroke)
+ gimp_stroke_close (context.stroke);
+ }
+
+ return vectors;
+}
+
+
+static inline void
+gimp_text_vector_coords (const double x,
+ const double y,
+ GimpCoords *coords)
+{
+ const GimpCoords default_values = GIMP_COORDS_DEFAULT_VALUES;
+
+ *coords = default_values;
+
+ coords->x = x;
+ coords->y = y;
+}
+
+static gint
+moveto (RenderContext *context,
+ const double x,
+ const double y)
+{
+ GimpCoords start;
+
+#if GIMP_TEXT_DEBUG
+ g_printerr ("moveto %f, %f\n", x, y);
+#endif
+
+ gimp_text_vector_coords (x, y, &start);
+
+ if (context->stroke)
+ gimp_stroke_close (context->stroke);
+
+ context->stroke = gimp_bezier_stroke_new_moveto (&start);
+
+ gimp_vectors_stroke_add (context->vectors, context->stroke);
+ g_object_unref (context->stroke);
+
+ return 0;
+}
+
+static gint
+lineto (RenderContext *context,
+ const double x,
+ const double y)
+{
+ GimpCoords end;
+
+#if GIMP_TEXT_DEBUG
+ g_printerr ("lineto %f, %f\n", x, y);
+#endif
+
+ if (! context->stroke)
+ return 0;
+
+ gimp_text_vector_coords (x, y, &end);
+
+ gimp_bezier_stroke_lineto (context->stroke, &end);
+
+ return 0;
+}
+
+static gint
+cubicto (RenderContext *context,
+ const double x1,
+ const double y1,
+ const double x2,
+ const double y2,
+ const double x3,
+ const double y3)
+{
+ GimpCoords control1;
+ GimpCoords control2;
+ GimpCoords end;
+
+#if GIMP_TEXT_DEBUG
+ g_printerr ("cubicto %f, %f\n", x3, y3);
+#endif
+
+ if (! context->stroke)
+ return 0;
+
+ gimp_text_vector_coords (x1, y1, &control1);
+ gimp_text_vector_coords (x2, y2, &control2);
+ gimp_text_vector_coords (x3, y3, &end);
+
+ gimp_bezier_stroke_cubicto (context->stroke, &control1, &control2, &end);
+
+ return 0;
+}
+
+static gint
+closepath (RenderContext *context)
+{
+#if GIMP_TEXT_DEBUG
+ g_printerr ("moveto\n");
+#endif
+
+ if (! context->stroke)
+ return 0;
+
+ gimp_stroke_close (context->stroke);
+
+ context->stroke = NULL;
+
+ return 0;
+}
+
+static void
+gimp_text_render_vectors (cairo_t *cr,
+ RenderContext *context)
+{
+ cairo_path_t *path;
+ gint i;
+
+ path = cairo_copy_path (cr);
+
+ for (i = 0; i < path->num_data; i += path->data[i].header.length)
+ {
+ cairo_path_data_t *data = &path->data[i];
+
+ /* if the drawing operation is the final moveto of the glyph,
+ * break to avoid creating an empty point. This is because cairo
+ * always adds a moveto after each closepath.
+ */
+ if (i + data->header.length >= path->num_data)
+ break;
+
+ switch (data->header.type)
+ {
+ case CAIRO_PATH_MOVE_TO:
+ moveto (context, data[1].point.x, data[1].point.y);
+ break;
+
+ case CAIRO_PATH_LINE_TO:
+ lineto (context, data[1].point.x, data[1].point.y);
+ break;
+
+ case CAIRO_PATH_CURVE_TO:
+ cubicto (context,
+ data[1].point.x, data[1].point.y,
+ data[2].point.x, data[2].point.y,
+ data[3].point.x, data[3].point.y);
+ break;
+
+ case CAIRO_PATH_CLOSE_PATH:
+ closepath (context);
+ break;
+ }
+ }
+
+ cairo_path_destroy (path);
+}
diff --git a/app/text/gimptext-vectors.h b/app/text/gimptext-vectors.h
new file mode 100644
index 0000000..df01aae
--- /dev/null
+++ b/app/text/gimptext-vectors.h
@@ -0,0 +1,29 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GimpText-vectors
+ * Copyright (C) 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/>.
+ */
+
+#ifndef __GIMP_TEXT_VECTORS_H__
+#define __GIMP_TEXT_VECTORS_H__
+
+
+GimpVectors * gimp_text_vectors_new (GimpImage *image,
+ GimpText *text);
+
+
+#endif /* __GIMP_TEXT_VECTORS_H__ */
diff --git a/app/text/gimptext-xlfd.c b/app/text/gimptext-xlfd.c
new file mode 100644
index 0000000..16245b5
--- /dev/null
+++ b/app/text/gimptext-xlfd.c
@@ -0,0 +1,301 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GimpText
+ * Copyright (C) 2002-2004 Sven Neumann <sven@gimp.org>
+ *
+ * Some of this code was copied from Pango (pangox-fontmap.c)
+ * and was originally written by Owen Taylor <otaylor@redhat.com>.
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+
+#include <gio/gio.h>
+
+#include "libgimpbase/gimpbase.h"
+
+#include "text-types.h"
+
+#include "gimptext.h"
+#include "gimptext-xlfd.h"
+
+
+#define XLFD_MAX_FIELD_LEN 64
+
+/* These are the field numbers in the X Logical Font Description fontnames,
+ e.g. -adobe-courier-bold-o-normal--25-180-100-100-m-150-iso8859-1 */
+enum
+{
+ XLFD_FOUNDRY = 0,
+ XLFD_FAMILY = 1,
+ XLFD_WEIGHT = 2,
+ XLFD_SLANT = 3,
+ XLFD_SET_WIDTH = 4,
+ XLFD_ADD_STYLE = 5,
+ XLFD_PIXELS = 6,
+ XLFD_POINTS = 7,
+ XLFD_RESOLUTION_X = 8,
+ XLFD_RESOLUTION_Y = 9,
+ XLFD_SPACING = 10,
+ XLFD_AVERAGE_WIDTH = 11,
+ XLFD_CHARSET = 12,
+ XLFD_NUM_FIELDS
+};
+
+static gchar * gimp_text_get_xlfd_field (const gchar *fontname,
+ gint field_num,
+ gchar *buffer);
+static gchar * launder_font_name (gchar *name);
+
+
+/**
+ * gimp_text_font_name_from_xlfd:
+ * @xlfd: X Logical Font Description
+ *
+ * Attempts to extract a meaningful font name from the "family",
+ * "weight", "slant" and "stretch" fields of an X Logical Font
+ * Description.
+ *
+ * Return value: a newly allocated string.
+ **/
+gchar *
+gimp_text_font_name_from_xlfd (const gchar *xlfd)
+{
+ gchar *fields[4];
+ gchar buffers[4][XLFD_MAX_FIELD_LEN];
+ gint i = 0;
+
+ /* family */
+ fields[i] = gimp_text_get_xlfd_field (xlfd, XLFD_FAMILY, buffers[i]);
+ if (fields[i])
+ i++;
+
+ /* weight */
+ fields[i] = gimp_text_get_xlfd_field (xlfd, XLFD_WEIGHT, buffers[i]);
+ if (fields[i] && strcmp (fields[i], "medium"))
+ i++;
+
+ /* slant */
+ fields[i] = gimp_text_get_xlfd_field (xlfd, XLFD_SLANT, buffers[i]);
+ if (fields[i])
+ {
+ switch (*fields[i])
+ {
+ case 'i':
+ strcpy (buffers[i], "italic");
+ i++;
+ break;
+ case 'o':
+ strcpy (buffers[i], "oblique");
+ i++;
+ break;
+ case 'r':
+ break;
+ }
+ }
+
+ /* stretch */
+ fields[i] = gimp_text_get_xlfd_field (xlfd, XLFD_SET_WIDTH, buffers[i]);
+ if (fields[i] && strcmp (fields[i], "normal"))
+ i++;
+
+ if (i < 4)
+ fields[i] = NULL;
+
+ return launder_font_name (g_strconcat (fields[0], " ",
+ fields[1], " ",
+ fields[2], " ",
+ fields[3], NULL));
+}
+
+/**
+ * gimp_text_font_size_from_xlfd:
+ * @xlfd: X Logical Font Description
+ * @size: return location for the font size
+ * @size_unit: return location for the font size unit
+ *
+ * Attempts to extract the font size from an X Logical Font
+ * Description.
+ *
+ * Return value: %TRUE on success, %FALSE otherwise.
+ **/
+gboolean
+gimp_text_font_size_from_xlfd (const gchar *xlfd,
+ gdouble *size,
+ GimpUnit *size_unit)
+{
+ gchar buffer[XLFD_MAX_FIELD_LEN];
+ gchar *field;
+
+ if (!xlfd)
+ return FALSE;
+
+ field = gimp_text_get_xlfd_field (xlfd, XLFD_PIXELS, buffer);
+ if (field)
+ {
+ *size = atoi (field);
+ *size_unit = GIMP_UNIT_PIXEL;
+ return TRUE;
+ }
+
+ field = gimp_text_get_xlfd_field (xlfd, XLFD_POINTS, buffer);
+ if (field)
+ {
+ *size = atoi (field) / 10.0;
+ *size_unit = GIMP_UNIT_POINT;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * gimp_text_set_font_from_xlfd:
+ * @text: a #GimpText object
+ * @xlfd: X Logical Font Description
+ *
+ * Attempts to extract font name and font size from @xlfd and sets
+ * them on the #GimpText object.
+ **/
+void
+gimp_text_set_font_from_xlfd (GimpText *text,
+ const gchar *xlfd)
+{
+ gchar *font;
+ gdouble size;
+ GimpUnit size_unit;
+
+ g_return_if_fail (GIMP_IS_TEXT (text));
+
+ if (!xlfd)
+ return;
+
+ font = gimp_text_font_name_from_xlfd (xlfd);
+
+#if GIMP_TEXT_DEBUG
+ g_printerr ("XLFD: %s font: %s\n", xlfd, font ? font : "(null)");
+#endif
+
+ if (gimp_text_font_size_from_xlfd (xlfd, &size, &size_unit))
+ {
+ g_object_set (text,
+ "font-size", size,
+ "font-size-unit", size_unit,
+ font ? "font" : NULL, font,
+ NULL);
+ }
+ else if (font)
+ {
+ g_object_set (text,
+ "font", font,
+ NULL);
+ }
+
+ g_free (font);
+}
+
+/**
+ * gimp_text_get_xlfd_field:
+ * @fontname: an XLFD fontname
+ * @field_num: field index
+ * @buffer: buffer of at least XLFD_MAX_FIELD_LEN chars
+ *
+ * Fills the buffer with the specified field from the X Logical Font
+ * Description name, and returns it. Note: For the charset field, we
+ * also return the encoding, e.g. 'iso8859-1'.
+ *
+ * This function is basically copied from pangox-fontmap.c.
+ *
+ * Returns: a pointer to the filled buffer or %NULL if fontname is
+ * %NULL, the field is longer than XFLD_MAX_FIELD_LEN or it contains
+ * just an asterisk.
+ **/
+static gchar *
+gimp_text_get_xlfd_field (const gchar *fontname,
+ gint field_num,
+ gchar *buffer)
+{
+ const gchar *t1, *t2;
+ gchar *p;
+ gint countdown, num_dashes;
+ gsize len;
+
+ if (!fontname)
+ return NULL;
+
+ /* we assume this is a valid fontname...that is, it has 14 fields */
+
+ for (t1 = fontname, countdown = field_num; *t1 && (countdown >= 0); t1++)
+ if (*t1 == '-')
+ countdown--;
+
+ num_dashes = (field_num == XLFD_CHARSET) ? 2 : 1;
+
+ for (t2 = t1; *t2; t2++)
+ {
+ if (*t2 == '-' && --num_dashes == 0)
+ break;
+ }
+
+ if (t2 > t1)
+ {
+ /* Check we don't overflow the buffer */
+ len = (gsize) t2 - (gsize) t1;
+ if (len > XLFD_MAX_FIELD_LEN - 1)
+ return NULL;
+
+ if (*t1 == '*')
+ return NULL;
+
+ strncpy (buffer, t1, len);
+ buffer[len] = 0;
+
+ /* Convert to lower case. */
+ for (p = buffer; *p; p++)
+ *p = g_ascii_tolower (*p);
+ }
+ else
+ {
+ return NULL;
+ }
+
+ return buffer;
+}
+
+/* Guard against font names that end in numbers being interpreted as a
+ * font size in pango font descriptions
+ */
+static gchar *
+launder_font_name (gchar *name)
+{
+ gchar *laundered_name;
+ gchar last_char;
+
+ last_char = name[strlen (name) - 1];
+
+ if (g_ascii_isdigit (last_char) || last_char == '.')
+ {
+ laundered_name = g_strconcat (name, ",", NULL);
+ g_free (name);
+
+ return laundered_name;
+ }
+ else
+ return name;
+}
diff --git a/app/text/gimptext-xlfd.h b/app/text/gimptext-xlfd.h
new file mode 100644
index 0000000..3a9c509
--- /dev/null
+++ b/app/text/gimptext-xlfd.h
@@ -0,0 +1,36 @@
+/* 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/>.
+ */
+
+#ifndef __GIMP_TEXT_XLFD_H__
+#define __GIMP_TEXT_XLFD_H__
+
+
+/* handle X Logical Font Descriptions for compat */
+
+gchar * gimp_text_font_name_from_xlfd (const gchar *xlfd);
+gboolean gimp_text_font_size_from_xlfd (const gchar *xlfd,
+ gdouble *size,
+ GimpUnit *size_unit);
+
+void gimp_text_set_font_from_xlfd (GimpText *text,
+ const gchar *xlfd);
+
+
+#endif /* __GIMP_TEXT_COMPAT_H__ */
diff --git a/app/text/gimptext.c b/app/text/gimptext.c
new file mode 100644
index 0000000..fe671a4
--- /dev/null
+++ b/app/text/gimptext.c
@@ -0,0 +1,596 @@
+/* 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 <cairo.h>
+#include <gegl.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <pango/pango.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpmath/gimpmath.h"
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpconfig/gimpconfig.h"
+
+#include "text-types.h"
+
+#include "core/gimp-memsize.h"
+#include "core/gimp-utils.h"
+#include "core/gimpmarshal.h"
+#include "core/gimpstrokeoptions.h"
+
+#include "gimptext.h"
+
+
+enum
+{
+ PROP_0,
+ 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,
+ /* for backward compatibility */
+ PROP_HINTING
+};
+
+enum
+{
+ CHANGED,
+ LAST_SIGNAL
+};
+
+
+static void gimp_text_finalize (GObject *object);
+static void gimp_text_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_text_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_text_dispatch_properties_changed (GObject *object,
+ guint n_pspecs,
+ GParamSpec **pspecs);
+static gint64 gimp_text_get_memsize (GimpObject *object,
+ gint64 *gui_size);
+
+
+G_DEFINE_TYPE_WITH_CODE (GimpText, gimp_text, GIMP_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG, NULL))
+
+#define parent_class gimp_text_parent_class
+
+static guint text_signals[LAST_SIGNAL] = { 0 };
+
+
+static void
+gimp_text_class_init (GimpTextClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass);
+ GimpRGB black;
+ GimpMatrix2 identity;
+ gchar *language;
+
+ text_signals[CHANGED] =
+ g_signal_new ("changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpTextClass, changed),
+ NULL, NULL,
+ gimp_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ object_class->finalize = gimp_text_finalize;
+ object_class->get_property = gimp_text_get_property;
+ object_class->set_property = gimp_text_set_property;
+ object_class->dispatch_properties_changed = gimp_text_dispatch_properties_changed;
+
+ gimp_object_class->get_memsize = gimp_text_get_memsize;
+
+ gimp_rgba_set (&black, 0.0, 0.0, 0.0, GIMP_OPACITY_OPAQUE);
+ gimp_matrix2_identity (&identity);
+
+ GIMP_CONFIG_PROP_STRING (object_class, PROP_TEXT,
+ "text",
+ NULL, NULL,
+ NULL,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_STRING (object_class, PROP_MARKUP,
+ "markup",
+ NULL, NULL,
+ NULL,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_STRING (object_class, PROP_FONT,
+ "font",
+ NULL, NULL,
+ "Sans-serif",
+ GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_FONT_SIZE,
+ "font-size",
+ NULL, NULL,
+ 0.0, 8192.0, 24.0,
+ GIMP_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.
+ */
+ GIMP_CONFIG_PROP_UNIT (object_class, PROP_UNIT,
+ "font-size-unit",
+ NULL, NULL,
+ TRUE, FALSE, GIMP_UNIT_PIXEL,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_ANTIALIAS,
+ "antialias",
+ NULL, NULL,
+ TRUE,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_ENUM (object_class, PROP_HINT_STYLE,
+ "hint-style",
+ NULL, NULL,
+ GIMP_TYPE_TEXT_HINT_STYLE,
+ GIMP_TEXT_HINT_STYLE_MEDIUM,
+ GIMP_PARAM_STATIC_STRINGS |
+ GIMP_CONFIG_PARAM_DEFAULTS);
+
+ GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_KERNING,
+ "kerning",
+ NULL, NULL,
+ FALSE,
+ GIMP_PARAM_STATIC_STRINGS |
+ GIMP_CONFIG_PARAM_DEFAULTS);
+
+ language = gimp_get_default_language (NULL);
+
+ GIMP_CONFIG_PROP_STRING (object_class, PROP_LANGUAGE,
+ "language",
+ NULL, NULL,
+ language,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ g_free (language);
+
+ GIMP_CONFIG_PROP_ENUM (object_class, PROP_BASE_DIR,
+ "base-direction",
+ NULL, NULL,
+ GIMP_TYPE_TEXT_DIRECTION,
+ GIMP_TEXT_DIRECTION_LTR,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_RGB (object_class, PROP_COLOR,
+ "color",
+ NULL, NULL,
+ FALSE, &black,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_ENUM (object_class, PROP_OUTLINE,
+ "outline",
+ NULL, NULL,
+ GIMP_TYPE_TEXT_OUTLINE,
+ GIMP_TEXT_OUTLINE_NONE,
+ GIMP_PARAM_STATIC_STRINGS |
+ GIMP_CONFIG_PARAM_DEFAULTS);
+
+ GIMP_CONFIG_PROP_ENUM (object_class, PROP_JUSTIFICATION,
+ "justify",
+ NULL, NULL,
+ GIMP_TYPE_TEXT_JUSTIFICATION,
+ GIMP_TEXT_JUSTIFY_LEFT,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_INDENTATION,
+ "indent",
+ NULL, NULL,
+ -8192.0, 8192.0, 0.0,
+ GIMP_PARAM_STATIC_STRINGS |
+ GIMP_CONFIG_PARAM_DEFAULTS);
+
+ GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_LINE_SPACING,
+ "line-spacing",
+ NULL, NULL,
+ -8192.0, 8192.0, 0.0,
+ GIMP_PARAM_STATIC_STRINGS |
+ GIMP_CONFIG_PARAM_DEFAULTS);
+
+ GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_LETTER_SPACING,
+ "letter-spacing",
+ NULL, NULL,
+ -8192.0, 8192.0, 0.0,
+ GIMP_PARAM_STATIC_STRINGS |
+ GIMP_CONFIG_PARAM_DEFAULTS);
+
+ GIMP_CONFIG_PROP_ENUM (object_class, PROP_BOX_MODE,
+ "box-mode",
+ NULL, NULL,
+ GIMP_TYPE_TEXT_BOX_MODE,
+ GIMP_TEXT_BOX_DYNAMIC,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_BOX_WIDTH,
+ "box-width",
+ NULL, NULL,
+ 0.0, GIMP_MAX_IMAGE_SIZE, 0.0,
+ GIMP_PARAM_STATIC_STRINGS |
+ GIMP_CONFIG_PARAM_DEFAULTS);
+
+ GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_BOX_HEIGHT,
+ "box-height",
+ NULL, NULL,
+ 0.0, GIMP_MAX_IMAGE_SIZE, 0.0,
+ GIMP_PARAM_STATIC_STRINGS |
+ GIMP_CONFIG_PARAM_DEFAULTS);
+
+ GIMP_CONFIG_PROP_UNIT (object_class, PROP_BOX_UNIT,
+ "box-unit",
+ NULL, NULL,
+ TRUE, FALSE, GIMP_UNIT_PIXEL,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_MATRIX2 (object_class, PROP_TRANSFORMATION,
+ "transformation",
+ NULL, NULL,
+ &identity,
+ GIMP_PARAM_STATIC_STRINGS |
+ GIMP_CONFIG_PARAM_DEFAULTS);
+
+ GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_OFFSET_X,
+ "offset-x",
+ NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ GIMP_PARAM_STATIC_STRINGS |
+ GIMP_CONFIG_PARAM_DEFAULTS);
+
+ GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_OFFSET_Y,
+ "offset-y",
+ NULL, NULL,
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ GIMP_PARAM_STATIC_STRINGS |
+ GIMP_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, GIMP_MAX_IMAGE_SIZE, 0,
+ G_PARAM_CONSTRUCT |
+ GIMP_PARAM_WRITABLE));
+
+ /* the old hinting options have been replaced by 'hint-style' */
+ GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_HINTING,
+ "hinting",
+ NULL, NULL,
+ TRUE,
+ GIMP_PARAM_STATIC_STRINGS);
+}
+
+static void
+gimp_text_init (GimpText *text)
+{
+}
+
+static void
+gimp_text_finalize (GObject *object)
+{
+ GimpText *text = GIMP_TEXT (object);
+
+ g_clear_pointer (&text->text, g_free);
+ g_clear_pointer (&text->markup, g_free);
+ g_clear_pointer (&text->font, g_free);
+ g_clear_pointer (&text->language, g_free);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gimp_text_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpText *text = GIMP_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_string (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_HINTING:
+ g_value_set_boolean (value,
+ text->hint_style != GIMP_TEXT_HINT_STYLE_NONE);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_text_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpText *text = GIMP_TEXT (object);
+ GimpRGB *color;
+ GimpMatrix2 *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:
+ {
+ const gchar *font = g_value_get_string (value);
+
+ g_free (text->font);
+
+ if (font)
+ {
+ gsize len = strlen (font);
+
+ if (g_str_has_suffix (font, " Not-Rotated"))
+ len -= strlen ( " Not-Rotated");
+
+ text->font = g_strndup (font, len);
+ }
+ else
+ {
+ text->font = NULL;
+ }
+ }
+ 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_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 GIMP versions
+ */
+ if (text->hint_style == GIMP_TEXT_HINT_STYLE_MEDIUM)
+ text->hint_style = (g_value_get_boolean (value) ?
+ GIMP_TEXT_HINT_STYLE_MEDIUM :
+ GIMP_TEXT_HINT_STYLE_NONE);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_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
+gimp_text_get_memsize (GimpObject *object,
+ gint64 *gui_size)
+{
+ GimpText *text = GIMP_TEXT (object);
+ gint64 memsize = 0;
+
+ memsize += gimp_string_get_memsize (text->text);
+ memsize += gimp_string_get_memsize (text->markup);
+ memsize += gimp_string_get_memsize (text->font);
+ memsize += gimp_string_get_memsize (text->language);
+
+ return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
+ gui_size);
+}
+
+void
+gimp_text_get_transformation (GimpText *text,
+ GimpMatrix3 *matrix)
+{
+ g_return_if_fail (GIMP_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;
+}
diff --git a/app/text/gimptext.h b/app/text/gimptext.h
new file mode 100644
index 0000000..8a292be
--- /dev/null
+++ b/app/text/gimptext.h
@@ -0,0 +1,83 @@
+/* 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/>.
+ */
+
+#ifndef __GIMP_TEXT_H__
+#define __GIMP_TEXT_H__
+
+
+#include "core/gimpobject.h"
+
+
+#define GIMP_TYPE_TEXT (gimp_text_get_type ())
+#define GIMP_TEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TEXT, GimpText))
+#define GIMP_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TEXT, GimpTextClass))
+#define GIMP_IS_TEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TEXT))
+#define GIMP_IS_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_TEXT))
+#define GIMP_TEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_TEXT, GimpTextClass))
+
+
+typedef struct _GimpTextClass GimpTextClass;
+
+struct _GimpText
+{
+ GimpObject parent_instance;
+
+ gchar *text;
+ gchar *markup;
+ gchar *font;
+ GimpUnit unit;
+ gdouble font_size;
+ gboolean antialias;
+ GimpTextHintStyle hint_style;
+ gboolean kerning;
+ gchar *language;
+ GimpTextDirection base_dir;
+ GimpRGB color;
+ GimpTextOutline outline;
+ GimpTextJustification justify;
+ gdouble indent;
+ gdouble line_spacing;
+ gdouble letter_spacing;
+ GimpTextBoxMode box_mode;
+ gdouble box_width;
+ gdouble box_height;
+ GimpUnit box_unit;
+ GimpMatrix2 transformation;
+ gdouble offset_x;
+ gdouble offset_y;
+
+ gdouble border;
+};
+
+struct _GimpTextClass
+{
+ GimpObjectClass parent_class;
+
+ void (* changed) (GimpText *text);
+};
+
+
+GType gimp_text_get_type (void) G_GNUC_CONST;
+
+void gimp_text_get_transformation (GimpText *text,
+ GimpMatrix3 *matrix);
+
+
+#endif /* __GIMP_TEXT_H__ */
diff --git a/app/text/gimptextlayer-transform.c b/app/text/gimptextlayer-transform.c
new file mode 100644
index 0000000..20e6720
--- /dev/null
+++ b/app/text/gimptextlayer-transform.c
@@ -0,0 +1,201 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GimpTextLayer
+ * 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 <gdk-pixbuf/gdk-pixbuf.h>
+#include <gegl.h>
+
+#include "libgimpmath/gimpmath.h"
+
+#include "text-types.h"
+
+#include "core/gimp-transform-utils.h"
+#include "core/gimpimage-undo.h"
+
+#include "gimptext.h"
+#include "gimptextlayer.h"
+#include "gimptextlayer-transform.h"
+
+
+static GimpItemClass * gimp_text_layer_parent_class (void) G_GNUC_CONST;
+
+static gboolean gimp_text_layer_get_transformation (GimpTextLayer *layer,
+ GimpMatrix3 *matrix);
+static gboolean gimp_text_layer_set_transformation (GimpTextLayer *layer,
+ GimpMatrix3 *matrix);
+
+
+void
+gimp_text_layer_scale (GimpItem *item,
+ gint new_width,
+ gint new_height,
+ gint new_offset_x,
+ gint new_offset_y,
+ GimpInterpolationType interpolation_type,
+ GimpProgress *progress)
+{
+ /* TODO */
+}
+
+static gboolean
+gimp_text_layer_transform_flip (GimpTextLayer *layer,
+ GimpOrientationType flip_type,
+ gdouble axis)
+{
+ GimpMatrix3 matrix;
+
+ if (! gimp_text_layer_get_transformation (layer, &matrix))
+ return FALSE;
+
+ gimp_transform_matrix_flip (&matrix, flip_type, axis);
+
+ return gimp_text_layer_set_transformation (layer, &matrix);
+}
+
+void
+gimp_text_layer_flip (GimpItem *item,
+ GimpContext *context,
+ GimpOrientationType flip_type,
+ gdouble axis,
+ gboolean clip_result)
+{
+ GimpTextLayer *layer = GIMP_TEXT_LAYER (item);
+
+ if (gimp_text_layer_transform_flip (layer, flip_type, axis))
+ {
+ GimpLayerMask *mask = gimp_layer_get_mask (GIMP_LAYER (layer));
+
+ if (mask)
+ gimp_item_flip (GIMP_ITEM (mask), context,
+ flip_type, axis, clip_result);
+ }
+ else
+ {
+ gimp_text_layer_parent_class ()->flip (item, context,
+ flip_type, axis, clip_result);
+ }
+}
+
+static gboolean
+gimp_text_layer_transform_rotate (GimpTextLayer *layer,
+ GimpRotationType rotate_type,
+ gdouble center_x,
+ gdouble center_y)
+{
+ GimpMatrix3 matrix;
+
+ if (! gimp_text_layer_get_transformation (layer, &matrix))
+ return FALSE;
+
+ gimp_transform_matrix_rotate (&matrix, rotate_type, center_x, center_y);
+
+ return gimp_text_layer_set_transformation (layer, &matrix);
+}
+
+void
+gimp_text_layer_rotate (GimpItem *item,
+ GimpContext *context,
+ GimpRotationType rotate_type,
+ gdouble center_x,
+ gdouble center_y,
+ gboolean clip_result)
+{
+ GimpTextLayer *layer = GIMP_TEXT_LAYER (item);
+
+ if (! gimp_text_layer_transform_rotate (layer,
+ rotate_type, center_x, center_y))
+ {
+ GimpLayerMask *mask = gimp_layer_get_mask (GIMP_LAYER (layer));
+
+ if (mask)
+ gimp_item_rotate (GIMP_ITEM (mask), context,
+ rotate_type, center_x, center_y, clip_result);
+ }
+ else
+ {
+ gimp_text_layer_parent_class ()->rotate (item, context,
+ rotate_type, center_x, center_y,
+ clip_result);
+ }
+}
+
+void
+gimp_text_layer_transform (GimpItem *item,
+ GimpContext *context,
+ const GimpMatrix3 *matrix,
+ GimpTransformDirection direction,
+ GimpInterpolationType interpolation_type,
+ gboolean supersample,
+ GimpTransformResize clip_result,
+ GimpProgress *progress)
+{
+ /* TODO */
+}
+
+static GimpItemClass *
+gimp_text_layer_parent_class (void)
+{
+ static GimpItemClass *parent_class = NULL;
+
+ if (! parent_class)
+ {
+ gpointer klass = g_type_class_peek (GIMP_TYPE_TEXT_LAYER);
+
+ parent_class = g_type_class_peek_parent (klass);
+ }
+
+ return parent_class;
+}
+
+static gboolean
+gimp_text_layer_get_transformation (GimpTextLayer *layer,
+ GimpMatrix3 *matrix)
+{
+ if (! layer->text || layer->modified)
+ return FALSE;
+
+ gimp_text_get_transformation (layer->text, matrix);
+
+ return TRUE;
+}
+
+static gboolean
+gimp_text_layer_set_transformation (GimpTextLayer *layer,
+ GimpMatrix3 *matrix)
+{
+ GimpMatrix2 trafo;
+
+ if (! gimp_matrix3_is_affine (matrix))
+ return FALSE;
+
+ trafo.coeff[0][0] = matrix->coeff[0][0];
+ trafo.coeff[0][1] = matrix->coeff[0][1];
+ trafo.coeff[1][0] = matrix->coeff[1][0];
+ trafo.coeff[1][1] = matrix->coeff[1][1];
+
+ gimp_text_layer_set (GIMP_TEXT_LAYER (layer), NULL,
+ "transformation", &trafo,
+ "offset-x", matrix->coeff[0][2],
+ "offset-y", matrix->coeff[1][2],
+ NULL);
+
+ return TRUE;
+}
diff --git a/app/text/gimptextlayer-transform.h b/app/text/gimptextlayer-transform.h
new file mode 100644
index 0000000..bf17a86
--- /dev/null
+++ b/app/text/gimptextlayer-transform.h
@@ -0,0 +1,53 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GimpTextLayer
+ * 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/>.
+ */
+
+#ifndef __GIMP_TEXT_LAYER_TRANSFORM_H__
+#define __GIMP_TEXT_LAYER_TRANSFORM_H__
+
+
+void gimp_text_layer_scale (GimpItem *item,
+ gint new_width,
+ gint new_height,
+ gint new_offset_x,
+ gint new_offset_y,
+ GimpInterpolationType interpolation_type,
+ GimpProgress *progress);
+void gimp_text_layer_flip (GimpItem *item,
+ GimpContext *context,
+ GimpOrientationType flip_type,
+ gdouble axis,
+ gboolean clip_result);
+void gimp_text_layer_rotate (GimpItem *item,
+ GimpContext *context,
+ GimpRotationType rotate_type,
+ gdouble center_x,
+ gdouble center_y,
+ gboolean clip_result);
+void gimp_text_layer_transform (GimpItem *item,
+ GimpContext *context,
+ const GimpMatrix3 *matrix,
+ GimpTransformDirection direction,
+ GimpInterpolationType interpolation_type,
+ gboolean supersample,
+ GimpTransformResize clip_result,
+ GimpProgress *progress);
+
+
+#endif /* __GIMP_TEXT_LAYER_TRANSFORM_H__ */
diff --git a/app/text/gimptextlayer-xcf.c b/app/text/gimptextlayer-xcf.c
new file mode 100644
index 0000000..33452a1
--- /dev/null
+++ b/app/text/gimptextlayer-xcf.c
@@ -0,0 +1,220 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GimpText
+ * Copyright (C) 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 <gdk-pixbuf/gdk-pixbuf.h>
+#include <gegl.h>
+#include <cairo.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "text-types.h"
+
+#include "core/gimp.h"
+#include "core/gimpdrawable-private.h" /* eek */
+#include "core/gimpimage.h"
+#include "core/gimpparasitelist.h"
+
+#include "gimptext.h"
+#include "gimptext-parasite.h"
+#include "gimptextlayer.h"
+#include "gimptextlayer-xcf.h"
+
+#include "gimp-intl.h"
+
+
+enum
+{
+ TEXT_LAYER_XCF_NONE = 0,
+ TEXT_LAYER_XCF_DONT_AUTO_RENAME = 1 << 0,
+ TEXT_LAYER_XCF_MODIFIED = 1 << 1
+};
+
+
+static GimpLayer * gimp_text_layer_from_layer (GimpLayer *layer,
+ GimpText *text);
+
+
+gboolean
+gimp_text_layer_xcf_load_hack (GimpLayer **layer)
+{
+ const gchar *name;
+ GimpText *text = NULL;
+ const GimpParasite *parasite;
+
+ g_return_val_if_fail (layer != NULL, FALSE);
+ g_return_val_if_fail (GIMP_IS_LAYER (*layer), FALSE);
+
+ name = gimp_text_parasite_name ();
+ parasite = gimp_item_parasite_find (GIMP_ITEM (*layer), name);
+
+ if (parasite)
+ {
+ GError *error = NULL;
+
+ text = gimp_text_from_parasite (parasite, &error);
+
+ if (error)
+ {
+ gimp_message (gimp_item_get_image (GIMP_ITEM (*layer))->gimp, NULL,
+ GIMP_MESSAGE_ERROR,
+ _("Problems parsing the text parasite for layer '%s':\n"
+ "%s\n\n"
+ "Some text properties may be wrong. "
+ "Unless you want to edit the text layer, "
+ "you don't need to worry about this."),
+ gimp_object_get_name (*layer),
+ error->message);
+ g_clear_error (&error);
+ }
+ }
+ else
+ {
+ name = gimp_text_gdyntext_parasite_name ();
+
+ parasite = gimp_item_parasite_find (GIMP_ITEM (*layer), name);
+
+ if (parasite)
+ text = gimp_text_from_gdyntext_parasite (parasite);
+ }
+
+ if (text)
+ {
+ *layer = gimp_text_layer_from_layer (*layer, text);
+
+ /* let the text layer knows what parasite was used to create it */
+ GIMP_TEXT_LAYER (*layer)->text_parasite = name;
+ }
+
+ return (text != NULL);
+}
+
+void
+gimp_text_layer_xcf_save_prepare (GimpTextLayer *layer)
+{
+ GimpText *text;
+
+ g_return_if_fail (GIMP_IS_TEXT_LAYER (layer));
+
+ /* If the layer has a text parasite already, it wasn't changed and we
+ * can simply save the original parasite back which is still attached.
+ */
+ if (layer->text_parasite)
+ return;
+
+ text = gimp_text_layer_get_text (layer);
+ if (text)
+ {
+ GimpParasite *parasite = gimp_text_to_parasite (text);
+
+ /* Don't push an undo because the parasite only exists temporarily
+ * while the text layer is saved to XCF.
+ */
+ gimp_item_parasite_attach (GIMP_ITEM (layer), parasite, FALSE);
+
+ gimp_parasite_free (parasite);
+ }
+}
+
+guint32
+gimp_text_layer_get_xcf_flags (GimpTextLayer *text_layer)
+{
+ guint flags = 0;
+
+ g_return_val_if_fail (GIMP_IS_TEXT_LAYER (text_layer), 0);
+
+ if (! text_layer->auto_rename)
+ flags |= TEXT_LAYER_XCF_DONT_AUTO_RENAME;
+
+ if (text_layer->modified)
+ flags |= TEXT_LAYER_XCF_MODIFIED;
+
+ return flags;
+}
+
+void
+gimp_text_layer_set_xcf_flags (GimpTextLayer *text_layer,
+ guint32 flags)
+{
+ g_return_if_fail (GIMP_IS_TEXT_LAYER (text_layer));
+
+ g_object_set (text_layer,
+ "auto-rename", (flags & TEXT_LAYER_XCF_DONT_AUTO_RENAME) == 0,
+ "modified", (flags & TEXT_LAYER_XCF_MODIFIED) != 0,
+ NULL);
+}
+
+
+/**
+ * gimp_text_layer_from_layer:
+ * @layer: a #GimpLayer object
+ * @text: a #GimpText object
+ *
+ * Converts a standard #GimpLayer and a #GimpText object into a
+ * #GimpTextLayer. The new text layer takes ownership of the @text and
+ * @layer objects. The @layer object is rendered unusable by this
+ * function. Don't even try to use if afterwards!
+ *
+ * This is a gross hack that is needed in order to load text layers
+ * from XCF files in a backwards-compatible way. Please don't use it
+ * for anything else!
+ *
+ * Return value: a newly allocated #GimpTextLayer object
+ **/
+static GimpLayer *
+gimp_text_layer_from_layer (GimpLayer *layer,
+ GimpText *text)
+{
+ GimpTextLayer *text_layer;
+ GimpDrawable *drawable;
+
+ g_return_val_if_fail (GIMP_IS_LAYER (layer), NULL);
+ g_return_val_if_fail (GIMP_IS_TEXT (text), NULL);
+
+ text_layer = g_object_new (GIMP_TYPE_TEXT_LAYER,
+ "image", gimp_item_get_image (GIMP_ITEM (layer)),
+ NULL);
+
+ gimp_item_replace_item (GIMP_ITEM (text_layer), GIMP_ITEM (layer));
+
+ drawable = GIMP_DRAWABLE (text_layer);
+
+ gimp_drawable_steal_buffer (drawable, GIMP_DRAWABLE (layer));
+
+ gimp_layer_set_opacity (GIMP_LAYER (text_layer),
+ gimp_layer_get_opacity (layer), FALSE);
+ gimp_layer_set_mode (GIMP_LAYER (text_layer),
+ gimp_layer_get_mode (layer), FALSE);
+ gimp_layer_set_blend_space (GIMP_LAYER (text_layer),
+ gimp_layer_get_blend_space (layer), FALSE);
+ gimp_layer_set_composite_space (GIMP_LAYER (text_layer),
+ gimp_layer_get_composite_space (layer), FALSE);
+ gimp_layer_set_composite_mode (GIMP_LAYER (text_layer),
+ gimp_layer_get_composite_mode (layer), FALSE);
+ gimp_layer_set_lock_alpha (GIMP_LAYER (text_layer),
+ gimp_layer_get_lock_alpha (layer), FALSE);
+
+ gimp_text_layer_set_text (text_layer, text);
+
+ g_object_unref (text);
+ g_object_unref (layer);
+
+ return GIMP_LAYER (text_layer);
+}
diff --git a/app/text/gimptextlayer-xcf.h b/app/text/gimptextlayer-xcf.h
new file mode 100644
index 0000000..3d08dfa
--- /dev/null
+++ b/app/text/gimptextlayer-xcf.h
@@ -0,0 +1,34 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GimpText
+ * Copyright (C) 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/>.
+ */
+
+#ifndef __GIMP_TEXT_LAYER_XCF_H__
+#define __GIMP_TEXT_LAYER_XCF_H__
+
+
+gboolean gimp_text_layer_xcf_load_hack (GimpLayer **layer);
+
+void gimp_text_layer_xcf_save_prepare (GimpTextLayer *text_layer);
+
+guint32 gimp_text_layer_get_xcf_flags (GimpTextLayer *text_layer);
+void gimp_text_layer_set_xcf_flags (GimpTextLayer *text_layer,
+ guint32 flags);
+
+
+#endif /* __GIMP_TEXT_LAYER_XCF_H__ */
diff --git a/app/text/gimptextlayer.c b/app/text/gimptextlayer.c
new file mode 100644
index 0000000..fb4146a
--- /dev/null
+++ b/app/text/gimptextlayer.c
@@ -0,0 +1,861 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GimpTextLayer
+ * Copyright (C) 2002-2004 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/pangocairo.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpconfig/gimpconfig.h"
+
+#include "text-types.h"
+
+#include "gegl/gimp-babl.h"
+#include "gegl/gimp-gegl-loops.h"
+#include "gegl/gimp-gegl-utils.h"
+
+#include "core/gimp.h"
+#include "core/gimp-utils.h"
+#include "core/gimpcontext.h"
+#include "core/gimpcontainer.h"
+#include "core/gimpdatafactory.h"
+#include "core/gimpimage.h"
+#include "core/gimpimage-color-profile.h"
+#include "core/gimpimage-undo.h"
+#include "core/gimpimage-undo-push.h"
+#include "core/gimpitemtree.h"
+#include "core/gimpparasitelist.h"
+
+#include "gimptext.h"
+#include "gimptextlayer.h"
+#include "gimptextlayer-transform.h"
+#include "gimptextlayout.h"
+#include "gimptextlayout-render.h"
+
+#include "gimp-intl.h"
+
+
+enum
+{
+ PROP_0,
+ PROP_TEXT,
+ PROP_AUTO_RENAME,
+ PROP_MODIFIED
+};
+
+struct _GimpTextLayerPrivate
+{
+ GimpTextDirection base_dir;
+};
+
+static void gimp_text_layer_finalize (GObject *object);
+static void gimp_text_layer_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_text_layer_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static gint64 gimp_text_layer_get_memsize (GimpObject *object,
+ gint64 *gui_size);
+
+static GimpItem * gimp_text_layer_duplicate (GimpItem *item,
+ GType new_type);
+static gboolean gimp_text_layer_rename (GimpItem *item,
+ const gchar *new_name,
+ const gchar *undo_desc,
+ GError **error);
+
+static void gimp_text_layer_set_buffer (GimpDrawable *drawable,
+ gboolean push_undo,
+ const gchar *undo_desc,
+ GeglBuffer *buffer,
+ const GeglRectangle *bounds);
+static void gimp_text_layer_push_undo (GimpDrawable *drawable,
+ const gchar *undo_desc,
+ GeglBuffer *buffer,
+ gint x,
+ gint y,
+ gint width,
+ gint height);
+
+static void gimp_text_layer_convert_type (GimpLayer *layer,
+ GimpImage *dest_image,
+ const Babl *new_format,
+ GimpColorProfile *dest_profile,
+ GeglDitherMethod layer_dither_type,
+ GeglDitherMethod mask_dither_type,
+ gboolean push_undo,
+ GimpProgress *progress);
+
+static void gimp_text_layer_text_changed (GimpTextLayer *layer);
+static gboolean gimp_text_layer_render (GimpTextLayer *layer);
+static void gimp_text_layer_render_layout (GimpTextLayer *layer,
+ GimpTextLayout *layout);
+
+
+G_DEFINE_TYPE_WITH_PRIVATE (GimpTextLayer, gimp_text_layer, GIMP_TYPE_LAYER)
+
+#define parent_class gimp_text_layer_parent_class
+
+
+static void
+gimp_text_layer_class_init (GimpTextLayerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass);
+ GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass);
+ GimpItemClass *item_class = GIMP_ITEM_CLASS (klass);
+ GimpDrawableClass *drawable_class = GIMP_DRAWABLE_CLASS (klass);
+ GimpLayerClass *layer_class = GIMP_LAYER_CLASS (klass);
+
+ object_class->finalize = gimp_text_layer_finalize;
+ object_class->get_property = gimp_text_layer_get_property;
+ object_class->set_property = gimp_text_layer_set_property;
+
+ gimp_object_class->get_memsize = gimp_text_layer_get_memsize;
+
+ viewable_class->default_icon_name = "gimp-text-layer";
+
+ item_class->duplicate = gimp_text_layer_duplicate;
+ item_class->rename = gimp_text_layer_rename;
+
+#if 0
+ item_class->scale = gimp_text_layer_scale;
+ item_class->flip = gimp_text_layer_flip;
+ item_class->rotate = gimp_text_layer_rotate;
+ item_class->transform = gimp_text_layer_transform;
+#endif
+
+ item_class->default_name = _("Text Layer");
+ item_class->rename_desc = _("Rename Text Layer");
+ item_class->translate_desc = _("Move Text Layer");
+ item_class->scale_desc = _("Scale Text Layer");
+ item_class->resize_desc = _("Resize Text Layer");
+ item_class->flip_desc = _("Flip Text Layer");
+ item_class->rotate_desc = _("Rotate Text Layer");
+ item_class->transform_desc = _("Transform Text Layer");
+
+ drawable_class->set_buffer = gimp_text_layer_set_buffer;
+ drawable_class->push_undo = gimp_text_layer_push_undo;
+
+ layer_class->convert_type = gimp_text_layer_convert_type;
+
+ GIMP_CONFIG_PROP_OBJECT (object_class, PROP_TEXT,
+ "text",
+ NULL, NULL,
+ GIMP_TYPE_TEXT,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_AUTO_RENAME,
+ "auto-rename",
+ NULL, NULL,
+ TRUE,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_MODIFIED,
+ "modified",
+ NULL, NULL,
+ FALSE,
+ GIMP_PARAM_STATIC_STRINGS);
+}
+
+static void
+gimp_text_layer_init (GimpTextLayer *layer)
+{
+ layer->text = NULL;
+ layer->text_parasite = NULL;
+ layer->private = gimp_text_layer_get_instance_private (layer);
+}
+
+static void
+gimp_text_layer_finalize (GObject *object)
+{
+ GimpTextLayer *layer = GIMP_TEXT_LAYER (object);
+
+ g_clear_object (&layer->text);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gimp_text_layer_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpTextLayer *text_layer = GIMP_TEXT_LAYER (object);
+
+ switch (property_id)
+ {
+ case PROP_TEXT:
+ g_value_set_object (value, text_layer->text);
+ break;
+ case PROP_AUTO_RENAME:
+ g_value_set_boolean (value, text_layer->auto_rename);
+ break;
+ case PROP_MODIFIED:
+ g_value_set_boolean (value, text_layer->modified);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_text_layer_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpTextLayer *text_layer = GIMP_TEXT_LAYER (object);
+
+ switch (property_id)
+ {
+ case PROP_TEXT:
+ gimp_text_layer_set_text (text_layer, g_value_get_object (value));
+ break;
+ case PROP_AUTO_RENAME:
+ text_layer->auto_rename = g_value_get_boolean (value);
+ break;
+ case PROP_MODIFIED:
+ text_layer->modified = g_value_get_boolean (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static gint64
+gimp_text_layer_get_memsize (GimpObject *object,
+ gint64 *gui_size)
+{
+ GimpTextLayer *text_layer = GIMP_TEXT_LAYER (object);
+ gint64 memsize = 0;
+
+ memsize += gimp_object_get_memsize (GIMP_OBJECT (text_layer->text),
+ gui_size);
+
+ return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
+ gui_size);
+}
+
+static GimpItem *
+gimp_text_layer_duplicate (GimpItem *item,
+ GType new_type)
+{
+ GimpItem *new_item;
+
+ g_return_val_if_fail (g_type_is_a (new_type, GIMP_TYPE_DRAWABLE), NULL);
+
+ new_item = GIMP_ITEM_CLASS (parent_class)->duplicate (item, new_type);
+
+ if (GIMP_IS_TEXT_LAYER (new_item))
+ {
+ GimpTextLayer *layer = GIMP_TEXT_LAYER (item);
+ GimpTextLayer *new_layer = GIMP_TEXT_LAYER (new_item);
+
+ gimp_config_sync (G_OBJECT (layer), G_OBJECT (new_layer), 0);
+
+ if (layer->text)
+ {
+ GimpText *text = gimp_config_duplicate (GIMP_CONFIG (layer->text));
+
+ gimp_text_layer_set_text (new_layer, text);
+
+ g_object_unref (text);
+ }
+
+ /* this is just the parasite name, not a pointer to the parasite */
+ if (layer->text_parasite)
+ new_layer->text_parasite = layer->text_parasite;
+
+ new_layer->private->base_dir = layer->private->base_dir;
+ }
+
+ return new_item;
+}
+
+static gboolean
+gimp_text_layer_rename (GimpItem *item,
+ const gchar *new_name,
+ const gchar *undo_desc,
+ GError **error)
+{
+ if (GIMP_ITEM_CLASS (parent_class)->rename (item, new_name, undo_desc, error))
+ {
+ g_object_set (item, "auto-rename", FALSE, NULL);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+gimp_text_layer_set_buffer (GimpDrawable *drawable,
+ gboolean push_undo,
+ const gchar *undo_desc,
+ GeglBuffer *buffer,
+ const GeglRectangle *bounds)
+{
+ GimpTextLayer *layer = GIMP_TEXT_LAYER (drawable);
+ GimpImage *image = gimp_item_get_image (GIMP_ITEM (layer));
+
+ if (push_undo && ! layer->modified)
+ gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_DRAWABLE_MOD,
+ undo_desc);
+
+ GIMP_DRAWABLE_CLASS (parent_class)->set_buffer (drawable,
+ push_undo, undo_desc,
+ buffer, bounds);
+
+ if (push_undo && ! layer->modified)
+ {
+ gimp_image_undo_push_text_layer_modified (image, NULL, layer);
+
+ g_object_set (drawable, "modified", TRUE, NULL);
+
+ gimp_image_undo_group_end (image);
+ }
+}
+
+static void
+gimp_text_layer_push_undo (GimpDrawable *drawable,
+ const gchar *undo_desc,
+ GeglBuffer *buffer,
+ gint x,
+ gint y,
+ gint width,
+ gint height)
+{
+ GimpTextLayer *layer = GIMP_TEXT_LAYER (drawable);
+ GimpImage *image = gimp_item_get_image (GIMP_ITEM (layer));
+
+ if (! layer->modified)
+ gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_DRAWABLE, undo_desc);
+
+ GIMP_DRAWABLE_CLASS (parent_class)->push_undo (drawable, undo_desc,
+ buffer,
+ x, y, width, height);
+
+ if (! layer->modified)
+ {
+ gimp_image_undo_push_text_layer_modified (image, NULL, layer);
+
+ g_object_set (drawable, "modified", TRUE, NULL);
+
+ gimp_image_undo_group_end (image);
+ }
+}
+
+static void
+gimp_text_layer_convert_type (GimpLayer *layer,
+ GimpImage *dest_image,
+ const Babl *new_format,
+ GimpColorProfile *dest_profile,
+ GeglDitherMethod layer_dither_type,
+ GeglDitherMethod mask_dither_type,
+ gboolean push_undo,
+ GimpProgress *progress)
+{
+ GimpTextLayer *text_layer = GIMP_TEXT_LAYER (layer);
+ GimpImage *image = gimp_item_get_image (GIMP_ITEM (text_layer));
+
+ if (! text_layer->text ||
+ text_layer->modified ||
+ layer_dither_type != GEGL_DITHER_NONE)
+ {
+ GIMP_LAYER_CLASS (parent_class)->convert_type (layer, dest_image,
+ new_format,
+ dest_profile,
+ layer_dither_type,
+ mask_dither_type,
+ push_undo,
+ progress);
+ }
+ else
+ {
+ if (push_undo)
+ gimp_image_undo_push_text_layer_convert (image, NULL, text_layer);
+
+ text_layer->convert_format = new_format;
+
+ gimp_text_layer_render (text_layer);
+
+ text_layer->convert_format = NULL;
+ }
+}
+
+
+/* public functions */
+
+/**
+ * gimp_text_layer_new:
+ * @image: the #GimpImage the layer should belong to
+ * @text: a #GimpText object
+ *
+ * Creates a new text layer.
+ *
+ * Return value: a new #GimpTextLayer or %NULL in case of a problem
+ **/
+GimpLayer *
+gimp_text_layer_new (GimpImage *image,
+ GimpText *text)
+{
+ GimpTextLayer *layer;
+
+ g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
+ g_return_val_if_fail (GIMP_IS_TEXT (text), NULL);
+
+ if (! text->text && ! text->markup)
+ return NULL;
+
+ layer =
+ GIMP_TEXT_LAYER (gimp_drawable_new (GIMP_TYPE_TEXT_LAYER,
+ image, NULL,
+ 0, 0, 1, 1,
+ gimp_image_get_layer_format (image,
+ TRUE)));
+
+ gimp_layer_set_mode (GIMP_LAYER (layer),
+ gimp_image_get_default_new_layer_mode (image),
+ FALSE);
+
+ gimp_text_layer_set_text (layer, text);
+
+ if (! gimp_text_layer_render (layer))
+ {
+ g_object_unref (layer);
+ return NULL;
+ }
+
+ return GIMP_LAYER (layer);
+}
+
+void
+gimp_text_layer_set_text (GimpTextLayer *layer,
+ GimpText *text)
+{
+ g_return_if_fail (GIMP_IS_TEXT_LAYER (layer));
+ g_return_if_fail (text == NULL || GIMP_IS_TEXT (text));
+
+ if (layer->text == text)
+ return;
+
+ if (layer->text)
+ {
+ g_signal_handlers_disconnect_by_func (layer->text,
+ G_CALLBACK (gimp_text_layer_text_changed),
+ layer);
+
+ g_clear_object (&layer->text);
+ }
+
+ if (text)
+ {
+ layer->text = g_object_ref (text);
+ layer->private->base_dir = layer->text->base_dir;
+
+ g_signal_connect_object (text, "changed",
+ G_CALLBACK (gimp_text_layer_text_changed),
+ layer, G_CONNECT_SWAPPED);
+ }
+
+ g_object_notify (G_OBJECT (layer), "text");
+ gimp_viewable_invalidate_preview (GIMP_VIEWABLE (layer));
+}
+
+GimpText *
+gimp_text_layer_get_text (GimpTextLayer *layer)
+{
+ g_return_val_if_fail (GIMP_IS_TEXT_LAYER (layer), NULL);
+
+ return layer->text;
+}
+
+void
+gimp_text_layer_set (GimpTextLayer *layer,
+ const gchar *undo_desc,
+ const gchar *first_property_name,
+ ...)
+{
+ GimpImage *image;
+ GimpText *text;
+ va_list var_args;
+
+ g_return_if_fail (gimp_item_is_text_layer (GIMP_ITEM (layer)));
+ g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (layer)));
+
+ text = gimp_text_layer_get_text (layer);
+ if (! text)
+ return;
+
+ image = gimp_item_get_image (GIMP_ITEM (layer));
+
+ gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_TEXT, undo_desc);
+
+ g_object_freeze_notify (G_OBJECT (layer));
+
+ if (layer->modified)
+ {
+ gimp_image_undo_push_text_layer_modified (image, NULL, layer);
+
+ /* pass copy_tiles = TRUE so we not only ref the tiles; after
+ * being a text layer again, undo doesn't care about the
+ * layer's pixels any longer because they are generated, so
+ * changing the text would happily overwrite the layer's
+ * pixels, changing the pixels on the undo stack too without
+ * any chance to ever undo again.
+ */
+ gimp_image_undo_push_drawable_mod (image, NULL,
+ GIMP_DRAWABLE (layer), TRUE);
+ }
+
+ gimp_image_undo_push_text_layer (image, undo_desc, layer, NULL);
+
+ va_start (var_args, first_property_name);
+
+ g_object_set_valist (G_OBJECT (text), first_property_name, var_args);
+
+ va_end (var_args);
+
+ g_object_set (layer, "modified", FALSE, NULL);
+
+ g_object_thaw_notify (G_OBJECT (layer));
+
+ gimp_image_undo_group_end (image);
+}
+
+/**
+ * gimp_text_layer_discard:
+ * @layer: a #GimpTextLayer
+ *
+ * Discards the text information. This makes @layer behave like a
+ * normal layer.
+ */
+void
+gimp_text_layer_discard (GimpTextLayer *layer)
+{
+ g_return_if_fail (GIMP_IS_TEXT_LAYER (layer));
+ g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (layer)));
+
+ if (! layer->text)
+ return;
+
+ gimp_image_undo_push_text_layer (gimp_item_get_image (GIMP_ITEM (layer)),
+ _("Discard Text Information"),
+ layer, NULL);
+
+ gimp_text_layer_set_text (layer, NULL);
+}
+
+gboolean
+gimp_item_is_text_layer (GimpItem *item)
+{
+ return (GIMP_IS_TEXT_LAYER (item) &&
+ GIMP_TEXT_LAYER (item)->text &&
+ GIMP_TEXT_LAYER (item)->modified == FALSE);
+}
+
+
+/* private functions */
+
+static const Babl *
+gimp_text_layer_get_format (GimpTextLayer *layer)
+{
+ if (layer->convert_format)
+ return layer->convert_format;
+
+ return gimp_drawable_get_format (GIMP_DRAWABLE (layer));
+}
+
+static void
+gimp_text_layer_text_changed (GimpTextLayer *layer)
+{
+ /* If the text layer was created from a parasite, it's time to
+ * remove that parasite now.
+ */
+ if (layer->text_parasite)
+ {
+ /* Don't push an undo because the parasite only exists temporarily
+ * while the text layer is loaded from XCF.
+ */
+ gimp_item_parasite_detach (GIMP_ITEM (layer), layer->text_parasite,
+ FALSE);
+ layer->text_parasite = NULL;
+ }
+
+ if (layer->text->box_mode == GIMP_TEXT_BOX_DYNAMIC)
+ {
+ gint old_width;
+ gint new_width;
+ GimpItem *item = GIMP_ITEM (layer);
+ GimpTextDirection old_base_dir = layer->private->base_dir;
+ GimpTextDirection new_base_dir = layer->text->base_dir;
+
+ old_width = gimp_item_get_width (item);
+ gimp_text_layer_render (layer);
+ new_width = gimp_item_get_width (item);
+
+ if (old_base_dir != new_base_dir)
+ {
+ switch (old_base_dir)
+ {
+ case GIMP_TEXT_DIRECTION_LTR:
+ case GIMP_TEXT_DIRECTION_RTL:
+ case GIMP_TEXT_DIRECTION_TTB_LTR:
+ case GIMP_TEXT_DIRECTION_TTB_LTR_UPRIGHT:
+ switch (new_base_dir)
+ {
+ case GIMP_TEXT_DIRECTION_TTB_RTL:
+ case GIMP_TEXT_DIRECTION_TTB_RTL_UPRIGHT:
+ gimp_item_translate (item, -new_width, 0, FALSE);
+ break;
+
+ case GIMP_TEXT_DIRECTION_LTR:
+ case GIMP_TEXT_DIRECTION_RTL:
+ case GIMP_TEXT_DIRECTION_TTB_LTR:
+ case GIMP_TEXT_DIRECTION_TTB_LTR_UPRIGHT:
+ break;
+ }
+ break;
+
+ case GIMP_TEXT_DIRECTION_TTB_RTL:
+ case GIMP_TEXT_DIRECTION_TTB_RTL_UPRIGHT:
+ switch (new_base_dir)
+ {
+ case GIMP_TEXT_DIRECTION_LTR:
+ case GIMP_TEXT_DIRECTION_RTL:
+ case GIMP_TEXT_DIRECTION_TTB_LTR:
+ case GIMP_TEXT_DIRECTION_TTB_LTR_UPRIGHT:
+ gimp_item_translate (item, old_width, 0, FALSE);
+ break;
+
+ case GIMP_TEXT_DIRECTION_TTB_RTL:
+ case GIMP_TEXT_DIRECTION_TTB_RTL_UPRIGHT:
+ break;
+ }
+ break;
+ }
+ }
+ else if ((new_base_dir == GIMP_TEXT_DIRECTION_TTB_RTL ||
+ new_base_dir == GIMP_TEXT_DIRECTION_TTB_RTL_UPRIGHT))
+ {
+ if (old_width != new_width)
+ gimp_item_translate (item, old_width - new_width, 0, FALSE);
+ }
+ }
+ else
+ gimp_text_layer_render (layer);
+
+ layer->private->base_dir = layer->text->base_dir;
+}
+
+static gboolean
+gimp_text_layer_render (GimpTextLayer *layer)
+{
+ GimpDrawable *drawable;
+ GimpItem *item;
+ GimpImage *image;
+ GimpContainer *container;
+ GimpTextLayout *layout;
+ gdouble xres;
+ gdouble yres;
+ gint width;
+ gint height;
+ GError *error = NULL;
+
+ if (! layer->text)
+ return FALSE;
+
+ drawable = GIMP_DRAWABLE (layer);
+ item = GIMP_ITEM (layer);
+ image = gimp_item_get_image (item);
+ container = gimp_data_factory_get_container (image->gimp->font_factory);
+
+ gimp_data_factory_data_wait (image->gimp->font_factory);
+
+ if (gimp_container_is_empty (container))
+ {
+ gimp_message_literal (image->gimp, NULL, GIMP_MESSAGE_ERROR,
+ _("Due to lack of any fonts, "
+ "text functionality is not available."));
+ return FALSE;
+ }
+
+ gimp_image_get_resolution (image, &xres, &yres);
+
+ layout = gimp_text_layout_new (layer->text, xres, yres, &error);
+ if (error)
+ {
+ gimp_message_literal (image->gimp, NULL, GIMP_MESSAGE_ERROR, error->message);
+ g_error_free (error);
+ }
+
+ g_object_freeze_notify (G_OBJECT (drawable));
+
+ if (gimp_text_layout_get_size (layout, &width, &height) &&
+ (width != gimp_item_get_width (item) ||
+ height != gimp_item_get_height (item) ||
+ gimp_text_layer_get_format (layer) !=
+ gimp_drawable_get_format (drawable)))
+ {
+ GeglBuffer *new_buffer;
+
+ new_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, width, height),
+ gimp_text_layer_get_format (layer));
+ gimp_drawable_set_buffer (drawable, FALSE, NULL, new_buffer);
+ g_object_unref (new_buffer);
+
+ if (gimp_layer_get_mask (GIMP_LAYER (layer)))
+ {
+ GimpLayerMask *mask = gimp_layer_get_mask (GIMP_LAYER (layer));
+
+ static GimpContext *unused_eek = NULL;
+
+ if (! unused_eek)
+ unused_eek = gimp_context_new (image->gimp, "eek", NULL);
+
+ gimp_item_resize (GIMP_ITEM (mask),
+ unused_eek, GIMP_FILL_TRANSPARENT,
+ width, height, 0, 0);
+ }
+ }
+
+ if (layer->auto_rename)
+ {
+ GimpItem *item = GIMP_ITEM (layer);
+ gchar *name = NULL;
+
+ if (layer->text->text)
+ {
+ name = gimp_utf8_strtrim (layer->text->text, 30);
+ }
+ else if (layer->text->markup)
+ {
+ gchar *tmp = gimp_markup_extract_text (layer->text->markup);
+ name = gimp_utf8_strtrim (tmp, 30);
+ g_free (tmp);
+ }
+
+ if (! name || ! name[0])
+ {
+ g_free (name);
+ name = g_strdup (_("Empty Text Layer"));
+ }
+
+ if (gimp_item_is_attached (item))
+ {
+ gimp_item_tree_rename_item (gimp_item_get_tree (item), item,
+ name, FALSE, NULL);
+ g_free (name);
+ }
+ else
+ {
+ gimp_object_take_name (GIMP_OBJECT (layer), name);
+ }
+ }
+
+ if (width > 0 && height > 0)
+ gimp_text_layer_render_layout (layer, layout);
+
+ g_object_unref (layout);
+
+ g_object_thaw_notify (G_OBJECT (drawable));
+
+ return (width > 0 && height > 0);
+}
+
+static void
+gimp_text_layer_render_layout (GimpTextLayer *layer,
+ GimpTextLayout *layout)
+{
+ GimpDrawable *drawable = GIMP_DRAWABLE (layer);
+ GimpItem *item = GIMP_ITEM (layer);
+ GimpImage *image = gimp_item_get_image (item);
+ GeglBuffer *buffer;
+ GimpColorTransform *transform;
+ cairo_t *cr;
+ cairo_surface_t *surface;
+ gint width;
+ gint height;
+ cairo_status_t status;
+
+ g_return_if_fail (gimp_drawable_has_alpha (drawable));
+
+ width = gimp_item_get_width (item);
+ height = gimp_item_get_height (item);
+
+ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+ status = cairo_surface_status (surface);
+
+ if (status != CAIRO_STATUS_SUCCESS)
+ {
+ GimpImage *image = gimp_item_get_image (item);
+
+ gimp_message_literal (image->gimp, NULL, GIMP_MESSAGE_ERROR,
+ _("Your text cannot be rendered. It is likely too big. "
+ "Please make it shorter or use a smaller font."));
+ cairo_surface_destroy (surface);
+ return;
+ }
+
+ cr = cairo_create (surface);
+ gimp_text_layout_render (layout, cr, layer->text->base_dir, FALSE);
+ cairo_destroy (cr);
+
+ cairo_surface_flush (surface);
+
+ buffer = gimp_cairo_surface_create_buffer (surface);
+
+ transform = gimp_image_get_color_transform_from_srgb_u8 (image);
+
+ if (transform)
+ {
+ gimp_color_transform_process_buffer (transform,
+ buffer,
+ NULL,
+ gimp_drawable_get_buffer (drawable),
+ NULL);
+ }
+ else
+ {
+ gimp_gegl_buffer_copy (buffer, NULL, GEGL_ABYSS_NONE,
+ gimp_drawable_get_buffer (drawable), NULL);
+ }
+
+ g_object_unref (buffer);
+ cairo_surface_destroy (surface);
+
+ gimp_drawable_update (drawable, 0, 0, width, height);
+}
diff --git a/app/text/gimptextlayer.h b/app/text/gimptextlayer.h
new file mode 100644
index 0000000..87c61a7
--- /dev/null
+++ b/app/text/gimptextlayer.h
@@ -0,0 +1,78 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GimpTextLayer
+ * 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/>.
+ */
+
+#ifndef __GIMP_TEXT_LAYER_H__
+#define __GIMP_TEXT_LAYER_H__
+
+
+#include "core/gimplayer.h"
+
+
+#define GIMP_TYPE_TEXT_LAYER (gimp_text_layer_get_type ())
+#define GIMP_TEXT_LAYER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TEXT_LAYER, GimpTextLayer))
+#define GIMP_TEXT_LAYER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TEXT_LAYER, GimpTextLayerClass))
+#define GIMP_IS_TEXT_LAYER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TEXT_LAYER))
+#define GIMP_IS_TEXT_LAYER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_TEXT_LAYER))
+#define GIMP_TEXT_LAYER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_TEXT_LAYER, GimpTextLayerClass))
+
+
+typedef struct _GimpTextLayerClass GimpTextLayerClass;
+typedef struct _GimpTextLayerPrivate GimpTextLayerPrivate;
+
+struct _GimpTextLayer
+{
+ GimpLayer layer;
+
+ GimpText *text;
+ const gchar *text_parasite; /* parasite name that this text was set from,
+ * and that should be removed when the text
+ * is changed.
+ */
+ gboolean auto_rename;
+ gboolean modified;
+
+ const Babl *convert_format;
+
+ GimpTextLayerPrivate *private;
+};
+
+struct _GimpTextLayerClass
+{
+ GimpLayerClass parent_class;
+};
+
+
+GType gimp_text_layer_get_type (void) G_GNUC_CONST;
+
+GimpLayer * gimp_text_layer_new (GimpImage *image,
+ GimpText *text);
+GimpText * gimp_text_layer_get_text (GimpTextLayer *layer);
+void gimp_text_layer_set_text (GimpTextLayer *layer,
+ GimpText *text);
+void gimp_text_layer_discard (GimpTextLayer *layer);
+void gimp_text_layer_set (GimpTextLayer *layer,
+ const gchar *undo_desc,
+ const gchar *first_property_name,
+ ...) G_GNUC_NULL_TERMINATED;
+
+gboolean gimp_item_is_text_layer (GimpItem *item);
+
+
+#endif /* __GIMP_TEXT_LAYER_H__ */
diff --git a/app/text/gimptextlayout-render.c b/app/text/gimptextlayout-render.c
new file mode 100644
index 0000000..f3605e3
--- /dev/null
+++ b/app/text/gimptextlayout-render.c
@@ -0,0 +1,77 @@
+/* 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 <pango/pangocairo.h>
+
+#include "text-types.h"
+
+#include "gimptextlayout.h"
+#include "gimptextlayout-render.h"
+
+
+void
+gimp_text_layout_render (GimpTextLayout *layout,
+ cairo_t *cr,
+ GimpTextDirection base_dir,
+ gboolean path)
+{
+ PangoLayout *pango_layout;
+ cairo_matrix_t trafo;
+ gint x, y;
+ gint width, height;
+
+ g_return_if_fail (GIMP_IS_TEXT_LAYOUT (layout));
+ g_return_if_fail (cr != NULL);
+
+ cairo_save (cr);
+
+ gimp_text_layout_get_offsets (layout, &x, &y);
+ cairo_translate (cr, x, y);
+
+ gimp_text_layout_get_transform (layout, &trafo);
+ cairo_transform (cr, &trafo);
+
+ if (base_dir == GIMP_TEXT_DIRECTION_TTB_RTL ||
+ base_dir == GIMP_TEXT_DIRECTION_TTB_RTL_UPRIGHT)
+ {
+ gimp_text_layout_get_size (layout, &width, &height);
+ cairo_translate (cr, width, 0);
+ cairo_rotate (cr, G_PI_2);
+ }
+
+ if (base_dir == GIMP_TEXT_DIRECTION_TTB_LTR ||
+ base_dir == GIMP_TEXT_DIRECTION_TTB_LTR_UPRIGHT)
+ {
+ gimp_text_layout_get_size (layout, &width, &height);
+ cairo_translate (cr, 0, height);
+ cairo_rotate (cr, -G_PI_2);
+ }
+
+ pango_layout = gimp_text_layout_get_pango_layout (layout);
+
+ if (path)
+ pango_cairo_layout_path (cr, pango_layout);
+ else
+ pango_cairo_show_layout (cr, pango_layout);
+
+ cairo_restore (cr);
+}
diff --git a/app/text/gimptextlayout-render.h b/app/text/gimptextlayout-render.h
new file mode 100644
index 0000000..456480a
--- /dev/null
+++ b/app/text/gimptextlayout-render.h
@@ -0,0 +1,31 @@
+/* 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/>.
+ */
+
+#ifndef __GIMP_TEXT_LAYOUT_RENDER_H__
+#define __GIMP_TEXT_LAYOUT_RENDER_H__
+
+
+void gimp_text_layout_render (GimpTextLayout *layout,
+ cairo_t *cr,
+ GimpTextDirection base_dir,
+ gboolean path);
+
+
+#endif /* __GIMP_TEXT_LAYOUT_RENDER_H__ */
diff --git a/app/text/gimptextlayout.c b/app/text/gimptextlayout.c
new file mode 100644
index 0000000..3d77c3d
--- /dev/null
+++ b/app/text/gimptextlayout.c
@@ -0,0 +1,805 @@
+/* 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);
+ }
+ /* 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
+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;
+}
diff --git a/app/text/gimptextlayout.h b/app/text/gimptextlayout.h
new file mode 100644
index 0000000..042751b
--- /dev/null
+++ b/app/text/gimptextlayout.h
@@ -0,0 +1,79 @@
+/* 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/>.
+ */
+
+#ifndef __GIMP_TEXT_LAYOUT_H__
+#define __GIMP_TEXT_LAYOUT_H__
+
+
+#define GIMP_TYPE_TEXT_LAYOUT (gimp_text_layout_get_type ())
+#define GIMP_TEXT_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TEXT_LAYOUT, GimpTextLayout))
+#define GIMP_IS_TEXT_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TEXT_LAYOUT))
+
+
+typedef struct _GimpTextLayoutClass GimpTextLayoutClass;
+
+struct _GimpTextLayoutClass
+{
+ GObjectClass parent_class;
+};
+
+
+GType gimp_text_layout_get_type (void) G_GNUC_CONST;
+
+GimpTextLayout * gimp_text_layout_new (GimpText *text,
+ gdouble xres,
+ gdouble yres,
+ GError **error);
+gboolean gimp_text_layout_get_size (GimpTextLayout *layout,
+ gint *width,
+ gint *height);
+void gimp_text_layout_get_offsets (GimpTextLayout *layout,
+ gint *x,
+ gint *y);
+void gimp_text_layout_get_resolution (GimpTextLayout *layout,
+ gdouble *xres,
+ gdouble *yres);
+
+GimpText * gimp_text_layout_get_text (GimpTextLayout *layout);
+PangoLayout * gimp_text_layout_get_pango_layout (GimpTextLayout *layout);
+
+void gimp_text_layout_get_transform (GimpTextLayout *layout,
+ cairo_matrix_t *matrix);
+
+void gimp_text_layout_transform_rect (GimpTextLayout *layout,
+ PangoRectangle *rect);
+void gimp_text_layout_transform_point (GimpTextLayout *layout,
+ gdouble *x,
+ gdouble *y);
+void gimp_text_layout_transform_distance (GimpTextLayout *layout,
+ gdouble *x,
+ gdouble *y);
+
+void gimp_text_layout_untransform_rect (GimpTextLayout *layout,
+ PangoRectangle *rect);
+void gimp_text_layout_untransform_point (GimpTextLayout *layout,
+ gdouble *x,
+ gdouble *y);
+void gimp_text_layout_untransform_distance (GimpTextLayout *layout,
+ gdouble *x,
+ gdouble *y);
+
+
+#endif /* __GIMP_TEXT_LAYOUT_H__ */
diff --git a/app/text/gimptextundo.c b/app/text/gimptextundo.c
new file mode 100644
index 0000000..8d1a74b
--- /dev/null
+++ b/app/text/gimptextundo.c
@@ -0,0 +1,307 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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 <gdk-pixbuf/gdk-pixbuf.h>
+#include <gegl.h>
+
+#include "libgimpconfig/gimpconfig.h"
+
+#include "text-types.h"
+
+#include "gegl/gimp-babl.h"
+
+#include "core/gimp-memsize.h"
+#include "core/gimpitem.h"
+#include "core/gimpitemundo.h"
+
+#include "gimptext.h"
+#include "gimptextlayer.h"
+#include "gimptextundo.h"
+
+
+enum
+{
+ PROP_0,
+ PROP_PARAM
+};
+
+
+static void gimp_text_undo_constructed (GObject *object);
+static void gimp_text_undo_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_text_undo_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static gint64 gimp_text_undo_get_memsize (GimpObject *object,
+ gint64 *gui_size);
+
+static void gimp_text_undo_pop (GimpUndo *undo,
+ GimpUndoMode undo_mode,
+ GimpUndoAccumulator *accum);
+static void gimp_text_undo_free (GimpUndo *undo,
+ GimpUndoMode undo_mode);
+
+
+G_DEFINE_TYPE (GimpTextUndo, gimp_text_undo, GIMP_TYPE_ITEM_UNDO)
+
+#define parent_class gimp_text_undo_parent_class
+
+
+static void
+gimp_text_undo_class_init (GimpTextUndoClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass);
+ GimpUndoClass *undo_class = GIMP_UNDO_CLASS (klass);
+
+ object_class->constructed = gimp_text_undo_constructed;
+ object_class->set_property = gimp_text_undo_set_property;
+ object_class->get_property = gimp_text_undo_get_property;
+
+ gimp_object_class->get_memsize = gimp_text_undo_get_memsize;
+
+ undo_class->pop = gimp_text_undo_pop;
+ undo_class->free = gimp_text_undo_free;
+
+ g_object_class_install_property (object_class, PROP_PARAM,
+ g_param_spec_param ("param", NULL, NULL,
+ G_TYPE_PARAM,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+gimp_text_undo_init (GimpTextUndo *undo)
+{
+}
+
+static void
+gimp_text_undo_constructed (GObject *object)
+{
+ GimpTextUndo *text_undo = GIMP_TEXT_UNDO (object);
+ GimpTextLayer *layer;
+
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+
+ gimp_assert (GIMP_IS_TEXT_LAYER (GIMP_ITEM_UNDO (text_undo)->item));
+
+ layer = GIMP_TEXT_LAYER (GIMP_ITEM_UNDO (text_undo)->item);
+
+ switch (GIMP_UNDO (object)->undo_type)
+ {
+ case GIMP_UNDO_TEXT_LAYER:
+ if (text_undo->pspec)
+ {
+ gimp_assert (text_undo->pspec->owner_type == GIMP_TYPE_TEXT);
+
+ text_undo->value = g_slice_new0 (GValue);
+
+ g_value_init (text_undo->value, text_undo->pspec->value_type);
+ g_object_get_property (G_OBJECT (layer->text),
+ text_undo->pspec->name, text_undo->value);
+ }
+ else if (layer->text)
+ {
+ text_undo->text = gimp_config_duplicate (GIMP_CONFIG (layer->text));
+ }
+ break;
+
+ case GIMP_UNDO_TEXT_LAYER_MODIFIED:
+ text_undo->modified = layer->modified;
+ break;
+
+ case GIMP_UNDO_TEXT_LAYER_CONVERT:
+ text_undo->format = gimp_drawable_get_format (GIMP_DRAWABLE (layer));
+ break;
+
+ default:
+ gimp_assert_not_reached ();
+ }
+}
+
+static void
+gimp_text_undo_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpTextUndo *text_undo = GIMP_TEXT_UNDO (object);
+
+ switch (property_id)
+ {
+ case PROP_PARAM:
+ text_undo->pspec = g_value_get_param (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_text_undo_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpTextUndo *text_undo = GIMP_TEXT_UNDO (object);
+
+ switch (property_id)
+ {
+ case PROP_PARAM:
+ g_value_set_param (value, (GParamSpec *) text_undo->pspec);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static gint64
+gimp_text_undo_get_memsize (GimpObject *object,
+ gint64 *gui_size)
+{
+ GimpTextUndo *undo = GIMP_TEXT_UNDO (object);
+ gint64 memsize = 0;
+
+ memsize += gimp_g_value_get_memsize (undo->value);
+ memsize += gimp_object_get_memsize (GIMP_OBJECT (undo->text), NULL);
+
+ return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
+ gui_size);
+}
+
+static void
+gimp_text_undo_pop (GimpUndo *undo,
+ GimpUndoMode undo_mode,
+ GimpUndoAccumulator *accum)
+{
+ GimpTextUndo *text_undo = GIMP_TEXT_UNDO (undo);
+ GimpTextLayer *layer = GIMP_TEXT_LAYER (GIMP_ITEM_UNDO (undo)->item);
+
+ GIMP_UNDO_CLASS (parent_class)->pop (undo, undo_mode, accum);
+
+ switch (undo->undo_type)
+ {
+ case GIMP_UNDO_TEXT_LAYER:
+ if (text_undo->pspec)
+ {
+ GValue *value;
+
+ g_return_if_fail (layer->text != NULL);
+
+ value = g_slice_new0 (GValue);
+ g_value_init (value, text_undo->pspec->value_type);
+
+ g_object_get_property (G_OBJECT (layer->text),
+ text_undo->pspec->name, value);
+
+ g_object_set_property (G_OBJECT (layer->text),
+ text_undo->pspec->name, text_undo->value);
+
+ g_value_unset (text_undo->value);
+ g_slice_free (GValue, text_undo->value);
+
+ text_undo->value = value;
+ }
+ else
+ {
+ GimpText *text;
+
+ text = (layer->text ?
+ gimp_config_duplicate (GIMP_CONFIG (layer->text)) : NULL);
+
+ if (layer->text && text_undo->text)
+ gimp_config_sync (G_OBJECT (text_undo->text),
+ G_OBJECT (layer->text), 0);
+ else
+ gimp_text_layer_set_text (layer, text_undo->text);
+
+ if (text_undo->text)
+ g_object_unref (text_undo->text);
+
+ text_undo->text = text;
+ }
+ break;
+
+ case GIMP_UNDO_TEXT_LAYER_MODIFIED:
+ {
+ gboolean modified;
+
+#if 0
+ g_print ("setting layer->modified from %s to %s\n",
+ layer->modified ? "TRUE" : "FALSE",
+ text_undo->modified ? "TRUE" : "FALSE");
+#endif
+
+ modified = layer->modified;
+ g_object_set (layer, "modified", text_undo->modified, NULL);
+ text_undo->modified = modified;
+
+ gimp_viewable_invalidate_preview (GIMP_VIEWABLE (layer));
+ }
+ break;
+
+ case GIMP_UNDO_TEXT_LAYER_CONVERT:
+ {
+ const Babl *format;
+
+ format = gimp_drawable_get_format (GIMP_DRAWABLE (layer));
+ gimp_drawable_convert_type (GIMP_DRAWABLE (layer),
+ gimp_item_get_image (GIMP_ITEM (layer)),
+ gimp_babl_format_get_base_type (text_undo->format),
+ gimp_babl_format_get_precision (text_undo->format),
+ babl_format_has_alpha (text_undo->format),
+ NULL,
+ GEGL_DITHER_NONE, GEGL_DITHER_NONE,
+ FALSE, NULL);
+ text_undo->format = format;
+ }
+ break;
+
+ default:
+ gimp_assert_not_reached ();
+ }
+}
+
+static void
+gimp_text_undo_free (GimpUndo *undo,
+ GimpUndoMode undo_mode)
+{
+ GimpTextUndo *text_undo = GIMP_TEXT_UNDO (undo);
+
+ g_clear_object (&text_undo->text);
+
+ if (text_undo->pspec)
+ {
+ g_value_unset (text_undo->value);
+ g_slice_free (GValue, text_undo->value);
+
+ text_undo->value = NULL;
+ text_undo->pspec = NULL;
+ }
+
+ GIMP_UNDO_CLASS (parent_class)->free (undo, undo_mode);
+}
diff --git a/app/text/gimptextundo.h b/app/text/gimptextundo.h
new file mode 100644
index 0000000..913aeb9
--- /dev/null
+++ b/app/text/gimptextundo.h
@@ -0,0 +1,55 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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/>.
+ */
+
+#ifndef __GIMP_TEXT_UNDO_H__
+#define __GIMP_TEXT_UNDO_H__
+
+
+#include "core/gimpitemundo.h"
+
+
+#define GIMP_TYPE_TEXT_UNDO (gimp_text_undo_get_type ())
+#define GIMP_TEXT_UNDO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TEXT_UNDO, GimpTextUndo))
+#define GIMP_TEXT_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TEXT_UNDO, GimpTextUndoClass))
+#define GIMP_IS_TEXT_UNDO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TEXT_UNDO))
+#define GIMP_IS_TEXT_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_TEXT_UNDO))
+#define GIMP_TEXT_UNDO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_TEXT_UNDO, GimpTextUndoClass))
+
+
+typedef struct _GimpTextUndoClass GimpTextUndoClass;
+
+struct _GimpTextUndo
+{
+ GimpItemUndo parent_instance;
+
+ GimpText *text;
+ const GParamSpec *pspec;
+ GValue *value;
+ gboolean modified;
+ const Babl *format;
+};
+
+struct _GimpTextUndoClass
+{
+ GimpItemClass parent_class;
+};
+
+
+GType gimp_text_undo_get_type (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_TEXT_UNDO_H__ */
diff --git a/app/text/text-enums.c b/app/text/text-enums.c
new file mode 100644
index 0000000..cc18ec9
--- /dev/null
+++ b/app/text/text-enums.c
@@ -0,0 +1,73 @@
+
+/* Generated data (by gimp-mkenums) */
+
+#include "config.h"
+#include <gio/gio.h>
+#include "libgimpbase/gimpbase.h"
+#include "text-enums.h"
+#include "gimp-intl.h"
+
+/* enumerations from "text-enums.h" */
+GType
+gimp_text_box_mode_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_TEXT_BOX_DYNAMIC, "GIMP_TEXT_BOX_DYNAMIC", "dynamic" },
+ { GIMP_TEXT_BOX_FIXED, "GIMP_TEXT_BOX_FIXED", "fixed" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_TEXT_BOX_DYNAMIC, NC_("text-box-mode", "Dynamic"), NULL },
+ { GIMP_TEXT_BOX_FIXED, NC_("text-box-mode", "Fixed"), NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpTextBoxMode", values);
+ gimp_type_set_translation_context (type, "text-box-mode");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_text_outline_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_TEXT_OUTLINE_NONE, "GIMP_TEXT_OUTLINE_NONE", "none" },
+ { GIMP_TEXT_OUTLINE_STROKE_ONLY, "GIMP_TEXT_OUTLINE_STROKE_ONLY", "stroke-only" },
+ { GIMP_TEXT_OUTLINE_STROKE_FILL, "GIMP_TEXT_OUTLINE_STROKE_FILL", "stroke-fill" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_TEXT_OUTLINE_NONE, "GIMP_TEXT_OUTLINE_NONE", NULL },
+ { GIMP_TEXT_OUTLINE_STROKE_ONLY, "GIMP_TEXT_OUTLINE_STROKE_ONLY", NULL },
+ { GIMP_TEXT_OUTLINE_STROKE_FILL, "GIMP_TEXT_OUTLINE_STROKE_FILL", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpTextOutline", values);
+ gimp_type_set_translation_context (type, "text-outline");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+
+/* Generated data ends here */
+
diff --git a/app/text/text-enums.h b/app/text/text-enums.h
new file mode 100644
index 0000000..20bd327
--- /dev/null
+++ b/app/text/text-enums.h
@@ -0,0 +1,45 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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/>.
+ */
+
+#ifndef __TEXT_ENUMS_H__
+#define __TEXT_ENUMS_H__
+
+
+#define GIMP_TYPE_TEXT_BOX_MODE (gimp_text_box_mode_get_type ())
+
+GType gimp_text_box_mode_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_TEXT_BOX_DYNAMIC, /*< desc="Dynamic" >*/
+ GIMP_TEXT_BOX_FIXED /*< desc="Fixed" >*/
+} GimpTextBoxMode;
+
+
+#define GIMP_TYPE_TEXT_OUTLINE (gimp_text_outline_get_type ())
+
+GType gimp_text_outline_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_TEXT_OUTLINE_NONE,
+ GIMP_TEXT_OUTLINE_STROKE_ONLY,
+ GIMP_TEXT_OUTLINE_STROKE_FILL
+} GimpTextOutline;
+
+
+#endif /* __TEXT_ENUMS_H__ */
diff --git a/app/text/text-types.h b/app/text/text-types.h
new file mode 100644
index 0000000..83b6190
--- /dev/null
+++ b/app/text/text-types.h
@@ -0,0 +1,38 @@
+/* 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/>.
+ */
+
+#ifndef __TEXT_TYPES_H__
+#define __TEXT_TYPES_H__
+
+#include "core/core-types.h"
+
+#include "text/text-enums.h"
+
+
+typedef struct _GimpFont GimpFont;
+typedef struct _GimpFontFactory GimpFontFactory;
+typedef struct _GimpFontList GimpFontList;
+typedef struct _GimpText GimpText;
+typedef struct _GimpTextLayer GimpTextLayer;
+typedef struct _GimpTextLayout GimpTextLayout;
+typedef struct _GimpTextUndo GimpTextUndo;
+
+
+#endif /* __TEXT_TYPES_H__ */