Coverage Report

Created: 2022-04-16 11:23

/src/ghostpdl/base/gxfapi.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2021 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, modified
8
   or distributed except as expressly authorized under the terms of that
9
   license.  Refer to licensing information at http://www.artifex.com/
10
   or contact Artifex Software, Inc.,  1305 Grant Avenue - Suite 200,
11
   Novato, CA 94945, U.S.A., +1(415)492-9861, for further information.
12
*/
13
14
15
#include "memory_.h"
16
17
#include "gsmemory.h"
18
#include "gserrors.h"
19
#include "gxdevice.h"
20
#include "gxfont.h"
21
#include "gxfont1.h"
22
#include "gxpath.h"
23
#include "gxfcache.h"
24
#include "gxchrout.h"
25
#include "gximask.h"
26
#include "gscoord.h"
27
#include "gspaint.h"
28
#include "gspath.h"
29
#include "gzstate.h"
30
#include "gxfcid.h"
31
#include "gxchar.h"             /* for st_gs_show_enum and MAX_CCACHE_TEMP_BITMAP_BITS */
32
#include "gdebug.h"
33
#include "gsimage.h"
34
#include "gsbittab.h"
35
#include "gzpath.h"
36
#include "gxdevsop.h"
37
38
#include "gxfapi.h"
39
40
extern_gs_get_fapi_server_inits();
41
42
/* FIXME */
43
static int
44
gs_fapi_renderer_retcode(gs_memory_t *mem, gs_fapi_server *I,
45
                         gs_fapi_retcode rc)
46
436k
{
47
436k
    if (rc == 0)
48
434k
        return 0;
49
1.98k
    if (gs_debug_c('1')) {
50
0
        emprintf2(mem,
51
0
                  "Error: Font Renderer Plugin ( %s ) return code = %d\n",
52
0
                  I->ig.d->subtype, rc);
53
0
    }
54
1.98k
    return rc < 0 ? rc : gs_error_invalidfont;
55
436k
}
56
57
typedef struct gs_fapi_outline_handler_s
58
{
59
    gs_fapi_server *fserver;
60
    struct gx_path_s *path;
61
    fixed x0;
62
    fixed y0;
63
    bool close_path;
64
    bool need_close;            /* This stuff fixes unclosed paths being rendered with UFST */
65
} gs_fapi_outline_handler;
66
67
static inline int64_t
68
import_shift(int64_t x, int64_t n)
69
154k
{
70
154k
    return (n > 0 ? (x << n) : (x >> -n));
71
154k
}
72
73
static inline int
74
export_shift(int x, int n)
75
8.02k
{
76
8.02k
    return (n > 0 ? (x >> n) : (x << -n));
77
8.02k
}
78
79
static inline int
80
fapi_round(double x)
81
0
{
82
0
    return (int)(x + 0.5);
83
0
}
84
85
static int
86
add_closepath(gs_fapi_path *I)
87
3.67k
{
88
3.67k
    gs_fapi_outline_handler *olh = (gs_fapi_outline_handler *) I->olh;
89
90
3.67k
    if (olh->need_close == true) {
91
3.40k
        olh->need_close = false;
92
3.40k
        I->gs_error = gx_path_close_subpath_notes(olh->path, 0);
93
3.40k
    }
94
3.67k
    return (I->gs_error);
95
3.67k
}
96
97
static int
98
add_move(gs_fapi_path *I, int64_t x, int64_t y)
99
4.66k
{
100
4.66k
    gs_fapi_outline_handler *olh = (gs_fapi_outline_handler *) I->olh;
101
102
4.66k
    x = import_shift(x, I->shift);
103
4.66k
    y = -import_shift(y, I->shift);
104
4.66k
    if (olh->fserver->transform_outline) {
105
1.40k
        gs_point pt;
106
1.40k
        I->gs_error = gs_distance_transform((double)fixed2float((float)x), (double)fixed2float((float)y), &olh->fserver->outline_mat, &pt);
107
1.40k
        if (I->gs_error < 0)
108
0
            return I->gs_error;
109
1.40k
        x = float2fixed(pt.x);
110
1.40k
        y = float2fixed(pt.y);
111
1.40k
    }
112
4.66k
    x += olh->x0;
113
4.66k
    y += olh->y0;
114
115
4.66k
    if (x > (int64_t) max_coord_fixed || x < (int64_t) min_coord_fixed
116
4.66k
     || y > (int64_t) max_coord_fixed || y < (int64_t) min_coord_fixed) {
117
547
         I->gs_error = gs_error_undefinedresult;
118
547
    }
119
4.11k
    else {
120
121
4.11k
        if (olh->need_close && olh->close_path)
122
1.21k
            if ((I->gs_error = add_closepath(I)) < 0)
123
0
                return (I->gs_error);
124
4.11k
        olh->need_close = false;
125
126
/*        dprintf2("%f %f moveto\n", fixed2float(x), fixed2float(y)); */
127
4.11k
        I->gs_error = gx_path_add_point(olh->path, (fixed) x, (fixed) y);
128
4.11k
    }
129
4.66k
    return (I->gs_error);
130
4.66k
}
131
132
static int
133
add_line(gs_fapi_path *I, int64_t x, int64_t y)
134
17.2k
{
135
17.2k
    gs_fapi_outline_handler *olh = (gs_fapi_outline_handler *) I->olh;
136
137
17.2k
    x = import_shift(x, I->shift);
138
17.2k
    y = -import_shift(y, I->shift);
139
17.2k
    if (olh->fserver->transform_outline) {
140
1.48k
        gs_point pt;
141
1.48k
        I->gs_error = gs_distance_transform((double)fixed2float((float)x), (double)fixed2float((float)y), &olh->fserver->outline_mat, &pt);
142
1.48k
        if (I->gs_error < 0)
143
0
            return I->gs_error;
144
1.48k
        x = float2fixed(pt.x);
145
1.48k
        y = float2fixed(pt.y);
146
1.48k
    }
147
17.2k
    x += olh->x0;
148
17.2k
    y += olh->y0;
149
150
17.2k
    if (x > (int64_t) max_coord_fixed || x < (int64_t) min_coord_fixed
151
17.2k
     || y > (int64_t) max_coord_fixed || y < (int64_t) min_coord_fixed) {
152
701
         I->gs_error = gs_error_undefinedresult;
153
701
    }
154
16.5k
    else {
155
16.5k
        olh->need_close = true;
156
157
/*        dprintf2("%f %f lineto\n", fixed2float(x), fixed2float(y)); */
158
16.5k
        I->gs_error = gx_path_add_line_notes(olh->path, (fixed) x, (fixed) y, 0);
159
16.5k
    }
160
17.2k
    return (I->gs_error);
161
17.2k
}
162
163
static int
164
add_curve(gs_fapi_path *I, int64_t x0, int64_t y0, int64_t x1, int64_t y1,
165
          int64_t x2, int64_t y2)
166
18.4k
{
167
18.4k
    gs_fapi_outline_handler *olh = (gs_fapi_outline_handler *) I->olh;
168
169
18.4k
    x0 = import_shift(x0, I->shift);
170
18.4k
    y0 = -import_shift(y0, I->shift);
171
18.4k
    x1 = import_shift(x1, I->shift);
172
18.4k
    y1 = -import_shift(y1, I->shift);
173
18.4k
    x2 = import_shift(x2, I->shift);
174
18.4k
    y2 = -import_shift(y2, I->shift);
175
176
18.4k
    if (olh->fserver->transform_outline) {
177
108
        gs_point pt;
178
108
        I->gs_error = gs_distance_transform((double)fixed2float((float)x0), (double)fixed2float((float)y0), &olh->fserver->outline_mat, &pt);
179
108
        if (I->gs_error < 0)
180
0
            return I->gs_error;
181
108
        x0 = float2fixed(pt.x);
182
108
        y0 = float2fixed(pt.y);
183
108
        I->gs_error = gs_distance_transform((double)fixed2float((float)x1), (double)fixed2float((float)y1), &olh->fserver->outline_mat, &pt);
184
108
        if (I->gs_error < 0)
185
0
            return I->gs_error;
186
108
        x1 = float2fixed(pt.x);
187
108
        y1 = float2fixed(pt.y);
188
108
        I->gs_error = gs_distance_transform((double)fixed2float((float)x2), (double)fixed2float((float)y2), &olh->fserver->outline_mat, &pt);
189
108
        if (I->gs_error < 0)
190
0
            return I->gs_error;
191
108
        x2 = float2fixed(pt.x);
192
108
        y2 = float2fixed(pt.y);
193
108
    }
194
18.4k
    x0 += olh->x0;
195
18.4k
    y0 += olh->y0;
196
18.4k
    x1 += olh->x0;
197
18.4k
    y1 += olh->y0;
198
18.4k
    x2 += olh->x0;
199
18.4k
    y2 += olh->y0;
200
201
18.4k
    if (x0 > (int64_t) max_coord_fixed || x0 < (int64_t) min_coord_fixed
202
18.4k
     || y0 > (int64_t) max_coord_fixed || y0 < (int64_t) min_coord_fixed
203
18.4k
     || x1 > (int64_t) max_coord_fixed || x1 < (int64_t) min_coord_fixed
204
18.4k
     || y1 > (int64_t) max_coord_fixed || y1 < (int64_t) min_coord_fixed
205
18.4k
     || x2 > (int64_t) max_coord_fixed || x2 < (int64_t) min_coord_fixed
206
18.4k
     || y2 > (int64_t) max_coord_fixed || y2 < (int64_t) min_coord_fixed)
207
8
    {
208
8
        I->gs_error = gs_error_undefinedresult;
209
8
    }
210
18.4k
    else {
211
18.4k
        olh->need_close = true;
212
213
/*        dprintf6("%f %f %f %f %f %f curveto\n", fixed2float(x0), fixed2float(y0), fixed2float(x1), fixed2float(y1), fixed2float(x2), fixed2float(y2));*/
214
18.4k
        I->gs_error = gx_path_add_curve_notes(olh->path, (fixed) x0, (fixed) y0, (fixed) x1, (fixed) y1, (fixed) x2, (fixed) y2, 0);
215
18.4k
    }
216
18.4k
    return (I->gs_error);
217
18.4k
}
218
219
static gs_fapi_path path_interface_stub =
220
    { NULL, 0, 0, add_move, add_line, add_curve, add_closepath };
221
222
int
223
gs_fapi_get_metrics_count(gs_fapi_font *ff)
224
201k
{
225
201k
    if (!ff->is_type1 && ff->is_cid) {
226
77.2k
        gs_font_cid2 *pfcid = (gs_font_cid2 *) ff->client_font_data;
227
228
77.2k
        return (pfcid->cidata.MetricsCount);
229
77.2k
    }
230
123k
    return 0;
231
201k
}
232
233
/*
234
 * Lifted from psi/zchar.c
235
 * Return true if we only need the width from the rasterizer
236
 * and can short-circuit the full rendering of the character,
237
 * false if we need the actual character bits.  This is only safe if
238
 * we know the character is well-behaved, i.e., is not defined by an
239
 * arbitrary PostScript procedure.
240
 */
241
static inline bool
242
fapi_gs_char_show_width_only(const gs_text_enum_t *penum)
243
198k
{
244
198k
    if (!gs_text_is_width_only(penum))
245
140k
        return false;
246
57.4k
    switch (penum->orig_font->FontType) {
247
55.6k
        case ft_encrypted:
248
55.6k
        case ft_encrypted2:
249
55.6k
        case ft_CID_encrypted:
250
55.6k
        case ft_CID_TrueType:
251
55.6k
        case ft_CID_bitmap:
252
55.6k
        case ft_TrueType:
253
55.6k
            return true;
254
1.81k
        default:
255
1.81k
            return false;
256
57.4k
    }
257
57.4k
}
258
259
260
261
/* If we're rendering an uncached glyph, we need to know
262
 * whether we're filling it with a pattern, and whether
263
 * transparency is involved - if so, we have to produce
264
 * a path outline, and not a bitmap.
265
 */
