Coverage Report

Created: 2025-06-10 07:15

/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
436k
#define FAPI_ROUND(v) (v >= 0 ? v + 0.5 : v - 0.5)
43
436k
#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
1.62M
{
52
1.62M
    if (rc == 0)
53
1.61M
        return 0;
54
11.4k
    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
11.4k
    return rc < 0 ? rc : gs_error_invalidfont;
60
1.62M
}
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
1.96M
{
75
1.96M
    return (n > 0 ? (x << n) : (x >> -n));
76
1.96M
}
77
78
static inline int
79
export_shift(int x, int n)
80
47.1k
{
81
47.1k
    return (n > 0 ? (x >> n) : (x << -n));
82
47.1k
}
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
45.3k
{
93
45.3k
    gs_fapi_outline_handler *olh = (gs_fapi_outline_handler *) I->olh;
94
95
45.3k
    if (olh->need_close == true) {
96
36.8k
        olh->need_close = false;
97
36.8k
        I->gs_error = gx_path_close_subpath_notes(olh->path, 0);
98
36.8k
    }
99
45.3k
    return (I->gs_error);
100
45.3k
}
101
102
static int
103
add_move(gs_fapi_path *I, int64_t x, int64_t y)
104
46.9k
{
105
46.9k
    gs_fapi_outline_handler *olh = (gs_fapi_outline_handler *) I->olh;
106
107
46.9k
    x = import_shift(x, I->shift);
108
46.9k
    y = -import_shift(y, I->shift);
109
46.9k
    if (olh->fserver->transform_outline) {
110
10.6k
        gs_point pt;
111
10.6k
        I->gs_error = gs_distance_transform((double)fixed2float((float)x), (double)fixed2float((float)y), &olh->fserver->outline_mat, &pt);
112
10.6k
        if (I->gs_error < 0)
113
0
            return I->gs_error;
114
10.6k
        x = float2fixed(pt.x);
115
10.6k
        y = float2fixed(pt.y);
116
10.6k
    }
117
46.9k
    x += olh->x0;
118
46.9k
    y += olh->y0;
119
120
46.9k
    if (x > (int64_t) max_coord_fixed || x < (int64_t) min_coord_fixed
121
46.9k
     || y > (int64_t) max_coord_fixed || y < (int64_t) min_coord_fixed) {
122
9.35k
         I->gs_error = gs_error_undefinedresult;
123
9.35k
    }
124
37.5k
    else {
125
126
37.5k
        if (olh->need_close && olh->close_path)
127
14.4k
            if ((I->gs_error = add_closepath(I)) < 0)
128
0
                return (I->gs_error);
129
37.5k
        olh->need_close = false;
130
131
/*        dprintf2("%f %f moveto\n", fixed2float(x), fixed2float(y)); */
132
37.5k
        I->gs_error = gx_path_add_point(olh->path, (fixed) x, (fixed) y);
133
37.5k
    }
134
46.9k
    return (I->gs_error);
135
46.9k
}
136
137
static int
138
add_line(gs_fapi_path *I, int64_t x, int64_t y)
139
221k
{
140
221k
    gs_fapi_outline_handler *olh = (gs_fapi_outline_handler *) I->olh;
141
142
221k
    x = import_shift(x, I->shift);
143
221k
    y = -import_shift(y, I->shift);
144
221k
    if (olh->fserver->transform_outline) {
145
5.80k
        gs_point pt;
146
5.80k
        I->gs_error = gs_distance_transform((double)fixed2float((float)x), (double)fixed2float((float)y), &olh->fserver->outline_mat, &pt);
147
5.80k
        if (I->gs_error < 0)
148
0
            return I->gs_error;
149
5.80k
        x = float2fixed(pt.x);
150
5.80k
        y = float2fixed(pt.y);
151
5.80k
    }
152
221k
    x += olh->x0;
153
221k
    y += olh->y0;
154
155
221k
    if (x > (int64_t) max_coord_fixed || x < (int64_t) min_coord_fixed
156
221k
     || y > (int64_t) max_coord_fixed || y < (int64_t) min_coord_fixed) {
157
382
         I->gs_error = gs_error_undefinedresult;
158
382
    }
159
221k
    else {
160
221k
        olh->need_close = true;
161
162
/*        dprintf2("%f %f lineto\n", fixed2float(x), fixed2float(y)); */
163
221k
        I->gs_error = gx_path_add_line_notes(olh->path, (fixed) x, (fixed) y, 0);
164
221k
    }
165
221k
    return (I->gs_error);
166
221k
}
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
237k
{
172
237k
    gs_fapi_outline_handler *olh = (gs_fapi_outline_handler *) I->olh;
173
174
237k
    x0 = import_shift(x0, I->shift);
175
237k
    y0 = -import_shift(y0, I->shift);
176
237k
    x1 = import_shift(x1, I->shift);
177
237k
    y1 = -import_shift(y1, I->shift);
178
237k
    x2 = import_shift(x2, I->shift);
179
237k
    y2 = -import_shift(y2, I->shift);
180
181
237k
    if (olh->fserver->transform_outline) {
182
2.80k
        gs_point pt;
183
2.80k
        I->gs_error = gs_distance_transform((double)fixed2float((float)x0), (double)fixed2float((float)y0), &olh->fserver->outline_mat, &pt);
184
2.80k
        if (I->gs_error < 0)
185
0
            return I->gs_error;
186
2.80k
        x0 = float2fixed(pt.x);
187
2.80k
        y0 = float2fixed(pt.y);
188
2.80k
        I->gs_error = gs_distance_transform((double)fixed2float((float)x1), (double)fixed2float((float)y1), &olh->fserver->outline_mat, &pt);
189
2.80k
        if (I->gs_error < 0)
190
0
            return I->gs_error;
191
2.80k
        x1 = float2fixed(pt.x);
192
2.80k
        y1 = float2fixed(pt.y);
193
2.80k
        I->gs_error = gs_distance_transform((double)fixed2float((float)x2), (double)fixed2float((float)y2), &olh->fserver->outline_mat, &pt);
194
2.80k
        if (I->gs_error < 0)
195
0
            return I->gs_error;
196
2.80k
        x2 = float2fixed(pt.x);
197
2.80k
        y2 = float2fixed(pt.y);
198
2.80k
    }
199
237k
    x0 += olh->x0;
200
237k
    y0 += olh->y0;
201
237k
    x1 += olh->x0;
202
237k
    y1 += olh->y0;
203
237k
    x2 += olh->x0;
204
237k
    y2 += olh->y0;
205
206
237k
    if (x0 > (int64_t) max_coord_fixed || x0 < (int64_t) min_coord_fixed
207
237k
     || y0 > (int64_t) max_coord_fixed || y0 < (int64_t) min_coord_fixed
208
237k
     || x1 > (int64_t) max_coord_fixed || x1 < (int64_t) min_coord_fixed
209
237k
     || y1 > (int64_t) max_coord_fixed || y1 < (int64_t) min_coord_fixed
210
237k
     || x2 > (int64_t) max_coord_fixed || x2 < (int64_t) min_coord_fixed
211
237k
     || y2 > (int64_t) max_coord_fixed || y2 < (int64_t) min_coord_fixed)
212
248
    {
213
248
        I->gs_error = gs_error_undefinedresult;
214
248
    }
215
237k
    else {
216
237k
        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
237k
        I->gs_error = gx_path_add_curve_notes(olh->path, (fixed) x0, (fixed) y0, (fixed) x1, (fixed) y1, (fixed) x2, (fixed) y2, 0);
220
237k
    }
221
237k
    return (I->gs_error);
222
237k
}
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
645k
{
230
645k
    if (!ff->is_type1 && ff->is_cid) {
231
115k
        gs_font_cid2 *pfcid = (gs_font_cid2 *) ff->client_font_data;
232
233
115k
        return (pfcid->cidata.MetricsCount);
234
115k
    }
235
529k
    return 0;
236
645k
}
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
524k
{
249
524k
    if (!gs_text_is_width_only(penum))
250
507k
        return false;
251
16.8k
    switch (penum->orig_font->FontType) {
252
4.42k
        case ft_encrypted:
253
4.42k
        case ft_encrypted2:
254
4.42k
        case ft_CID_encrypted:
255
4.42k
        case ft_CID_TrueType:
256
4.42k
        case ft_CID_bitmap:
257
8.92k
        case ft_TrueType:
258
8.92k
            return true;
259
7.91k
        default:
260
7.91k
            return false;
261
16.8k
    }
262
16.8k
}
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
504k
{
274
504k
    gx_device *dev = gs_currentdevice_inline(pgs);
275
276
504k
    return ((!gs_color_writes_pure(pgs)) && dev_proc(dev, dev_spec_op)(dev, gxdso_supports_pattern_transparency, NULL, 0));
277
504k
}
278
279
static inline bool
280
recreate_multiple_master(gs_font_base *pbfont)
281
524k
{
282
524k
    bool r = false;
283
524k
    gs_fapi_server *I = pbfont->FAPI;
284
524k
    bool changed = false;
285
286
524k
    if (I && I->face.font_id != gs_no_id &&
287
524k
        (pbfont->FontType == ft_encrypted
288
491k
        || pbfont->FontType == ft_encrypted2)) {
289
354k
        gs_font_type1 *pfont1 = (gs_font_type1 *) pbfont;
290
354k
        if (pfont1->data.WeightVector.count != 0
291
354k
            && I->face.WeightVector.count != pfont1->data.WeightVector.count) {
292
0
            changed = true;
293
0
        }
294
354k
        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
354k
        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
354k
    }
306
524k
    return r;
307
524k
}
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
524k
{
314
524k
    gs_gstate *pgs = (gs_gstate *) penum_s->pgs;
315
316
524k
    log2_scale->x = 0;
317
524k
    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
524k
    gx_compute_text_oversampling(penum_s, (gs_font *) pbfont, abits,
326
524k
                                 log2_scale);
327
328
524k
    return (pgs->in_charpath || pbfont->PaintType != 0 ||
329
524k
            (pgs->in_cachedevice != CACHE_DEVICE_CACHING
330
504k
             && using_transparency_pattern((gs_gstate *) penum_s->pgs))
331
524k
            || (pgs->in_cachedevice != CACHE_DEVICE_CACHING
332
501k
                && (log2_scale->x > 0 || log2_scale->y > 0))
333
524k
            || (pgs->in_cachedevice != CACHE_DEVICE_CACHING && abits > 1));
334
524k
}
335
336
static inline void
337
gs_fapi_release_typeface(gs_fapi_server *I, void **server_font_data)
338
117k
{
339
117k
    I->release_typeface(I, *server_font_data);
340
117k
    I->face.font_id = gs_no_id;
341
117k
    if (I->ff.server_font_data == *server_font_data)
342
33.1k
        I->ff.server_font_data = 0;
343
117k
    *server_font_data = 0;
344
117k
}
345
346
static int
347
notify_remove_font(void *proc_data, void *event_data)
348
119k
{                               /* gs_font_finalize passes event_data == NULL, so check it here. */
349
119k
    if (event_data == NULL) {
350
119k
        gs_font_base *pbfont = proc_data;
351
119k
        gs_fapi_server *I = pbfont->FAPI;
352
353
119k
        if (pbfont->FAPI_font_data != 0) {
354
117k
            gs_fapi_release_typeface(I, &pbfont->FAPI_font_data);
355
117k
        }
356
119k
    }
357
119k
    return 0;
358
119k
}
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
120k
{                               /* 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
120k
    gs_memory_t *mem = pfont->memory;
386
120k
    gs_font_base *pbfont = (gs_font_base *)pfont;
387
120k
    int code, bbox_set = 0;
388
120k
    int BBox[4], scale;
389
120k
    int units[2];
390
120k
    double size;
391
120k
    gs_fapi_font_scale font_scale =
392
120k
        { {1, 0, 0, 1, 0, 0}, {0, 0}, {1, 1}, true };
393
394
120k
    scale = 1 << I->frac_shift;
395
120k
    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
120k
    if (size < 1000)
400
119k
        size = 1000;
401
402
120k
    font_scale.matrix[0] = font_scale.matrix[3] = (int)(size * scale + 0.5);
403
404
120k
    font_scale.HWResolution[0] = (fracint) (72 * scale);
405
120k
    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
120k
    I->ff.subfont = subfont;
411
120k
    I->ff.font_file_path = font_file_path;
412
120k
    I->ff.is_type1 = FAPI_ISTYPE1GLYPHDATA(pbfont);
413
120k
    I->ff.is_vertical = (pbfont->WMode != 0);
414
120k
    I->ff.memory = mem;
415
120k
    I->ff.client_ctx_p = I->client_ctx_p;
416
120k
    I->ff.client_font_data = pbfont;
417
120k
    I->ff.client_font_data2 = pbfont;
418
120k
    I->ff.server_font_data = pbfont->FAPI_font_data;    /* Possibly pass it from zFAPIpassfont. */
419
120k
    if (full_font_buf) {
420
103k
        I->ff.full_font_buf = (char *)full_font_buf->data;
421
103k
        I->ff.full_font_buf_len = full_font_buf->size;
422
103k
    }
423
16.7k
    else {
424
16.7k
        I->ff.full_font_buf = NULL;
425
16.7k
        I->ff.full_font_buf_len = 0;
426
16.7k
    }
427
120k
    I->ff.is_cid = FAPI_ISCIDFONT(pbfont);
428
120k
    I->ff.is_outline_font = pbfont->PaintType != 0;
429
430
120k
    if (!I->ff.is_mtx_skipped)
431
120k
        I->ff.is_mtx_skipped = (gs_fapi_get_metrics_count(&I->ff) != 0);
432
433
120k
    if ((code = gs_fapi_renderer_retcode(mem, I, I->get_scaled_font(I, &I->ff,
434
120k
                                                                    (const
435
120k
                                                                     gs_fapi_font_scale
436
120k
                                                                     *)
437
120k
                                                                    &font_scale, xlatmap, gs_fapi_toplevel_begin)))
438
120k
        < 0)
439
382
        return code;
440
119k
    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
119k
    if (I->ff.server_font_data != 0
448
119k
        && (font_file_path != NULL || full_font_buf != NULL)) {
449
102k
        if ((code =
450
102k
             gs_fapi_renderer_retcode(mem, I,
451
102k
                                      I->get_font_bbox(I, &I->ff,
452
102k
                                                       BBox, units))) < 0) {
453
0
            gs_fapi_release_typeface(I, &pbfont->FAPI_font_data);
454
0
            return code;
455
0
        }
456
        /* Refine FontBBox : */
457
102k
        pbfont->FontBBox.p.x = ((double)BBox[0] / units[0]);
458
102k
        pbfont->FontBBox.p.y = ((double)BBox[1] / units[1]);
459
102k
        pbfont->FontBBox.q.x = ((double)BBox[2] / units[0]);
460
102k
        pbfont->FontBBox.q.y = ((double)BBox[3] / units[1]);
461
462
102k
        bbox_set = 1;
463
102k
    }
464
465
119k
    if (xlatmap != NULL && pbfont->FAPI_font_data != NULL) {
466
1.06k
        if ((code =
467
1.06k
             gs_fapi_renderer_retcode(mem, I,
468
1.06k
                                      I->get_decodingID(I, &I->ff,
469
1.06k
                                                        decodingID))) < 0) {
470
0
            gs_fapi_release_typeface(I, &pbfont->FAPI_font_data);
471
0
            return code;
472
0
        }
473
1.06k
    }
474
475
    /* Prepare descendant fonts : */
476
119k
    if (font_file_path == NULL && I->ff.is_type1 && I->ff.is_cid) {     /* Renderers should expect same condition. */
477
221
        gs_font_cid0 *pfcid = (gs_font_cid0 *) pbfont;
478
221
        gs_font_type1 **FDArray = pfcid->cidata.FDArray;
479
221
        int i, n = pfcid->cidata.FDArray_size;
480
481
221
        I->ff.is_type1 = true;
482
221
        I->ff.is_vertical = false;      /* A subfont may be shared with another fonts. */
483
221
        I->ff.memory = mem;
484
221
        I->ff.client_ctx_p = I->client_ctx_p;
485
594
        for (i = 0; i < n; i++) {
486
377
            gs_font_type1 *pbfont1 = FDArray[i];
487
377
            int BBox_temp[4];
488
377
            int units_temp[2];
489
490
377
            pbfont1->FontBBox = pbfont->FontBBox;       /* Inherit FontBBox from the type 9 font. */
491
492
377
            I->ff.client_font_data = pbfont1;
493
377
            pbfont1->FAPI = pbfont->FAPI;
494
377
            I->ff.client_font_data2 = pbfont1;
495
377
            I->ff.server_font_data = pbfont1->FAPI_font_data;
496
377
            I->ff.is_cid = true;
497
377
            I->ff.is_outline_font = pbfont1->PaintType != 0;
498
377
            if (!I->ff.is_mtx_skipped)
499
377
                I->ff.is_mtx_skipped = (gs_fapi_get_metrics_count(&I->ff) != 0);
500
377
            I->ff.subfont = 0;
501
377
            if ((code =
502
377
                 gs_fapi_renderer_retcode(mem, I,
503
377
                                          I->get_scaled_font(I, &I->ff,
504
377
                                                             (const
505
377
                                                              gs_fapi_font_scale
506
377
                                                              *)&font_scale,
507
377
                                                             NULL, i))) < 0) {
508
4
                break;
509
4
            }
510
511
373
            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
373
            if ((code =
514
373
                 gs_fapi_renderer_retcode(mem, I,
515
373
                                          I->get_font_bbox(I, &I->ff,
516
373
                                                           BBox_temp, units_temp))) < 0) {
517
0
                break;
518
0
            }
519
373
            code = gs_notify_register(&pbfont1->notify_list, notify_remove_font, pbfont1);
520
373
            if (code < 0) {
521
0
                emprintf(mem,
522
0
                         "Ignoring gs_notify_register() failure for FAPI font.....");
523
0
            }
524
373
        }
525
221
        if (i == n) {
526
217
            code =
527
217
                gs_fapi_renderer_retcode(mem, I,
528
217
                                         I->get_scaled_font(I, &I->ff,
529
217
                                                            (const
530
217
                                                             gs_fapi_font_scale
531
217
                                                             *)&font_scale,
532
217
                                                            NULL,
533
217
                                                            gs_fapi_toplevel_complete));
534
217
            if (code >= 0)
535
217
                return bbox_set;        /* Full success. */
536
217
        }
537
        /* Fail, release server's font data : */
538
8
        for (i = 0; i < n; i++) {
539
4
            gs_font_type1 *pbfont1 = FDArray[i];
540
541
4
            if (pbfont1->FAPI_font_data != NULL)
542
0
                gs_fapi_release_typeface(I, &pbfont1->FAPI_font_data);
543
4
        }
544
545
4
        if (pbfont->FAPI_font_data != NULL) {
546
0
            gs_fapi_release_typeface(I, &pbfont->FAPI_font_data);
547
0
        }
548
4
        return_error(gs_error_invalidfont);
549
221
    }
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
119k
    code = gs_fapi_renderer_retcode(mem, I, I->get_scaled_font(I, &I->ff,
555
119k
                                                               (const
556
119k
                                                                gs_fapi_font_scale
557
119k
                                                                *)&font_scale,
558
119k
                                                               xlatmap,
559
119k
                                                               gs_fapi_toplevel_complete));
560
119k
    if (code < 0) {
561
0
        gs_fapi_release_typeface(I, &pbfont->FAPI_font_data);
562
0
        return code;
563
0
    }
564
119k
    code =
565
119k
        gs_notify_register(&pbfont->notify_list, notify_remove_font, pbfont);
566
119k
    if (code < 0) {
567
0
        gs_fapi_release_typeface(I, &pbfont->FAPI_font_data);
568
0
        return code;
569
0
    }
570
571
119k
    return bbox_set;
572
119k
}
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
40.8k
{
579
40.8k
    gs_fapi_path path_interface = path_interface_stub;
580
40.8k
    gs_fapi_outline_handler olh;
581
40.8k
    int code = 0;
582
40.8k
    gs_gstate *pgs = penum_s->pgs;
583
40.8k
    struct gx_path_s path1;
584
585
40.8k
    (void)gx_path_init_local(&path1, mem);
586
587
40.8k
    olh.fserver = I;
588
40.8k
    olh.path = &path1;
589
40.8k
    olh.x0 = pgs->ctm.tx_fixed - float2fixed(penum_s->fapi_glyph_shift.x);
590
40.8k
    olh.y0 = pgs->ctm.ty_fixed - float2fixed(penum_s->fapi_glyph_shift.y);
591
40.8k
    olh.close_path = close_path;
592
40.8k
    olh.need_close = false;
593
40.8k
    path_interface.olh = &olh;
594
40.8k
    path_interface.shift = import_shift_v;
595
40.8k
    if ((code =
596
40.8k
         gs_fapi_renderer_retcode(mem, I,
597
40.8k
                                  I->get_char_outline(I,
598
40.8k
                                                      &path_interface))) < 0
599
40.8k
        || path_interface.gs_error != 0) {
600
9.98k
        if (path_interface.gs_error != 0) {
601
9.98k
            code = path_interface.gs_error;
602
9.98k
            goto done;
603
9.98k
        }
604
0
        else {
605
0
            goto done;
606
0
        }
607
9.98k
    }
608
30.9k
    if (olh.need_close && olh.close_path)
609
0
        if ((code = add_closepath(&path_interface)) < 0)
610
0
            goto done;
611
30.9k
    code = gx_path_copy(&path1, path);
612
40.8k
done:
613
40.8k
    code = code >= 0 || code == gs_error_undefinedresult ? 0 : code;
614
40.8k
    gx_path_free(&path1, "outline_char");
615
40.8k
    return code;
616
30.9k
}
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
523k
{                               /* optimize : move this stuff to FAPI_refine_font */
623
523k
    gs_matrix mat;
624
523k
    gs_matrix *m = &mat;
625
523k
    int rounding_x, rounding_y; /* Striking out the 'float' representation error in FontMatrix. */
626
523k
    double sx, sy;
627
523k
    gs_fapi_server *I = pbfont->FAPI;
628
629
523k
#if 1
630
523k
    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
523k
    if (m->xx == 0 && m->xy == 0 && m->yx == 0 && m->yy == 0)
638
0
        m = &pbfont->base->FontMatrix;
639
523k
    sx = hypot(m->xx, m->xy) * metrics->em_x / FontMatrix_div;
640
523k
    sy = hypot(m->yx, m->yy) * metrics->em_y / FontMatrix_div;
641
523k
    rounding_x = (int)(0x00800000 / sx);
642
523k
    rounding_y = (int)(0x00800000 / sy);
643
523k
    *em_scale_x = (int)(sx * rounding_x + 0.5) / (double)rounding_x;
644
523k
    *em_scale_y = (int)(sy * rounding_y + 0.5) / (double)rounding_y;
645
523k
}
646
647
static int
648
fapi_copy_mono(gx_device *dev1, gs_fapi_raster *rast, int dx, int dy)
649
465k
{
650
465k
    int line_step = bitmap_raster(rast->width), code;
651
652
465k
    if (rast->line_step >= line_step) {
653
2.56k
        return dev_proc(dev1, copy_mono) (dev1, rast->p, 0, rast->line_step,
654
2.56k
                                          0, dx, dy, rast->width,
655
2.56k
                                          rast->height, 0, 1);
656
2.56k
    }
657
462k
    else {                      /* bitmap data needs to be aligned, make the aligned copy : */
658
462k
        byte *p = gs_alloc_byte_array(dev1->memory, rast->height, line_step,
659
462k
                                      "fapi_copy_mono");
660
462k
        byte *q = p, *r = rast->p, *pe;
661
662
462k
        if (p == NULL)
663
0
            return_error(gs_error_VMerror);
664
462k
        pe = p + rast->height * line_step;
665
8.03M
        for (; q < pe; q += line_step, r += rast->line_step) {
666
7.56M
            memcpy(q, r, rast->line_step);
667
7.56M
            memset(q + rast->line_step, 0, line_step - rast->line_step);
668
7.56M
        }
669
462k
        code =
670
462k
            dev_proc(dev1, copy_mono) (dev1, p, 0, line_step, 0, dx, dy,
671
462k
                                       rast->width, rast->height, 0, 1);
672
462k
        gs_free_object(dev1->memory, p, "fapi_copy_mono");
673
462k
        return code;
674
462k
    }
675
465k
}
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
8.58k
{
827
8.58k
    gx_device *dev = penum->dev;
828
8.58k
    gs_gstate *penum_pgs = (gs_gstate *) penum->pgs;
829
8.58k
    int code;
830
8.58k
    const gx_clip_path *pcpath = pgs->clip_path;
831
8.58k
    const gx_drawing_color *pdcolor = gs_currentdevicecolor_inline(penum->pgs);
832
8.58k
    int rast_orig_x = rast->orig_x;
833
8.58k
    int rast_orig_y = -rast->orig_y;
834
8.58k
    gs_font_base *pbfont = (gs_font_base *)pfont;
835
8.58k
    gs_fapi_server *I = pbfont->FAPI;
836
837
8.58k
    extern_st(st_gs_show_enum);
838
839
8.58k
    byte *r = rast->p;
840
8.58k
    byte *src, *dst;
841
8.58k
    int h, padbytes, cpbytes, dstr = bitmap_raster(rast->width);
842
8.58k
    int sstr = rast->line_step;
843
844
8.58k
    double dx = penum_pgs->ctm.tx + (double)rast_orig_x / (1 << frac_pixel_shift) + 0.5;
845
8.58k
    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
8.58k
    if (gs_color_writes_pure(penum_pgs) && I->ff.embolden == 0.0) {
851
8.58k
        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
4.75k
            r = gs_alloc_bytes(penum->memory, (size_t)dstr * rast->height,
865
4.75k
                               "fapi_finish_render_aux");
866
4.75k
            if (!r) {
867
0
                return_error(gs_error_VMerror);
868
0
            }
869
870
4.75k
            cpbytes = sstr < dstr ? sstr : dstr;
871
4.75k
            padbytes = dstr - cpbytes;
872
4.75k
            h = rast->height;
873
4.75k
            src = rast->p;
874
4.75k
            dst = r;
875
4.75k
            if (padbytes > 0) {
876
302k
                while (h-- > 0) {
877
297k
                    memcpy(dst, src, cpbytes);
878
297k
                    memset(dst + cpbytes, 0, padbytes);
879
297k
                    src += sstr;
880
297k
                    dst += dstr;
881
297k
                }
882
4.75k
            }
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
4.75k
        }
891
892
8.58k
        if (gs_object_type(penum->memory, penum) == &st_gs_show_enum) {
893
8.58k
            dx += penum->fapi_glyph_shift.x;
894
8.58k
            dy += penum->fapi_glyph_shift.y;
895
8.58k
        }
896
        /* Processing an image object operation, but this may be for a text object */
897
8.58k
        ensure_tag_is_set(pgs, pgs->device, GS_TEXT_TAG); /* NB: may unset_dev_color */
898
8.58k
        code = gx_set_dev_color(pgs);
899
8.58k
        if (code != 0)
900
0
            return code;
901
8.58k
        code = gs_gstate_color_load(pgs);
902
8.58k
        if (code < 0)
903
0
            return code;
904
905
8.58k
        code = gx_image_fill_masked(dev, r, 0, dstr, gx_no_bitmap_id,
906
8.58k
                                    (int)dx, (int)dy,
907
8.58k
                                    rast->width, rast->height,
908
8.58k
                                    pdcolor, 1, rop3_default, pcpath);
909
8.58k
        if (rast->p != r) {
910
4.75k
            gs_free_object(penum->memory, r, "fapi_finish_render_aux");
911
4.75k
        }
912
8.58k
    }
