comparison mupdf-source/source/fitz/harfbuzz.c @ 3:2c135c81b16c

MERGE: upstream PyMuPDF 1.26.4 with MuPDF 1.26.7
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:44:09 +0200
parents b50eed0cc0ef
children
comparison
equal deleted inserted replaced
0:6015a75abc2d 3:2c135c81b16c
1 // Copyright (C) 2004-2021 Artifex Software, Inc.
2 //
3 // This file is part of MuPDF.
4 //
5 // MuPDF is free software: you can redistribute it and/or modify it under the
6 // terms of the GNU Affero General Public License as published by the Free
7 // Software Foundation, either version 3 of the License, or (at your option)
8 // any later version.
9 //
10 // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
11 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13 // details.
14 //
15 // You should have received a copy of the GNU Affero General Public License
16 // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
17 //
18 // Alternative licensing terms are available from the licensor.
19 // For commercial licensing, see <https://www.artifex.com/> or contact
20 // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
21 // CA 94129, USA, for further information.
22
23 /*
24 * Some additional glue functions for using Harfbuzz with
25 * custom allocators.
26 */
27
28 #include "mupdf/fitz.h"
29
30 #if FZ_ENABLE_HTML_ENGINE
31
32 #include "hb.h"
33
34 #include <assert.h>
35
36 /* Harfbuzz has some major design flaws (for our usage
37 * at least).
38 *
39 * By default it uses malloc and free as the underlying
40 * allocators. Thus in its default form we cannot get
41 * a record (much less control) over how much allocation
42 * is done.
43 *
44 * Harfbuzz does allow build options to control where
45 * malloc and free go - in particular we point them at
46 * fz_hb_malloc and fz_hb_free in our implementation.
47 * Unfortunately, this has problems too.
48 *
49 * Firstly, there is no mechanism for getting a context
50 * through the call. Most other libraries allow us to
51 * pass a "void *" value in, and have it passed through
52 * to arrive unchanged at the allocator functions.
53 *
54 * Without this rudimentary functionality, we are forced
55 * to serialise all access to Harfbuzz.
56 *
57 * By taking a mutex around all calls to Harfbuzz, we
58 * can use a static of our own to get a fz_context safely
59 * through to the allocators. This obviously costs us
60 * performance in the multi-threaded case.
61 *
62 * This does not protect us against the possibility of
63 * other people calling harfbuzz; for instance, if we
64 * link MuPDF into an app that either calls harfbuzz
65 * itself, or uses another library that calls harfbuzz,
66 * there is no guarantee that that library will take
67 * the same lock while calling harfbuzz. This leaves
68 * us open to the possibility of crashes. The only
69 * way around this would be to use completely separate
70 * harfbuzz instances.
71 *
72 * In order to ensure that allocations throughout mupdf
73 * are done consistently, we get harfbuzz to call our
74 * own fz_hb_malloc/realloc/calloc/free functions that
75 * call down to fz_malloc/realloc/calloc/free. These
76 * require context variables, so we get our fz_hb_lock
77 * and unlock to set these. Any attempt to call through
78 * without setting these will be detected.
79 *
80 * It is therefore vital that any fz_lock/fz_unlock
81 * handlers are shared between all the fz_contexts in
82 * use at a time.
83 *
84 * Secondly, Harfbuzz allocates some 'internal' memory
85 * on the first call, and leaves this linked from static
86 * variables. By default, this data is never freed back.
87 * This means it is impossible to clear the library back
88 * to a default state. Memory debugging will always show
89 * Harfbuzz as having leaked a set amount of memory.
90 *
91 * There is a mechanism in Harfbuzz for freeing these
92 * blocks - that of building with HAVE_ATEXIT. This
93 * causes the blocks to be freed back on exit, but a)
94 * this doesn't reset the fz_context value, so we can't
95 * free them correctly, and b) any fz_context value it
96 * did keep would already have been closed down due to
97 * the program exit.
98 *
99 * In addition, because of these everlasting blocks, we
100 * cannot safely call Harfbuzz after we close down any
101 * allocator that Harfbuzz has been using (because
102 * Harfbuzz may still be holding pointers to data within
103 * that allocators managed space).
104 *
105 * There is nothing we can do about the leaking blocks
106 * except to add some hacks to our memory debugging
107 * library to allow it to suppress the blocks that
108 * harfbuzz leaks.
109 *
110 * Consequently, we leave them to leak, and warn Memento
111 * about this.
112 */
113
114 /* Potentially we can write different versions
115 * of get_context and set_context for different
116 * threading systems.
117 *
118 * This simple version relies on harfbuzz never
119 * trying to make 2 allocations at once on
120 * different threads. The only way that can happen
121 * is when one of those other threads is someone
122 * outside MuPDF calling harfbuzz while MuPDF
123 * is running. This will cause us such huge
124 * problems that for now, we'll just forbid it.
125 */
126
127 static fz_context *fz_hb_secret = NULL;
128
129 static void set_hb_context(fz_context *ctx)
130 {
131 fz_hb_secret = ctx;
132 }
133
134 static fz_context *get_hb_context(void)
135 {
136 return fz_hb_secret;
137 }
138
139 void fz_hb_lock(fz_context *ctx)
140 {
141 fz_ft_lock(ctx);
142
143 set_hb_context(ctx);
144 }
145
146 void fz_hb_unlock(fz_context *ctx)
147 {
148 set_hb_context(NULL);
149
150 fz_ft_unlock(ctx);
151 }
152
153 void *fz_hb_malloc(size_t size)
154 {
155 fz_context *ctx = get_hb_context();
156
157 assert(ctx != NULL);
158
159 return Memento_label(fz_malloc_no_throw(ctx, size), "hb");
160 }
161
162 void *fz_hb_calloc(size_t n, size_t size)
163 {
164 fz_context *ctx = get_hb_context();
165
166 assert(ctx != NULL);
167
168 return Memento_label(fz_calloc_no_throw(ctx, n, size), "hb");
169 }
170
171 void *fz_hb_realloc(void *ptr, size_t size)
172 {
173 fz_context *ctx = get_hb_context();
174
175 assert(ctx != NULL);
176
177 return Memento_label(fz_realloc_no_throw(ctx, ptr, size), "hb");
178 }
179
180 void fz_hb_free(void *ptr)
181 {
182 fz_context *ctx = get_hb_context();
183
184 assert(ctx != NULL);
185
186 fz_free(ctx, ptr);
187 }
188
189 #endif