Coverage Report

Created: 2026-10-28 08:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/base/gxttfb.c
Line
Count
Source
1
/* Copyright (C) 2001-2026 Artifex Software, Inc.
2
   All Rights Reserved.
3
4
   This software is provided AS-IS with no warranty, either express or
5
   implied.
6
7
   This software is distributed under license and may not be copied,
8
   modified or distributed except as expressly authorized under the terms
9
   of the license contained in the file LICENSE in this distribution.
10
11
   Refer to licensing information at http://www.artifex.com or contact
12
   Artifex Software, Inc.,  39 Mesa Street, Suite 108A, San Francisco,
13
   CA 94129, USA, for further information.
14
*/
15
16
17
/* A bridge to True Type interpreter. */
18
19
#include "gx.h"
20
#include "gxfont.h"
21
#include "gxfont42.h"
22
#include "gxttfb.h"
23
#include "gxfixed.h"
24
#include "gxpath.h"
25
#include "gxfcache.h"
26
#include "gxmatrix.h"
27
#include "gxhintn.h"
28
#include "gzpath.h"
29
#include "ttfmemd.h"
30
#include "gsstruct.h"
31
#include "gserrors.h"
32
#include "gsfont.h"
33
#include "gdebug.h"
34
#include "memory_.h"
35
#include "math_.h"
36
#include "gxgstate.h"
37
#include "gxpaint.h"
38
#include "gzspotan.h"
39
#include <stdarg.h>
40
41
gs_public_st_composite(st_gx_ttfReader, gx_ttfReader,
42
    "gx_ttfReader", gx_ttfReader_enum_ptrs, gx_ttfReader_reloc_ptrs);
43
44
static
45
0
ENUM_PTRS_WITH(gx_ttfReader_enum_ptrs, gx_ttfReader *mptr)
46
0
    {
47
        /* The fields 'pfont' and 'glyph_data' may contain pointers from global
48
           to local memory ( see a comment in gxttfb.h).
49
           They must be NULL when a garbager is invoked.
50
           Due to that we don't enumerate and don't relocate them.
51
         */
52
0
        DISCARD(mptr);
53
0
        return 0;
54
0
    }
55
0
    ENUM_PTR(0, gx_ttfReader, memory);
56
0
ENUM_PTRS_END
57
58
0
static RELOC_PTRS_WITH(gx_ttfReader_reloc_ptrs, gx_ttfReader *mptr)
59
0
    DISCARD(mptr);
60
0
    RELOC_PTR(gx_ttfReader, memory);