266
static inline bool
267
using_transparency_pattern(gs_gstate *pgs)
268
197k
{
269
197k
    gx_device *dev = gs_currentdevice_inline(pgs);
270
271
197k
    return ((!gs_color_writes_pure(pgs)) && dev_proc(dev, dev_spec_op)(dev, gxdso_supports_pattern_transparency, NULL, 0));
272
197k
}
273
274
static inline bool
275
recreate_multiple_master(gs_font_base *pbfont)
276
198k
{
277
198k
    bool r = false;
278
198k
    gs_fapi_server *I = pbfont->FAPI;
279
198k
    bool changed = false;
280
281
198k
    if (I && I->face.font_id != gs_no_id &&
282
198k
        (pbfont->FontType == ft_encrypted
283
196k
        || pbfont->FontType == ft_encrypted2)) {
284
111k
        gs_font_type1 *pfont1 = (gs_font_type1 *) pbfont;
285
111k
        if (pfont1->data.WeightVector.count != 0
286
111k
            && I->face.WeightVector.count != pfont1->data.WeightVector.count) {
287
0
            changed = true;
288
0
        }
289
111k
        else if (pfont1->data.WeightVector.count != 0) {
290
0
            changed = (memcmp(I->face.WeightVector.values, pfont1->data.WeightVector.values,
291
0
                             pfont1->data.WeightVector.count * sizeof(pfont1->data.WeightVector.values[0])) != 0);
292
0
        }
293
294
111k
        if (changed) {
295
0
            r = (I->set_mm_weight_vector(I, &I->ff, pfont1->data.WeightVector.values, pfont1->data.WeightVector.count) == gs_error_invalidaccess);
296
0
            I->face.WeightVector.count = pfont1->data.WeightVector.count;
297
0
            memcpy(I->face.WeightVector.values, pfont1->data.WeightVector.values,
298
0
                   pfont1->data.WeightVector.count * sizeof(pfont1->data.WeightVector.values[0]));
299
0
        }
300
111k
    }
301
198k
    return r;
302
198k
}
303
304
static bool
305
produce_outline_char(gs_show_enum *penum_s,
306
                     gs_font_base *pbfont, int abits,
307
                     gs_log2_scale_point *log2_scale)
308
198k
{
309
198k
    gs_gstate *pgs = (gs_gstate *) penum_s->pgs;
310
311
198k
    log2_scale->x = 0;
312
198k
    log2_scale->y = 0;
313
314
    /* Checking both gx_compute_text_oversampling() result, and abits (below) may seem redundant,
315
     * and hopefully it will be soon, but for now, gx_compute_text_oversampling() could opt to
316
     * "oversample" sufficiently small glyphs (fwiw, I don't think gx_compute_text_oversampling is
317
     * working as intended in that respect), regardless of the device's anti-alias setting.
318
     * This was an old, partial solution for dropouts in small glyphs.
319
     */
320
198k
    gx_compute_text_oversampling(penum_s, (gs_font *) pbfont, abits,
321
198k
                                 log2_scale);
322
323
198k
    return (pgs->in_charpath || pbfont->PaintType != 0 ||
324
198k
            (pgs->in_cachedevice != CACHE_DEVICE_CACHING
325
197k
             && using_transparency_pattern((gs_gstate *) penum_s->pgs))
326
198k
            || (pgs->in_cachedevice != CACHE_DEVICE_CACHING
327
197k
                && (log2_scale->x > 0 || log2_scale->y > 0))
328
198k
            || (pgs->in_cachedevice != CACHE_DEVICE_CACHING && abits > 1));
329
198k
}
330
331
static inline void
332
gs_fapi_release_typeface(gs_fapi_server *I, void **server_font_data)
333
2.95k
{
334
2.95k
    I->release_typeface(I, *server_font_data);
335
2.95k
    I->face.font_id = gs_no_id;
336
2.95k
    if (I->ff.server_font_data == *server_font_data)
337
1.48k
        I->ff.server_font_data = 0;
338
2.95k
    *server_font_data = 0;
339
2.95k
}
340
341
static int
342
notify_remove_font(void *proc_data, void *event_data)
343
2.99k
{                               /* gs_font_finalize passes event_data == NULL, so check it here. */
344
2.99k
    if (event_data == NULL) {
345
2.99k
        gs_font_base *pbfont = proc_data;
346
2.99k
        gs_fapi_server *I = pbfont->FAPI;
347
348
2.99k
        if (pbfont->FAPI_font_data != 0) {
349
2.95k
            gs_fapi_release_typeface(I, &pbfont->FAPI_font_data);
350
2.95k
        }
351
2.99k
    }
352
2.99k
    return 0;
353
2.99k
}
354
355
int
356
gs_fapi_prepare_font(gs_font *pfont, gs_fapi_server *I, int subfont, const char *font_file_path,
357
                     gs_string *full_font_buf, const char *xlatmap, const char **decodingID)
358
3.08k
{                               /* Returns 1 iff BBox is set. */
359
    /* Cleans up server's font data if failed. */
360
361
    /* A renderer may need to access the top level font's data of
362
     * a CIDFontType 0 (FontType 9) font while preparing its subfonts,
363
     * and/or perform a completion action with the top level font after
364
     * its descendants are prepared. Therefore with such fonts
365
     * we first call get_scaled_font(..., FAPI_TOPLEVEL_BEGIN), then
366
     * get_scaled_font(..., i) with eash descendant font index i,
367
     * and then get_scaled_font(..., FAPI_TOPLEVEL_COMPLETE).
368
     * For other fonts we don't call with 'i'.
369
     *
370
     * Actually server's data for top level CIDFontTYpe 0 non-disk fonts should not be important,
371
     * because with non-disk fonts FAPI_do_char never deals with the top-level font,
372
     * but does with its descendants individually.
373
     * Therefore a recommendation for the renderer is don't build any special
374
     * data for the top-level non-disk font of CIDFontType 0, but return immediately
375
     * with success code and NULL data pointer.
376
     *
377
     * get_scaled_font may be called for same font at second time,
378
     * so the renderen must check whether the data is already built.
379
     */
380
3.08k
    gs_memory_t *mem = pfont->memory;
381
3.08k
    gs_font_base *pbfont = (gs_font_base *)pfont;
382
3.08k
    int code, bbox_set = 0;
383
3.08k
    int BBox[4], scale;
384
3.08k
    int units[2];
385
3.08k
    double size;
386
3.08k
    gs_fapi_font_scale font_scale =
387
3.08k
        { {1, 0, 0, 1, 0, 0}, {0, 0}, {1, 1}, true };
388
389
3.08k
    scale = 1 << I->frac_shift;
390
3.08k
    size = 1 / hypot(pbfont->FontMatrix.xx, pbfont->FontMatrix.xy);
391
    /* I believe this is just to ensure minimal rounding problems with scalers that
392
       scale the FontBBox values with the font scale.
393
     */
394
3.08k
    if (size < 1000)
395
3.04k
        size = 1000;
396
397
3.08k
    font_scale.matrix[0] = font_scale.matrix[3] = (int)(size * scale + 0.5);
398
399
3.08k
    font_scale.HWResolution[0] = (fracint) (72 * scale);
400
3.08k
    font_scale.HWResolution[1] = (fracint) (72 * scale);
401
402
    /* The interpreter specific parts of the gs_fapi_font should
403
     * be assinged by the caller - now do the generic parts.
404
     */
405
3.08k
    I->ff.subfont = subfont;
406
3.08k
    I->ff.font_file_path = font_file_path;
407
3.08k
    I->ff.is_type1 = FAPI_ISTYPE1GLYPHDATA(pbfont);
408
3.08k
    I->ff.is_vertical = (pbfont->WMode != 0);
409
3.08k
    I->ff.memory = mem;
410
3.08k
    I->ff.client_ctx_p = I->client_ctx_p;
411
3.08k
    I->ff.client_font_data = pbfont;
412
3.08k
    I->ff.client_font_data2 = pbfont;
413
3.08k
    I->ff.server_font_data = pbfont->FAPI_font_data;    /* Possibly pass it from zFAPIpassfont. */
414
3.08k
    if (full_font_buf) {
415
3.08k
        I->ff.full_font_buf = (char *)full_font_buf->data;
416
3.08k
        I->ff.full_font_buf_len = full_font_buf->size;
417
3.08k
    }
418
0
    else {
419
0
        I->ff.full_font_buf = NULL;
420
0
        I->ff.full_font_buf_len = 0;
421
0
    }
422
3.08k
    I->ff.is_cid = FAPI_ISCIDFONT(pbfont);
423
3.08k
    I->ff.is_outline_font = pbfont->PaintType != 0;
424
425
3.08k
    if (!I->ff.is_mtx_skipped)
426
3.08k
        I->ff.is_mtx_skipped = (gs_fapi_get_metrics_count(&I->ff) != 0);
427
428
3.08k
    if ((code = gs_fapi_renderer_retcode(mem, I, I->get_scaled_font(I, &I->ff,
429
3.08k
                                                                    (const
430
3.08k
                                                                     gs_fapi_font_scale
431
3.08k
                                                                     *)
432
3.08k
                                                                    &font_scale, xlatmap, gs_fapi_toplevel_begin)))
433
3.08k
        < 0)
434
76
        return code;
435
3.01k
    pbfont->FAPI_font_data = I->ff.server_font_data;    /* Save it back to GS font. */
436
437
    /* We only want to "refine" the FontBBox for fonts where we allow FAPI to be
438
       treated as a "black box", handing over the entire font to the FAPI server.
439
       That is, fonts where we give the server either the file, or a buffer with
440
       the entire font description in it.
441
     */
442
3.01k
    if (I->ff.server_font_data != 0
443
3.01k
        && (font_file_path != NULL || full_font_buf != NULL)) {
444
2.98k
        if ((code =
445
2.98k
             gs_fapi_renderer_retcode(mem, I,
446
2.98k
                                      I->get_font_bbox(I, &I->ff,
447
2.98k
                                                       BBox, units))) < 0) {
448
0
            gs_fapi_release_typeface(I, &pbfont->FAPI_font_data);
449
0
            return code;
450
0
        }
451
        /* Refine FontBBox : */
452
2.98k
        pbfont->FontBBox.p.x = ((double)BBox[0] / units[0]);
453
2.98k
        pbfont->FontBBox.p.y = ((double)BBox[1] / units[1]);
454
2.98k
        pbfont->FontBBox.q.x = ((double)BBox[2] / units[0]);
455
2.98k
        pbfont->FontBBox.q.y = ((double)BBox[3] / units[1]);
456
457
2.98k
        bbox_set = 1;
458
2.98k
    }
459
460
3.01k
    if (xlatmap != NULL && pbfont->FAPI_font_data != NULL) {
461
0
        if ((code =
462
0
             gs_fapi_renderer_retcode(mem, I,
463
0
                                      I->get_decodingID(I, &I->ff,
464
0
                                                        decodingID))) < 0) {
465
0
            gs_fapi_release_typeface(I, &pbfont->FAPI_font_data);
466
0
            return code;
467
0
        }
468
0
    }
469
470
    /* Prepare descendant fonts : */
471
3.01k
    if (font_file_path == NULL && I->ff.is_type1 && I->ff.is_cid) {     /* Renderers should expect same condition. */
472
26
        gs_font_cid0 *pfcid = (gs_font_cid0 *) pbfont;
473
26
        gs_font_type1 **FDArray = pfcid->cidata.FDArray;
474
26
        int i, n = pfcid->cidata.FDArray_size;
475
476
26
        I->ff.is_type1 = true;
477
26
        I->ff.is_vertical = false;      /* A subfont may be shared with another fonts. */
478
26
        I->ff.memory = mem;
479
26
        I->ff.client_ctx_p = I->client_ctx_p;
480
40
        for (i = 0; i < n; i++) {
481
26
            gs_font_type1 *pbfont1 = FDArray[i];
482
26
            int BBox_temp[4];
483
26
            int units_temp[2];
484
485
26
            pbfont1->FontBBox = pbfont->FontBBox;       /* Inherit FontBBox from the type 9 font. */
486
487
26
            I->ff.client_font_data = pbfont1;
488
26
            pbfont1->FAPI = pbfont->FAPI;
489
26
            I->ff.client_font_data2 = pbfont1;
490
26
            I->ff.server_font_data = pbfont1->FAPI_font_data;
491
26
            I->ff.is_cid = true;
492
26
            I->ff.is_outline_font = pbfont1->PaintType != 0;
493
26
            if (!I->ff.is_mtx_skipped)
494
26
                I->ff.is_mtx_skipped = (gs_fapi_get_metrics_count(&I->ff) != 0);
495
26
            I->ff.subfont = 0;
496
26
            if ((code =
497
26
                 gs_fapi_renderer_retcode(mem, I,
498
26
                                          I->get_scaled_font(I, &I->ff,
499
26
                                                             (const
500
26
                                                              gs_fapi_font_scale
501
26
                                                              *)&font_scale,
502
26
                                                             NULL, i))) < 0) {
503
12
                break;
504
12
            }
505
506
14
            pbfont1->FAPI_font_data = I->ff.server_font_data;   /* Save it back to GS font. */
507
            /* Try to do something with the descendant font to ensure that it's working : */
508
14
            if ((code =
509
14
                 gs_fapi_renderer_retcode(mem, I,
510
14
                                          I->get_font_bbox(I, &I->ff,
511
14
                                                           BBox_temp, units_temp))) < 0) {
512
0
                break;
513
0
            }
514
14
            code = gs_notify_register(&pbfont1->notify_list, notify_remove_font, pbfont1);
515
14
            if (code < 0) {
516
0
                emprintf(mem,
517
0
                         "Ignoring gs_notify_register() failure for FAPI font.....");
518
0
            }
519
14
        }
520
26
        if (i == n) {
521
14
            code =
522
14
                gs_fapi_renderer_retcode(mem, I,
523
14
                                         I->get_scaled_font(I, &I->ff,
524
14
                                                            (const
525
14
                                                             gs_fapi_font_scale
526
14
                                                             *)&font_scale,
527
14
                                                            NULL,
528
14
                                                            gs_fapi_toplevel_complete));
529
14
            if (code >= 0)
530
14
                return bbox_set;        /* Full success. */
531
14
        }
532
        /* Fail, release server's font data : */
533
24
        for (i = 0; i < n; i++) {
534
12
            gs_font_type1 *pbfont1 = FDArray[i];
535
536
12
            if (pbfont1->FAPI_font_data != NULL)
537
0
                gs_fapi_release_typeface(I, &pbfont1->FAPI_font_data);
538
12
        }
539
540
12
        if (pbfont->FAPI_font_data != NULL) {
541
0
            gs_fapi_release_typeface(I, &pbfont->FAPI_font_data);
542
0
        }
543
12
        return_error(gs_error_invalidfont);
544
26
    }
545
546
    /* This was an "else", but could elicit a warning from static analysis tools
547
     * about the potential for a non-void function without a return value.
548
     */
549
2.98k
    code = gs_fapi_renderer_retcode(mem, I, I->get_scaled_font(I, &I->ff,
550
2.98k
                                                               (const
551
2.98k
                                                                gs_fapi_font_scale
552
2.98k
                                                                *)&font_scale,
553
2.98k
                                                               xlatmap,
554
2.98k
                                                               gs_fapi_toplevel_complete));
555
2.98k
    if (code < 0) {
556
0
        gs_fapi_release_typeface(I, &pbfont->FAPI_font_data);
557
0
        return code;
558
0
    }
559
2.98k
    code =
560
2.98k
        gs_notify_register(&pbfont->notify_list, notify_remove_font, pbfont);
561
2.98k
    if (code < 0) {
562
0
        gs_fapi_release_typeface(I, &pbfont->FAPI_font_data);
563
0
        return code;
564
0
    }
565
566
2.98k
    return bbox_set;
567
2.98k
}
568
569
570
static int
571
outline_char(gs_memory_t *mem, gs_fapi_server *I, int import_shift_v,
572
             gs_show_enum *penum_s, struct gx_path_s *path, bool close_path)
