Coverage Report

Created: 2025-07-07 10:01

/work/workdir/UnpackedTarball/cairo/src/cairo-font-face-twin.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright © 2004 Keith Packard
3
 * Copyright © 2008 Red Hat, Inc.
4
 *
5
 * This library is free software; you can redistribute it and/or
6
 * modify it either under the terms of the GNU Lesser General Public
7
 * License version 2.1 as published by the Free Software Foundation
8
 * (the "LGPL") or, at your option, under the terms of the Mozilla
9
 * Public License Version 1.1 (the "MPL"). If you do not alter this
10
 * notice, a recipient may use your version of this file under either
11
 * the MPL or the LGPL.
12
 *
13
 * You should have received a copy of the LGPL along with this library
14
 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
15
 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
16
 * You should have received a copy of the MPL along with this library
17
 * in the file COPYING-MPL-1.1
18
 *
19
 * The contents of this file are subject to the Mozilla Public License
20
 * Version 1.1 (the "License"); you may not use this file except in
21
 * compliance with the License. You may obtain a copy of the License at
22
 * http://www.mozilla.org/MPL/
23
 *
24
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
25
 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
26
 * the specific language governing rights and limitations.
27
 *
28
 * The Original Code is the cairo graphics library.
29
 *
30
 * The Initial Developer of the Original Code is Keith Packard
31
 *
32
 * Contributor(s):
33
 *      Keith Packard <keithp@keithp.com>
34
 *      Behdad Esfahbod <behdad@behdad.org>
35
 */
36
37
#include "cairoint.h"
38
#include "cairo-error-private.h"
39
40
#include <math.h>
41
42
/*
43
 * This file implements a user-font rendering the descendant of the Hershey
44
 * font coded by Keith Packard for use in the Twin window system.
45
 * The actual font data is in cairo-font-face-twin-data.c
46
 *
47
 * Ported to cairo user font and extended by Behdad Esfahbod.
48
 */
49
50
51
52
static cairo_user_data_key_t twin_properties_key;
53
54
55
/*
56
 * Face properties
57
 */