61
0
RELOC_PTRS_END
62
63
static bool gx_ttfReader__Eof(ttfReader *self)
64
1.50M
{
65
1.50M
    gx_ttfReader *r = (gx_ttfReader *)self;
66
67
1.50M
    if (r->extra_glyph_index != -1)
68
52.9k
        return r->pos >= r->glyph_data.bits.size;
69
    /* We can't know whether pfont->data.string_proc has more bytes,
70
       so we never report Eof for it. */
71
1.45M
    return false;
72
1.50M
}
73
74
static void gx_ttfReader__Read(ttfReader *self, void *p, int n)
75
6.03M
{
76
6.03M
    gx_ttfReader *r = (gx_ttfReader *)self;
77
6.03M
    const byte *q;
78
79
6.03M
    if (r->error >= 0) {
80
5.90M
        if (r->extra_glyph_index != -1) {
81
4.30M
            q = r->glyph_data.bits.data + r->pos;
82
4.30M
            r->error = ((r->pos >= r->glyph_data.bits.size ||
83
4.30M
                        r->glyph_data.bits.size - r->pos < n) ?
84
4.30M
                            gs_note_error(gs_error_invalidfont) : 0);
85
4.30M
            if (r->error == 0)
86
4.30M
                memcpy(p, q, n);
87
4.30M
        } else {
88
1.60M
            unsigned int cnt;
89
1.60M
            r->error = 0;
90
91
1.60M
            for (cnt = 0; cnt < (uint)n; cnt += r->error) {
92
1.60M
                r->error = r->pfont->data.string_proc(r->pfont, (ulong)r->pos + cnt, (ulong)n - cnt, &q);
93
1.60M
                if (r->error < 0)
94
0
                    break;
95
1.60M
                else if ( r->error == 0) {
96
1.60M
                    memcpy((char *)p + cnt, q, n - cnt);
97
1.60M
                    break;
98
1.60M
                } else {
99
0
                    memcpy((char *)p + cnt, q, r->error);
100
0
                }
101
1.60M
            }
102
1.60M
        }
103
5.90M
    }
104
6.03M
    if (r->error < 0) {
105
124k
        memset(p, 0, n);
106
124k
        return;
107
124k
    }
108
5.90M
    r->pos += n;
109
5.90M
}
110
111
static void gx_ttfReader__Seek(ttfReader *self, int nPos)
112
66.2k
{
113
66.2k
    gx_ttfReader *r = (gx_ttfReader *)self;
114
115
66.2k
    r->pos = nPos;
116
66.2k
}
117
118
static int gx_ttfReader__Tell(ttfReader *self)
119
92.8k
{
120
92.8k
    gx_ttfReader *r = (gx_ttfReader *)self;
121
122
92.8k
    return r->pos;
123
92.8k
}
124
125
static bool gx_ttfReader__Error(ttfReader *self)
126
94.4k
{
127
94.4k
    gx_ttfReader *r = (gx_ttfReader *)self;
128
129
94.4k
    return r->error;
130
94.4k
}
131
132
static int gx_ttfReader__LoadGlyph(ttfReader *self, int glyph_index, const byte **p, int *size)
133
52.3k
{
134
52.3k
    gx_ttfReader *r = (gx_ttfReader *)self;
135
52.3k
    gs_font_type42 *pfont = r->pfont;
136
52.3k
    int code;
137
138
52.3k
    if (r->extra_glyph_index != -1)
139
0
        return 0; /* We only maintain a single glyph buffer.
140
                     It's enough because ttfOutliner__BuildGlyphOutline
141
                     is optimized for that, and pfont->data.get_outline
142
                     implements a charstring cache. */
143
52.3k
    r->glyph_data.memory = pfont->memory;
144
52.3k
    code = pfont->data.get_outline(pfont, (uint)glyph_index, &r->glyph_data);
145
52.3k
    r->extra_glyph_index = glyph_index;
146
52.3k
    r->pos = 0;
147
52.3k
    if (code < 0)
148
0
        r->error = code;
149
52.3k
    else if (code > 0) {
150
        /* Should not happen. */
151
0
        r->error = gs_note_error(gs_error_unregistered);
152
52.3k
    } else {
153
52.3k
        *p = r->glyph_data.bits.data;
154
52.3k
        *size = r->glyph_data.bits.size;
155
52.3k
    }
156
52.3k
    return 2; /* found */
157
52.3k
}
158
159
static void gx_ttfReader__ReleaseGlyph(ttfReader *self, int glyph_index)
160
53.2k
{
161
53.2k
    gx_ttfReader *r = (gx_ttfReader *)self;
162
163
53.2k
    if (r->extra_glyph_index != glyph_index)
164
820
        return;
165
52.3k
    r->extra_glyph_index = -1;
166
52.3k
    gs_glyph_data_free(&r->glyph_data, "gx_ttfReader__ReleaseExtraGlyph");
167
52.3k
}
168
169
static void gx_ttfReader__Reset(gx_ttfReader *self)
170
53.8k
{
171
53.8k
    if (self->extra_glyph_index != -1) {
172
0
        self->extra_glyph_index = -1;
173
0
        gs_glyph_data_free(&self->glyph_data, "gx_ttfReader__Reset");
174
0
    }
175
53.8k
    self->error = 0;
176
53.8k
    self->pos = 0;
177
53.8k
}
178
179
gx_ttfReader *gx_ttfReader__create(gs_memory_t *mem)
180
2.13k
{
181
2.13k
    gx_ttfReader *r = gs_alloc_struct(mem, gx_ttfReader, &st_gx_ttfReader, "gx_ttfReader__create");
182
183
2.13k
    if (r != NULL) {
184
2.13k
        r->super.Eof = gx_ttfReader__Eof;
185
2.13k
        r->super.Read = gx_ttfReader__Read;
186
2.13k
        r->super.Seek = gx_ttfReader__Seek;
187
2.13k
        r->super.Tell = gx_ttfReader__Tell;
188
2.13k
        r->super.Error = gx_ttfReader__Error;
189
2.13k
        r->super.LoadGlyph = gx_ttfReader__LoadGlyph;
190
2.13k
        r->super.ReleaseGlyph = gx_ttfReader__ReleaseGlyph;
191
2.13k
        r->pos = 0;
192
2.13k
        r->error = 0;
193
2.13k
        r->extra_glyph_index = -1;
194
2.13k
        memset(&r->glyph_data, 0, sizeof(r->glyph_data));
195
2.13k
        r->pfont = NULL;
196
2.13k
        r->memory = mem;
197
2.13k
        gx_ttfReader__Reset(r);
198
2.13k
    }
199
2.13k
    return r;
200
2.13k
}
201
202
void gx_ttfReader__destroy(gx_ttfReader *self)
203
2.13k
{
204
2.13k
    gs_free_object(self->memory, self, "gx_ttfReader__destroy");
205
2.13k
}
206
207
static int
208
gx_ttfReader__default_get_metrics(const ttfReader *ttf, uint glyph_index, bool bVertical,
209
                                  short *sideBearing, unsigned short *nAdvance)