573
3.70k
{
574
3.70k
    gs_fapi_path path_interface = path_interface_stub;
575
3.70k
    gs_fapi_outline_handler olh;
576
3.70k
    int code = 0;
577
3.70k
    gs_gstate *pgs = penum_s->pgs;
578
3.70k
    struct gx_path_s path1;
579
580
3.70k
    (void)gx_path_init_local(&path1, mem);
581
582
3.70k
    olh.fserver = I;
583
3.70k
    olh.path = &path1;
584
3.70k
    olh.x0 = pgs->ctm.tx_fixed - float2fixed(penum_s->fapi_glyph_shift.x);
585
3.70k
    olh.y0 = pgs->ctm.ty_fixed - float2fixed(penum_s->fapi_glyph_shift.y);
586
3.70k
    olh.close_path = close_path;
587
3.70k
    olh.need_close = false;
588
3.70k
    path_interface.olh = &olh;
589
3.70k
    path_interface.shift = import_shift_v;
590
3.70k
    if ((code =
591
3.70k
         gs_fapi_renderer_retcode(mem, I,
592
3.70k
                                  I->get_char_outline(I,
593
3.70k
                                                      &path_interface))) < 0
594
3.70k
        || path_interface.gs_error != 0) {
595
1.25k
        if (path_interface.gs_error != 0) {
596
1.25k
            code = path_interface.gs_error;
597
1.25k
            goto done;
598
1.25k
        }
599
0
        else {
600
0
            goto done;
601
0
        }
602
1.25k
    }
603
2.45k
    if (olh.need_close && olh.close_path)
604
0
        if ((code = add_closepath(&path_interface)) < 0)
605
0
            goto done;
606
2.45k
    code = gx_path_copy(&path1, path);
607
3.70k
done:
608
3.70k
    code = code >= 0 || code == gs_error_undefinedresult ? 0 : code;
609
3.70k
    gx_path_free(&path1, "outline_char");
610
3.70k
    return code;
611
2.45k
}
612
613
static void
614
compute_em_scale(const gs_font_base *pbfont, gs_fapi_metrics *metrics,
615
                 double FontMatrix_div, double *em_scale_x,
616
                 double *em_scale_y)
617
197k
{                               /* optimize : move this stuff to FAPI_refine_font */
618
197k
    gs_matrix mat;
619
197k
    gs_matrix *m = &mat;
620
197k
    int rounding_x, rounding_y; /* Striking out the 'float' representation error in FontMatrix. */
621
197k
    double sx, sy;
622
197k
    gs_fapi_server *I = pbfont->FAPI;
623
624
197k
#if 1
625
197k
    I->get_fontmatrix(I, m);
626
#else
627
    /* Temporary: replace with a FAPI call to check *if* the library needs a replacement matrix */
628
    memset(m, 0x00, sizeof(gs_matrix));
629
    m->xx = m->yy = 1.0;
630
#endif
631
632
197k
    if (m->xx == 0 && m->xy == 0 && m->yx == 0 && m->yy == 0)
633
0
        m = &pbfont->base->FontMatrix;
634
197k
    sx = hypot(m->xx, m->xy) * metrics->em_x / FontMatrix_div;
635
197k
    sy = hypot(m->yx, m->yy) * metrics->em_y / FontMatrix_div;
636
197k
    rounding_x = (int)(0x00800000 / sx);
637
197k
    rounding_y = (int)(0x00800000 / sy);
638
197k
    *em_scale_x = (int)(sx * rounding_x + 0.5) / (double)rounding_x;
639
197k
    *em_scale_y = (int)(sy * rounding_y + 0.5) / (double)rounding_y;
640
197k
}
641
642
static int
643
fapi_copy_mono(gx_device *dev1, gs_fapi_raster *rast, int dx, int dy)
644
133k
{
645
133k
    int line_step = bitmap_raster(rast->width), code;
646
647
133k
    if (rast->line_step >= line_step) {
648
374
        return dev_proc(dev1, copy_mono) (dev1, rast->p, 0, rast->line_step,
649
374
                                          0, dx, dy, rast->width,
650
374
                                          rast->height, 0, 1);
651
374
    }
652
133k
    else {                      /* bitmap data needs to be aligned, make the aligned copy : */
653
133k
        byte *p = gs_alloc_byte_array(dev1->memory, rast->height, line_step,
654
133k
                                      "fapi_copy_mono");
655
133k
        byte *q = p, *r = rast->p, *pe;
656
657
133k
        if (p == NULL)
658
0
            return_error(gs_error_VMerror);
659
133k
        pe = p + rast->height * line_step;
660
2.08M
        for (; q < pe; q += line_step, r += rast->line_step) {
661
1.95M
            memcpy(q, r, rast->line_step);
662
1.95M
            memset(q + rast->line_step, 0, line_step - rast->line_step);
663
1.95M
        }
664
133k
        code =
665
133k
            dev_proc(dev1, copy_mono) (dev1, p, 0, line_step, 0, dx, dy,
666
133k
                                       rast->width, rast->height, 0, 1);
667
133k
        gs_free_object(dev1->memory, p, "fapi_copy_mono");
668
133k
        return code;
669
133k
    }
670
133k
}
671
672
/*
673
 * For PCL/PXL pseudo-bolding, we have to "smear" a bitmap horizontally and
674
 * vertically by ORing together a rectangle of bits below and to the left of
675
 * each output bit.  We do this separately for horizontal and vertical
676
 * smearing.  Eventually, we will replace the current procedure, which takes
677
 * time proportional to W * H * (N + log2(N)), with one that is only
678
 * proportional to N (but takes W * N additional buffer space).
679
 */
680
681
/* Allocate the line buffer for bolding.  We need 2 + bold scan lines. */
682
static byte *
683
alloc_bold_lines(gs_memory_t *mem, uint width, int bold, client_name_t cname)
684
0
{       return gs_alloc_byte_array(mem, 2 + bold, bitmap_raster(width + bold),
685
0
                                   cname);
686
0
}
687
688
/* Merge one (aligned) scan line into another, for vertical smearing. */
689
static void
690
bits_merge(byte *dest, const byte *src, uint nbytes)
691
0
{       long *dp = (long *)dest;
692
0
        const long *sp = (const long *)src;
693
0
        uint n = (nbytes + sizeof(long) - 1) >> ARCH_LOG2_SIZEOF_LONG;
694
695
0
        for ( ; n >= 4; sp += 4, dp += 4, n -= 4 )
696
0
          dp[0] |= sp[0], dp[1] |= sp[1], dp[2] |= sp[2], dp[3] |= sp[3];
697
0
        for ( ; n; ++sp, ++dp, --n )
698
0
          *dp |= *sp;
699
0
}
700
701
/* Smear a scan line horizontally.  Note that the output is wider than */
702
/* the input by the amount of bolding (smear_width). */
703
static void
704
bits_smear_horizontally(byte *dest, const byte *src, uint width,
705
  uint smear_width)
706
0
{       uint bits_on = 0;
707
0
        const byte *sp = src;
708
0
        uint sbyte = *sp;
709
0
        byte *dp = dest;
710
0
        uint dbyte = sbyte;
711
0
        uint sdmask = 0x80;
712
0
        const byte *zp = src;
713
0
        uint zmask = 0x80;
714
0
        uint i = 0;
715
716
        /* Process the first smear_width bits. */
717
0
        { uint stop = min(smear_width, width);
718
719
0
          for ( ; i < stop; ++i ) {
720
0
            if ( sbyte & sdmask )
721
0
              bits_on++;
722
0
            else if ( bits_on )
723
0
              dbyte |= sdmask;
724
0
            if ( (sdmask >>= 1) == 0 )
725
0
              sdmask = 0x80, *dp++ = dbyte, dbyte = sbyte = *++sp;
726
0
          }
727
0
        }
728
729
        /* Process all but the last smear_width bits. */
730
0
        { for ( ; i < width; ++i ) {
731
0
            if ( sbyte & sdmask )
732
0
              bits_on++;
733
0
            else if ( bits_on )
734
0
              dbyte |= sdmask;
735
0
            if ( *zp & zmask )
736
0
              --bits_on;
737
0
            if ( (sdmask >>= 1) == 0 ) {
738
0
              sdmask = 0x80;
739
0
              *dp++ = dbyte;
740
0
on:           switch ( (dbyte = sbyte = *++sp) ) {
741
0
                case 0xff:
742
0
                  if ( width - i <= 8 )
743
0
                    break;
744
0
                  *dp++ = 0xff;
745
0
                  bits_on += 8 -
746
0
                    byte_count_bits[(*zp & (zmask - 1)) + (zp[1] & -(int)zmask)];
747
0
                  ++zp;
748
0
                  i += 8;
749
0
                  goto on;
750
0
                case 0:
751
0
                  if ( bits_on || width - i <= 8 )
752
0
                    break;
753
0
                  *dp++ = 0;
754
                  /* We know there can't be any bits to be zeroed, */
755
                  /* because bits_on can't go negative. */
756
0
                  ++zp;
757
0
                  i += 8;
758
0
                  goto on;
759
0
                default:
760
0
                  ;
761
0
              }
762
0
            }
763
0
            if ( (zmask >>= 1) == 0 )
764
0
              zmask = 0x80, ++zp;
765
0
          }
766
0
        }
767
768
        /* Process the last smear_width bits. */
769
        /****** WRONG IF width < smear_width ******/
770
0
        { uint stop = width + smear_width;
771
772
0
          for ( ; i < stop; ++i ) {
773
0
            if ( bits_on )
774
0
              dbyte |= sdmask;
775
0
            if ( (sdmask >>= 1) == 0 )
776
0
              sdmask = 0x80, *dp++ = dbyte, dbyte = 0;
777
0
            if ( *zp & zmask )
778
0
              --bits_on;
779
0
            if ( (zmask >>= 1) == 0 )
780
0
              zmask = 0x80, ++zp;
781
0
          }
782
0
        }
783
784
0
        if ( sdmask != 0x80 )
785
0
          *dp = dbyte;
786
0
}
787
788
static const int frac_pixel_shift = 4;
789
790
/* NOTE: fapi_image_uncached_glyph() doesn't check various paramters: it assumes fapi_finish_render_aux()
791
 * has done so: if it gets called from another function, the function must either do all the parameter
792
 * validation, or fapi_image_uncached_glyph() must be changed to include the validation.
793
 */