58
59
/* We synthesize multiple faces from the twin data.  Here is the parameters. */
60
61
/* The following tables and matching code are copied from Pango */
62
63
/* CSS weight */
64
typedef enum {
65
  TWIN_WEIGHT_THIN = 100,
66
  TWIN_WEIGHT_ULTRALIGHT = 200,
67
  TWIN_WEIGHT_LIGHT = 300,
68
  TWIN_WEIGHT_BOOK = 380,
69
  TWIN_WEIGHT_NORMAL = 400,
70
  TWIN_WEIGHT_MEDIUM = 500,
71
  TWIN_WEIGHT_SEMIBOLD = 600,
72
  TWIN_WEIGHT_BOLD = 700,
73
  TWIN_WEIGHT_ULTRABOLD = 800,
74
  TWIN_WEIGHT_HEAVY = 900,
75
  TWIN_WEIGHT_ULTRAHEAVY = 1000
76
} twin_face_weight_t;
77
78
/* CSS stretch */
79
typedef enum {
80
  TWIN_STRETCH_ULTRA_CONDENSED,
81
  TWIN_STRETCH_EXTRA_CONDENSED,
82
  TWIN_STRETCH_CONDENSED,
83
  TWIN_STRETCH_SEMI_CONDENSED,
84
  TWIN_STRETCH_NORMAL,
85
  TWIN_STRETCH_SEMI_EXPANDED,
86
  TWIN_STRETCH_EXPANDED,
87
  TWIN_STRETCH_EXTRA_EXPANDED,
88
  TWIN_STRETCH_ULTRA_EXPANDED
89
} twin_face_stretch_t;
90
91
typedef struct
92
{
93
  int value;
94
  const char str[16];
95
} FieldMap;
96
97
static const FieldMap slant_map[] = {
98
  { CAIRO_FONT_SLANT_NORMAL, "" },
99
  { CAIRO_FONT_SLANT_NORMAL, "Roman" },
100
  { CAIRO_FONT_SLANT_OBLIQUE, "Oblique" },
101
  { CAIRO_FONT_SLANT_ITALIC, "Italic" }
102
};
103
104
static const FieldMap smallcaps_map[] = {
105
  { FALSE, "" },
106
  { TRUE, "Small-Caps" }
107
};
108
109
static const FieldMap weight_map[] = {
110
  { TWIN_WEIGHT_THIN, "Thin" },
111
  { TWIN_WEIGHT_ULTRALIGHT, "Ultra-Light" },
112
  { TWIN_WEIGHT_ULTRALIGHT, "Extra-Light" },
113
  { TWIN_WEIGHT_LIGHT, "Light" },
114
  { TWIN_WEIGHT_BOOK, "Book" },
115
  { TWIN_WEIGHT_NORMAL, "" },
116
  { TWIN_WEIGHT_NORMAL, "Regular" },
117
  { TWIN_WEIGHT_MEDIUM, "Medium" },
118
  { TWIN_WEIGHT_SEMIBOLD, "Semi-Bold" },
119
  { TWIN_WEIGHT_SEMIBOLD, "Demi-Bold" },
120
  { TWIN_WEIGHT_BOLD, "Bold" },
121
  { TWIN_WEIGHT_ULTRABOLD, "Ultra-Bold" },
122
  { TWIN_WEIGHT_ULTRABOLD, "Extra-Bold" },
123
  { TWIN_WEIGHT_HEAVY, "Heavy" },
124
  { TWIN_WEIGHT_HEAVY, "Black" },
125
  { TWIN_WEIGHT_ULTRAHEAVY, "Ultra-Heavy" },
126
  { TWIN_WEIGHT_ULTRAHEAVY, "Extra-Heavy" },
127
  { TWIN_WEIGHT_ULTRAHEAVY, "Ultra-Black" },
128
  { TWIN_WEIGHT_ULTRAHEAVY, "Extra-Black" }
129
};
130
131
static const FieldMap stretch_map[] = {
132
  { TWIN_STRETCH_ULTRA_CONDENSED, "Ultra-Condensed" },
133
  { TWIN_STRETCH_EXTRA_CONDENSED, "Extra-Condensed" },
134
  { TWIN_STRETCH_CONDENSED,       "Condensed" },
135
  { TWIN_STRETCH_SEMI_CONDENSED,  "Semi-Condensed" },
136
  { TWIN_STRETCH_NORMAL,          "" },
137
  { TWIN_STRETCH_SEMI_EXPANDED,   "Semi-Expanded" },
138
  { TWIN_STRETCH_EXPANDED,        "Expanded" },
139
  { TWIN_STRETCH_EXTRA_EXPANDED,  "Extra-Expanded" },
140
  { TWIN_STRETCH_ULTRA_EXPANDED,  "Ultra-Expanded" }
141
};
142
143
static const FieldMap monospace_map[] = {
144
  { FALSE, "" },
145
  { TRUE, "Mono" },
146
  { TRUE, "Monospace" }
147
};
148
149
150
typedef struct _twin_face_properties {
151
    cairo_font_slant_t  slant;
152
    twin_face_weight_t  weight;
153
    twin_face_stretch_t stretch;
154
155
    /* lets have some fun */
156
    cairo_bool_t monospace;
157
    cairo_bool_t smallcaps;
158
} twin_face_properties_t;
159
160
static cairo_bool_t
161
field_matches (const char *s1,
162
               const char *s2,
163
               int len)
164
0
{
165
0
  int c1, c2;
166
167
0
  while (len && *s1 && *s2)
168
0
    {
169
0
#define TOLOWER(c) \
170
0
   (((c) >= 'A' && (c) <= 'Z') ? (c) - 'A' + 'a' : (c))
171
172
0
      c1 = TOLOWER (*s1);
173
0
      c2 = TOLOWER (*s2);
174
0
      if (c1 != c2) {
175
0
        if (c1 == '-') {
176
0
          s1++;
177
0
          continue;
178
0
        }
179
0
        return FALSE;
180
0
      }
181
0
      s1++; s2++;
182
0
      len--;
183
0
    }
184
185
0
  return len == 0 && *s1 == '\0';
186
0
}
187
188
static cairo_bool_t
189
parse_int (const char *word,
190
     size_t      wordlen,
191
     int        *out)