210
52.6k
{
211
52.6k
    gx_ttfReader *self = (gx_ttfReader *)ttf;
212
52.6k
    float sbw[4];
213
52.6k
    int sbw_offset = bVertical;
214
52.6k
    int code;
215
52.6k
    int factor = self->pfont->data.unitsPerEm;
216
217
52.6k
    code = self->pfont->data.get_metrics(self->pfont, glyph_index, bVertical, sbw);
218
52.6k
    if (code < 0)
219
260
        return code;
220
    /* Due to an obsolete convention, simple_glyph_metrics scales
221
       the metrics into 1x1 rectangle as Postscript like.
222
       In same time, the True Type interpreter needs
223
       the original design units.
224
       Undo the scaling here with accurate rounding. */
225
52.3k
    *sideBearing = (short)floor(sbw[0 + sbw_offset] * factor + 0.5);
226
52.3k
    *nAdvance = (short)floor(sbw[2 + sbw_offset] * factor + 0.5);
227
52.3k
    return 0;
228
52.6k
}
229
230
void gx_ttfReader__set_font(gx_ttfReader *self, gs_font_type42 *pfont)
231
107k
{
232
107k
    self->pfont = pfont;
233
107k
    self->super.get_metrics = gx_ttfReader__default_get_metrics;
234
107k
}
235
236
/*----------------------------------------------*/
237
238
static void DebugRepaint(ttfFont *ttf)
239
0
{
240
0
}
241
242
#ifdef DEBUG
243
static int DebugPrint(ttfFont *ttf, const char *fmt, ...)
244
{
245
    char buf[500];
246
    va_list args;
247
    int count;
248
249
    if (gs_debug_c('Y')) {
250
        va_start(args, fmt);
251
        count = vsnprintf(buf, sizeof(buf), fmt, args);
252
        /* NB: moved debug output from stdout to stderr
253
         */
254
        errwrite(ttf->DebugMem, buf, count);
255
        va_end(args);
256
    }
257
    return 0;
258
}
259
#endif
260
261
static void WarnBadInstruction(gs_font_type42 *pfont, int glyph_index)
262
0
{
263
0
    char buf[gs_font_name_max + 1];
264
0
    int l;
265
0
    gs_font_type42 *base_font = pfont;
266
267
0
    while ((gs_font_type42 *)base_font->base != base_font)
268
0
        base_font = (gs_font_type42 *)base_font->base;
269
0
    if (!base_font->data.warning_bad_instruction) {
270
0
        l = min(sizeof(buf) - 1, base_font->font_name.size);
271
0
        memcpy(buf, base_font->font_name.chars, l);
272
0
        buf[l] = 0;
273
0
        if (glyph_index >= 0)
274
0
            emprintf2(pfont->memory,
275
0
                      "Failed to interpret TT instructions for glyph index %d of font %s. "
276
0
                      "Continue ignoring instructions of the font.\n",
277
0
                      glyph_index, buf);
278
0
        else
279
0
            emprintf1(pfont->memory,
280
0
                      "Failed to interpret TT instructions in font %s. "
281
0
                      "Continue ignoring instructions of the font.\n",
282
0
                      buf);
283
0
        base_font->data.warning_bad_instruction = true;
284
0
    }
285
0
}
286
287
static void WarnPatented(gs_font_type42 *pfont, ttfFont *ttf, const char *txt)
288
0
{
289
0
    if (!ttf->design_grid) {
290
0
        char buf[gs_font_name_max + 1];
291
0
        int l;
292
0
        gs_font_type42 *base_font = pfont;
293
294
0
        while ((gs_font_type42 *)base_font->base != base_font)
295
0
            base_font = (gs_font_type42 *)base_font->base;
296
0
        if (!base_font->data.warning_patented) {
297
0
            l = min(sizeof(buf) - 1, base_font->font_name.size);
298
0
            memcpy(buf, base_font->font_name.chars, l);
299
0
            buf[l] = 0;
300
0
            emprintf2(pfont->memory,
301
0
                      "%s %s requires a patented True Type interpreter.\n",
302
0
                      txt,
303
0
                      buf);
304
0
            base_font->data.warning_patented = true;
305
0
        }
306
0
    }
307
0
}
308
309
/*----------------------------------------------*/
310
311
struct gx_ttfMemory_s {
312
    ttfMemory super;
313
    gs_memory_t *memory;
314
};
315
316
gs_private_st_simple(st_gx_ttfMemory, gx_ttfMemory, "gx_ttfMemory");
317
/* st_gx_ttfMemory::memory points to a root. */
318
319
static void *gx_ttfMemory__alloc_bytes(ttfMemory *self, int size,  const char *cname)
320
39.3k
{
321
39.3k
    gs_memory_t *mem = ((gx_ttfMemory *)self)->memory;
322
323
39.3k
    return gs_alloc_bytes(mem, size, cname);
324
39.3k
}
325
326
static void *gx_ttfMemory__alloc_struct(ttfMemory *self, const ttfMemoryDescriptor *d,  const char *cname)
327
6.49k
{
328
6.49k
    gs_memory_t *mem = ((gx_ttfMemory *)self)->memory;
329
330
6.49k
    return mem->procs.alloc_struct(mem, (const gs_memory_struct_type_t *)d, cname);
331
6.49k
}
332
333
static void gx_ttfMemory__free(ttfMemory *self, void *p,  const char *cname)
334
71.2k
{
335
71.2k
    gs_memory_t *mem = ((gx_ttfMemory *)self)->memory;
336
337
71.2k
    gs_free_object(mem, p, cname);
338
71.2k
}
339
340
/*----------------------------------------------*/
341
342
static inline float reminder(float v, int x)
343
107k
{
344
107k
    return ((v / x) - floor(v / x)) * x;
345
107k
}
346
347
static void decompose_matrix(const gs_font_type42 *pfont, const gs_matrix * char_tm,
348
    const gs_log2_scale_point *log2_scale, bool design_grid,
349
    gs_point *char_size, gs_point *subpix_origin, gs_matrix *post_transform, bool *dg)
