/src/mupdf/source/html/html-font.c
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (C) 2004-2025 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 | | #include "mupdf/fitz.h" |
24 | | #include "html-imp.h" |
25 | | |
26 | | #include <string.h> |
27 | | |
28 | | static fz_font * |
29 | | fz_load_html_default_font(fz_context *ctx, fz_html_font_set *set, const char *family, int is_bold, int is_italic) |
30 | 1.29k | { |
31 | 1.29k | int is_mono = !strcmp(family, "monospace"); |
32 | 1.29k | int is_sans = !strcmp(family, "sans-serif"); |
33 | 1.29k | const char *real_family = is_mono ? "Courier" : is_sans ? "Helvetica" : "Charis SIL"; |
34 | 1.29k | const char *backup_family = is_mono ? "Courier" : is_sans ? "Helvetica" : "Times"; |
35 | 1.29k | int idx = (is_mono ? 8 : is_sans ? 4 : 0) + is_bold * 2 + is_italic; |
36 | 1.29k | if (!set->fonts[idx]) |
37 | 329 | { |
38 | 329 | const unsigned char *data; |
39 | 329 | int size; |
40 | | |
41 | 329 | data = fz_lookup_builtin_font(ctx, real_family, is_bold, is_italic, &size); |
42 | 329 | if (!data) |
43 | 0 | data = fz_lookup_builtin_font(ctx, backup_family, is_bold, is_italic, &size); |
44 | 329 | if (!data) |
45 | 0 | fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "cannot load html font: %s", real_family); |
46 | 329 | set->fonts[idx] = fz_new_font_from_memory(ctx, NULL, data, size, 0, 1); |
47 | 329 | fz_font_flags(set->fonts[idx])->is_serif = !is_sans; |
48 | 329 | } |
49 | 1.29k | return set->fonts[idx]; |
50 | 1.29k | } |
51 | | |
52 | | void |
53 | | fz_add_html_font_face(fz_context *ctx, fz_html_font_set *set, |
54 | | const char *family, int is_bold, int is_italic, int is_small_caps, |
55 | | const char *src, fz_font *font) |
56 | 237 | { |
57 | 237 | fz_html_font_face *custom = fz_malloc_struct(ctx, fz_html_font_face); |
58 | 237 | fz_font_flags_t *flags; |
59 | | |
60 | 237 | flags = fz_font_flags(font); |
61 | 237 | if (is_bold && !flags->is_bold) |
62 | 0 | flags->fake_bold = 1; |
63 | 237 | if (is_italic && !flags->is_italic) |
64 | 0 | flags->fake_italic = 1; |
65 | | |
66 | 474 | fz_try(ctx) |
67 | 474 | { |
68 | 237 | custom->font = fz_keep_font(ctx, font); |
69 | 237 | custom->src = fz_strdup(ctx, src); |
70 | 237 | custom->family = fz_strdup(ctx, family); |
71 | 237 | custom->is_bold = is_bold; |
72 | 237 | custom->is_italic = is_italic; |
73 | 237 | custom->is_small_caps = is_small_caps; |
74 | 237 | custom->next = set->custom; |
75 | 237 | set->custom = custom; |
76 | 237 | } |
77 | 474 | fz_catch(ctx) |
78 | 0 | { |
79 | 0 | fz_drop_font(ctx, custom->font); |
80 | 0 | fz_free(ctx, custom->src); |
81 | 0 | fz_free(ctx, custom->family); |
82 | 0 | fz_rethrow(ctx); |
83 | 0 | } |
84 | 237 | } |
85 | | |
86 | | fz_font * |
87 | | fz_load_html_font(fz_context *ctx, fz_html_font_set *set, |
88 | | const char *family, int is_bold, int is_italic, int is_small_caps) |
89 | 1.94k | { |
90 | 1.94k | fz_html_font_face *custom; |
91 | 1.94k | const unsigned char *data; |
92 | 1.94k | int best_score = 0; |
93 | 1.94k | fz_font *best_font = NULL; |
94 | 1.94k | fz_font *font; |
95 | 1.94k | int size; |
96 | | |
97 | 2.18k | for (custom = set->custom; custom; custom = custom->next) |
98 | 237 | { |
99 | 237 | if (!strcmp(family, custom->family)) |
100 | 175 | { |
101 | 175 | int score = |
102 | 175 | 1 * (is_bold == custom->is_bold) + |
103 | 175 | 2 * (is_italic == custom->is_italic) + |
104 | 175 | 4 * (is_small_caps == custom->is_small_caps); |
105 | 175 | if (score > best_score) |
106 | 175 | { |
107 | 175 | best_score = score; |
108 | 175 | best_font = custom->font; |
109 | 175 | } |
110 | 175 | } |
111 | 237 | } |
112 | | |
113 | | // We found a perfect match! |
114 | 1.94k | if (best_font && best_score == 1 + 2 + 4) |
115 | 175 | return best_font; |
116 | | |
117 | | // Try to load a perfect match. |
118 | 1.77k | data = fz_lookup_builtin_font(ctx, family, is_bold, is_italic, &size); |
119 | 1.77k | if (!data) |
120 | 1.53k | data = fz_lookup_builtin_font(ctx, family, 0, 0, &size); |
121 | 1.77k | if (data) |
122 | 237 | { |
123 | 237 | font = fz_new_font_from_memory(ctx, NULL, data, size, 0, 0); |
124 | 474 | fz_try(ctx) |
125 | 474 | fz_add_html_font_face(ctx, set, family, is_bold, is_italic, 0, "<builtin>", font); |
126 | 474 | fz_always(ctx) |
127 | 237 | fz_drop_font(ctx, font); |
128 | 237 | fz_catch(ctx) |
129 | 0 | fz_rethrow(ctx); |
130 | 237 | return font; |
131 | 237 | } |
132 | 1.53k | else |
133 | 1.53k | { |
134 | 1.53k | font = fz_load_system_font(ctx, family, is_bold, is_italic, 0); |
135 | 1.53k | if (font) |
136 | 0 | { |
137 | 0 | fz_try(ctx) |
138 | 0 | fz_add_html_font_face(ctx, set, family, is_bold, is_italic, 0, "<system>", font); |
139 | 0 | fz_always(ctx) |
140 | 0 | fz_drop_font(ctx, font); |
141 | 0 | fz_catch(ctx) |
142 | 0 | fz_rethrow(ctx); |
143 | 0 | return font; |
144 | 0 | } |
145 | 1.53k | } |
146 | | |
147 | | // Use the imperfect match from before. |
148 | 1.53k | if (best_font) |
149 | 0 | return best_font; |
150 | | |
151 | | // Handle the "default" font aliases. |
152 | 1.53k | if (!strcmp(family, "monospace") || !strcmp(family, "sans-serif") || !strcmp(family, "serif")) |
153 | 1.29k | return fz_load_html_default_font(ctx, set, family, is_bold, is_italic); |
154 | | |
155 | 242 | return NULL; |
156 | 1.53k | } |
157 | | |
158 | | fz_html_font_set *fz_new_html_font_set(fz_context *ctx) |
159 | 299 | { |
160 | 299 | return fz_malloc_struct(ctx, fz_html_font_set); |
161 | 299 | } |
162 | | |
163 | | void fz_drop_html_font_set(fz_context *ctx, fz_html_font_set *set) |
164 | 299 | { |
165 | 299 | fz_html_font_face *font, *next; |
166 | 299 | int i; |
167 | | |
168 | 299 | if (!set) |
169 | 0 | return; |
170 | | |
171 | 299 | font = set->custom; |
172 | 536 | while (font) |
173 | 237 | { |
174 | 237 | next = font->next; |
175 | 237 | fz_drop_font(ctx, font->font); |
176 | 237 | fz_free(ctx, font->src); |
177 | 237 | fz_free(ctx, font->family); |
178 | 237 | fz_free(ctx, font); |
179 | 237 | font = next; |
180 | 237 | } |
181 | | |
182 | 3.88k | for (i = 0; i < (int)nelem(set->fonts); ++i) |
183 | 3.58k | fz_drop_font(ctx, set->fonts[i]); |
184 | | |
185 | 299 | fz_free(ctx, set); |
186 | 299 | } |