192
0
{
193
0
  char *end;
194
0
  long val = strtol (word, &end, 10);
195
0
  int i = val;
196
197
0
  if (end != word && (end == word + wordlen) && val >= 0 && val == i)
198
0
    {
199
0
      if (out)
200
0
        *out = i;
201
202
0
      return TRUE;
203
0
    }
204
205
0
  return FALSE;
206
0
}
207
208
static cairo_bool_t
209
find_field (const char *what,
210
      const FieldMap *map,
211
      int n_elements,
212
      const char *str,
213
      int len,
214
      int *val)
215
0
{
216
0
  int i;
217
0
  cairo_bool_t had_prefix = FALSE;
218
219
0
  if (what)
220
0
    {
221
0
      i = strlen (what);
222
0
      if (len > i && 0 == strncmp (what, str, i) && str[i] == '=')
223
0
  {
224
0
    str += i + 1;
225
0
    len -= i + 1;
226
0
    had_prefix = TRUE;
227
0
  }
228
0
    }
229
230
0
  for (i=0; i<n_elements; i++)
231
0
    {
232
0
      if (map[i].str[0] && field_matches (map[i].str, str, len))
233
0
  {
234
0
    if (val)
235
0
      *val = map[i].value;
236
0
    return TRUE;
237
0
  }
238
0
    }
239
240
0
  if (!what || had_prefix)
241
0
    return parse_int (str, len, val);
242
243
0
  return FALSE;
244
0
}
245
246
static void
247
parse_field (twin_face_properties_t *props,
248
       const char *str,
249
       int len)
250
0
{
251
0
  if (field_matches ("Normal", str, len))
252
0
    return;
253
254
0
#define FIELD(NAME) \
255
0
  if (find_field (STRINGIFY (NAME), NAME##_map, ARRAY_LENGTH (NAME##_map), str, len, \
256
0
      (int *)(void *)&props->NAME)) \
257
0
      return; \
258
0
259
0
  FIELD (weight);
260
0
  FIELD (slant);
261
0
  FIELD (stretch);
262
0
  FIELD (smallcaps);
263
0
  FIELD (monospace);
264
265
0
#undef FIELD
266
0
}
267
268
static void
269
face_props_parse (twin_face_properties_t *props,
270
       const char *s)
271
0
{
272
0
    const char *start, *end;
273
274
0
    for (start = end = s; *end; end++) {
275
0
  if (*end != ' ' && *end != ':')
276
0
      continue;
277
278
0
  if (start < end)
279
0
    parse_field (props, start, end - start);
280
0
  start = end + 1;
281
0
    }
282
0
    if (start < end)
283
0
      parse_field (props, start, end - start);
284
0
}
285
286
static twin_face_properties_t *
287
twin_font_face_create_properties (cairo_font_face_t *twin_face)
288
0
{
289
0
    twin_face_properties_t *props;
290
291
0
    props = _cairo_malloc (sizeof (twin_face_properties_t));
292
0
    if (unlikely (props == NULL))
293
0
  return NULL;
294
295
0
    props->stretch  = TWIN_STRETCH_NORMAL;
296
0
    props->slant = CAIRO_FONT_SLANT_NORMAL;
297
0
    props->weight = TWIN_WEIGHT_NORMAL;
298
0
    props->monospace = FALSE;
299
0
    props->smallcaps = FALSE;
300
301
0
    if (unlikely (cairo_font_face_set_user_data (twin_face,
302
0
              &twin_properties_key,
303
0
              props, free))) {
304
0
  free (props);
305
0
  return NULL;
306
0
    }
307
308
0
    return props;
309
0
}
310
311
static cairo_status_t
312
twin_font_face_set_properties_from_toy (cairo_font_face_t *twin_face,
313
          cairo_toy_font_face_t *toy_face)
314
0
{
315
0
    twin_face_properties_t *props;
316
317
0
    props = twin_font_face_create_properties (twin_face);
318
0
    if (unlikely (props == NULL))
319
0
  return _cairo_error (CAIRO_STATUS_NO_MEMORY);
320
321
0
    props->slant = toy_face->slant;
322
0
    props->weight = toy_face->weight == CAIRO_FONT_WEIGHT_NORMAL ?
323
0
        TWIN_WEIGHT_NORMAL : TWIN_WEIGHT_BOLD;
324
0
    face_props_parse (props, toy_face->family);
325
326
0
    return CAIRO_STATUS_SUCCESS;
327
0
}
328
329
330
/*
331
 * Scaled properties
332
 */