794
static int
795
fapi_image_uncached_glyph(gs_font *pfont, gs_gstate *pgs, gs_show_enum *penum,
796
                          gs_fapi_raster *rast, const int import_shift_v)
797
3.51k
{
798
3.51k
    gx_device *dev = penum->dev;
799
3.51k
    gs_gstate *penum_pgs = (gs_gstate *) penum->pgs;
800
3.51k
    int code;
801
3.51k
    const gx_clip_path *pcpath = pgs->clip_path;
802
3.51k
    const gx_drawing_color *pdcolor = gs_currentdevicecolor_inline(penum->pgs);
803
3.51k
    int rast_orig_x = rast->orig_x;
804
3.51k
    int rast_orig_y = -rast->orig_y;
805
3.51k
    gs_font_base *pbfont = (gs_font_base *)pfont;
806
3.51k
    gs_fapi_server *I = pbfont->FAPI;
807
808
3.51k
    extern_st(st_gs_show_enum);
809
810
3.51k
    byte *r = rast->p;
811
3.51k
    byte *src, *dst;
812
3.51k
    int h, padbytes, cpbytes, dstr = bitmap_raster(rast->width);
813
3.51k
    int sstr = rast->line_step;
814
815
3.51k
    double dx = penum_pgs->ctm.tx + (double)rast_orig_x / (1 << frac_pixel_shift) + 0.5;
816
3.51k
    double dy = penum_pgs->ctm.ty + (double)rast_orig_y / (1 << frac_pixel_shift) + 0.5;
817
818
    /* we can only safely use the gx_image_fill_masked() "shortcut" if we're drawing
819
     * a "simple" colour, rather than a pattern.
820
     */
821
3.51k
    if (gs_color_writes_pure(penum_pgs) && I->ff.embolden == 0.0) {
822
3.51k
        if (dstr != sstr) {
823
824
            /* If the stride of the bitmap we've got doesn't match what the rest
825
             * of the Ghostscript world expects, make one that does.
826
             * Ghostscript aligns bitmap raster memory in a platform specific
827
             * manner, so see gxbitmap.h for details.
828
             *
829
             * Ideally the padding bytes wouldn't matter, but currently the
830
             * clist code ends up compressing it using bitmap compression. To
831
             * ensure consistency across runs (and to get the best possible
832
             * compression ratios) we therefore set such bytes to zero. It would
833
             * be nicer if this was fixed in future.
834
             */
835
2.25k
            r = gs_alloc_bytes(penum->memory, (size_t)dstr * rast->height,
836
2.25k
                               "fapi_finish_render_aux");
837
2.25k
            if (!r) {
838
0
                return_error(gs_error_VMerror);
839
0
            }
840
841
2.25k
            cpbytes = sstr < dstr ? sstr : dstr;
842
2.25k
            padbytes = dstr - cpbytes;
843
2.25k
            h = rast->height;
844
2.25k
            src = rast->p;
845
2.25k
            dst = r;
846
2.25k
            if (padbytes > 0) {
847
233k
                while (h-- > 0) {
848
230k
                    memcpy(dst, src, cpbytes);
849
230k
                    memset(dst + cpbytes, 0, padbytes);
850
230k
                    src += sstr;
851
230k
                    dst += dstr;
852
230k
                }
853
2.25k
            }
854
0
            else {
855
0
                while (h-- > 0) {
856
0
                    memcpy(dst, src, cpbytes);
857
0
                    src += sstr;
858
0
                    dst += dstr;
859
0
                }
860
0
            }
861
2.25k
        }
862
863
3.51k
        if (gs_object_type(penum->memory, penum) == &st_gs_show_enum) {
864
3.51k
            dx += penum->fapi_glyph_shift.x;
865
3.51k
            dy += penum->fapi_glyph_shift.y;
866
3.51k
        }
867
        /* Processing an image object operation, but this may be for a text object */
868
3.51k
        ensure_tag_is_set(pgs, pgs->device, GS_TEXT_TAG); /* NB: may unset_dev_color */
869
3.51k
        code = gx_set_dev_color(pgs);
870
3.51k
        if (code != 0)
871
0
            return code;
872
3.51k
        code = gs_gstate_color_load(pgs);
873
3.51k
        if (code < 0)
874
0
            return code;
875
876
3.51k
        code = gx_image_fill_masked(dev, r, 0, dstr, gx_no_bitmap_id,
877
3.51k
                                    (int)dx, (int)dy,
878
3.51k
                                    rast->width, rast->height,
879
3.51k
                                    pdcolor, 1, rop3_default, pcpath);
880
3.51k
        if (rast->p != r) {
881
2.25k
            gs_free_object(penum->memory, r, "fapi_finish_render_aux");
882
2.25k
        }
883
3.51k
    }
884
0
    else {
885
0
        gs_memory_t *mem = penum->memory->non_gc_memory;
886
0
        gs_image_enum *pie;
887
0
        gs_image_t image;
888
0
        int iy, nbytes;
889
0
        uint used;
890
0
        int code1;
891
0
        int w, h;
892
0
        int x = (int)dx;
893
0
        int y = (int)dy;
894
0
        uint bold = 0;
895
0
        byte *bold_lines = NULL;
896
0
        byte *line = NULL;
897
0
        int ascent = 0;
898
899
0
        pie = gs_image_enum_alloc(mem, "image_char(image_enum)");
900
0
        if (!pie) {
901
0
            return_error(gs_error_VMerror);
902
0
        }
903
904
0
        w = rast->width;
905
0
        h = rast->height;
906
0
        if (I->ff.embolden != 0.0) {
907
0
            bold = (uint)(2 * h * I->ff.embolden + 0.5);
908
0
            ascent += bold;
909
0
            bold_lines = alloc_bold_lines(pgs->memory, w, bold, "fapi_image_uncached_glyph(bold_line)");
910
0
            if (bold_lines == NULL) {
911
0
                code = gs_note_error(gs_error_VMerror);
912
0
                return(code);
913
0
            }
914
0
            line = gs_alloc_byte_array(pgs->memory, 1, bitmap_raster(w + bold) + 1, "fapi_copy_mono");
915
0
            if (line == 0) {
916
0
                gs_free_object(pgs->memory, bold_lines, "fapi_image_uncached_glyph(bold_lines)");
917
0
                code = gs_note_error(gs_error_VMerror);
918
0
                return(code);
919
0
            }
920
0
        }
921
922
        /* Make a matrix that will place the image */
923
        /* at (x,y) with no transformation. */
924
0
        gs_image_t_init_mask(&image, true);
925
0
        gs_make_translation((double) - x, (double) (- y + ascent), &image.ImageMatrix);
926
0
        gs_matrix_multiply(&ctm_only(penum_pgs), &image.ImageMatrix, &image.ImageMatrix);
927
0
        image.Width = w + bold;
928
0
        image.Height = h + bold;
929
0
        image.adjust = false;
930
0
        code = gs_image_init(pie, &image, false, true, penum_pgs);
931
0
        nbytes = (rast->width + 7) >> 3;
932
933
0
        switch (code) {
934
0
            case 1:            /* empty image */
935
0
                code = 0;
936
0
            default:
937
0
                break;
938
0
            case 0:
939
0
                if (bold == 0) {
940
0
                    for (iy = 0; iy < h && code >= 0; iy++, r += sstr) {
941
0
                        code = gs_image_next(pie, r, nbytes, &used);
942
0
                    }
943
0
                }
944
0
                else {
945
0
                    uint dest_raster = bitmap_raster(image.Width);
946
0
                    uint dest_bytes = (image.Width + 7) >> 3;
947
0
                    int n1 = bold + 1;
948
0
#define merged_line(i) (bold_lines + ((i) % n1 + 1) * dest_raster)
949
950
0
                    for ( y = 0; y < image.Height; y++ ) {
951
0
                        int y0 = (y < bold ? 0 : y - bold);
952
0
                        int y1 = min(y + 1, rast->height);
953
0
                        int kmask;
954
0
                        bool first = true;
955
956
0
                        if ( y < rast->height ) {
957
0
                            memcpy(line, r + y * rast->line_step, rast->line_step);
958
0
                            memset(line + rast->line_step, 0x00, (dest_raster + 1) -  rast->line_step);
959
960
0
                            bits_smear_horizontally(merged_line(y), line, rast->width, bold);
961
                            /* Now re-establish the invariant -- see below. */
962
0
                            kmask = 1;
963
964
0
                            for ( ; (y & kmask) == kmask && y - kmask >= y0;
965
0
                                  kmask = (kmask << 1) + 1) {
966
967
0
                                bits_merge(merged_line(y - kmask), merged_line(y - (kmask >> 1)), dest_bytes);
968
0
                            }
969
0
                        }
970
971
                       /*
972
                        * As of this point in the loop, we maintain the following
973
                        * invariant to cache partial merges of groups of lines: for
974
                        * each Y, y0 <= Y < y1, let K be the maximum k such that Y
975
                        * mod 2^k = 0 and Y + 2^k < y1; then merged_line(Y) holds
976
                        * the union of horizontally smeared source lines Y through
977
                        * Y + 2^k - 1.  The idea behind this is similar to the idea
978
                        * of quicksort.
979
                        */
980
981
                        /* Now construct the output line. */
982
0
                        first = true;
983
984
0
                        for ( iy = y1 - 1; iy >= y0; --iy ) {
985
0
                          kmask = 1;
986
987
0
                          while ( (iy & kmask) == kmask && iy - kmask >= y0 )
988
0
                            iy -= kmask, kmask <<= 1;
989
0
                          if ( first ) {
990
0
                            memcpy(bold_lines, merged_line(iy), dest_bytes);
991
0
                            first = false;
992
0
                          }
993
0
                          else
994
0
                            bits_merge(bold_lines, merged_line(iy), dest_bytes);
995
0
                        }
996
0
                        code = gs_image_next(pie, bold_lines, dest_bytes, &used);
997
0
                    }
998
0
                }
999
0
#undef merged_line
1000
0
        }
1001
1002
0
        if (bold_lines)
1003
0
            gs_free_object(pgs->memory, bold_lines, "fapi_image_uncached_glyph(bold_lines)");
1004
1005
0
        if (line)
1006
0
            gs_free_object(pgs->memory, line, "fapi_image_uncached_glyph(line)");
1007
1008
0
        code1 = gs_image_cleanup_and_free_enum(pie, penum_pgs);
1009
0
        if (code >= 0 && code1 < 0)
1010
0
            code = code1;
1011
0
    }
1012
3.51k
    return (code);
1013
3.51k
}
1014
1015
int
1016
gs_fapi_finish_render(gs_font *pfont, gs_gstate *pgs, gs_text_enum_t *penum, gs_fapi_server *I)
1017
196k
{
1018
196k
    gs_show_enum *penum_s = (gs_show_enum *) penum;
1019
196k
    gs_gstate *penum_pgs;
1020
196k
    gx_device *dev1;
1021
196k
    const int import_shift_v = _fixed_shift - 32;       /* we always 32.32 values for the outline interface now */
1022
196k
    gs_fapi_raster rast;
1023
196k
    int code;
1024
196k
    gs_memory_t *mem = pfont->memory;
1025
196k
    gs_font_base *pbfont = (gs_font_base *)pfont;
1026
1027
196k
    if (penum == NULL)
1028
0
        return_error(gs_error_undefined);
1029
1030
196k
    penum_pgs = penum_s->pgs;
1031
1032
196k
    dev1 = gs_currentdevice_inline(penum_pgs);  /* Possibly changed by zchar_set_cache. */
1033
1034
    /* Even for "non-marking" text operations (for example, stringwidth) we are expected
1035
     * to have a glyph bitmap for the cache, if we're using the cache. For the
1036
     * non-cacheing, non-marking cases, we must not draw the glyph.
1037
     */
1038
196k
    if (pgs->in_charpath && !SHOW_IS(penum, TEXT_DO_NONE)) {
1039
865
        gs_point pt;
1040
1041
865
        if ((code = gs_currentpoint(penum_pgs, &pt)) < 0)
1042
0
            return code;
1043
1044
865
        if ((code =
1045
865
             outline_char(mem, I, import_shift_v, penum_s, penum_pgs->path,
1046
865
                          !pbfont->PaintType)) < 0) {
1047
0
            return code;
1048
0
        }
1049
1050
865
        if ((code =
1051
865
             gx_path_add_char_path(penum_pgs->show_gstate->path,
1052
865
                                   penum_pgs->path,
1053
865
                                   penum_pgs->in_charpath)) < 0) {
1054
0
            return code;
1055
0
        }
1056
1057
865
    }
1058
195k
    else {
1059
195k
        int code;
1060
195k
        memset(&rast, 0x00, sizeof(rast));
1061
1062
195k
        if ((code = I->get_char_raster(I, &rast)) < 0 && code != gs_error_unregistered)
1063
0
            return code;
1064
1065
195k
        if (!SHOW_IS(penum, TEXT_DO_NONE) && I->use_outline) {
1066
            /* The server provides an outline instead the raster. */
1067
2.85k
            gs_point pt;
1068
1069
            /* This mimics code which is used below in the case where I->Use_outline is set.
1070
             * This is usually caused by setting -dTextAlphaBits, and will result in 'undefinedresult'
1071
             * errors. We want the code to work the same regardless off the setting of -dTextAlphaBits
1072
             * and it seems the simplest way to provoke the same error is to do the same steps....
1073
             * Note that we do not actually ever use the returned value.
1074
             */
1075
2.85k
            if ((code = gs_currentpoint(penum_pgs, &pt)) < 0)
1076
12
                return code;
1077
1078
2.84k
            if ((code =
1079
2.84k
                 outline_char(mem, I, import_shift_v, penum_s,
1080
2.84k
                              penum_pgs->path, !pbfont->PaintType)) < 0)
1081
0
                return code;
1082
2.84k
            if ((code =
1083
2.84k
                 gs_gstate_setflat((gs_gstate *) penum_pgs,
1084
2.84k
                                   gs_char_flatness(penum_pgs->show_gstate, 1.0))) < 0)
1085
0
                return code;
1086
2.84k
            if (pbfont->PaintType) {
1087
0
                float lw = gs_currentlinewidth(penum_pgs);
1088
1089
0
                gs_setlinewidth(penum_pgs, pbfont->StrokeWidth);
1090
0
                code = gs_stroke(penum_pgs);
1091
0
                gs_setlinewidth(penum_pgs, lw);
1092
0
                if (code < 0)
1093
0
                    return code;
1094
0
            }
1095
2.84k
            else {
1096
2.84k
                gs_in_cache_device_t in_cachedevice =
1097
2.84k
                    penum_pgs->in_cachedevice;
1098
1099
2.84k
                penum_pgs->in_cachedevice = CACHE_DEVICE_NOT_CACHING;
1100
1101
2.84k
                penum_pgs->fill_adjust.x = penum_pgs->fill_adjust.y = 0;
1102
1103
2.84k
                code = gs_fill(penum_pgs);
1104
1105
2.84k
                penum_pgs->in_cachedevice = in_cachedevice;
1106
1107
2.84k
                if (code < 0)
1108
0
                    return code;
1109
2.84k
            }
1110
2.84k
            if ((code = gs_moveto(penum_pgs, pt.x, pt.y)) < 0)
1111
0
                return code;
1112
2.84k
        }
1113
193k
        else {
1114
193k
            int rast_orig_x = rast.orig_x;
1115
193k
            int rast_orig_y = -rast.orig_y;
1116
193k
            gs_point pt;
1117
1118
            /* This mimics code which is used above in the case where I->Use_outline is set.
1119
             * This is usually caused by setting -dTextAlphaBits, and will result in 'undefinedresult'
1120
             * errors. We want the code to work the same regardless off the setting of -dTextAlphaBits
1121
             * and it seems the simplest way to provoke the same error is to do the same steps....
1122
             * Note that we do not actually ever use the returned value.
1123
             */
1124
193k
            if ((code = gs_currentpoint(penum_pgs, &pt)) < 0)
1125
114
                return code;
1126
1127
192k
            if (penum_pgs->in_cachedevice == CACHE_DEVICE_CACHING) {    /* Using GS cache */
1128
                /*  GS and renderer may transform coordinates few differently.
1129
                   The best way is to make set_cache_device to take the renderer's bitmap metrics immediately,
1130
                   but we need to account CDevProc, which may truncate the bitmap.
1131
                   Perhaps GS overestimates the bitmap size,
1132
                   so now we only add a compensating shift - the dx and dy.
1133
                 */
1134
133k
                if (rast.width != 0) {
1135
133k
                    int shift_rd = _fixed_shift - frac_pixel_shift;
1136
133k
                    int rounding = 1 << (frac_pixel_shift - 1);
1137
133k
                    int dx =
1138
133k
                        arith_rshift_slow((penum_pgs->
1139
133k
                                           ctm.tx_fixed >> shift_rd) +
1140
133k
                                          rast_orig_x + rounding,
1141
133k
                                          frac_pixel_shift);
1142
133k
                    int dy =
1143
133k
                        arith_rshift_slow((penum_pgs->
1144
133k
                                           ctm.ty_fixed >> shift_rd) +
1145
133k
                                          rast_orig_y + rounding,
1146
133k
                                          frac_pixel_shift);
1147
1148
133k
                    if (dx + rast.left_indent < 0
1149
133k
                        || dx + rast.left_indent + rast.black_width >
1150
133k
                        dev1->width) {
1151
#ifdef DEBUG
1152
                        if (gs_debug_c('m')) {
1153
                            emprintf2(dev1->memory,
1154
                                      "Warning : Cropping a FAPI glyph while caching : dx=%d,%d.\n",
1155
                                      dx + rast.left_indent,
1156
                                      dx + rast.left_indent +
1157
                                      rast.black_width - dev1->width);
1158
                        }
1159
#endif
1160
0
                        if (dx + rast.left_indent < 0)
1161
0
                            dx -= dx + rast.left_indent;
1162
0
                    }
1163
133k
                    if (dy + rast.top_indent < 0
1164
133k
                        || dy + rast.top_indent + rast.black_height >
1165
133k
                        dev1->height) {
1166
#ifdef DEBUG
1167
                        if (gs_debug_c('m')) {
1168
                            emprintf2(dev1->memory,
1169
                                      "Warning : Cropping a FAPI glyph while caching : dx=%d,%d.\n",
1170
                                      dy + rast.top_indent,
1171
                                      dy + rast.top_indent +
1172
                                      rast.black_height - dev1->height);
1173
                        }
1174
#endif
1175
0
                        if (dy + rast.top_indent < 0)
1176
0
                            dy -= dy + rast.top_indent;
1177
0
                    }
1178
133k
                    if ((code = fapi_copy_mono(dev1, &rast, dx, dy)) < 0)
1179
0
                        return code;
1180
1181
133k
                    if (penum_s->cc != NULL) {
1182
133k
                        penum_s->cc->offset.x +=
1183
133k
                            float2fixed(penum_s->fapi_glyph_shift.x);
1184
133k
                        penum_s->cc->offset.y +=
1185
133k
                            float2fixed(penum_s->fapi_glyph_shift.y);
1186
133k
                    }
1187
133k
                }
1188
133k
            }
1189
59.1k
            else if (!SHOW_IS(penum, TEXT_DO_NONE)) {   /* Not using GS cache */
1190
3.51k
                if ((code =
1191
3.51k
                     fapi_image_uncached_glyph(pfont, pgs, penum_s, &rast,
1192
3.51k
                                               import_shift_v)) < 0)
1193
0
                    return code;
1194
3.51k
            }
1195
192k
        }
1196
195k
    }
1197
1198
196k
    return 0;
1199
196k
}
1200
1201
1202
0
#define GET_U16_MSB(p) (((uint)((p)[0]) << 8) + (p)[1])
1203
0
#define GET_S16_MSB(p) (int)((GET_U16_MSB(p) ^ 0x8000) - 0x8000)
1204
1205
391k
#define MTX_EQ(mtx1,mtx2) (mtx1->xx == mtx2->xx && mtx1->xy == mtx2->xy && \
1206
191k
                           mtx1->yx == mtx2->yx && mtx1->yy == mtx2->yy)