350
53.8k
{
351
    /*
352
     *  char_tm maps to subpixels.
353
     */
354
    /*
355
     *  We use a Free Type 1 True Type interpreter, which cannot perform
356
     *  a grid-fitting with skewing/rotation. It appears acceptable
357
     *  because we want to minimize invocations of patented instructions.
358
     *  We believe that skewing/rotation requires the patented intrivial cases
359
     *  of projection/freedom vectors.
360
     */
361
53.8k
    int scale_x = 1 << log2_scale->x;
362
53.8k
    int scale_y = 1 << log2_scale->y;
363
53.8k
    bool atp = gs_currentaligntopixels(pfont->dir);
364
53.8k
    bool design_grid1;
365
366
53.8k
    char_size->x = hypot(char_tm->xx, char_tm->xy);
367
53.8k
    char_size->y = hypot(char_tm->yx, char_tm->yy);
368
53.8k
    if (char_size->x <= 2 && char_size->y <= 2) {
369
        /* Disable the grid fitting for very small fonts. */
370
0
        design_grid1 = true;
371
0
    } else
372
53.8k
        design_grid1 = design_grid || !(gs_currentgridfittt(pfont->dir) & 1);
373
53.8k
    *dg = design_grid1;
374
53.8k
    subpix_origin->x = (atp ? 0 : reminder(char_tm->tx, scale_x) / scale_x);
375
53.8k
    subpix_origin->y = (atp ? 0 : reminder(char_tm->ty, scale_y) / scale_y);
376
53.8k
    post_transform->xx = char_tm->xx / (design_grid1 ? 1 : char_size->x);
377
53.8k
    post_transform->xy = char_tm->xy / (design_grid1 ? 1 : char_size->x);
378
53.8k
    post_transform->yx = char_tm->yx / (design_grid1 ? 1 : char_size->y);
379
53.8k
    post_transform->yy = char_tm->yy / (design_grid1 ? 1 : char_size->y);
380
53.8k
    post_transform->tx = char_tm->tx - subpix_origin->x;
381
53.8k
    post_transform->ty = char_tm->ty - subpix_origin->y;
382
53.8k
}
383
384
/*----------------------------------------------*/
385
386
ttfFont *ttfFont__create(gs_font_dir *dir)
387
2.13k
{
388
2.13k
    gs_memory_t *mem = dir->memory->stable_memory;
389
2.13k
    ttfFont *ttf;
390
391
2.13k
    if (dir->ttm == NULL) {
392
1.11k
        gx_ttfMemory *m = gs_alloc_struct(mem, gx_ttfMemory, &st_gx_ttfMemory, "ttfFont__create(gx_ttfMemory)");
393
394
1.11k
        if (!m)
395
0
            return 0;
396
1.11k
        m->super.alloc_struct = gx_ttfMemory__alloc_struct;
397
1.11k
        m->super.alloc_bytes = gx_ttfMemory__alloc_bytes;
398
1.11k
        m->super.free = gx_ttfMemory__free;
399
1.11k
        m->memory = mem;
400
1.11k
        dir->ttm = m;
401
1.11k
    }
402
2.13k
    if(ttfInterpreter__obtain(&dir->ttm->super, &dir->tti))
403
0
        return 0;
404
2.13k
    if(gx_san__obtain(mem, &dir->san))
405
0
        return 0;
406
2.13k
    ttf = gs_alloc_struct(mem, ttfFont, &st_ttfFont, "ttfFont__create");
407
2.13k
    if (ttf == NULL)
408
0
        return 0;
409
#ifdef DEBUG
410
    ttfFont__init(ttf, &dir->ttm->super, DebugRepaint,
411
                  (gs_debug_c('Y') ? DebugPrint : NULL), mem);
412
#else
413
2.13k
    ttfFont__init(ttf, &dir->ttm->super, DebugRepaint, NULL, mem);
414
2.13k
#endif
415
416
2.13k
    return ttf;
417
2.13k
}
418
419
void ttfFont__destroy(ttfFont *self, gs_font_dir *dir)
420
2.13k
{
421
2.13k
    gs_memory_t *mem = dir->memory->stable_memory;
422
423
2.13k
    ttfFont__finit(self);
424
2.13k
    gs_free_object(mem, self, "ttfFont__destroy");
425
2.13k
    ttfInterpreter__release(&dir->tti);
426
2.13k
    gx_san__release(&dir->san);
427
2.13k
    if (dir->tti == NULL && dir->ttm != NULL) {
428
1.11k
        gs_free_object(mem, dir->ttm, "ttfFont__destroy(gx_ttfMemory)");
429
1.11k
        dir->ttm = NULL;
430
1.11k
    }
431
2.13k
}
432
433
int ttfFont__Open_aux(ttfFont *self, ttfInterpreter *tti, gx_ttfReader *r, gs_font_type42 *pfont,
434
               const gs_matrix * char_tm, const gs_log2_scale_point *log2_scale,
435
               bool design_grid)