913
0
    else {
914
0
        gs_memory_t *mem = penum->memory->non_gc_memory;
915
0
        gs_image_enum *pie;
916
0
        gs_image_t image;
917
0
        int iy, nbytes;
918
0
        uint used;
919
0
        int code1;
920
0
        int w, h;
921
0
        int x = (int)dx;
922
0
        int y = (int)dy;
923
0
        uint bold = 0;
924
0
        byte *bold_lines = NULL;
925
0
        byte *line = NULL;
926
0
        int ascent = 0;
927
928
0
        pie = gs_image_enum_alloc(mem, "image_char(image_enum)");
929
0
        if (!pie) {
930
0
            return_error(gs_error_VMerror);
931
0
        }
932
933
0
        w = rast->width;
934
0
        h = rast->height;
935
0
        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
0
        gs_image_t_init_mask(&image, true);
954
0
        gs_make_translation((double) - x, (double) (- y + ascent), &image.ImageMatrix);
955
0
        gs_matrix_multiply(&ctm_only(penum_pgs), &image.ImageMatrix, &image.ImageMatrix);
956
0
        image.Width = w + bold;
957
0
        image.Height = h + bold;
958
0
        image.adjust = false;
959
0
        code = gs_image_init(pie, &image, false, true, penum_pgs);
960
0
        nbytes = (rast->width + 7) >> 3;
961
962
0
        switch (code) {
963
0
            case 1:            /* empty image */
964
0
                code = 0;
965
0
            default:
966
0
                break;
967
0
            case 0:
968
0
                if (bold == 0) {
969
0
                    for (iy = 0; iy < h && code >= 0; iy++, r += sstr) {
970
0
                        code = gs_image_next(pie, r, nbytes, &used);
971
0
                    }
972
0
                }
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
0
#undef merged_line
1029
0
        }
1030
1031
0
        if (bold_lines)
1032
0
            gs_free_object(pgs->memory, bold_lines, "fapi_image_uncached_glyph(bold_lines)");
1033
1034
0
        if (line)
1035
0
            gs_free_object(pgs->memory, line, "fapi_image_uncached_glyph(line)");
1036
1037
0
        code1 = gs_image_cleanup_and_free_enum(pie, penum_pgs);
1038
0
        if (code >= 0 && code1 < 0)
1039
0
            code = code1;
1040
0
    }