1207
1208
int
1209
gs_fapi_do_char(gs_font *pfont, gs_gstate *pgs, gs_text_enum_t *penum, char *font_file_path,
1210
                bool bBuildGlyph, gs_string *charstring, gs_string *glyphname,
1211
                gs_char chr, gs_glyph index, int subfont)
1212
198k
{                               /* Stack : <font> <code|name> --> - */
1213
198k
    gs_show_enum *penum_s = (gs_show_enum *) penum;
1214
198k
    gx_device *dev = gs_currentdevice_inline(pgs);
1215
1216
    /*
1217
       fixme: the following code needs to optimize with a maintainence of scaled font objects
1218
       in graphics library and in interpreter. Now we suppose that the renderer
1219
       uses font cache, so redundant font opening isn't a big expense.
1220
     */
1221
198k
    gs_fapi_char_ref cr =
1222
198k
        { 0, {0}, 0, false, NULL, 0, 0, 0, 0, 0, gs_fapi_metrics_notdef };
1223
198k
    gs_font_base *pbfont = (gs_font_base *)pfont;
1224
198k
    const gs_matrix *ctm = &ctm_only(pgs);
1225
198k
    int scale;
1226
198k
    gs_fapi_metrics metrics;
1227
198k
    gs_fapi_server *I = pbfont->FAPI;
1228
1229
198k
    gs_string enc_char_name_string;
1230
198k
    bool bCID = (FAPI_ISCIDFONT(pbfont) || charstring != NULL);
1231
198k
    bool bIsType1GlyphData = FAPI_ISTYPE1GLYPHDATA(pbfont);
1232
198k
    gs_log2_scale_point log2_scale = { 0, 0 };
1233
198k
    int alpha_bits = (*dev_proc(dev, get_alpha_bits)) (dev, go_text);
1234
198k
    double FontMatrix_div = 1;
1235
198k
    bool bVertical = (gs_rootfont(pgs)->WMode != 0), bVertical0 = bVertical;
1236
198k
    double *sbwp, sbw[4] = { 0, 0, 0, 0 };
1237
198k
    double em_scale_x, em_scale_y;
1238
198k
    gs_rect char_bbox;
1239
198k
    int code;
1240
198k
    bool imagenow = false;
1241
198k
    bool align_to_pixels = gs_currentaligntopixels(pbfont->dir);
1242
198k
    gs_memory_t *mem = pfont->memory;
1243
198k
    enum
1244
198k
    {
1245
198k
        SBW_DONE,
1246
198k
        SBW_SCALE,
1247
198k
        SBW_FROM_RENDERER
1248
198k
    } sbw_state = SBW_SCALE;
1249
1250
198k
    if ( index == GS_NO_GLYPH )
1251
0
        return 0;
1252
1253
198k
    I->use_outline = false;
1254
198k
    I->transform_outline = false;
1255
1256
198k
    if (penum == 0)
1257
0
        return_error(gs_error_undefined);
1258
1259
198k
    I->use_outline =
1260
198k
        produce_outline_char(penum_s, pbfont, alpha_bits, &log2_scale);
1261
1262
198k
    if (I->use_outline) {
1263
865
        I->max_bitmap = 0;
1264
865
    }
1265
197k
    else {
1266
        /* FIX ME: It can be a very bad thing, right now, for the FAPI code to decide unilaterally to
1267
         * produce an outline, when the rest of GS expects a bitmap, so we give ourselves a
1268
         * 50% leeway on the maximum cache bitmap, just to be sure. Or the same maximum bitmap size
1269
         * used in gxchar.c
1270
         */
1271
197k
        I->max_bitmap =
1272
197k
            pbfont->dir->ccache.upper + (pbfont->dir->ccache.upper >> 1) <
1273
197k
            MAX_CCACHE_TEMP_BITMAP_BITS ? pbfont->dir->ccache.upper +
1274
197k
            (pbfont->dir->ccache.upper >> 1) : MAX_CCACHE_TEMP_BITMAP_BITS;
1275
197k
    }
1276
1277
198k
    I->grid_fit = pbfont->dir->grid_fit_tt;
1278
1279
    /* Compute the scale : */
1280
198k
    if (!SHOW_IS(penum, TEXT_DO_NONE) && !I->use_outline) {
1281
139k
        gs_currentcharmatrix(pgs, NULL, 1);     /* make char_tm valid */
1282
139k
        penum_s->fapi_log2_scale = log2_scale;
1283
139k
    }
1284
58.3k
    else {
1285
58.3k
        log2_scale.x = 0;
1286
58.3k
        log2_scale.y = 0;
1287
58.3k
    }
1288
1289
    /* Prepare font data
1290
     * This needs done here (earlier than it used to be) because FAPI/UFST has conflicting
1291
     * requirements in what I->get_fontmatrix() returns based on font type, so it needs to
1292
     * find the font type.
1293
     */
1294
1295
198k
    I->ff.memory = pbfont->memory;
1296
198k
    I->ff.subfont = subfont;
1297
198k
    I->ff.font_file_path = font_file_path;
1298
198k
    I->ff.client_font_data = pbfont;
1299
198k
    I->ff.client_font_data2 = pbfont;
1300
198k
    I->ff.server_font_data = pbfont->FAPI_font_data;
1301
198k
    I->ff.is_type1 = bIsType1GlyphData;
1302
198k
    I->ff.is_cid = bCID;
1303
198k
    I->ff.is_outline_font = pbfont->PaintType != 0;
1304
198k
    I->ff.metrics_only = fapi_gs_char_show_width_only(penum);
1305
1306
198k
    if (!I->ff.is_mtx_skipped)
1307
198k
        I->ff.is_mtx_skipped = (gs_fapi_get_metrics_count(&I->ff) != 0);
1308
1309
198k
    I->ff.is_vertical = bVertical;
1310
198k
    I->ff.client_ctx_p = I->client_ctx_p;
1311
1312
198k
    if (recreate_multiple_master(pbfont)) {
1313
0
        if ((void *)pbfont->base == (void *)pbfont) {
1314
0
           gs_fapi_release_typeface(I, &pbfont->FAPI_font_data);
1315
0
        }
1316
0
        else {
1317
0
            pbfont->FAPI_font_data = NULL;
1318
0
            pbfont->FAPI->face.font_id = gs_no_id;
1319
0
        }
1320
0
    }
1321
1322
198k
   scale = 1 << I->frac_shift;
1323
199k
retry_oversampling:
1324
199k
    if (I->face.font_id != pbfont->id ||
1325
199k
        !MTX_EQ((&I->face.ctm), ctm) ||
1326
199k
        I->face.log2_scale.x != log2_scale.x ||
1327
199k
        I->face.log2_scale.y != log2_scale.y ||
1328
199k
        I->face.align_to_pixels != align_to_pixels ||
1329
199k
        I->face.HWResolution[0] != dev->HWResolution[0] ||
1330
199k
        I->face.HWResolution[1] != dev->HWResolution[1]
1331
199k
        ) {
1332
25.0k
        gs_fapi_font_scale font_scale = { {1, 0, 0, 1, 0, 0}
1333
25.0k
        , {0, 0}
1334
25.0k
        , {1, 1}
1335
25.0k
        , true
1336
25.0k
        };
1337
1338
25.0k
        gs_matrix lctm, scale_mat, scale_ctm;
1339
25.0k
        I->face.font_id = pbfont->id;
1340
25.0k
        I->face.log2_scale = log2_scale;
1341
25.0k
        I->face.align_to_pixels = align_to_pixels;
1342
25.0k
        I->face.HWResolution[0] = dev->HWResolution[0];
1343
25.0k
        I->face.HWResolution[1] = dev->HWResolution[1];
1344
1345
25.0k
        font_scale.subpixels[0] = 1 << log2_scale.x;
1346
25.0k
        font_scale.subpixels[1] = 1 << log2_scale.y;
1347
25.0k
        font_scale.align_to_pixels = align_to_pixels;
1348
1349
        /* We apply the entire transform to the glyph (that is ctm x FontMatrix)
1350
         * at render time.
1351
         */
1352
25.0k
        lctm = *ctm;
1353
26.5k
retry_scaling:
1354
26.5k
        I->face.ctm = lctm;
1355
26.5k
        memset(&scale_ctm, 0x00, sizeof(gs_matrix));
1356
26.5k
        scale_ctm.xx = dev->HWResolution[0] / 72;
1357
26.5k
        scale_ctm.yy = dev->HWResolution[1] / 72;
1358
1359
26.5k
        if ((code = gs_matrix_invert((const gs_matrix *)&scale_ctm, &scale_ctm)) < 0)
1360
0
            return code;
1361
1362
26.5k
        if ((code = gs_matrix_multiply(&lctm, &scale_ctm, &scale_mat)) < 0)  /* scale_mat ==  CTM - resolution scaling */
1363
0
            return code;
1364
1365
26.5k
        if ((code = I->get_fontmatrix(I, &scale_ctm)) < 0)
1366
0
            return code;
1367
1368
26.5k
        if ((code = gs_matrix_invert((const gs_matrix *)&scale_ctm, &scale_ctm)) < 0)
1369
0
            return code;
1370
1371
26.5k
        if ((code = gs_matrix_multiply(&scale_mat, &scale_ctm, &scale_mat)) < 0)  /* scale_mat ==  CTM - resolution scaling - FontMatrix scaling */
1372
0
            return code;
1373
1374
26.5k
        if (((int64_t)(scale_mat.xx * FontMatrix_div * scale + 0.5)) != ((int32_t)(scale_mat.xx * FontMatrix_div * scale + 0.5))
1375
26.5k
        ||  ((int64_t)(scale_mat.xy * FontMatrix_div * scale + 0.5)) != ((int32_t)(scale_mat.xy * FontMatrix_div * scale + 0.5))
1376
26.5k
        ||  ((int64_t)(scale_mat.yx * FontMatrix_div * scale + 0.5)) != ((int32_t)(scale_mat.yx * FontMatrix_div * scale + 0.5))
1377
26.5k
        ||  ((int64_t)(scale_mat.yy * FontMatrix_div * scale + 0.5)) != ((int32_t)(scale_mat.yy * FontMatrix_div * scale + 0.5))) {
1378
            /* Overflow
1379
               If the scaling is large enough to overflow the 16.16 representation, we forcibly produce an outline
1380
               unscaled except an arbitrary "midrange" scale (chosen to avoid under/overflow issues). And
1381
               then scale the points as we create the Ghostscript path outline_char().
1382
               If the glyph is this large, we're really not worried about hinting or dropout detection etc.
1383
             */
1384
1385
1.49k
            memset(&lctm, 0x00, sizeof(gs_matrix));
1386
1.49k
            lctm.xx = 256.0;
1387
1.49k
            lctm.yy = 256.0;
1388
1.49k
            I->transform_outline = true;
1389
1.49k
            I->use_outline = true;
1390
1.49k
            if ((code = gs_matrix_invert((const gs_matrix *)&lctm, &scale_ctm)) < 0)
1391
0
                 return code;
1392
1.49k
            if ((code = gs_matrix_multiply(ctm, &scale_ctm, &scale_mat)) < 0)  /* scale_mat ==  CTM - resolution scaling */
1393
0
                return code;
1394
1395
1.49k
            I->outline_mat = scale_mat;
1396
1.49k
            goto retry_scaling;
1397
1.49k
        }
1398
25.0k
        else {
1399
25.0k
            font_scale.matrix[0] = (fracint) (scale_mat.xx * FontMatrix_div * scale + 0.5);
1400
25.0k
            font_scale.matrix[1] = -(fracint) (scale_mat.xy * FontMatrix_div * scale + 0.5);
1401
25.0k
            font_scale.matrix[2] = (fracint) (scale_mat.yx * FontMatrix_div * scale + 0.5);
1402
25.0k
            font_scale.matrix[3] = -(fracint) (scale_mat.yy * FontMatrix_div * scale + 0.5);
1403
25.0k
            font_scale.matrix[4] = (fracint) (scale_mat.tx * FontMatrix_div * scale + 0.5);
1404
25.0k
            font_scale.matrix[5] = (fracint) (scale_mat.ty * FontMatrix_div * scale + 0.5);
1405
25.0k
        }
1406
1407
        /* Note: the ctm mapping here is upside down. */
1408
25.0k
        font_scale.HWResolution[0] =
1409
25.0k
            (fracint) ((double)dev->HWResolution[0] *
1410
25.0k
                       font_scale.subpixels[0] * scale);
1411
25.0k
        font_scale.HWResolution[1] =
1412
25.0k
            (fracint) ((double)dev->HWResolution[1] *
1413
25.0k
                       font_scale.subpixels[1] * scale);
1414
1415
1416
25.0k
        if ((hypot((double)font_scale.matrix[0], (double)font_scale.matrix[2])
1417
25.0k
             == 0.0
1418
25.0k
             || hypot((double)font_scale.matrix[1],
1419
25.0k
                      (double)font_scale.matrix[3]) == 0.0)) {
1420
1421
            /* If the matrix is degenerate, force a scale to 1 unit. */
1422
63
            memset(&font_scale.matrix, 0x00, sizeof(font_scale.matrix));
1423
63
            if (!font_scale.matrix[0])
1424
63
                font_scale.matrix[0] = 1;
1425
63
            if (!font_scale.matrix[3])
1426
63
                font_scale.matrix[3] = 1;
1427
63
        }
1428
1429
25.0k
        if ((code =
1430
25.0k
             gs_fapi_renderer_retcode(mem, I,
1431
25.0k
                                      I->get_scaled_font(I, &I->ff,
1432
25.0k
                                                         &font_scale, NULL,
1433
25.0k
                                                         gs_fapi_toplevel_prepared))) < 0) {
1434
0
            return code;
1435
0
        }
1436
25.0k
        pbfont->FAPI_font_data = I->ff.server_font_data;    /* Save it back to GS font. */
1437
25.0k
    }
1438
1439
199k
    cr.char_codes_count = 1;
1440
199k
    cr.char_codes[0] = index;
1441
199k
    cr.client_char_code = chr;
1442
199k
    cr.is_glyph_index = true;
1443
199k
    enc_char_name_string.data = NULL;
1444
199k
    enc_char_name_string.size = 0;
1445
1446
199k
    if (I->ff.get_glyphname_or_cid) {
1447
199k
        if ((code =
1448
199k
             I->ff.get_glyphname_or_cid(penum, pbfont, charstring, glyphname, index,
1449
199k
                                        &enc_char_name_string, font_file_path,
1450
199k
                                        &cr, bCID)) < 0)
1451
0
            return (code);
1452
199k
    }
1453
1454
    /* Compute the metrics replacement : */
1455
199k
    if (bCID && !bIsType1GlyphData) {
1456
77.0k
        gs_font_cid2 *pfcid = (gs_font_cid2 *) pbfont;
1457
77.0k
        int MetricsCount = pfcid->cidata.MetricsCount;
1458
1459
77.0k
        if (MetricsCount > 0) {
1460
0
            const byte *data_ptr;
1461
0
            int l = I->ff.get_glyphdirectory_data(&(I->ff), cr.char_codes[0],
1462
0
                                                  &data_ptr);
1463
1464
            /* We do not need to apply the unitsPerEm scale here because
1465
             * these values are read directly from the glyph data.
1466
             */
1467
0
            if (MetricsCount == 2 && l >= 4) {
1468
0
                if (!bVertical0) {
1469
0
                    cr.sb_x = GET_S16_MSB(data_ptr + 2) * scale;
1470
0
                    cr.aw_x = GET_U16_MSB(data_ptr + 0) * scale;
1471
0
                    cr.metrics_type = gs_fapi_metrics_replace;
1472
0
                }
1473
0
            }
1474
0
            else if (l >= 8) {
1475
0
                cr.sb_y = GET_S16_MSB(data_ptr + 2) * scale;
1476
0
                cr.aw_y = GET_U16_MSB(data_ptr + 0) * scale;
1477
0
                cr.sb_x = GET_S16_MSB(data_ptr + 6) * scale;
1478
0
                cr.aw_x = GET_U16_MSB(data_ptr + 4) * scale;
1479
0
                cr.metrics_type = gs_fapi_metrics_replace;
1480
0
            }
1481
0
        }
1482
77.0k
    }
1483
    /* Metrics in GS are scaled to a 1.0x1.0 square, as that's what Postscript
1484
     * fonts expect. But for Truetype we need the "native" units,
1485
     */
1486
199k
    em_scale_x = 1.0;
1487
199k
    if (pfont->FontType == ft_TrueType) {
1488
8.45k
        gs_font_type42 *pfont42 = (gs_font_type42 *) pfont;
1489
8.45k
        em_scale_x = pfont42->data.unitsPerEm;
1490
8.45k
    }
1491
1492
199k
    if (cr.metrics_type != gs_fapi_metrics_replace && bVertical) {
1493
0
        double pwv[4];
1494
1495
0
        code =
1496
0
            I->ff.fapi_get_metrics(&I->ff, &enc_char_name_string, index, pwv,
1497
0
                                   bVertical);
1498
0
        if (code < 0)
1499
0
            return code;
1500
0
        if (code == 0 /* metricsNone */ ) {
1501
0
            if (bCID && (!bIsType1GlyphData && font_file_path)) {
1502
0
                cr.sb_x = (fracint)(fapi_round( (sbw[2] / 2)         * scale) * em_scale_x);
1503
0
                cr.sb_y = (fracint)(fapi_round( pbfont->FontBBox.q.y * scale) * em_scale_x);
1504
0
                cr.aw_y = (fracint)(fapi_round(-pbfont->FontBBox.q.x * scale) * em_scale_x);    /* Sic ! */
1505
0
                cr.metrics_scale = 1;
1506
0
                cr.metrics_type = gs_fapi_metrics_replace;
1507
0
                sbw[0] = sbw[2] / 2;
1508
0
                sbw[1] = pbfont->FontBBox.q.y;
1509
0
                sbw[2] = 0;
1510
0
                sbw[3] = -pbfont->FontBBox.q.x; /* Sic ! */
1511
0
                sbw_state = SBW_DONE;
1512
0
            }
1513
0
            else {
1514
0
                bVertical = false;
1515
0
            }
1516
0
        }
1517
0
        else {
1518
0
            cr.sb_x = (fracint)(fapi_round(pwv[2] * scale) * em_scale_x);
1519
0
            cr.sb_y = (fracint)(fapi_round(pwv[3] * scale) * em_scale_x);
1520
0
            cr.aw_x = (fracint)(fapi_round(pwv[0] * scale) * em_scale_x);
1521
0
            cr.aw_y = (fracint)(fapi_round(pwv[1] * scale) * em_scale_x);
1522
0
            cr.metrics_scale = (bIsType1GlyphData ? 1000 : 1);
1523
0
            cr.metrics_type = (code == 2 /* metricsSideBearingAndWidth */ ? gs_fapi_metrics_replace
1524
0
                               : gs_fapi_metrics_replace_width);
1525
0
            sbw[0] = pwv[2];
1526
0
            sbw[1] = pwv[3];
1527
0
            sbw[2] = pwv[0];
1528
0
            sbw[3] = pwv[1];
1529
0
            sbw_state = SBW_DONE;
1530
0
        }
1531
0
    }
1532
199k
    if (cr.metrics_type == gs_fapi_metrics_notdef && !bVertical) {
1533
199k
        code =
1534
199k
            I->ff.fapi_get_metrics(&I->ff, &enc_char_name_string, (int)index, sbw,
1535
199k
                                   bVertical);
1536
199k
        if (code < 0)
1537
0
            return code;
1538
199k
        if (code == 0 /* metricsNone */ ) {
1539
199k
            sbw_state = SBW_FROM_RENDERER;
1540
199k
            if (pbfont->FontType == 2) {
1541
8.02k
                gs_font_type1 *pfont1 = (gs_font_type1 *) pbfont;
1542
1543
8.02k
                cr.aw_x =
1544
8.02k
                    export_shift(pfont1->data.defaultWidthX,
1545
8.02k
                                 _fixed_shift - I->frac_shift);
1546
8.02k
                cr.metrics_scale = 1000;
1547
8.02k
                cr.metrics_type = gs_fapi_metrics_add;
1548
8.02k
            }
1549
199k
        }
1550
0
        else {
1551
0
            cr.sb_x = fapi_round(sbw[0] * scale * em_scale_x);
1552
0
            cr.sb_y = fapi_round(sbw[1] * scale * em_scale_x);
1553
0
            cr.aw_x = fapi_round(sbw[2] * scale * em_scale_x);
1554
0
            cr.aw_y = fapi_round(sbw[3] * scale * em_scale_x);
1555
0
            cr.metrics_scale = (bIsType1GlyphData ? 1000 : 1);
1556
0
            cr.metrics_type = (code == 2 /* metricsSideBearingAndWidth */ ? gs_fapi_metrics_replace
1557
0
                               : gs_fapi_metrics_replace_width);
1558
0
            sbw_state = SBW_DONE;
1559
0
        }
1560
199k
    }
1561
199k
    memset(&metrics, 0x00, sizeof(metrics));
1562
    /* Take metrics from font : */
1563
199k
    if (I->ff.metrics_only) {
1564
55.6k
        code = I->get_char_outline_metrics(I, &I->ff, &cr, &metrics);
1565
55.6k
    }
1566
144k
    else if (I->use_outline) {
1567
3.73k
        code = I->get_char_outline_metrics(I, &I->ff, &cr, &metrics);
1568
3.73k
    }
1569
140k
    else {
1570
140k
        code = I->get_char_raster_metrics(I, &I->ff, &cr, &metrics);
1571
        /* A VMerror could be a real out of memory, or the glyph being too big for a bitmap
1572
         * so it's worth retrying as an outline glyph
1573
         */
1574
140k
        if (code == gs_error_VMerror) {
1575
1.75k
            I->use_outline = true;
1576
1.75k
            goto retry_oversampling;
1577
1.75k
        }
1578
138k
        if (code == gs_error_limitcheck) {
1579
0
            if (log2_scale.x > 0 || log2_scale.y > 0) {
1580
0
                penum_s->fapi_log2_scale.x = log2_scale.x =
1581
0
                    penum_s->fapi_log2_scale.y = log2_scale.y = 0;
1582
0
                I->release_char_data(I);
1583
0
                goto retry_oversampling;
1584
0
            }
1585
0
            code = I->get_char_outline_metrics(I, &I->ff, &cr, &metrics);
1586
0
        }
1587
138k
    }
1588
1589
    /* This handles the situation where a charstring has been replaced with a PS procedure.
1590
     * against the rules, but not *that* rare.
1591
     * It's also something that GS does internally to simulate font styles.
1592
     */
1593
198k
    if (code > 0) {
1594
0
        return (gs_error_unregistered);
1595
0
    }
1596
1597
198k
    if ((code = gs_fapi_renderer_retcode(mem, I, code)) < 0)
1598
641
        return code;
1599
1600
197k
    compute_em_scale(pbfont, &metrics, FontMatrix_div, &em_scale_x,
1601
197k
                     &em_scale_y);
1602
197k
    char_bbox.p.x = metrics.bbox_x0 / em_scale_x;
1603
197k
    char_bbox.p.y = metrics.bbox_y0 / em_scale_y;
1604
197k
    char_bbox.q.x = metrics.bbox_x1 / em_scale_x;
1605
197k
    char_bbox.q.y = metrics.bbox_y1 / em_scale_y;
1606
1607
    /* We must use the FontBBox, but it seems some buggy fonts have glyphs which extend outside the
1608
     * FontBBox, so we have to do this....
1609
     */
1610
197k
    if (pbfont->FontType != ft_MicroType && !bCID
1611
197k
        && pbfont->FontBBox.q.x > pbfont->FontBBox.p.x
1612
197k
        && pbfont->FontBBox.q.y > pbfont->FontBBox.p.y) {
1613
117k
        char_bbox.p.x = min(char_bbox.p.x, pbfont->FontBBox.p.x);
1614
117k
        char_bbox.p.y = min(char_bbox.p.y, pbfont->FontBBox.p.y);
1615
117k
        char_bbox.q.x = max(char_bbox.q.x, pbfont->FontBBox.q.x);
1616
117k
        char_bbox.q.y = max(char_bbox.q.y, pbfont->FontBBox.q.y);
1617
117k
    }
1618
1619
197k
    if (pbfont->PaintType != 0) {
1620
0
        float w = pbfont->StrokeWidth / 2;
1621
1622
0
        char_bbox.p.x -= w;
1623
0
        char_bbox.p.y -= w;
1624
0
        char_bbox.q.x += w;
1625
0
        char_bbox.q.y += w;
1626
0
    }
1627
197k
    penum_s->fapi_glyph_shift.x = penum_s->fapi_glyph_shift.y = 0;
1628
197k
    if (sbw_state == SBW_FROM_RENDERER) {
1629
197k
        int can_replace_metrics;
1630
1631
197k
        if ((code =
1632
197k
             gs_fapi_renderer_retcode(mem, I,
1633
197k
                                      I->can_replace_metrics(I, &I->ff, &cr,
1634
197k
                                                             &can_replace_metrics)))
1635
197k
            < 0)
1636
0
            return code;
1637
1638
197k
        sbw[2] = metrics.escapement / em_scale_x;
1639
197k
        sbw[3] = metrics.v_escapement / em_scale_y;
1640
197k
        if (pbfont->FontType == 2 && !can_replace_metrics) {
1641
0
            gs_font_type1 *pfont1 = (gs_font_type1 *) pbfont;
1642
1643
0
            sbw[2] += fixed2float(pfont1->data.nominalWidthX);
1644
0
        }
1645
197k
    }
1646
0
    else if (sbw_state == SBW_SCALE) {
1647
0
        sbw[0] = (double)cr.sb_x / scale / em_scale_x;
1648
0
        sbw[1] = (double)cr.sb_y / scale / em_scale_y;
1649
0
        sbw[2] = (double)cr.aw_x / scale / em_scale_x;
1650
0
        sbw[3] = (double)cr.aw_y / scale / em_scale_y;
1651
0
    }
1652
1653
    /* Setup cache and render : */
1654
197k
    if (cr.metrics_type == gs_fapi_metrics_replace) {
1655
        /*
1656
         * Here we don't take care of replaced advance width
1657
         * because gs_text_setcachedevice handles it.
1658
         */
1659
0
        int can_replace_metrics;
1660
1661
0
        if ((code =
1662
0
             gs_fapi_renderer_retcode(mem, I,
1663
0
                                      I->can_replace_metrics(I, &I->ff, &cr,
1664
0
                                                             &can_replace_metrics)))
1665
0
            < 0)
1666
0
            return code;
1667
0
        if (!can_replace_metrics) {
1668
            /*
1669
             * The renderer should replace the lsb, but it can't.
1670
             * To work around we compute a displacement in integral pixels
1671
             * and later shift the bitmap to it. The raster will be inprecise
1672
             * with non-integral pixels shift.
1673
             */
1674
0
            char_bbox.q.x -= char_bbox.p.x;
1675
0
            char_bbox.p.x = 0;
1676
0
            gs_distance_transform((metrics.bbox_x0 / em_scale_x - sbw[0]),
1677
0
                                  0, ctm, &penum_s->fapi_glyph_shift);
1678
0
            penum_s->fapi_glyph_shift.x *= 1 << log2_scale.x;
1679
0
            penum_s->fapi_glyph_shift.y *= 1 << log2_scale.y;
1680
0
        }
1681
0
    }
1682
1683
    /*
1684
     * We assume that if bMetricsFromGlyphDirectory is true,
1685
     * the font does not specify Metrics[2] and/or CDevProc
1686
     * If someday we meet a font contradicting this assumption,
1687
     * zchar_set_cache to be improved with additional flag,
1688
     * to ignore Metrics[2] and CDevProc.
1689
     *
1690
     * Note that for best quality the result of CDevProc
1691
     * to be passed to I->get_char_raster_metrics, because
1692
     * both raster and metrics depend on replaced lsb.
1693
     * Perhaps in many cases the metrics from font is
1694
     * used as an argument for CDevProc. Only way to resolve
1695
     * is to call I->get_char_raster_metrics twice (before
1696
     * and after CDevProc), or better to split it into
1697
     * smaller functions. Unfortunately UFST cannot retrieve metrics
1698
     * quickly and separately from raster. Only way to resolve is
1699
     * to devide the replaced lsb into 2 parts, which correspond to
1700
     * integral and fractinal pixels, then pass the fractional shift
1701
     * to renderer and apply the integer shift after it.
1702
     *
1703
     * Besides that, we are not sure what to do if a font
1704
     * contains both Metrics[2] and CDevProc. Should
1705
     * CDevProc to be applied to Metrics[2] or to the metrics
1706
     * from glyph code ? Currently we keep a compatibility
1707
     * to the native GS font renderer without a deep analyzis.
1708
     */
1709
1710
    /* Don't allow caching if we're only returning the metrics */
1711
197k
    if (I->ff.metrics_only) {
1712
55.6k
        pgs->in_cachedevice = CACHE_DEVICE_NOT_CACHING;
1713
55.6k
    }
1714
1715
197k
    if (pgs->in_cachedevice == CACHE_DEVICE_CACHING) {
1716
0
        sbwp = sbw;
1717
0
    }
1718
197k
    else {
1719
        /* Very occasionally, if we don't do this, setcachedevice2
1720
         * will decide we are cacheing, when we're not, and this
1721
         * causes problems when we get to show_update().
1722
         */
1723
197k
        sbwp = NULL;
1724
1725
197k
        if (I->use_outline) {
1726
            /* HACK!!
1727
             * The decision about whether to cache has already been
1728
             * we need to prevent it being made again....
1729
             */
1730
3.71k
            pgs->in_cachedevice = CACHE_DEVICE_NOT_CACHING;
1731
3.71k
        }
1732
197k
    }
1733
1734
197k
    if (bCID) {
1735
76.7k
        code =
1736
76.7k
            I->ff.fapi_set_cache(penum, pbfont, &enc_char_name_string, index + GS_MIN_CID_GLYPH,
1737
76.7k
                                 sbw + 2, &char_bbox, sbwp, &imagenow);
1738
76.7k
    }
1739
120k
    else {
1740
120k
        code =
1741
120k
            I->ff.fapi_set_cache(penum, pbfont, &enc_char_name_string, index,
1742
120k
                                 sbw + 2, &char_bbox, sbwp, &imagenow);
1743
120k
    }
1744
1745
    /* If we can render the glyph now, do so.
1746
     * we may not be able to in the PS world if there's a CDevProc in the font
1747
     * in which case gs_fapi_finish_render() will be called from the PS
1748
     * "function" zfapi_finish_render() which has been pushed onto the
1749
     * stack.
1750
     */
1751
197k
    if (code >= 0 && imagenow == true) {
1752
196k
        code = gs_fapi_finish_render(pfont, pgs, penum, I);
1753
196k
        I->release_char_data(I);
1754
196k
    }
1755
1756
197k
    if (code != 0) {
1757
877
        if (code < 0) {
1758
            /* An error */
1759
877
            I->release_char_data(I);
1760
877
        }
1761
0
        else {
1762
            /* Callout to CDevProc, zsetcachedevice2, zfapi_finish_render. */
1763
0
        }
1764
877
    }
1765
1766
197k
    return code;
1767
197k
}
1768
1769
int
1770
gs_fapi_get_font_info(gs_font *pfont, gs_fapi_font_info item, int index,
1771
                      void *data, int *data_len)
