Coverage Report

Created: 2025-06-10 06:49

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