Coverage Report

Created: 2025-11-09 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libwebsockets/lib/misc/dlo/dlo-text.c
Line
Count
Source
1
/*
2
 * lws abstract display
3
 *
4
 * Copyright (C) 2019 - 2022 Andy Green <andy@warmcat.com>
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a copy
7
 * of this software and associated documentation files (the "Software"), to
8
 * deal in the Software without restriction, including without limitation the
9
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10
 * sell copies of the Software, and to permit persons to whom the Software is
11
 * furnished to do so, subject to the following conditions:
12
 *
13
 * The above copyright notice and this permission notice shall be included in
14
 * all copies or substantial portions of the Software.
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22
 * IN THE SOFTWARE.
23
 *
24
 * Display List Object: text
25
 */
26
27
#include <private-lib-core.h>
28
#include "private-lib-drivers-display-dlo.h"
29
30
size_t
31
utf8_bytes(uint8_t u)
32
0
{
33
0
  if ((u & 0x80) == 0)
34
0
    return 1;
35
36
0
  if ((u & 0xe0) == 0xc0)
37
0
    return 2;
38
39
0
  if ((u & 0xf0) == 0xe0)
40
0
    return 3;
41
42
0
  if ((u & 0xf8) == 0xf0)
43
0
    return 4;
44
45
0
  return 0;
46
0
}
47
48
static int
49
utf8_unicode(const char *utf8, size_t *utf8_len, uint32_t *unicode)
50
0
{
51
0
  size_t glyph_len = utf8_bytes((uint8_t)*utf8);
52
0
  size_t n;
53
54
0
  if (!glyph_len || glyph_len > *utf8_len) {
55
0
    (*utf8_len)--;
56
0
    return 1;
57
0
  }
58
59
0
  if (glyph_len == 1)
60
0
    *unicode = (uint32_t)*utf8++;
61
0
  else {
62
0
    *unicode = (uint32_t)((*utf8++) & (0x7f >> glyph_len));
63
0
    for (n = 1; n < glyph_len; n++)
64
0
      *unicode = (*unicode << 6) | ((*utf8++) & 0x3f);
65
0
  }
66
67
0
  *utf8_len -= glyph_len;
68
69
0
  return 0;
70
0
}
71
72
void
73
lws_display_dlo_text_destroy(struct lws_dlo *dlo)
74
0
{
75
0
  lws_dlo_text_t *text = lws_container_of(dlo, lws_dlo_text_t, dlo);
76
77
0
  lws_free_set_NULL(text->kern);
78
0
  lws_free_set_NULL(text->text);
79
80
0
  lwsac_free(&text->ac_glyphs);
81
0
}
82
83
int
84
lws_display_dlo_text_update(lws_dlo_text_t *text, lws_display_colour_t dc,
85
          lws_fx_t indent, const char *utf8, size_t text_len)