436
2.13k
{
437
2.13k
    gs_point char_size, subpix_origin;
438
2.13k
    gs_matrix post_transform;
439
    /*
440
     * Ghostscript proceses a TTC index in gs/lib/gs_ttf.ps,
441
     * and *pfont already adjusted to it.
442
     * Therefore TTC headers never comes here.
443
     */
444
2.13k
    unsigned int nTTC = 0;
445
2.13k
    bool dg;
446
447
2.13k
    decompose_matrix(pfont, char_tm, log2_scale, design_grid, &char_size, &subpix_origin, &post_transform, &dg);
448
2.13k
    switch(ttfFont__Open(tti, self, &r->super, nTTC, char_size.x, char_size.y, dg)) {
449
2.13k
        case fNoError:
450
2.13k
            return 0;
451
0
        case fMemoryError:
452
0
            return_error(gs_error_VMerror);
453
0
        case fUnimplemented:
454
0
            return_error(gs_error_unregistered);
455
0
        case fBadInstruction:
456
0
            WarnBadInstruction(pfont, -1);
457
0
            goto recover;
458
0
        case fPatented:
459
0
            WarnPatented(pfont, self, "The font");
460
0
        recover:
461
0
            self->patented = true;
462
0
            return 0;
463
0
        default:
464
0
            { int code = r->super.Error(&r->super);
465
466
0
                if (code < 0)
467
0
                    return code;
468
0
                return_error(gs_error_invalidfont);
469
0
            }
470
2.13k
    }
471
2.13k
}
472
473
/*----------------------------------------------*/
474
475
typedef struct gx_ttfExport_s {
476
    ttfExport super;
477
    gx_path *path;
478
    gs_fixed_point w;
479
    int error;
480
    bool monotonize;
481
} gx_ttfExport;
482
483
static void gx_ttfExport__MoveTo(ttfExport *self, FloatPoint *p)
484
71.9k
{
485
71.9k
    gx_ttfExport *e = (gx_ttfExport *)self;
486
487
71.9k
    if (e->error >= 0)
488
71.9k
        e->error = gx_path_add_point(e->path, float2fixed(p->x), float2fixed(p->y));
489
71.9k
}
490
491
static void gx_ttfExport__LineTo(ttfExport *self, FloatPoint *p)
492
326k
{
493
326k
    gx_ttfExport *e = (gx_ttfExport *)self;
494
495
326k
    if (e->error >= 0)
496
326k
        e->error = gx_path_add_line_notes(e->path, float2fixed(p->x), float2fixed(p->y), sn_none);
497
326k
}
498
499
static void gx_ttfExport__CurveTo(ttfExport *self, FloatPoint *p0, FloatPoint *p1, FloatPoint *p2)
500
844k
{
501
844k
    gx_ttfExport *e = (gx_ttfExport *)self;
502
503
844k
    if (e->error >= 0) {
504
844k
        if (e->monotonize) {
505
844k
            curve_segment s;
506
507
844k
            s.notes = sn_none;
508
844k
            s.p1.x = float2fixed(p0->x), s.p1.y = float2fixed(p0->y),
509
844k
            s.p2.x = float2fixed(p1->x), s.p2.y = float2fixed(p1->y),
510
844k
            s.pt.x = float2fixed(p2->x), s.pt.y = float2fixed(p2->y);
511
844k
            e->error = gx_curve_monotonize(e->path, &s);
512
844k
        } else
513
0
            e->error = gx_path_add_curve_notes(e->path, float2fixed(p0->x), float2fixed(p0->y),
514
0
                                     float2fixed(p1->x), float2fixed(p1->y),
515
0
                                     float2fixed(p2->x), float2fixed(p2->y), sn_none);
516
844k
    }
517
844k
}
518
519
static void gx_ttfExport__Close(ttfExport *self)
520
71.9k
{
521
71.9k
    gx_ttfExport *e = (gx_ttfExport *)self;
522
523
71.9k
    if (e->error >= 0)
524
71.9k
        e->error = gx_path_close_subpath_notes(e->path, sn_none);
525
71.9k
}
526
527
static void gx_ttfExport__Point(ttfExport *self, FloatPoint *p, bool bOnCurve, bool bNewPath)
528
0
{
529
    /* Never called. */
530
0
}
531
532
static void gx_ttfExport__SetWidth(ttfExport *self, FloatPoint *p)
533
50.3k
{
534
50.3k
    gx_ttfExport *e = (gx_ttfExport *)self;
535
536
50.3k
    e->w.x = float2fixed(p->x);
537
50.3k
    e->w.y = float2fixed(p->y);
538
50.3k
}
539
540
static void gx_ttfExport__DebugPaint(ttfExport *self)
541
0
{
542
0
}
543
544
/*----------------------------------------------*/
545
546
static int
547
path_to_hinter(t1_hinter *h, gx_path *path)
548
0
{   int code;
549
0
    gs_path_enum penum;
550
0
    gs_fixed_point pts[3];
551
0
    gs_fixed_point p = {0, 0}; /* initialize to avoid a warning */
552
0
    bool first = true;
553
0
    int op;
554
555
0
    code = gx_path_enum_init(&penum, path);
556
0
    if (code < 0)
557
0
        return code;
558
0
    while ((op = gx_path_enum_next(&penum, pts)) != 0) {
559
0
        switch (op) {
560
0
            case gs_pe_moveto:
561
0
                if (first) {
562
0
                    first = false;
563
0
                    p = pts[0];
564
0
                    code = t1_hinter__rmoveto(h, p.x, p.y);
565
0
                } else
566
0
                    code = t1_hinter__rmoveto(h, pts[0].x - p.x, pts[0].y - p.y);
567
0
                break;
568
0
            case gs_pe_lineto:
569
0
            case gs_pe_gapto:
570
0
                code = t1_hinter__rlineto(h, pts[0].x - p.x, pts[0].y - p.y);
571
0
                break;
572
0
            case gs_pe_curveto:
573
0
                code = t1_hinter__rcurveto(h, pts[0].x - p.x, pts[0].y - p.y,
574
0
                                        pts[1].x - pts[0].x, pts[1].y - pts[0].y,
575
0
                                        pts[2].x - pts[1].x, pts[2].y - pts[1].y);
576
0
                pts[0] = pts[2];
577
0
                break;
578
0
            case gs_pe_closepath:
579
0
                code = t1_hinter__closepath(h);
580
0
                break;
581
0
            default:
582
0
                return_error(gs_error_unregistered);
583
0
        }
584
0
        if (code < 0)
585
0
            return code;
586
0
        p = pts[0];
587
0
    }
588
0
    return 0;
589
0
}
590
591
0
#define exch(a,b) a^=b; b^=a; a^=b;
592
593
static void
594
transpose_path(gx_path *path)
595
0
{   segment *s = (segment *)path->first_subpath;
596
597
0
    exch(path->bbox.p.x, path->bbox.p.y);
598
0
    exch(path->bbox.q.x, path->bbox.q.y);
599
0
    for (; s; s = s->next) {
600
0
        if (s->type == s_curve) {
601
0
            curve_segment *c = (curve_segment *)s;
602
603
0
            exch(c->p1.x, c->p1.y);
604
0
            exch(c->p2.x, c->p2.y);
605
0
        }
606
0
        exch(s->pt.x, s->pt.y);
607
0
    }
608
0
}
609
610
typedef struct {
611
    t1_hinter super;
612
    int transpose;
613
    fixed midx;
614
} t1_hinter_aux;
615
616
static int
617
stem_hint_handler(void *client_data, gx_san_sect *ss)
618
0
{
619
0
    t1_hinter_aux *h = (t1_hinter_aux *)client_data;
620
621
0
    if (ss->side_mask == 3) {
622
        /* Orient horizontal hints to help with top/bottom alignment zones.
623
           Otherwize glyphs may get a random height due to serif adjustsment. */
624
0
        if (ss->xl > h->midx && h->transpose)
625
0
            return (h->transpose ? t1_hinter__hstem : t1_hinter__vstem)
626
0
                        (&h->super, ss->xr, ss->xl - ss->xr);
627
0
        else
628
0
            return (h->transpose ? t1_hinter__hstem : t1_hinter__vstem)
629
0
                        (&h->super, ss->xl, ss->xr - ss->xl);
630
0
    } else
631
0
        return t1_hinter__overall_hstem(&h->super, ss->xl, ss->xr - ss->xl, ss->side_mask);
632
0
}
633
634
0
#define OVERALL_HINT 0 /* Overall hints help to emulate Type 1 alignment zones
635
                          (except for overshoot suppression.)