1041
8.58k
    return (code);
1042
8.58k
}
1043
1044
int
1045
gs_fapi_finish_render(gs_font *pfont, gs_gstate *pgs, gs_text_enum_t *penum, gs_fapi_server *I)
1046
523k
{
1047
523k
    gs_show_enum *penum_s = (gs_show_enum *) penum;
1048
523k
    gs_gstate *penum_pgs;
1049
523k
    gx_device *dev1;
1050
523k
    const int import_shift_v = _fixed_shift - 32;       /* we always 32.32 values for the outline interface now */
1051
523k
    gs_fapi_raster rast;
1052
523k
    int code;
1053
523k
    gs_memory_t *mem = pfont->memory;
1054
523k
    gs_font_base *pbfont = (gs_font_base *)pfont;
1055
1056
523k
    if (penum == NULL)
1057
0
        return_error(gs_error_undefined);
1058
1059
523k
    penum_pgs = penum_s->pgs;
1060
1061
523k
    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
523k
    if (pgs->in_charpath && !SHOW_IS(penum, TEXT_DO_NONE)) {
1068
19.8k
        gs_point pt;
1069
1070
19.8k
        if ((code = gs_currentpoint(penum_pgs, &pt)) < 0)
1071
0
            return code;
1072
1073
19.8k
        if ((code =
1074
19.8k
             outline_char(mem, I, import_shift_v, penum_s, penum_pgs->path,
1075
19.8k
                          !pbfont->PaintType)) < 0) {
1076
0
            return code;
1077
0
        }
1078
1079
19.8k
        if ((code =
1080
19.8k
             gx_path_add_char_path(penum_pgs->show_gstate->path,
1081
19.8k
                                   penum_pgs->path,
1082
19.8k
                                   penum_pgs->in_charpath)) < 0) {
1083
0
            return code;
1084
0
        }
1085
1086
19.8k
    }
1087
503k
    else {
1088
503k
        int code;
1089
503k
        memset(&rast, 0x00, sizeof(rast));
1090
1091
503k
        if ((code = I->get_char_raster(I, &rast)) < 0 && code != gs_error_unregistered)
1092
0
            return code;
1093
1094
503k
        if (!SHOW_IS(penum, TEXT_DO_NONE) && I->use_outline) {
1095
            /* The server provides an outline instead the raster. */
1096
21.0k
            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
21.0k
            if ((code = gs_currentpoint(penum_pgs, &pt)) < 0)
1105
25
                return code;
1106
1107
21.0k
            if ((code =
1108
21.0k
                 outline_char(mem, I, import_shift_v, penum_s,
1109
21.0k
                              penum_pgs->path, !pbfont->PaintType)) < 0)
1110
0
                return code;
1111
21.0k
            if ((code =
1112
21.0k
                 gs_gstate_setflat((gs_gstate *) penum_pgs,
1113
21.0k
                                   gs_char_flatness(penum_pgs->show_gstate, 1.0))) < 0)
1114
0
                return code;
1115
21.0k
            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
21.0k
            else {
1125
21.0k
                gs_in_cache_device_t in_cachedevice =
1126
21.0k
                    penum_pgs->in_cachedevice;
1127
1128
21.0k
                penum_pgs->in_cachedevice = CACHE_DEVICE_NOT_CACHING;
1129
1130
21.0k
                penum_pgs->fill_adjust.x = penum_pgs->fill_adjust.y = 0;
1131
1132
21.0k
                code = gs_fill(penum_pgs);
1133
1134
21.0k
                penum_pgs->in_cachedevice = in_cachedevice;
1135
1136
21.0k
                if (code < 0)
1137
4
                    return code;
1138
21.0k
            }
1139
21.0k
            if ((code = gs_moveto(penum_pgs, pt.x, pt.y)) < 0)
1140
0
                return code;
1141
21.0k
        }
1142
482k
        else {
1143
482k
            int rast_orig_x = rast.orig_x;
1144
482k
            int rast_orig_y = -rast.orig_y;
1145
482k
            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
482k
            if ((code = gs_currentpoint(penum_pgs, &pt)) < 0)
1154
11
                return code;
1155
1156
482k
            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
465k
                if (rast.width != 0) {
1164
465k
                    int shift_rd = _fixed_shift - frac_pixel_shift;
1165
465k
                    int rounding = 1 << (frac_pixel_shift - 1);
1166
465k
                    int dx =
1167
465k
                        arith_rshift_slow((penum_pgs->
1168
465k
                                           ctm.tx_fixed >> shift_rd) +
1169
465k
                                          rast_orig_x + rounding,
1170
465k
                                          frac_pixel_shift);
1171
465k
                    int dy =
1172
465k
                        arith_rshift_slow((penum_pgs->
1173
465k
                                           ctm.ty_fixed >> shift_rd) +
1174
465k
                                          rast_orig_y + rounding,
1175
465k
                                          frac_pixel_shift);
1176
1177
465k
                    if (dx + rast.left_indent < 0
1178
465k
                        || dx + rast.left_indent + rast.black_width >
1179
465k
                        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
3
                        if (dx + rast.left_indent < 0)
1190
1
                            dx -= dx + rast.left_indent;
1191
3
                    }
1192
465k
                    if (dy + rast.top_indent < 0
1193
465k
                        || dy + rast.top_indent + rast.black_height >
1194
465k
                        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
465k
                    if ((code = fapi_copy_mono(dev1, &rast, dx, dy)) < 0)
1208
0
                        return code;
1209
1210
465k
                    if (penum_s->cc != NULL) {
1211
465k
                        penum_s->cc->offset.x +=
1212
465k
                            float2fixed(penum_s->fapi_glyph_shift.x);
1213
465k
                        penum_s->cc->offset.y +=
1214
465k
                            float2fixed(penum_s->fapi_glyph_shift.y);
1215
465k
                    }
1216
465k
                }
1217
465k
            }
1218
17.3k
            else if (!SHOW_IS(penum, TEXT_DO_NONE)) {   /* Not using GS cache */
1219
8.58k
                if ((code =
1220
8.58k
                     fapi_image_uncached_glyph(pfont, pgs, penum_s, &rast,
1221
8.58k
                                               import_shift_v)) < 0)
1222
0
                    return code;
1223
8.58k
            }
1224
482k
        }
1225
503k
    }
1226
1227
523k
    return 0;
1228
523k
}
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
1.01M
#define MTX_EQ(mtx1,mtx2) (mtx1->xx == mtx2->xx && mtx1->xy == mtx2->xy && \
1235
482k
                           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
