diff mupdf-source/source/fitz/harfbuzz.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mupdf-source/source/fitz/harfbuzz.c	Mon Sep 15 11:43:07 2025 +0200
@@ -0,0 +1,189 @@
+// Copyright (C) 2004-2021 Artifex Software, Inc.
+//
+// This file is part of MuPDF.
+//
+// MuPDF is free software: you can redistribute it and/or modify it under the
+// terms of the GNU Affero General Public License as published by the Free
+// Software Foundation, either version 3 of the License, or (at your option)
+// any later version.
+//
+// MuPDF 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 Affero General Public License for more
+// details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
+//
+// Alternative licensing terms are available from the licensor.
+// For commercial licensing, see <https://www.artifex.com/> or contact
+// Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
+// CA 94129, USA, for further information.
+
+/*
+ * Some additional glue functions for using Harfbuzz with
+ * custom allocators.
+ */
+
+#include "mupdf/fitz.h"
+
+#if FZ_ENABLE_HTML_ENGINE
+
+#include "hb.h"
+
+#include <assert.h>
+
+/* Harfbuzz has some major design flaws (for our usage
+ * at least).
+ *
+ * By default it uses malloc and free as the underlying
+ * allocators. Thus in its default form we cannot get
+ * a record (much less control) over how much allocation
+ * is done.
+ *
+ * Harfbuzz does allow build options to control where
+ * malloc and free go - in particular we point them at
+ * fz_hb_malloc and fz_hb_free in our implementation.
+ * Unfortunately, this has problems too.
+ *
+ * Firstly, there is no mechanism for getting a context
+ * through the call. Most other libraries allow us to
+ * pass a "void *" value in, and have it passed through
+ * to arrive unchanged at the allocator functions.
+ *
+ * Without this rudimentary functionality, we are forced
+ * to serialise all access to Harfbuzz.
+ *
+ * By taking a mutex around all calls to Harfbuzz, we
+ * can use a static of our own to get a fz_context safely
+ * through to the allocators. This obviously costs us
+ * performance in the multi-threaded case.
+ *
+ * This does not protect us against the possibility of
+ * other people calling harfbuzz; for instance, if we
+ * link MuPDF into an app that either calls harfbuzz
+ * itself, or uses another library that calls harfbuzz,
+ * there is no guarantee that that library will take
+ * the same lock while calling harfbuzz. This leaves
+ * us open to the possibility of crashes. The only
+ * way around this would be to use completely separate
+ * harfbuzz instances.
+ *
+ * In order to ensure that allocations throughout mupdf
+ * are done consistently, we get harfbuzz to call our
+ * own fz_hb_malloc/realloc/calloc/free functions that
+ * call down to fz_malloc/realloc/calloc/free. These
+ * require context variables, so we get our fz_hb_lock
+ * and unlock to set these. Any attempt to call through
+ * without setting these will be detected.
+ *
+ * It is therefore vital that any fz_lock/fz_unlock
+ * handlers are shared between all the fz_contexts in
+ * use at a time.
+ *
+ * Secondly, Harfbuzz allocates some 'internal' memory
+ * on the first call, and leaves this linked from static
+ * variables. By default, this data is never freed back.
+ * This means it is impossible to clear the library back
+ * to a default state. Memory debugging will always show
+ * Harfbuzz as having leaked a set amount of memory.
+ *
+ * There is a mechanism in Harfbuzz for freeing these
+ * blocks - that of building with HAVE_ATEXIT. This
+ * causes the blocks to be freed back on exit, but a)
+ * this doesn't reset the fz_context value, so we can't
+ * free them correctly, and b) any fz_context value it
+ * did keep would already have been closed down due to
+ * the program exit.
+ *
+ * In addition, because of these everlasting blocks, we
+ * cannot safely call Harfbuzz after we close down any
+ * allocator that Harfbuzz has been using (because
+ * Harfbuzz may still be holding pointers to data within
+ * that allocators managed space).
+ *
+ * There is nothing we can do about the leaking blocks
+ * except to add some hacks to our memory debugging
+ * library to allow it to suppress the blocks that
+ * harfbuzz leaks.
+ *
+ * Consequently, we leave them to leak, and warn Memento
+ * about this.
+ */
+
+/* Potentially we can write different versions
+ * of get_context and set_context for different
+ * threading systems.
+ *
+ * This simple version relies on harfbuzz never
+ * trying to make 2 allocations at once on
+ * different threads. The only way that can happen
+ * is when one of those other threads is someone
+ * outside MuPDF calling harfbuzz while MuPDF
+ * is running. This will cause us such huge
+ * problems that for now, we'll just forbid it.
+ */
+
+static fz_context *fz_hb_secret = NULL;
+
+static void set_hb_context(fz_context *ctx)
+{
+	fz_hb_secret = ctx;
+}
+
+static fz_context *get_hb_context(void)
+{
+	return fz_hb_secret;
+}
+
+void fz_hb_lock(fz_context *ctx)
+{
+	fz_ft_lock(ctx);
+
+	set_hb_context(ctx);
+}
+
+void fz_hb_unlock(fz_context *ctx)
+{
+	set_hb_context(NULL);
+
+	fz_ft_unlock(ctx);
+}
+
+void *fz_hb_malloc(size_t size)
+{
+	fz_context *ctx = get_hb_context();
+
+	assert(ctx != NULL);
+
+	return Memento_label(fz_malloc_no_throw(ctx, size), "hb");
+}
+
+void *fz_hb_calloc(size_t n, size_t size)
+{
+	fz_context *ctx = get_hb_context();
+
+	assert(ctx != NULL);
+
+	return Memento_label(fz_calloc_no_throw(ctx, n, size), "hb");
+}
+
+void *fz_hb_realloc(void *ptr, size_t size)
+{
+	fz_context *ctx = get_hb_context();
+
+	assert(ctx != NULL);
+
+	return Memento_label(fz_realloc_no_throw(ctx, ptr, size), "hb");
+}
+
+void fz_hb_free(void *ptr)
+{
+	fz_context *ctx = get_hb_context();
+
+	assert(ctx != NULL);
+
+	fz_free(ctx, ptr);
+}
+
+#endif