636
                          For example, without it comparefiles/type42_glyph_index.ps
637
                          some glyphs have different height due to
638
                          serifs are aligned in same way as horizontal stems,
639
                          but both sides of a stem have same priority.
640
641
                          This stuff appears low useful, because horizontal
642
                          hint orientation performs this job perfectly.
643
                          fixme : remove.
644
                          fixme : remove side_mask from gxhintn.c .
645
                          */
646
647
static int grid_fit(gx_device_spot_analyzer *padev, gx_path *path,
648
        gs_font_type42 *pfont, const gs_log2_scale_point *pscale, gx_ttfExport *e, ttfOutliner *o)
649
0
{
650
    /* Not completed yet. */
651
0
    gs_gstate gs_stub;
652
0
    gx_fill_params params;
653
0
    gx_device_color devc_stub;
654
0
    int code;
655
0
    t1_hinter_aux h;
656
0
    gs_matrix m, fm, fmb;
657
0
    gs_matrix_fixed ctm_temp;
658
0
    bool atp = gs_currentaligntopixels(pfont->dir);
659
0
    int FontType = 1; /* Will apply Type 1 hinter. */
660
0
    fixed sbx = 0, sby = 0; /* stub */
661
0
    double scale = 1.0 / o->pFont->nUnitsPerEm;
662
0
    gs_fixed_rect bbox;
663
664
0
    m.xx = o->post_transform.a;
665
0
    m.xy = o->post_transform.b;
666
0
    m.yx = o->post_transform.c;
667
0
    m.yy = o->post_transform.d;
668
0
    m.tx = o->post_transform.tx;
669
0
    m.ty = o->post_transform.ty;
670
0
    code = gs_matrix_fixed_from_matrix(&ctm_temp, &m);
671
0
    if (code < 0)
672
0
        return code;
673
0
    code = gs_matrix_scale(&pfont->FontMatrix, scale, scale, &fm);
674
0
    if (code < 0)
675
0
        return code;
676
0
    code = gs_matrix_scale(&pfont->base->FontMatrix, scale, scale, &fmb);
677
0
    if (code < 0)
678
0
        return code;
679
0
    t1_hinter__init(&h.super, path); /* Will export to */
680
0
    code = t1_hinter__set_mapping(&h.super, &ctm_temp,
681
0
                        &fm, &fmb,
682
0
                        pscale->x, pscale->x, 0, 0,
683
0
                        ctm_temp.tx_fixed, ctm_temp.ty_fixed, atp);
684
0
    if (code < 0)
685
0
        return code;
686
0
    if (!h.super.disable_hinting) {
687
0
        o->post_transform.a = o->post_transform.d = 1;
688
0
        o->post_transform.b = o->post_transform.c = 0;
689
0
        o->post_transform.tx = o->post_transform.ty = 0;
690
0
        code = ttfOutliner__DrawGlyphOutline(o);
691
0
        if (code < 0)
692
0
            return code;
693
0
        code = t1_hinter__set_font42_data(&h.super, FontType, &pfont->data, false);
694
0
        if (code < 0)
695
0
            return code;
696
0
        code = t1_hinter__sbw(&h.super, sbx, sby, e->w.x, e->w.y);
697
0
        if (code < 0)
698
0
            return code;
699
0
        code = gx_path_bbox(path, &bbox);
700
0
        if (code < 0)
701
0
            return code;
702
0
        memset(&gs_stub, 0, sizeof(gs_stub));
703
0
        gs_stub.memory = padev->memory;
704
0
        set_nonclient_dev_color(&devc_stub, 1);
705
0
        params.rule = gx_rule_winding_number;
706
0
        params.adjust.x = params.adjust.y = 0;
707
0
        params.flatness = fixed2float(max(bbox.q.x - bbox.p.x, bbox.q.y - bbox.p.y)) / 100.0;
708
709
0
        for (h.transpose = 0; h.transpose < 2; h.transpose++) {
710
0
            h.midx = (padev->xmin + padev->xmax) / 2;
711
0
            if (h.transpose)
712
0
                transpose_path(path);
713
0
            gx_san_begin(padev);
714
0
            code = dev_proc(padev, fill_path)((gx_device *)padev,
715
0
                            &gs_stub, path, &params, &devc_stub, NULL);
716
0
            gx_san_end(padev);
717
0
            if (code >= 0)
718
0
                code = gx_san_generate_stems(padev, OVERALL_HINT && h.transpose,
719
0
                                &h, stem_hint_handler);
720
0
            if (h.transpose)
721
0
                transpose_path(path);
722
0
            if (code < 0)
723
0
                return code;
724
0
        }
725
726
        /*  fixme : Storing hints permanently would be useful.
727
            Note that if (gftt & 1), the outline and hints are already scaled.
728
        */
729
0
        code = path_to_hinter(&h.super, path);
730
0
        if (code < 0)
731
0
            return code;
732
0
        code = gx_path_new(path);
733
0
        if (code < 0)
734
0
            return code;
735
0
        code = t1_hinter__endglyph(&h.super);
736
0
    } else {
737
0
        code = ttfOutliner__DrawGlyphOutline(o);
738
0
        if (code < 0)
739
0
            return e->error;
740
0
    }
741
0
    return code;
742
0
}
743
744
int gx_ttf_outline(ttfFont *ttf, gx_ttfReader *r, gs_font_type42 *pfont, int glyph_index,
745
        const gs_matrix *m, const gs_log2_scale_point *pscale,
746
        gx_path *path, bool design_grid)