524k
{                               /* Stack : <font> <code|name> --> - */
1242
524k
    gs_show_enum *penum_s = (gs_show_enum *) penum;
1243
524k
    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
524k
    gs_fapi_char_ref cr =
1251
524k
        { 0, {0}, 0, false, NULL, 0, 0, 0, 0, 0, gs_fapi_metrics_notdef };
1252
524k
    gs_font_base *pbfont = (gs_font_base *)pfont;
1253
524k
    const gs_matrix *ctm = &ctm_only(pgs);
1254
524k
    int scale;
1255
524k
    gs_fapi_metrics metrics;
1256
524k
    gs_fapi_server *I = pbfont->FAPI;
1257
1258
524k
    gs_string enc_char_name_string;
1259
524k
    bool bCID = (FAPI_ISCIDFONT(pbfont) || charstring != NULL);
1260
524k
    bool bIsType1GlyphData = FAPI_ISTYPE1GLYPHDATA(pbfont);
1261
524k
    gs_log2_scale_point log2_scale = { 0, 0 };
1262
524k
    int alpha_bits = (*dev_proc(dev, get_alpha_bits)) (dev, go_text);
1263
524k
    double FontMatrix_div = 1;
1264
524k
    bool bVertical = (gs_rootfont(pgs)->WMode != 0), bVertical0 = bVertical;
1265
524k
    double *sbwp, sbw[4] = { 0, 0, 0, 0 };
1266
524k
    double em_scale_x, em_scale_y;
1267
524k
    gs_rect char_bbox;
1268
524k
    int code;
1269
524k
    bool imagenow = false;
1270
524k
    bool align_to_pixels = gs_currentaligntopixels(pbfont->dir);
1271
524k
    gs_memory_t *mem = pfont->memory;
1272
524k
    enum
1273
524k
    {
1274
524k
        SBW_DONE,
1275
524k
        SBW_SCALE,
1276
524k
        SBW_FROM_RENDERER
1277
524k
    } sbw_state = SBW_SCALE;
1278
1279
524k
    if ( index == GS_NO_GLYPH )
1280
0
        return 0;
1281
1282
524k
    I->use_outline = false;
1283
524k
    I->transform_outline = false;
1284
1285
524k
    if (penum == 0)
1286
0
        return_error(gs_error_undefined);
1287
1288
524k
    I->use_outline =
1289
524k
        produce_outline_char(penum_s, pbfont, alpha_bits, &log2_scale);
1290
1291
524k
    if (I->use_outline) {
1292
23.5k
        I->max_bitmap = 0;
1293
23.5k
    }
1294
501k
    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
501k
        I->max_bitmap =
1301
501k
            pbfont->dir->ccache.upper + (pbfont->dir->ccache.upper >> 1) <
1302
501k
            MAX_CCACHE_TEMP_BITMAP_BITS ? pbfont->dir->ccache.upper +
1303
501k
            (pbfont->dir->ccache.upper >> 1) : MAX_CCACHE_TEMP_BITMAP_BITS;
1304
501k
    }