333
334
typedef struct _twin_scaled_properties {
335
  twin_face_properties_t *face_props;
336
337
  cairo_bool_t snap; /* hint outlines */
338
339
  double weight; /* unhinted pen width */
340
  double penx, peny; /* hinted pen width */
341
  double marginl, marginr; /* hinted side margins */
342
343
  double stretch; /* stretch factor */
344
} twin_scaled_properties_t;
345
346
static void
347
compute_hinting_scale (cairo_t *cr,
348
           double x, double y,
349
           double *scale, double *inv)
350
0
{
351
0
    cairo_user_to_device_distance (cr, &x, &y);
352
0
    *scale = x == 0 ? y : y == 0 ? x :sqrt (x*x + y*y);
353
0
    *inv = 1 / *scale;
354
0
}
355
356
static void
357
compute_hinting_scales (cairo_t *cr,
358
      double *x_scale, double *x_scale_inv,
359
      double *y_scale, double *y_scale_inv)
360
0
{
361
0
    double x, y;
362
363
0
    x = 1; y = 0;
364
0
    compute_hinting_scale (cr, x, y, x_scale, x_scale_inv);
365
366
0
    x = 0; y = 1;
367
0
    compute_hinting_scale (cr, x, y, y_scale, y_scale_inv);
368
0
}
369
370
0
#define SNAPXI(p) (_cairo_round ((p) * x_scale) * x_scale_inv)
371
0
#define SNAPYI(p) (_cairo_round ((p) * y_scale) * y_scale_inv)
372
373
/* This controls the global font size */
374
0
#define F(g)    ((g) / 72.)
375
376
static void
377
twin_hint_pen_and_margins(cairo_t *cr,
378
        double *penx, double *peny,
379
        double *marginl, double *marginr)
380
0
{
381
0
    double x_scale, x_scale_inv;
382
0
    double y_scale, y_scale_inv;
383
0
    double margin;
384
385
0
    compute_hinting_scales (cr,
386
0
          &x_scale, &x_scale_inv,
387
0
          &y_scale, &y_scale_inv);
388
389
0
    *penx = SNAPXI (*penx);
390
0
    if (*penx < x_scale_inv)
391
0
  *penx = x_scale_inv;
392
393
0
    *peny = SNAPYI (*peny);
394
0
    if (*peny < y_scale_inv)
395
0
  *peny = y_scale_inv;
396
397
0
    margin = *marginl + *marginr;
398
0
    *marginl = SNAPXI (*marginl);
399
0
    if (*marginl < x_scale_inv)
400
0
  *marginl = x_scale_inv;
401
402
0
    *marginr = margin - *marginl;
403
0
    if (*marginr < 0)
404
0
  *marginr = 0;
405
0
    *marginr = SNAPXI (*marginr);
406
0
}
407
408
static cairo_status_t
409
twin_scaled_font_compute_properties (cairo_scaled_font_t *scaled_font,
410
             cairo_t           *cr)
411
0
{
412
0
    cairo_status_t status;
413
0
    twin_scaled_properties_t *props;
414
415
0
    props = _cairo_malloc (sizeof (twin_scaled_properties_t));
416
0
    if (unlikely (props == NULL))
417
0
  return _cairo_error (CAIRO_STATUS_NO_MEMORY);
418
419
420
0
    props->face_props = cairo_font_face_get_user_data (cairo_scaled_font_get_font_face (scaled_font),
421
0
                   &twin_properties_key);
422
423
0
    props->snap = scaled_font->options.hint_style > CAIRO_HINT_STYLE_NONE;
424
425
    /* weight */
426
0
    props->weight = props->face_props->weight * (F (4) / TWIN_WEIGHT_NORMAL);
427
428
    /* pen & margins */
429
0
    props->penx = props->peny = props->weight;
430
0
    props->marginl = props->marginr = F (4);
431
0
    if (scaled_font->options.hint_style > CAIRO_HINT_STYLE_SLIGHT)
432
0
  twin_hint_pen_and_margins(cr,
433
0
          &props->penx, &props->peny,
434
0
          &props->marginl, &props->marginr);
435
436
    /* stretch */
437
0
    props->stretch = 1 + .1 * ((int) props->face_props->stretch - (int) TWIN_STRETCH_NORMAL);
438
439
440
    /* Save it */
441
0
    status = cairo_scaled_font_set_user_data (scaled_font,
442
0
                &twin_properties_key,
443
0
                props, free);
444
0
    if (unlikely (status))
445
0
  goto FREE_PROPS;
446
447
0
    return CAIRO_STATUS_SUCCESS;
448
449
0
FREE_PROPS:
450
0
    free (props);
451
0
    return status;
452
0
}
453
454
455
/*
456
 * User-font implementation
457
 */