1772
0
{
1773
0
    int code = 0;
1774
0
    gs_font_base *pbfont = (gs_font_base *) pfont;
1775
0
    gs_fapi_server *I = pbfont->FAPI;
1776
1777
0
    code = I->get_font_info(I, &I->ff, item, index, data, data_len);
1778
0
    return (code);
1779
0
}
1780
1781
/* On finding a suitable FAPI instance, fapi_id will be set to point to the string of the instance name.
1782
 * A specific FAPI instance can be requested with the "fapi_request" parameter, but if the requested
1783
 * isn't found, or rejects the font, we're re-search the available instances to find one that can handle
1784
 * the font. If only an exact match is suitable, it is up to the *caller* to enforce that.
1785
 */
1786
int
1787
gs_fapi_passfont(gs_font *pfont, int subfont, char *font_file_path,
1788
                 gs_string *full_font_buf, char *fapi_request, char *xlatmap,
1789
                 char **fapi_id, gs_fapi_get_server_param_callback get_server_param_cb)
1790
3.04k
{
1791
3.04k
    gs_font_base *pbfont;
1792
3.04k
    int code = 0;
1793
3.04k
    gs_fapi_server *I, **list;
1794
3.04k
    bool free_params = false;
1795
3.04k
    gs_memory_t *mem = pfont->memory;
1796
3.04k
    const char *decodingID = NULL;
1797
3.04k
    bool do_restart = false;
1798
1799
3.04k
    list = gs_fapi_get_server_list(mem);
1800
1801
3.04k
    if (!list) /* Should never happen */
1802
0
      return_error(gs_error_unregistered);
1803
1804
3.04k
    (*fapi_id) = NULL;
1805
1806
3.04k
    pbfont = (gs_font_base *) pfont;
1807
1808
3.04k
    I = *list;
1809
1810
3.04k
    if (fapi_request) {
1811
0
        if (gs_debug_c('1'))
1812
0
            dprintf1("Requested FAPI plugin: %s ", fapi_request);
1813
1814
0
        while ((I = *list) != NULL
1815
0
               && strncmp(I->ig.d->subtype, fapi_request,
1816
0
                          strlen(fapi_request)) != 0) {
1817
0
            list++;
1818
0
        }
1819
0
        if (!I) {
1820
0
            if (gs_debug_c('1'))
1821
0
                dprintf("not found. Falling back to normal plugin search\n");
1822
0
            list = (gs_fapi_server **) gs_fapi_get_server_list(mem);
1823
0
            I = *list;
1824
0
        }
1825
0
        else {
1826
0
            if (gs_debug_c('1'))
1827
0
                dprintf("found.\n");
1828
0
            do_restart = true;
1829
0
        }
1830
0
    }
1831
1832
3.13k
    while (I) {
1833
3.08k
        char *server_param = NULL;
1834
3.08k
        int server_param_size = 0;
1835
1836
3.08k
        (*get_server_param_cb) (I, (const char *)I->ig.d->subtype,
1837
3.08k
                                &server_param, &server_param_size);
1838
1839
3.08k
        if (server_param == NULL && server_param_size > 0) {
1840
0
            server_param =
1841
0
                (char *)gs_alloc_bytes_immovable(mem->non_gc_memory,
1842
0
                                         server_param_size,
1843
0
                                         "gs_fapi_passfont server params");
1844
0
            if (!server_param) {
1845
0
                return_error(gs_error_VMerror);
1846
0
            }
1847
0
            free_params = true;
1848
0
            (*get_server_param_cb) (I, (const char *)I->ig.d->subtype,
1849
0
                                    &server_param, &server_param_size);
1850
0
        }
1851
1852
3.08k
        if ((code =
1853
3.08k
             gs_fapi_renderer_retcode(mem, I,
1854
3.08k
                                      I->ensure_open(I, server_param,
1855
3.08k
                                                     server_param_size))) < 0) {
1856
0
            gs_free_object(mem->non_gc_memory, server_param,
1857
0
                           "gs_fapi_passfont server params");
1858
0
            return code;
1859
0
        }
1860
1861
3.08k
        if (free_params) {
1862
0
            gs_free_object(mem->non_gc_memory, server_param,
1863
0
                           "gs_fapi_passfont server params");
1864
0
        }
1865
1866
3.08k
        pbfont->FAPI = I;       /* we need the FAPI server during this stage */
1867
3.08k
        code =
1868
3.08k
            gs_fapi_prepare_font(pfont, I, subfont, font_file_path,
1869
3.08k
                                 full_font_buf, xlatmap, &decodingID);
1870
3.08k
        if (code >= 0) {
1871
2.99k
            (*fapi_id) = (char *)I->ig.d->subtype;
1872
2.99k
            return 0;
1873
2.99k
        }
1874
1875
        /* renderer failed, continue search */
1876
88
        pbfont->FAPI = NULL;
1877
88
        if (do_restart == true) {
1878
0
            if (gs_debug_c('1'))
1879
0
                dprintf1
1880
0
                    ("Requested FAPI plugin %s failed, searching for alternative plugin\n",
1881
0
                     I->ig.d->subtype);
1882
0
            list = (gs_fapi_server **) gs_fapi_get_server_list(mem);
1883
0
            do_restart = false;
1884
0
        }
1885
88
        else {
1886
88
            I = *list;
1887
88
            list++;
1888
88
        }
1889
88
    }
1890
44
    return (code);
1891
3.04k
}
1892
1893
bool
1894
gs_fapi_available(gs_memory_t *mem, char *server)
1895
4.40k
{
1896
4.40k
    bool retval = false;
1897
1898
4.40k
    if (server) {
1899
683
        gs_fapi_server *serv = NULL;
1900
1901
683
        retval = (gs_fapi_find_server(mem, server, &serv, NULL) >= 0);
1902
683
    }
1903
3.72k
    else {
1904
3.72k
        retval = ((mem->gs_lib_ctx->fapi_servers) != NULL) && (*(mem->gs_lib_ctx->fapi_servers) != NULL);
1905
3.72k
    }
1906
4.40k
    return (retval);
1907
4.40k
}
1908
1909
void
1910
gs_fapi_set_servers_client_data(gs_memory_t *mem, const gs_fapi_font *ff_proto, void *ctx_ptr)
1911
3.04k
{
1912
3.04k
    gs_fapi_server **servs = gs_fapi_get_server_list(mem);
1913
1914
3.04k
    if (servs) {
1915
6.08k
        while (*servs) {
1916
3.04k
            (*servs)->client_ctx_p = ctx_ptr;
1917
3.04k
            if (ff_proto)
1918
3.04k
                (*servs)->ff = *ff_proto;
1919
3.04k
            servs++;
1920
3.04k
        }
1921
3.04k
    }
1922
3.04k
}
1923
1924
gs_fapi_server **
1925
gs_fapi_get_server_list(gs_memory_t *mem)
1926
6.76k
{
1927
6.76k
    return (mem->gs_lib_ctx->fapi_servers);
1928
6.76k
}
1929
1930
int
1931
gs_fapi_find_server(gs_memory_t *mem, const char *name, gs_fapi_server **server,
1932
                    gs_fapi_get_server_param_callback get_server_param_cb)
