Coverage Report

Created: 2026-11-12 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mupdf/source/html/html-font.c
Line
Count
Source
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.01k
{
31
1.01k
  int is_mono = !strcmp(family, "monospace");
32
1.01k
  int is_sans = !strcmp(family, "sans-serif");
33
1.01k
  const char *real_family = is_mono ? "Courier" : is_sans ? "Helvetica" : "Charis SIL";
34
1.01k
  const char *backup_family = is_mono ? "Courier" : is_sans ? "Helvetica" : "Times";
35
1.01k
  int idx = (is_mono ? 8 : is_sans ? 4 : 0) + is_bold * 2 + is_italic;
36
1.01k
  if (!set->fonts[idx])
37
6
  {
38
6
    const unsigned char *data;
39
6
    int size;
40
41
6
    data = fz_lookup_builtin_font(ctx, real_family, is_bold, is_italic, &size);
42
6
    if (!data)
43
0
      data = fz_lookup_builtin_font(ctx, backup_family, is_bold, is_italic, &size);
44
6
    if (!data)
45
0
      fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "cannot load html font: %s", real_family);
46
6
    set->fonts[idx] = fz_new_font_from_memory(ctx, NULL, data, size, 0, 1);
47
6
    fz_font_flags(set->fonts[idx])->is_serif = !is_sans;
48
6
  }
49
1.01k
  return set->fonts[idx];
50
1.01k
}
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
0
{
57
0
  fz_html_font_face *custom = fz_malloc_struct(ctx, fz_html_font_face);
58
0
  fz_font_flags_t *flags;
59
60
0
  flags = fz_font_flags(font);
61
0
  if (is_bold && !flags->is_bold)
62
0
    flags->fake_bold = 1;
63
0
  if (is_italic && !flags->is_italic)
64
0
    flags->fake_italic = 1;
65
66
0
  fz_try(ctx)
67
0
  {
68
0
    custom->font = fz_keep_font(ctx, font);
69
0
    custom->src = fz_strdup(ctx, src);
70
0
    custom->family = fz_strdup(ctx, family);
71
0
    custom->is_bold = is_bold;
72
0
    custom->is_italic = is_italic;
73
0
    custom->is_small_caps = is_small_caps;
74
0
    custom->next = set->custom;
75
0
    set->custom = custom;
76
0
  }
77
0
  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
0
}
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.01k
{
90
1.01k
  fz_html_font_face *custom;
91
1.01k
  const unsigned char *data;
92
1.01k
  int best_score = 0;
93
1.01k
  fz_font *best_font = NULL;
94
1.01k
  fz_font *font;
95
1.01k
  int size;
96
97
1.01k
  for (custom = set->custom; custom; custom = custom->next)
98
0
  {
99
0
    if (!strcmp(family, custom->family))
100
0
    {
101
0
      int score =
102
0
        1 * (is_bold == custom->is_bold) +
103
0
        2 * (is_italic == custom->is_italic) +
104
0
        4 * (is_small_caps == custom->is_small_caps);
105
0
      if (score > best_score)
106
0
      {
107
0
        best_score = score;
108
0
        best_font = custom->font;
109
0
      }
110
0
    }
111
0
  }
112
113
  // We found a perfect match!
114
1.01k
  if (best_font && best_score == 1 + 2 + 4)
115
0
    return best_font;
116
117
  // FIXME: Font matching logic is not right here. Should we check for
118
  // user (system) fonts before we check for builtin ones to allow for
119
  // users to override inbuilt ones? If we fail to find an exact stylistic
120
  // match, we currently accept an inexact one before we look for an
121
  // exact (or better) user (system) font.
122
123
  // Try to load a perfect match.
124
1.01k
  data = fz_lookup_builtin_font(ctx, family, is_bold, is_italic, &size);
125
1.01k
  if (!data)
126
1.01k
    data = fz_lookup_builtin_font(ctx, family, 0, 0, &size);
127
1.01k
  if (data)
128
0
  {
129
0
    font = fz_new_font_from_memory(ctx, NULL, data, size, 0, 0);
130
0
    fz_try(ctx)
131
0
      fz_add_html_font_face(ctx, set, family, is_bold, is_italic, 0, "<builtin>", font);
132
0
    fz_always(ctx)
133
0
      fz_drop_font(ctx, font);
134
0
    fz_catch(ctx)
135
0
      fz_rethrow(ctx);
136
0
    return font;
137
0
  }
138
1.01k
  else
139
1.01k
  {
140
1.01k
    font = fz_load_system_font(ctx, family, is_bold, is_italic, 0);
141
1.01k
    if (font)
142
0
    {
143
0
      fz_try(ctx)
144
0
        fz_add_html_font_face(ctx, set, family, is_bold, is_italic, 0, "<system>", font);
145
0
      fz_always(ctx)
146
0
        fz_drop_font(ctx, font);
147
0
      fz_catch(ctx)
148
0
        fz_rethrow(ctx);
149
0
      return font;
150
0
    }
151
1.01k
  }
152
153
  // Use the imperfect match from before.
154
1.01k
  if (best_font)
155
0
    return best_font;
156
157
  // Handle the "default" font aliases.
158
1.01k
  if (!strcmp(family, "monospace") || !strcmp(family, "sans-serif") || !strcmp(family, "serif"))
159
1.01k
    return fz_load_html_default_font(ctx, set, family, is_bold, is_italic);
160
161
0
  return NULL;
162
1.01k
}
163
164
fz_html_font_set *fz_new_html_font_set(fz_context *ctx)
165
1
{
166
1
  return fz_malloc_struct(ctx, fz_html_font_set);
167
1
}
168
169
void fz_drop_html_font_set(fz_context *ctx, fz_html_font_set *set)
170
1
{
171
1
  fz_html_font_face *font, *next;
172
1
  int i;
173
174
1
  if (!set)
175
0
    return;
176
177
1
  font = set->custom;
178
1
  while (font)
179
0
  {
180
0
    next = font->next;
181
0
    fz_drop_font(ctx, font->font);
182
0
    fz_free(ctx, font->src);
183
0
    fz_free(ctx, font->family);
184
0
    fz_free(ctx, font);
185
0
    font = next;
186
0
  }
187
188
13
  for (i = 0; i < (int)nelem(set->fonts); ++i)
189
12
    fz_drop_font(ctx, set->fonts[i]);
190
191
1
  fz_free(ctx, set);
192
1
}