458
459
static cairo_status_t
460
twin_scaled_font_init (cairo_scaled_font_t  *scaled_font,
461
           cairo_t              *cr,
462
           cairo_font_extents_t *metrics)
463
0
{
464
0
  metrics->ascent  = F (54);
465
0
  metrics->descent = 1 - metrics->ascent;
466
467
0
  return twin_scaled_font_compute_properties (scaled_font, cr);
468
0
}
469
470
#define TWIN_GLYPH_MAX_SNAP_X 4
471
#define TWIN_GLYPH_MAX_SNAP_Y 7
472
473
typedef struct {
474
    int n_snap_x;
475
    int8_t snap_x[TWIN_GLYPH_MAX_SNAP_X];
476
    double snapped_x[TWIN_GLYPH_MAX_SNAP_X];
477
    int n_snap_y;
478
    int8_t snap_y[TWIN_GLYPH_MAX_SNAP_Y];
479
    double snapped_y[TWIN_GLYPH_MAX_SNAP_Y];
480
} twin_snap_info_t;
481
482
#define twin_glyph_left(g)      ((g)[0])
483
0
#define twin_glyph_right(g)     ((g)[1])
484
#define twin_glyph_ascent(g)    ((g)[2])
485
#define twin_glyph_descent(g)   ((g)[3])
486
487
0
#define twin_glyph_n_snap_x(g)  ((g)[4])
488
0
#define twin_glyph_n_snap_y(g)  ((g)[5])
489
0
#define twin_glyph_snap_x(g)    (&g[6])
490
0
#define twin_glyph_snap_y(g)    (twin_glyph_snap_x(g) + twin_glyph_n_snap_x(g))
491
0
#define twin_glyph_draw(g)      (twin_glyph_snap_y(g) + twin_glyph_n_snap_y(g))
492
493
static void
494
twin_compute_snap (cairo_t             *cr,
495
       twin_snap_info_t    *info,
496
       const signed char   *b)
497
0
{
498
0
    int     s, n;
499
0
    const signed char *snap;
500
0
    double x_scale, x_scale_inv;
501
0
    double y_scale, y_scale_inv;
502
503
0
    compute_hinting_scales (cr,
504
0
          &x_scale, &x_scale_inv,
505
0
          &y_scale, &y_scale_inv);
506
507
0
    snap = twin_glyph_snap_x (b);
508
0
    n = twin_glyph_n_snap_x (b);
509
0
    info->n_snap_x = n;
510
0
    assert (n <= TWIN_GLYPH_MAX_SNAP_X);
511
0
    for (s = 0; s < n; s++) {
512
0
  info->snap_x[s] = snap[s];
513
0
  info->snapped_x[s] = SNAPXI (F (snap[s]));
514
0
    }
515
516
0
    snap = twin_glyph_snap_y (b);
517
0
    n = twin_glyph_n_snap_y (b);
518
0
    info->n_snap_y = n;
519
0
    assert (n <= TWIN_GLYPH_MAX_SNAP_Y);
520
0
    for (s = 0; s < n; s++) {
521
0
  info->snap_y[s] = snap[s];
522
0
  info->snapped_y[s] = SNAPYI (F (snap[s]));
523
0
    }
524
0
}
525
526
static double
527
twin_snap (int8_t v, int n, int8_t *snap, double *snapped)
528
0
{
529
0
    int s;
530
531
0
    if (!n)
532
0
  return F(v);
533
534
0
    if (snap[0] == v)
535
0
  return snapped[0];
536
537
0
    for (s = 0; s < n - 1; s++)
538
0
    {
539
0
  if (snap[s+1] == v)
540
0
      return snapped[s+1];
541
542
0
  if (snap[s] <= v && v <= snap[s+1])
543
0
  {
544
0
      int before = snap[s];
545
0
      int after = snap[s+1];
546
0
      int dist = after - before;
547
0
      double snap_before = snapped[s];
548
0
      double snap_after = snapped[s+1];
549
0
      double dist_before = v - before;
550
0
      return snap_before + (snap_after - snap_before) * dist_before / dist;
551
0
  }
552
0
    }
553
0
    return F(v);
554
0
}
555
556
0
#define SNAPX(p)  twin_snap (p, info.n_snap_x, info.snap_x, info.snapped_x)
557
0
#define SNAPY(p)  twin_snap (p, info.n_snap_y, info.snap_y, info.snapped_y)
558
559
static cairo_status_t
560
twin_scaled_font_render_glyph (cairo_scaled_font_t  *scaled_font,
561
             unsigned long         glyph,
562
             cairo_t              *cr,
563
             cairo_text_extents_t *metrics)