1305
1306
524k
    if (pbfont->memory->gs_lib_ctx->font_dir != NULL)
1307
524k
        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
524k
    if (!SHOW_IS(penum, TEXT_DO_NONE) && !I->use_outline) {
1313
484k
        gs_currentcharmatrix(pgs, NULL, 1);     /* make char_tm valid */
1314
484k
        penum_s->fapi_log2_scale = log2_scale;
1315
484k
    }
1316
40.3k
    else {
1317
40.3k
        log2_scale.x = 0;
1318
40.3k
        log2_scale.y = 0;
1319
40.3k
    }
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
524k
    I->ff.memory = pbfont->memory;
1328
524k
    I->ff.subfont = subfont;
1329
524k
    I->ff.font_file_path = font_file_path;
1330
524k
    I->ff.client_font_data = pbfont;
1331
524k
    I->ff.client_font_data2 = pbfont;
1332
524k
    I->ff.server_font_data = pbfont->FAPI_font_data;
1333
524k
    I->ff.is_type1 = bIsType1GlyphData;
1334
524k
    I->ff.is_cid = bCID;
1335
524k
    I->ff.is_outline_font = pbfont->PaintType != 0;
1336
524k
    I->ff.metrics_only = fapi_gs_char_show_width_only(penum);
1337
1338
524k
    if (!I->ff.is_mtx_skipped)
