Issue #14139: wait for fonts to be loaded before loading files.

This was happening when trying to load a file before fonts were fully
loaded, which may happen when loading while starting GIMP (either from
CLI, or from a file browser, etc.), or simply just after start for
people with a lot of fonts, whose font loading may take a long time as
background task.

Note that I didn't manage to reproduce properly because from reports, it
seems like the problem appears where some fonts may be only partly
loaded so gimp_font_get_hash() fails to load the font and get a hash. I
never managed to trigger such a case and no reporters answered my call
for testing of debug builds.
As a consequence, I'll just assume that simply waiting for all fonts to
be loaded before starting to load images would work out.

Note that the crash was not happening when text layers were using the
older syntax (pre-XCF 19) of text layer data, since it was not storing
any font hash, which means that we were not trying to compare hashes. It
would also not crash if fonts were not fully loaded yet, but we didn't
have any weird intermediate state where fonts appeared in the list yet
their file were not hashable (cf. what I failed to reproduce, as
explained in previous paragraph). But both these cases were not ideal
either anyway because then we could load the XCF apparently OK… except
that the correct fonts would not be associated to the related text
layers (hence as soon as you start to edit said texts, the rendering
would break). So we should wait for fonts to be loaded, and that was a
bug even when you can't reproduce the crash.

When starting GIMP without loading an image, or simply when fonts are
loaded quickly enough, it won't make any difference. So it should not
make startup any slower for most common use cases and installations.
This commit is contained in:
Jehan 2026-01-04 21:40:36 +01:00
parent 56a17c73eb
commit bf6fcac0a9
6 changed files with 102 additions and 1 deletions

View file

@ -32,12 +32,14 @@
#include "gimp-gradients.h"
#include "gimp-memsize.h"
#include "gimp-palettes.h"
#include "gimp-utils.h"
#include "gimpcontainer.h"
#include "gimpbrush-load.h"
#include "gimpbrush.h"
#include "gimpbrushclipboard.h"
#include "gimpbrushgenerated-load.h"
#include "gimpbrushpipe-load.h"
#include "gimpcurve.h"
#include "gimpdataloaderfactory.h"
#include "gimpdynamics.h"
#include "gimpdynamics-load.h"
@ -300,6 +302,52 @@ gimp_data_factories_exit (Gimp *gimp)
g_clear_object (&gimp->tag_cache);
}
gboolean
gimp_data_factories_wait (Gimp *gimp)
{
GList *data_types;
GList *excluded;
gboolean loaded = TRUE;
/* TODO: when bumping GLib >= 2.80, use GTYPE_TO_POINTER instead. */
#define GIMPTYPE_TO_POINTER(t) ((gpointer) (guintptr) (t))
/* Curves are the only data type without a factory. */
excluded = g_list_prepend (NULL, GIMPTYPE_TO_POINTER (GIMP_TYPE_CURVE));
#undef GIMPTYPE_TO_POINTER
data_types = gimp_get_type_children (GIMP_TYPE_DATA, NULL, excluded);
g_list_free (excluded);
/* TODO: when bumping GLib >= 2.80, use GPOINTER_TO_TYPE instead. */
#define GIMPPOINTER_TO_TYPE(p) ((GType) (guintptr) (p))
for (GList *iter = data_types; iter; iter = iter->next)
{
GimpDataFactory *factory;
factory = gimp_get_data_factory (gimp, GIMPPOINTER_TO_TYPE (iter->data));
if (factory)
{
GimpAsyncSet *set;
set = gimp_data_factory_get_async_set (factory);
g_object_get (set, "empty", &loaded, NULL);
if (! loaded)
{
gimp_data_factory_data_wait (factory);
loaded = TRUE;
}
}
}
#undef GIMPPOINTER_TO_TYPE
g_list_free (data_types);
return loaded;
}
gint64
gimp_data_factories_get_memsize (Gimp *gimp,
gint64 *gui_size)

View file

@ -23,6 +23,8 @@ void gimp_data_factories_add_builtin (Gimp *gimp);
void gimp_data_factories_clear (Gimp *gimp);
void gimp_data_factories_exit (Gimp *gimp);
gboolean gimp_data_factories_wait (Gimp *gimp);
gint64 gimp_data_factories_get_memsize (Gimp *gimp,
gint64 *gui_size);
void gimp_data_factories_data_clean (Gimp *gimp);

View file

@ -1630,6 +1630,50 @@ gimp_version_cmp (const gchar *v1,
return -1;
}
/**
* gimp_get_type_children:
* @type: the %GType to find children types for.
* @types: set %NULL for the initial call (internal variable for
* recursive calls).
* @excluded: a list of types to exclude.
*
* Gather and recursively return all the subtypes of @type (except any
* type listed in @excluded).
*
* Note that @type itself is not included, so if you wished to have it,
* you must add it yourself after calling this function.
*
* Returns: a list of %Gtypes.
*/
GList *
gimp_get_type_children (GType type,
GList *types,
GList *excluded)
{
GType *dtypes;
guint n_types;
dtypes = g_type_children (type, &n_types);
/* TODO: when bumping GLib >= 2.80, use GTYPE_TO_POINTER instead. */
#define GIMPTYPE_TO_POINTER(t) ((gpointer) (guintptr) (t))
for (gint i = 0; i < n_types; i++)
{
if (g_list_find (excluded, GIMPTYPE_TO_POINTER (dtypes[i])))
continue;
types = gimp_get_type_children (dtypes[i], types, excluded);
types = g_list_prepend (types, GIMPTYPE_TO_POINTER (dtypes[i]));
}
#undef GIMPTYPE_TO_POINTER
g_free (dtypes);
return types;
}
/* Private functions */

View file

@ -155,3 +155,7 @@ gboolean gimp_win32_have_windows_ink (void);
gint gimp_version_cmp (const gchar *v1,
const gchar *v2);
GList * gimp_get_type_children (GType type,
GList *types,
GList *excluded);

View file

@ -892,7 +892,7 @@ gimp_is_restored (Gimp *gimp)
{
g_return_val_if_fail (GIMP_IS_GIMP (gimp), FALSE);
return gimp->initialized && gimp->restored;
return gimp->initialized && gimp->restored && gimp_data_factories_wait (gimp);
}
/**

View file

@ -30,6 +30,7 @@
#include "gegl/gimp-babl.h"
#include "core/gimp.h"
#include "core/gimp-data-factories.h"
#include "core/gimpcontext.h"
#include "core/gimpdocumentlist.h"
#include "core/gimpimage.h"
@ -523,6 +524,8 @@ file_open_with_proc_and_display (Gimp *gimp,
g_return_val_if_fail (monitor == NULL || G_IS_OBJECT (monitor), NULL);
g_return_val_if_fail (status != NULL, NULL);
gimp_data_factories_wait (gimp);
if (gimp->no_interface)
run_mode = GIMP_RUN_NONINTERACTIVE;