86
0
{
87
0
  const char *last_utf8 = utf8, *outf8 = utf8;
88
0
  size_t last_bp_n = 0, tlen = text_len;
89
0
  lws_fx_t t1, eff, last_bp_eff, t2;
90
0
  uint8_t r = 0;
91
0
  char uc;
92
93
0
  if (text->kern)
94
0
    lws_free_set_NULL(text->kern);
95
96
0
  if (text->text)
97
0
    lws_free_set_NULL(text->text);
98
99
0
  lws_dll2_owner_clear(&text->glyphs);
100
0
  lwsac_free(&text->ac_glyphs);
101
102
0
  text->indent = indent;
103
0
  text->dlo.dc = dc;
104
105
0
  lws_fx_set(eff, 0, 0);
106
107
  /*
108
   * Let's go through the new string glyph by glyph, we want to
109
   * calculate effective kerned widths, and optionally deal with wrapping.
110
   *
111
   * But we don't want to instantiate the glyph objects until we are
112
   * engaged with rendering them.  Otherwise we will carry around the
113
   * whole page-worth's of glyphs at once needlessly, which won't scale
114
   * for text-heavy pages.  lws_display_dlo_text_attach_glyphs() does the
115
   * same flow as this but to create the glyphs and is called later
116
   * as the text dlo becomes rasterized during rendering.
117
   */
118
119
/*  { char b1[22]; lwsl_err("eff %s\n", lws_fx_string(&eff, b1, sizeof(b1))); }
120
  { char b1[22]; lwsl_err("indent %s\n", lws_fx_string(&indent, b1, sizeof(b1))); }
121
  { char b1[22]; lwsl_err("boxw %s\n", lws_fx_string(&text->dlo.box.w, b1, sizeof(b1))); } */
122
123
0
  while (tlen &&
124
0
         lws_fx_comp(lws_fx_add(&t1, &eff, &indent), &text->dlo.box.w) < 0) {
125
0
    size_t ot = tlen;
126
0
    uint32_t unicode;
127
128
0
    if (!utf8_unicode(utf8, &tlen, &unicode)) {
129
0
      text->font->image_glyph(text, unicode, 0);
130
131
0
      uc = *utf8;
132
0
      utf8 += (ot - tlen);
133
134
0
      if (uc == ' ') { /* act to snip it if used */
135
0
        last_utf8 = utf8;
136
0
        last_bp_n = tlen;
137
0
        last_bp_eff = eff;
138
0
      }
139
140
0
      if (!lws_display_font_mcufont_getcwidth(text, unicode, &t2))
141
0
        lws_fx_add(&eff, &eff, &t2);
142
143
0
      if (uc == '-' || uc == ',' || uc == ';' || uc == ':') {
144
        /* act to leave it in */
145
0
        last_utf8 = utf8;
146
0
        last_bp_n = tlen;
147
0
        last_bp_eff = eff;
148
0
      }
149
0
    } else
150
0
      lwsl_err("%s: missing glyph\n", __func__);
151
0
  }
152
153
0
  if (last_bp_n &&
154
0
      lws_fx_comp(lws_fx_add(&t1, &eff, &indent), &text->dlo.box.w) >= 0) {
155
0
    eff = last_bp_eff;
156
0
    utf8 = last_utf8;
157
0
    tlen = last_bp_n;
158
0
    r = 1;
159
0
  }
160
161
0
  text->text_len = text_len - tlen;
162
0
  if (tlen == text_len) {
163
0
    lwsl_notice("we couldn't fit anything in there, newline\n");
164
0
    return 2;
165
0
  }
166
167
0
  text->text = lws_malloc(text->text_len + 1, __func__);
168
0
  if (!text->text)
169
0
    return -1;
170
171
0
  memcpy(text->text, outf8, text->text_len);
172
0
  text->text[text->text_len] = '\0';
173
174
0
  memset(&text->bounding_box, 0, sizeof(text->bounding_box));
175
0
  text->bounding_box.w = eff;
176
0
  text->bounding_box.h.whole = text->font_height;
177
0
  text->bounding_box.h.frac = 0;
178
179
0
  return r;
180
0
}
181
182
int
183
lws_display_dlo_text_attach_glyphs(lws_dlo_text_t *text)
184
0
{
185
0
  const char *utf8 = text->text;
186
0
  size_t tlen = text->text_len;
187
0
  lws_font_glyph_t *g = NULL;
188
0
  uint32_t unicode;
189
0
  lws_fx_t eff;
190
0
  uint8_t r = 0;
191
192
0
  lws_fx_set(eff, 0, 0);
193
194
0
  while (tlen) {
195
0
    size_t ot = tlen;
196
197
0
    g = NULL;
198
0
    if (!utf8_unicode(utf8, &tlen, &unicode))
199
      /* instantiate the glyphs this time */
200
0
      g = text->font->image_glyph(text, unicode, 1);
201
0
    if (g == NULL) {
202
0
      lwsl_warn("%s: no glyph for 0x%02X '%c'\n", __func__, (unsigned int)*utf8, *utf8);
203
0
      break;
204
0
    }
205
206
0
    utf8 += (ot - tlen);
207
0
    g->xpx = eff;
208
0
    lws_fx_add(&eff, &eff, &g->cwidth);
209
0
  }
210
211
0
  return r;
212
0
}
213
214
lws_dlo_text_t *
215
lws_display_dlo_text_new(lws_displaylist_t *dl, lws_dlo_t *dlo_parent,
216
       lws_box_t *box, const lws_display_font_t *font)