1339
524k
        I->ff.is_mtx_skipped = (gs_fapi_get_metrics_count(&I->ff) != 0);
1340
1341
524k
    I->ff.is_vertical = bVertical;
1342
524k
    I->ff.client_ctx_p = I->client_ctx_p;
1343
1344
524k
    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
524k
   scale = 1 << I->frac_shift;
1355
529k
retry_oversampling:
1356
529k
    if (I->face.font_id != pbfont->id ||
1357
529k
        !MTX_EQ((&I->face.ctm), ctm) ||
1358
529k
        I->face.log2_scale.x != log2_scale.x ||
1359
529k
        I->face.log2_scale.y != log2_scale.y ||
1360
529k
        I->face.align_to_pixels != align_to_pixels ||
1361
529k
        I->face.HWResolution[0] != dev->HWResolution[0] ||
1362
529k
        I->face.HWResolution[1] != dev->HWResolution[1]
1363
529k
        ) {
1364
72.7k
        gs_fapi_font_scale font_scale = { {1, 0, 0, 1, 0, 0}
1365
72.7k
        , {0, 0}
1366
72.7k
        , {1, 1}
1367
72.7k
        , true
1368
72.7k
        };
1369
1370
72.7k
        gs_matrix lctm, scale_mat, scale_ctm;
1371
72.7k
        I->face.font_id = pbfont->id;
1372
72.7k
        I->face.log2_scale = log2_scale;
1373
72.7k
        I->face.align_to_pixels = align_to_pixels;
1374
72.7k
        I->face.HWResolution[0] = dev->HWResolution[0];
1375
72.7k
        I->face.HWResolution[1] = dev->HWResolution[1];
1376
1377
72.7k
        font_scale.subpixels[0] = 1 << log2_scale.x;
1378
72.7k
        font_scale.subpixels[1] = 1 << log2_scale.y;
1379
72.7k
        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
72.7k
        lctm = *ctm;
1385
85.5k
retry_scaling:
1386
85.5k
        I->face.ctm = lctm;
1387
85.5k
        memset(&scale_ctm, 0x00, sizeof(gs_matrix));
1388
85.5k
        scale_ctm.xx = dev->HWResolution[0] / 72;
1389
85.5k
        scale_ctm.yy = dev->HWResolution[1] / 72;
1390
1391
85.5k
        if ((code = gs_matrix_invert((const gs_matrix *)&scale_ctm, &scale_ctm)) < 0)
1392
0
            return code;
1393
1394
85.5k
        if ((code = gs_matrix_multiply(&lctm, &scale_ctm, &scale_mat)) < 0)  /* scale_mat ==  CTM - resolution scaling */
1395
0
            return code;
1396
1397
85.5k
        if ((code = I->get_fontmatrix(I, &scale_ctm)) < 0)
1398
0
            return code;
1399
1400
85.5k
        if ((code = gs_matrix_invert((const gs_matrix *)&scale_ctm, &scale_ctm)) < 0)
1401
0
            return code;
1402
1403
85.5k
        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
85.5k
        if (((int64_t)(scale_mat.xx * FontMatrix_div * scale + 0.5)) != ((int32_t)(scale_mat.xx * FontMatrix_div * scale + 0.5))
1407
85.5k
        ||  ((int64_t)(scale_mat.xy * FontMatrix_div * scale + 0.5)) != ((int32_t)(scale_mat.xy * FontMatrix_div * scale + 0.5))
1408
85.5k
        ||  ((int64_t)(scale_mat.yx * FontMatrix_div * scale + 0.5)) != ((int32_t)(scale_mat.yx * FontMatrix_div * scale + 0.5))
1409
85.5k
        ||  ((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
12.7k
            memset(&lctm, 0x00, sizeof(gs_matrix));
1418
12.7k
            lctm.xx = 256.0;
1419
12.7k
            lctm.yy = 256.0;
1420
12.7k
            I->transform_outline = true;
1421
12.7k
            I->use_outline = true;
1422
12.7k
            if ((code = gs_matrix_invert((const gs_matrix *)&lctm, &scale_ctm)) < 0)
1423
0
                 return code;
1424
12.7k
            if ((code = gs_matrix_multiply(ctm, &scale_ctm, &scale_mat)) < 0)  /* scale_mat ==  CTM - resolution scaling */
1425
0
                return code;
1426
1427
12.7k
            I->outline_mat = scale_mat;
1428
12.7k
            goto retry_scaling;
1429
12.7k
        }
1430
72.7k
        else {
1431
72.7k
            font_scale.matrix[0] =  FAPI_ROUND_TO_FRACINT(scale_mat.xx * FontMatrix_div * scale);
1432
72.7k
            font_scale.matrix[1] = -FAPI_ROUND_TO_FRACINT(scale_mat.xy * FontMatrix_div * scale);
1433
72.7k
            font_scale.matrix[2] =  FAPI_ROUND_TO_FRACINT(scale_mat.yx * FontMatrix_div * scale);
1434
72.7k
            font_scale.matrix[3] = -FAPI_ROUND_TO_FRACINT(scale_mat.yy * FontMatrix_div * scale);
1435
72.7k
            font_scale.matrix[4] =  FAPI_ROUND_TO_FRACINT(scale_mat.tx * FontMatrix_div * scale);
1436
72.7k
            font_scale.matrix[5] =  FAPI_ROUND_TO_FRACINT(scale_mat.ty * FontMatrix_div * scale);
1437
72.7k
        }
1438
1439
        /* Note: the ctm mapping here is upside down. */
1440
72.7k
        font_scale.HWResolution[0] =
1441
72.7k
            (fracint) ((double)dev->HWResolution[0] *
1442
72.7k
                       font_scale.subpixels[0] * scale);
1443
72.7k
        font_scale.HWResolution[1] =
1444
72.7k
            (fracint) ((double)dev->HWResolution[1] *
1445
72.7k
                       font_scale.subpixels[1] * scale);
1446
1447
1448
72.7k
        if ((hypot((double)font_scale.matrix[0], (double)font_scale.matrix[2])
1449
72.7k
             == 0.0
1450
72.7k
             || hypot((double)font_scale.matrix[1],
1451
72.7k
                      (double)font_scale.matrix[3]) == 0.0)) {
1452
1453
            /* If the matrix is degenerate, force a scale to 1 unit. */
1454
7
            memset(&font_scale.matrix, 0x00, sizeof(font_scale.matrix));
1455
7
            if (!font_scale.matrix[0])
1456
7
                font_scale.matrix[0] = 1;
1457
7
            if (!font_scale.matrix[3])
1458
7
                font_scale.matrix[3] = 1;
1459
7
        }
1460
1461
72.7k
        if ((code =
1462
72.7k
             gs_fapi_renderer_retcode(mem, I,
1463
72.7k
                                      I->get_scaled_font(I, &I->ff,
1464
72.7k
                                                         &font_scale, NULL,
1465
72.7k
                                                         gs_fapi_toplevel_prepared))) < 0) {
1466
2
            return code;
1467
2
        }
1468
72.7k
        pbfont->FAPI_font_data = I->ff.server_font_data;    /* Save it back to GS font. */
1469
72.7k
    }
1470
1471
529k
    cr.char_codes_count = 1;
1472
529k
    cr.char_codes[0] = index;
1473
529k
    cr.client_char_code = chr;
1474
529k
    cr.is_glyph_index = true;
1475
529k
    enc_char_name_string.data = NULL;
1476
529k
    enc_char_name_string.size = 0;
1477
1478
529k
    if (I->ff.get_glyphname_or_cid) {
1479
529k
        if ((code =
1480
529k
             I->ff.get_glyphname_or_cid(penum, pbfont, charstring, glyphname, index,
1481
529k
                                        &enc_char_name_string, font_file_path,
1482
529k
                                        &cr, bCID)) < 0)
1483
0
            return (code);
1484
529k
    }
1485
1486
    /* Compute the metrics replacement : */
1487
529k
    if (bCID && !bIsType1GlyphData) {
1488
114k
        gs_font_cid2 *pfcid = (gs_font_cid2 *) pbfont;
1489
114k
        int MetricsCount = pfcid->cidata.MetricsCount;
1490
1491
114k
        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
114k
    }
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
529k
    em_scale_x = 1.0;
1519
529k
    if (pfont->FontType == ft_TrueType) {
1520
25.5k
        gs_font_type42 *pfont42 = (gs_font_type42 *) pfont;
1521
25.5k
        em_scale_x = pfont42->data.unitsPerEm;
1522
25.5k
    }
1523
1524
529k
    if (cr.metrics_type != gs_fapi_metrics_replace && bVertical) {
1525
607
        double pwv[4];
1526
1527
607
        code =
1528
607
            I->ff.fapi_get_metrics(&I->ff, &enc_char_name_string, index, pwv,
1529
607
                                   bVertical);
1530
607
        if (code < 0)
1531
0
            return code;
1532
607
        if (code == 0 /* metricsNone */ ) {
1533
607
            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
607
            else {
1546
607
                bVertical = false;
1547
607
            }
1548
607
        }
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
607
    }
1564
529k
    if (cr.metrics_type == gs_fapi_metrics_notdef && !bVertical) {
1565
529k
        code =
1566
529k
            I->ff.fapi_get_metrics(&I->ff, &enc_char_name_string, (int)index, sbw,
1567
529k
                                   bVertical);
1568
529k
        if (code < 0)
1569
0
            return code;
1570
529k
        if (code == 0 /* metricsNone */ ) {
1571
529k
            sbw_state = SBW_FROM_RENDERER;
1572
529k
            if (pbfont->FontType == 2) {
1573
47.1k
                gs_font_type1 *pfont1 = (gs_font_type1 *) pbfont;
1574
1575
47.1k
                cr.aw_x =
1576
47.1k
                    export_shift(pfont1->data.defaultWidthX,
1577
47.1k
                                 _fixed_shift - I->frac_shift);
1578
47.1k
                cr.metrics_scale = 1000;
1579
47.1k
                cr.metrics_type = gs_fapi_metrics_add;
1580
47.1k
            }
1581
529k
        }
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
529k
    }
1593
529k
    memset(&metrics, 0x00, sizeof(metrics));
1594
    /* Take metrics from font : */
1595
529k
    if (I->ff.metrics_only) {
1596
8.92k
        code = I->get_char_outline_metrics(I, &I->ff, &cr, &metrics);
1597
8.92k
    }
1598
520k
    else if (I->use_outline) {
1599
40.9k
        code = I->get_char_outline_metrics(I, &I->ff, &cr, &metrics);
1600
40.9k
    }
1601
479k
    else {
1602
479k
        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
479k
        if (code == gs_error_VMerror) {
1607
5.10k
            I->use_outline = true;
1608
5.10k
            goto retry_oversampling;
1609
5.10k
        }
1610
474k
        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
474k
    }
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
524k
    if (code > 0) {
1626
0
        return (gs_error_unregistered);
1627
0
    }
1628
1629
524k
    if ((code = gs_fapi_renderer_retcode(mem, I, code)) < 0)
1630
1.09k
        return code;
1631
1632
523k
    compute_em_scale(pbfont, &metrics, FontMatrix_div, &em_scale_x,
1633
523k
                     &em_scale_y);
1634
523k
    char_bbox.p.x = metrics.bbox_x0 / em_scale_x;
1635
523k
    char_bbox.p.y = metrics.bbox_y0 / em_scale_y;
1636
523k
    char_bbox.q.x = metrics.bbox_x1 / em_scale_x;
1637
523k
    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
523k
    if (pbfont->FontType != ft_MicroType && !bCID
1643
523k
        && pbfont->FontBBox.q.x > pbfont->FontBBox.p.x
1644
523k
        && pbfont->FontBBox.q.y > pbfont->FontBBox.p.y) {
1645
405k
        char_bbox.p.x = min(char_bbox.p.x, pbfont->FontBBox.p.x);
1646
405k
        char_bbox.p.y = min(char_bbox.p.y, pbfont->FontBBox.p.y);
1647
405k
        char_bbox.q.x = max(char_bbox.q.x, pbfont->FontBBox.q.x);
1648
405k
        char_bbox.q.y = max(char_bbox.q.y, pbfont->FontBBox.q.y);
1649
405k
    }
1650
1651
523k
    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
523k
    penum_s->fapi_glyph_shift.x = penum_s->fapi_glyph_shift.y = 0;
1660
523k
    if (sbw_state == SBW_FROM_RENDERER) {
1661
523k
        int can_replace_metrics;
1662
1663
523k
        if ((code =
1664
523k
             gs_fapi_renderer_retcode(mem, I,
1665
523k
                                      I->can_replace_metrics(I, &I->ff, &cr,
1666
523k
                                                             &can_replace_metrics)))
1667
523k
            < 0)
1668
0
            return code;
1669
1670
523k
        sbw[2] = metrics.escapement / em_scale_x;
1671
523k
        sbw[3] = metrics.v_escapement / em_scale_y;
1672
523k
        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
523k
    }
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
523k
    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
523k
    if (I->ff.metrics_only) {
1744
8.82k
        pgs->in_cachedevice = CACHE_DEVICE_NOT_CACHING;
1745
8.82k
    }
1746
1747
523k
    if (pgs->in_cachedevice == CACHE_DEVICE_CACHING) {
1748
0
        sbwp = sbw;
1749
0
    }
1750
523k
    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
523k
        sbwp = NULL;
1756
1757
523k
        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
40.9k
            pgs->in_cachedevice = CACHE_DEVICE_NOT_CACHING;
1763
40.9k
        }
1764
523k
    }