747
51.7k
{
748
51.7k
    gx_ttfExport e;
749
51.7k
    ttfOutliner o;
750
51.7k
    gs_point char_size, subpix_origin;
751
51.7k
    gs_matrix post_transform;
752
    /* Ghostscript proceses a TTC index in gs/lib/gs_ttf.ps, */
753
    /* so that TTC never comes here. */
754
51.7k
    FloatMatrix m1;
755
51.7k
    bool dg;
756
51.7k
    uint gftt = 2;
757
51.7k
    bool ttin = (gftt & 1);
758
51.7k
    int code;
759
    /*  gs_currentgridfittt values (binary) :
760
        00 - no grid fitting;
761
        01 - Grid fit with TT interpreter; On failure warn and render unhinted.
762
        10 - Interpret in the design grid and then autohint.
763
        11 - Grid fit with TT interpreter; On failure render autohinted.
764
    */
765
51.7k
    bool auth = (gftt & 2);
766
767
51.7k
    decompose_matrix(pfont, m, pscale, design_grid, &char_size, &subpix_origin, &post_transform, &dg);
768
51.7k
    m1.a = post_transform.xx;
769
51.7k
    m1.b = post_transform.xy;
770
51.7k
    m1.c = post_transform.yx;
771
51.7k
    m1.d = post_transform.yy;
772
51.7k
    m1.tx = post_transform.tx;
773
51.7k
    m1.ty = post_transform.ty;
774
51.7k
    e.super.bPoints = false;
775
51.7k
    e.super.bOutline = true;
776
51.7k
    e.super.MoveTo = gx_ttfExport__MoveTo;
777
51.7k
    e.super.LineTo = gx_ttfExport__LineTo;
778
51.7k
    e.super.CurveTo = gx_ttfExport__CurveTo;
779
51.7k
    e.super.Close = gx_ttfExport__Close;
780
51.7k
    e.super.Point = gx_ttfExport__Point;
781
51.7k
    e.super.SetWidth = gx_ttfExport__SetWidth;
782
51.7k
    e.super.DebugPaint = gx_ttfExport__DebugPaint;
783
51.7k
    e.error = 0;
784
51.7k
    e.path = path;
785
51.7k
    e.w.x = 0;
786
51.7k
    e.w.y = 0;
787
51.7k
    e.monotonize = auth;
788
51.7k
    gx_ttfReader__Reset(r);
789
51.7k
    ttfOutliner__init(&o, ttf, &r->super, &e.super, true, false, pfont->WMode != 0);
790
51.7k
    switch(ttfOutliner__Outline(&o, glyph_index, subpix_origin.x, subpix_origin.y, &m1)) {
791
0
        case fBadInstruction:
792
0
            WarnBadInstruction(pfont, glyph_index);
793
0
            goto recover;
794
0
        case fPatented:
795
            /* The returned outline did not apply a bytecode (it is not grid-fitted). */
796
0
            if (!auth)
797
0
                WarnPatented(pfont, ttf, "Some glyphs of the font");
798
0
        recover :
799
0
            if (!design_grid && auth)
800
0
                return grid_fit(pfont->dir->san, path, pfont, pscale, &e, &o);
801
            /* Falls through. */
802
50.3k
        case fNoError:
803
50.3k
            if (!design_grid && !ttin && auth)
804
0
                return grid_fit(pfont->dir->san, path, pfont, pscale, &e, &o);
805
50.3k
            code = ttfOutliner__DrawGlyphOutline(&o);
806
50.3k
            if (code < 0)
807
0
                return code;
808
50.3k
            return 0;
809
0
        case fMemoryError:
810
0
            return_error(gs_error_VMerror);
811
0
        case fUnimplemented:
812
0
            return_error(gs_error_unregistered);
813
1.38k
        case fBadFontData:
814
1.38k
            return_error(gs_error_invalidfont);
815
0
        default:
816
0
            { int code = r->super.Error(&r->super);
817
818
0
                if (code < 0)
819
0
                    return code;
820
0
                return_error(gs_error_invalidfont);
821
0
            }
822
51.7k
    }
823
51.7k
}