564
0
{
565
0
    double x1, y1, x2, y2, x3, y3;
566
0
    double marginl;
567
0
    twin_scaled_properties_t *props;
568
0
    twin_snap_info_t info;
569
0
    const int8_t *b;
570
0
    const int8_t *g;
571
0
    int8_t w;
572
0
    double gw;
573
574
0
    props = cairo_scaled_font_get_user_data (scaled_font, &twin_properties_key);
575
576
    /* Save glyph space, we need it when stroking */
577
0
    cairo_save (cr);
578
579
    /* center the pen */
580
0
    cairo_translate (cr, props->penx * .5, -props->peny * .5);
581
582
    /* small-caps */
583
0
    if (props->face_props->smallcaps && glyph >= 'a' && glyph <= 'z') {
584
0
  glyph += 'A' - 'a';
585
  /* 28 and 42 are small and capital letter heights of the glyph data */
586
0
  cairo_scale (cr, 1, 28. / 42);
587
0
    }
588
589
    /* slant */
590
0
    if (props->face_props->slant != CAIRO_FONT_SLANT_NORMAL) {
591
0
  cairo_matrix_t shear = { 1, 0, -.2, 1, 0, 0};
592
0
  cairo_transform (cr, &shear);
593
0
    }
594
595
0
    b = _cairo_twin_outlines +
596
0
  _cairo_twin_charmap[unlikely (glyph >= ARRAY_LENGTH (_cairo_twin_charmap)) ? 0 : glyph];
597
0
    g = twin_glyph_draw(b);
598
0
    w = twin_glyph_right(b);
599
0
    gw = F(w);
600
601
0
    marginl = props->marginl;
602
603
    /* monospace */
604
0
    if (props->face_props->monospace) {
605
0
  double monow = F(24);
606
0
  double extra =  props->penx + props->marginl + props->marginr;
607
0
  cairo_scale (cr, (monow + extra) / (gw + extra), 1);
608
0
  gw = monow;
609
610
  /* resnap margin for new transform */
611
0
  {
612
0
      double x, y, x_scale, x_scale_inv;
613
0
      x = 1; y = 0;
614
0
      compute_hinting_scale (cr, x, y, &x_scale, &x_scale_inv);
615
0
      marginl = SNAPXI (marginl);
616
0
  }
617
0
    }
618
619
0
    cairo_translate (cr, marginl, 0);
620
621
    /* stretch */
622
0
    cairo_scale (cr, props->stretch, 1);
623
624
0
    if (props->snap)
625
0
  twin_compute_snap (cr, &info, b);
626
0
    else
627
0
  info.n_snap_x = info.n_snap_y = 0;
628
629
    /* advance width */
630
0
    metrics->x_advance = gw * props->stretch + props->penx + props->marginl + props->marginr;
631
632
    /* glyph shape */
633
0
    for (;;) {
634
0
  switch (*g++) {
635
0
  case 'M':
636
0
      cairo_close_path (cr);
637
      /* fall through */
638
0
  case 'm':
639
0
      x1 = SNAPX(*g++);
640
0
      y1 = SNAPY(*g++);
641
0
      cairo_move_to (cr, x1, y1);
642
0
      continue;
643
0
  case 'L':
644
0
      cairo_close_path (cr);
645
      /* fall through */
646
0
  case 'l':
647
0
      x1 = SNAPX(*g++);
648
0
      y1 = SNAPY(*g++);
649
0
      cairo_line_to (cr, x1, y1);
650
0
      continue;
651
0
  case 'C':
652
0
      cairo_close_path (cr);
653
      /* fall through */
654
0
  case 'c':
655
0
      x1 = SNAPX(*g++);
656
0
      y1 = SNAPY(*g++);
657
0
      x2 = SNAPX(*g++);
658
0
      y2 = SNAPY(*g++);
659
0
      x3 = SNAPX(*g++);
660
0
      y3 = SNAPY(*g++);
661
0
      cairo_curve_to (cr, x1, y1, x2, y2, x3, y3);
662
0
      continue;
663
0
  case 'E':
664
0
      cairo_close_path (cr);
665
      /* fall through */
666
0
  case 'e':
667
0
      cairo_restore (cr); /* restore glyph space */
668
0
      cairo_set_tolerance (cr, 0.01);
669
0
      cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
670
0
      cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
671
0
      cairo_set_line_width (cr, 1);
672
0
      cairo_scale (cr, props->penx, props->peny);
673
0
      cairo_stroke (cr);
674
0
      break;
675
0
  case 'X':
676
      /* filler */
677
0
      continue;
678
0
  }
679
0
  break;
680
0
    }
681
682
0
    return CAIRO_STATUS_SUCCESS;
683
0
}
684
685
static cairo_status_t
686
twin_scaled_font_unicode_to_glyph (cairo_scaled_font_t *scaled_font,
687
           unsigned long        unicode,
688
           unsigned long       *glyph)