1933
683
{
1934
683
    gs_fapi_server **servs = gs_fapi_get_server_list(mem);
1935
683
    char *server_param = NULL;
1936
683
    int server_param_size = 0;
1937
683
    int code = 0;
1938
683
    bool free_params = false;
1939
1940
683
    (*server) = NULL;
1941
1942
1.36k
    while (servs && *servs && strcmp((char *)(*servs)->ig.d->subtype, (char *)name)) {
1943
683
        servs++;
1944
683
    }
1945
1946
683
    if (servs && *servs && get_server_param_cb) {
1947
0
        (*get_server_param_cb) ((*servs), (char *) (*servs)->ig.d->subtype,
1948
0
                                &server_param, &server_param_size);
1949
1950
0
        if (server_param == NULL && server_param_size > 0) {
1951
0
            server_param =
1952
0
                (char *)gs_alloc_bytes_immovable(mem->non_gc_memory,
1953
0
                                         server_param_size,
1954
0
                                         "gs_fapi_find_server server params");
1955
0
            if (!server_param) {
1956
0
                return_error(gs_error_VMerror);
1957
0
            }
1958
0
            free_params = true;
1959
0
            (*get_server_param_cb) ((*servs),
1960
0
                                    (const char *)(*servs)->ig.d->subtype,
1961
0
                                    &server_param, &server_param_size);
1962
0
        }
1963
1964
0
        code = gs_fapi_renderer_retcode(mem, (*servs),
1965
0
                                 (*servs)->ensure_open((*servs),
1966
0
                                                       server_param,
1967
0
                                                       server_param_size));
1968
1969
0
        if (free_params) {
1970
0
            gs_free_object(mem->non_gc_memory, server_param,
1971
0
                           "gs_fapi_find_server: server_param");
1972
0
        }
1973
1974
0
        (*server) = (*servs);
1975
0
    }
1976
683
    else {
1977
683
        if (!servs || !(*servs)) {
1978
683
            code = gs_error_invalidaccess;
1979
683
        }
1980
683
    }
1981
1982
1983
683
    return (code);
1984
683
}
1985
1986
int
1987
gs_fapi_init(gs_memory_t *mem)
1988
683
{
1989
683
    int code = 0;
1990
683
    int i, num_servers = 0;
1991
683
    gs_fapi_server **servs = NULL;
1992
683
    const gs_fapi_server_init_func *gs_fapi_server_inits =
1993
683
        gs_get_fapi_server_inits();
1994
1995
1.36k
    while (gs_fapi_server_inits[num_servers]) {
1996
683
        num_servers++;
1997
683
    }
1998
1999
683
    servs =
2000
683
        (gs_fapi_server **) gs_alloc_bytes_immovable(mem->non_gc_memory,
2001
683
                                                     (num_servers +
2002
683
                                                      1) *
2003
683
                                                     sizeof(gs_fapi_server *),
2004
683
                                                     "gs_fapi_init");
2005
683
    if (!servs) {
2006
0
        return_error(gs_error_VMerror);
2007
0
    }
2008
2009
1.36k
    for (i = 0; i < num_servers; i++) {
2010
683
        gs_fapi_server_init_func *f =
2011
683
            (gs_fapi_server_init_func *) & (gs_fapi_server_inits[i]);
2012
2013
683
        code = (*f) (mem, &(servs[i]));
2014
683
        if (code != 0) {
2015
0
            break;
2016
0
        }
2017
        /* No point setting this, as in PS, the interpreter context might move
2018
         * But set it to NULL, just for safety */
2019
683
        servs[i]->client_ctx_p = NULL;
2020
683
    }
2021
2022
1.36k
    for (; i < num_servers + 1; i++) {
2023
683
        servs[i] = NULL;
2024
683
    }
2025
2026
683
    mem->gs_lib_ctx->fapi_servers = servs;
2027
2028
683
    return (code);
2029
683
}
2030
2031
void
2032
gs_fapi_finit(gs_memory_t *mem)
2033
683
{
2034
683
    gs_fapi_server **servs = mem->gs_lib_ctx->fapi_servers;
2035
2036
1.36k
    while (servs && *servs) {
2037
683
        ((*servs)->ig.d->finit) (servs);
2038
683
        servs++;
2039
683
    }
2040
683
    gs_free_object(mem->non_gc_memory, mem->gs_lib_ctx->fapi_servers,
2041
683
                   "gs_fapi_finit: mem->gs_lib_ctx->fapi_servers");
2042
683
    mem->gs_lib_ctx->fapi_servers = NULL;
2043
683
}