1765
1766
523k
    if (bCID) {
1767
112k
        code =
1768
112k
            I->ff.fapi_set_cache(penum, pbfont, &enc_char_name_string, index + GS_MIN_CID_GLYPH,
1769
112k
                                 sbw + 2, &char_bbox, sbwp, &imagenow);
1770
112k
    }
1771
410k
    else {
1772
410k
        code =
1773
410k
            I->ff.fapi_set_cache(penum, pbfont, &enc_char_name_string, index,
1774
410k
                                 sbw + 2, &char_bbox, sbwp, &imagenow);
1775
410k
    }
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
523k
    if (code >= 0 && imagenow == true) {
1784
523k
        code = gs_fapi_finish_render(pfont, pgs, penum, I);
1785
523k
        I->release_char_data(I);
1786
523k
    }
1787
1788
523k
    if (code != 0) {
1789
134
        if (code < 0) {
1790
            /* An error */
1791
68
            I->release_char_data(I);
1792
68
        }
1793
66
        else {
1794
            /* Callout to CDevProc, zsetcachedevice2, zfapi_finish_render. */
1795
66
        }
1796
134
    }
1797
1798
523k
    return code;
1799
523k
}
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
106k
{
1823
106k
    gs_font_base *pbfont;
1824
106k
    int code = 0;
1825
106k
    gs_fapi_server *I, **list;
1826
106k
    bool free_params = false;
1827
106k
    gs_memory_t *mem = pfont->memory;
1828
106k
    const char *ldecodingID = NULL;
1829
106k
    bool do_restart = false;
1830
1831
106k
    list = gs_fapi_get_server_list(mem);
1832
1833
106k
    if (!list) /* Should never happen */
1834
0
      return_error(gs_error_unregistered);
1835
1836
106k
    (*fapi_id) = NULL;
1837
1838
106k
    pbfont = (gs_font_base *) pfont;
1839
1840
106k
    I = *list;
1841
1842
106k
    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
106k
    while (I) {
1865
106k
        char *server_param = NULL;
1866
106k
        int server_param_size = 0;
1867
1868
106k
        code = (*get_server_param_cb) (I, (const char *)I->ig.d->subtype,
1869
106k
                                &server_param, &server_param_size);
1870
1871
106k
        if (code < 0)
1872
0
            return code;
1873
1874
106k
        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
106k
        if ((code =
1888
106k
             gs_fapi_renderer_retcode(mem, I,
1889
106k
                                      I->ensure_open(I, server_param,
1890
106k
                                                     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
106k
        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
106k
        pbfont->FAPI = I;       /* we need the FAPI server during this stage */
1902
106k
        code =
1903
106k
            gs_fapi_prepare_font(pfont, I, subfont, font_file_path,
1904
106k
                                 full_font_buf, xlatmap, &ldecodingID);
1905
106k
        if (code >= 0) {
1906
106k
            if (decodingID != NULL)
1907
105k
                *decodingID = (char *)ldecodingID;
1908
106k
            (*fapi_id) = (char *)I->ig.d->subtype;
1909
106k
            return 0;
1910
106k
        }
1911
1912
        /* renderer failed, continue search */
1913
386
        pbfont->FAPI = NULL;
1914
386
        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
386
        else {
1923
386
            I = *list;
1924
386
            list++;
1925
386
        }
1926
386
    }
1927
193
    return (code);
1928
106k
}
1929
1930
bool
1931
gs_fapi_available(gs_memory_t *mem, char *server)
1932
130k
{
1933
130k
    bool retval = false;
1934
1935
130k
    if (server) {
1936
12.7k
        gs_fapi_server *serv = NULL;
1937
1938
12.7k
        retval = (gs_fapi_find_server(mem, server, &serv, NULL) >= 0);
1939
12.7k
    }
1940
118k
    else {
1941
118k
        retval = ((mem->gs_lib_ctx->fapi_servers) != NULL) && (*(mem->gs_lib_ctx->fapi_servers) != NULL);
1942
118k
    }
1943
130k
    return (retval);
1944
130k
}
1945
1946
void
1947
gs_fapi_set_servers_client_data(gs_memory_t *mem, const gs_fapi_font *ff_proto, void *ctx_ptr)
1948
131k
{
1949
131k
    gs_fapi_server **servs = gs_fapi_get_server_list(mem);
1950
1951
131k
    if (servs) {
1952
263k
        while (*servs) {
1953
131k
            (*servs)->client_ctx_p = ctx_ptr;
1954
131k
            if (ff_proto)
1955
118k
                (*servs)->ff = *ff_proto;
1956
131k
            servs++;
1957
131k
        }
1958
131k
    }
1959
131k
}
1960
1961
gs_fapi_server **
1962
gs_fapi_get_server_list(gs_memory_t *mem)
1963
263k
{
1964
263k
    return (mem->gs_lib_ctx->fapi_servers);
1965
263k
}
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
24.9k
{
1971
24.9k
    gs_fapi_server **servs = gs_fapi_get_server_list(mem);
1972
24.9k
    char *server_param = NULL;
1973
24.9k
    int server_param_size = 0;
1974
24.9k
    int code = 0;
1975
24.9k
    bool free_params = false;
1976
1977
24.9k
    (*server) = NULL;
1978
1979
37.6k
    while (servs && *servs && strcmp((char *)(*servs)->ig.d->subtype, (char *)name)) {
1980
12.7k
        servs++;
1981
12.7k
    }
1982
1983
24.9k
    if (servs && *servs && get_server_param_cb) {
1984
12.1k
        code = (*get_server_param_cb) ((*servs), (char *) (*servs)->ig.d->subtype,
1985
12.1k
                                &server_param, &server_param_size);
1986
1987
12.1k
        if (code < 0)
1988
0
            return code;
1989
1990
12.1k
        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
12.1k
        code = gs_fapi_renderer_retcode(mem, (*servs),
2005
12.1k
                                 (*servs)->ensure_open((*servs),
2006
12.1k
                                                       server_param,
2007
12.1k
                                                       server_param_size));
2008
2009
12.1k
        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
12.1k
        (*server) = (*servs);
2015
12.1k
    }
2016
12.7k
    else {
2017
12.7k
        if (!servs || !(*servs)) {
2018
12.7k
            code = gs_error_invalidaccess;
2019
12.7k
        }
2020
12.7k
    }
2021
2022
2023
24.9k
    return (code);
2024
24.9k
}
2025
2026
int
2027
gs_fapi_init(gs_memory_t *mem)
2028
12.7k
{
2029
12.7k
    int code = 0;
2030
12.7k
    int i, num_servers = 0;
2031
12.7k
    gs_fapi_server **servs = NULL;
2032
12.7k
    const gs_fapi_server_init_func *gs_fapi_server_inits =
2033
12.7k
        gs_get_fapi_server_inits();
2034
2035
25.4k
    while (gs_fapi_server_inits[num_servers]) {
2036
12.7k
        num_servers++;
2037
12.7k
    }
2038
2039
12.7k
    servs =
2040
12.7k
        (gs_fapi_server **) gs_alloc_bytes_immovable(mem->non_gc_memory,
2041
12.7k
                                                     (num_servers +
2042
12.7k
                                                      1) *
2043
12.7k
                                                     sizeof(gs_fapi_server *),
2044
12.7k
                                                     "gs_fapi_init");
2045
12.7k
    if (!servs) {
2046
0
        return_error(gs_error_VMerror);
2047
0
    }
2048
2049
25.4k
    for (i = 0; i < num_servers; i++) {
2050
12.7k
        gs_fapi_server_init_func *f =
2051
12.7k
            (gs_fapi_server_init_func *) & (gs_fapi_server_inits[i]);
2052
2053
12.7k
        code = (*f) (mem, &(servs[i]));
2054
12.7k
        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
12.7k
        servs[i]->client_ctx_p = NULL;
2060
12.7k
    }
2061
2062
25.4k
    for (; i < num_servers + 1; i++) {
2063
12.7k
        servs[i] = NULL;
2064
12.7k
    }
2065
2066
12.7k
    mem->gs_lib_ctx->fapi_servers = servs;
2067
2068
12.7k
    return (code);
2069
12.7k
}
2070
2071
void
2072
gs_fapi_finit(gs_memory_t *mem)
2073
12.7k
{
2074
12.7k
    gs_fapi_server **servs = mem->gs_lib_ctx->fapi_servers;
2075
2076
25.4k
    while (servs && *servs) {
2077
12.7k
        ((*servs)->ig.d->finit) (servs);
2078
12.7k
        servs++;
2079
12.7k
    }
2080
12.7k
    gs_free_object(mem->non_gc_memory, mem->gs_lib_ctx->fapi_servers,
2081
12.7k
                   "gs_fapi_finit: mem->gs_lib_ctx->fapi_servers");
2082
12.7k
    mem->gs_lib_ctx->fapi_servers = NULL;
2083
12.7k
}