689
0
{
690
    /* We use an identity charmap.  Which means we could live
691
     * with no unicode_to_glyph method too.  But we define this
692
     * to map all unknown chars to a single unknown glyph to
693
     * reduce pressure on cache. */
694
695
0
    if (likely (unicode < ARRAY_LENGTH (_cairo_twin_charmap)))
696
0
  *glyph = unicode;
697
0
    else
698
0
  *glyph = 0;
699
700
0
    return CAIRO_STATUS_SUCCESS;
701
0
}
702
703
704
/*
705
 * Face constructor
706
 */
707
708
static cairo_font_face_t *
709
_cairo_font_face_twin_create_internal (void)
710
0
{
711
0
    cairo_font_face_t *twin_font_face;
712
713
0
    twin_font_face = cairo_user_font_face_create ();
714
0
    cairo_user_font_face_set_init_func             (twin_font_face, twin_scaled_font_init);
715
0
    cairo_user_font_face_set_render_glyph_func     (twin_font_face, twin_scaled_font_render_glyph);
716
0
    cairo_user_font_face_set_unicode_to_glyph_func (twin_font_face, twin_scaled_font_unicode_to_glyph);
717
718
0
    return twin_font_face;
719
0
}
720
721
cairo_font_face_t *
722
_cairo_font_face_twin_create_fallback (void)
723
0
{
724
0
    cairo_font_face_t *twin_font_face;
725
726
0
    twin_font_face = _cairo_font_face_twin_create_internal ();
727
0
    if (! twin_font_face_create_properties (twin_font_face)) {
728
0
  cairo_font_face_destroy (twin_font_face);
729
0
  return (cairo_font_face_t *) &_cairo_font_face_nil;
730
0
    }
731
732
0
    return twin_font_face;
733
0
}
734
735
cairo_status_t
736
_cairo_font_face_twin_create_for_toy (cairo_toy_font_face_t   *toy_face,
737
              cairo_font_face_t      **font_face)
738
0
{
739
0
    cairo_status_t status;
740
0
    cairo_font_face_t *twin_font_face;
741
742
0
    twin_font_face = _cairo_font_face_twin_create_internal ();
743
0
    status = twin_font_face_set_properties_from_toy (twin_font_face, toy_face);
744
0
    if (status) {
745
0
  cairo_font_face_destroy (twin_font_face);
746
0
  return status;
747
0
    }
748
749
0
    *font_face = twin_font_face;
750
751
0
    return CAIRO_STATUS_SUCCESS;
752
0
}