217
0
{
218
0
  lws_dlo_text_t *text = lws_zalloc(sizeof(*text), __func__);
219
220
0
  if (!text)
221
0
    return NULL;
222
223
0
  text->dlo.render = font->renderer;
224
0
  text->dlo._destroy = lws_display_dlo_text_destroy;
225
0
  text->dlo.box = *box;
226
0
  text->font = font;
227
228
0
  lws_display_dlo_add(dl, dlo_parent, &text->dlo);
229
230
0
  return text;
231
0
}
232
233
static const char *
234
castrstr(const char *haystack, const char *needle)
235
0
{
236
0
  size_t sn = strlen(needle), h = strlen(haystack) - sn + 1, n;
237
0
  char c, c1;
238
239
0
  while (1) {
240
0
    for (n = 0; n < sn; n++) {
241
0
      c = (char)((haystack[h + n] >= 'A' && haystack[h + n] <= 'Z') ?
242
0
        haystack[h + n] + ('a' - 'A') : haystack[h + n]);
243
0
      c1 = (char)((needle[n] >= 'A' && needle[n] <= 'Z') ?
244
0
        needle[n] + ('a' - 'A') : needle[n]);
245
0
      if (c != c1)
246
0
        break;
247
0
    }
248
0
    if (n == sn)
249
0
      return &haystack[h];
250
251
0
    if (!h)
252
0
      break;
253
0
    h--;
254
0
  }
255
256
0
  return NULL;
257
0
}
258
259
int
260
lws_font_register(struct lws_context *cx, const uint8_t *data, size_t data_len)
261
0
{
262
0
  lws_display_font_t *a;
263
264
0
  if (lws_ser_ru32be(data) != LWS_FOURCC('M', 'C', 'U', 'F'))
265
0
    return 1;
266
267
0
  a = lws_zalloc(sizeof(*a), __func__);
268
0
  if (!a)
269
0
    return 1;
270
271
0
  a->choice.family_name = (const char *)data +
272
0
        lws_ser_ru32be(data + MCUFO_FOFS_FULLNAME);
273
274
0
  if (castrstr(a->choice.family_name, "serif") ||
275
0
      castrstr(a->choice.family_name, "roman"))
276
0
    a->choice.generic_name = "serif";
277
0
  else
278
0
    a->choice.generic_name = "sans";
279
280
0
  if (castrstr(a->choice.family_name, "italic") ||
281
0
      castrstr(a->choice.family_name, "oblique"))
282
0
    a->choice.style = 1;
283
284
0
  if (castrstr(a->choice.family_name, "extrabold") ||
285
0
      castrstr(a->choice.family_name, "extra bold"))
286
0
    a->choice.weight = 900;
287
0
  else
288
0
    if (castrstr(a->choice.family_name, "bold"))
289
0
        a->choice.weight = 700;
290
0
    else
291
0
      if (castrstr(a->choice.family_name, "extralight") ||
292
0
          castrstr(a->choice.family_name, "extra light"))
293
0
        a->choice.weight = 200;
294
0
      else
295
0
        if (castrstr(a->choice.family_name, "light"))
296
0
          a->choice.weight = 300;
297
0
        else
298
0
          a->choice.weight = 400;
299
300
0
  a->choice.fixed_height = lws_ser_ru16be(data + MCUFO16_LINE_HEIGHT);
301
302
0
  a->data = data;
303
0
  a->data_len = data_len;
304
0
  a->renderer = lws_display_font_mcufont_render;
305
0
  a->image_glyph = lws_display_font_mcufont_image_glyph;
306
307
0
  {
308
0
    lws_dlo_text_t t;
309
310
0
    memset(&t, 0, sizeof(t));
311
0
    t.font = a;
312
313
0
    lws_display_font_mcufont_getcwidth(&t, 'm', &a->em);
314
0
    a->ex.whole = a->choice.fixed_height;
315
0
    a->ex.frac = 0;
316
0
  }
317
318
0
  lws_dll2_clear(&a->list);
319
0
  lws_dll2_add_tail(&a->list, &cx->fonts);
320
321
0
  return 0;
322
0
}
323
324
static int
325
lws_font_destroy(struct lws_dll2 *d, void *user)
326
0
{
327
0
  lws_free(d);
328
0
  return 0;
329
0
}
330
331
void
332
lws_fonts_destroy(struct lws_context *cx)
333
0
{
334
0
  lws_dll2_foreach_safe(&cx->fonts, NULL, lws_font_destroy);
335
0
}
336
337
struct track {
338
  const lws_font_choice_t   *hints;
339
  const lws_display_font_t  *best;
340
  int       best_score;
341
};
342
343
static int
344
lws_fonts_score(struct lws_dll2 *d, void *user)
345
0
{
346
0
  const lws_display_font_t *f = lws_container_of(d, lws_display_font_t,
347
0
                   list);
348
0
  struct track *t = (struct track *)user;
349
0
  struct lws_tokenize ts;
350
0
  int score = 1000;
351
352
0
  if (t->hints->family_name) {
353
0
    memset(&ts, 0, sizeof(ts));
354
0
    ts.start = t->hints->family_name;
355
0
    ts.len = strlen(ts.start);
356
0
    ts.flags = LWS_TOKENIZE_F_COMMA_SEP_LIST;
357
358
0
    do {
359
0
      ts.e = (int8_t)lws_tokenize(&ts);
360
0
      if (ts.e == LWS_TOKZE_TOKEN) {
361
0
        if (!strncmp(f->choice.family_name, ts.token,
362
0
               ts.token_len)) {
363
0
          score = 0;
364
0
          break;
365
0
        }
366
367
0
        if (f->choice.generic_name &&
368
0
            !strncmp(f->choice.generic_name, ts.token,
369
0
                   ts.token_len)) {
370
0
          score -= 500;
371
0
          break;
372
0
        }
373
374
0
      }
375
376
0
    } while (ts.e > 0);
377
0
  }
378
379
0
  if (t->hints->weight)
380
0
    score += (t->hints->weight > f->choice.weight ?
381
0
      (t->hints->weight - f->choice.weight) :
382
0
      (f->choice.weight - t->hints->weight)) / 100;
383
384
0
  if (t->hints->style != f->choice.style)
385
0
    score += 100;
386
387
0
  if (t->hints->fixed_height)
388
0
    score += 10 * (t->hints->fixed_height > f->choice.fixed_height ?
389
0
        (t->hints->fixed_height - f->choice.fixed_height) :
390
0
        (f->choice.fixed_height - t->hints->fixed_height));
391
392
0
  if (score < t->best_score) {
393
0
    t->best_score = score;
394
0
    t->best = f;
395
0
  }
396
397
0
  return 0;
398
0
}
399
400
const lws_display_font_t *
401
lws_font_choose(struct lws_context *cx, const lws_font_choice_t *hints)
402
0
{
403
0
  struct track t;
404
405
0
  t.hints     = hints;
406
0
  t.best      = (const lws_display_font_t *)cx->fonts.head;
407
0
  t.best_score    = 99999999;
408
409
0
  if (t.hints)
410
0
    lws_dll2_foreach_safe(&cx->fonts, &t, lws_fonts_score);
411
412
0
  return t.best;
413
0
}