Coverage Report

Created: 2026-11-10 07:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/poppler/poppler/CairoFontEngine.cc
Line
Count
Source
1
//========================================================================
2
//
3
// CairoFontEngine.cc
4
//
5
// Copyright 2003 Glyph & Cog, LLC
6
// Copyright 2004 Red Hat, Inc
7
//
8
//========================================================================
9
10
//========================================================================
11
//
12
// Modified under the Poppler project - http://poppler.freedesktop.org
13
//
14
// All changes made under the Poppler project to this file are licensed
15
// under GPL version 2 or later
16
//
17
// Copyright (C) 2005-2007 Jeff Muizelaar <jeff@infidigm.net>
18
// Copyright (C) 2005, 2006 Kristian Høgsberg <krh@redhat.com>
19
// Copyright (C) 2005 Martin Kretzschmar <martink@gnome.org>
20
// Copyright (C) 2005, 2009, 2012, 2013, 2015, 2017-2019, 2021, 2022, 2024-2026 Albert Astals Cid <aacid@kde.org>
21
// Copyright (C) 2006, 2007, 2010, 2011 Carlos Garcia Campos <carlosgc@gnome.org>
22
// Copyright (C) 2007 Koji Otani <sho@bbr.jp>
23
// Copyright (C) 2008, 2009 Chris Wilson <chris@chris-wilson.co.uk>
24
// Copyright (C) 2008, 2012, 2014, 2016, 2017, 2022-2024 Adrian Johnson <ajohnson@redneon.com>
25
// Copyright (C) 2009 Darren Kenny <darren.kenny@sun.com>
26
// Copyright (C) 2010 Suzuki Toshiya <mpsuzuki@hiroshima-u.ac.jp>
27
// Copyright (C) 2010 Jan Kümmel <jan+freedesktop@snorc.org>
28
// Copyright (C) 2012 Hib Eris <hib@hiberis.nl>
29
// Copyright (C) 2013 Thomas Freitag <Thomas.Freitag@alfa.de>
30
// Copyright (C) 2015, 2016 Jason Crain <jason@aquaticape.us>
31
// Copyright (C) 2018 Adam Reichold <adam.reichold@t-online.de>
32
// Copyright (C) 2019 Christian Persch <chpe@src.gnome.org>
33
// Copyright (C) 2020 Michal <sudolskym@gmail.com>
34
// Copyright (C) 2021, 2022 Oliver Sander <oliver.sander@tu-dresden.de>
35
// Copyright (C) 2022 Marcel Fabian Krüger <tex@2krueger.de>
36
// Copyright (C) 2023 Pablo Correa Gómez <ablocorrea@hotmail.com>
37
// Copyright (C) 2023 Frederic Germain <frederic.germain@gmail.com>
38
// Copyright (C) 2023 Ilia Kats <ilia-kats@gmx.net>
39
// Copyright (C) 2024 Vincent Lefevre <vincent@vinc17.net>
40
// Copyright (C) 2024 Dmitry Shubin <dshubin@accusoft.com>
41
// Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela <sune@vuorela.dk>
42
// Copyright (C) 2025 Nelson Benítez León <nbenitezl@gmail.com>
43
// Copyright (C) 2026 lilydjwg <lilydjwg@gmail.com>
44
//
45
// To see a description of the changes please see the Changelog file that
46
// came with your tarball or type make ChangeLog if you are building from git
47
//
48
//========================================================================
49
50
#include <config.h>
51
52
#include <cstring>
53
#include "CairoFontEngine.h"
54
#include "CairoOutputDev.h"
55
#include "GlobalParams.h"
56
#include <fofi/FoFiTrueType.h>
57
#include <fofi/FoFiType1C.h>
58
#include "goo/ft_utils.h"
59
#include "Error.h"
60
#include "XRef.h"
61
#include "Gfx.h"
62
#include "Page.h"
63
64
constexpr int EMPTY_ARRAY = -1;
65
66
//------------------------------------------------------------------------
67
// CairoFont
68
//------------------------------------------------------------------------
69
70
5.02k
CairoFont::CairoFont(Ref refA, cairo_font_face_t *cairo_font_faceA, std::vector<int> &&codeToGIDA, bool substituteA, bool printingA) : ref(refA), cairo_font_face(cairo_font_faceA), substitute(substituteA), printing(printingA)
71
5.02k
{
72
5.02k
    codeToGID = std::move(codeToGIDA);
73
5.02k
}
74
75
CairoFont::~CairoFont()
76
5.02k
{
77
5.02k
    cairo_font_face_destroy(cairo_font_face);
78
5.02k
}
79
80
bool CairoFont::matches(Ref &other, bool /*printingA*/)
81
219k
{
82
219k
    return (other == ref);
83
219k
}
84
85
cairo_font_face_t *CairoFont::getFontFace()
86
135k
{
87
135k
    return cairo_font_face;
88
135k
}
89
90
std::optional<unsigned long> CairoFont::getGlyph(CharCode code)
91
4.55M
{
92
4.55M
    FT_UInt gid;
93
94
4.55M
    if (code < codeToGID.size()) {
95
4.45M
        if (codeToGID[code] == EMPTY_ARRAY) {
96
19.6k
            return std::nullopt;
97
19.6k
        }
98
4.43M
        gid = static_cast<FT_UInt>(codeToGID[code]);
99
4.43M
    } else {
100
101k
        gid = static_cast<FT_UInt>(code);
101
101k
    }
102
4.53M
    return { gid };
103
4.55M
}
104
105
double CairoFont::getSubstitutionCorrection(const std::shared_ptr<GfxFont> &gfxFont)
106
135k
{
107
135k
    double w1, w2, w3;
108
135k
    CharCode code;
109
135k
    const char *name;
110
111
    // for substituted fonts: adjust the font matrix -- compare the
112
    // width of 'm' in the original font and the substituted font
113
135k
    if (isSubstitute() && !gfxFont->isCIDFont()) {
114
5.15M
        for (code = 0; code < 256; ++code) {
115
5.14M
            if ((name = std::static_pointer_cast<Gfx8BitFont>(gfxFont)->getCharName(code)) && name[0] == 'm' && name[1] == '\0') {
116
41.2k
                break;
117
41.2k
            }
118
5.14M
        }
119
43.8k
        if (code < 256) {
120
41.2k
            w1 = std::static_pointer_cast<Gfx8BitFont>(gfxFont)->getWidth(code);
121
41.2k
            {
122
41.2k
                cairo_matrix_t m;
123
41.2k
                cairo_matrix_init_identity(&m);
124
41.2k
                cairo_font_options_t *options = cairo_font_options_create();
125
41.2k
                cairo_font_options_set_hint_style(options, CAIRO_HINT_STYLE_NONE);
126
41.2k
                cairo_font_options_set_hint_metrics(options, CAIRO_HINT_METRICS_OFF);
127
41.2k
                cairo_scaled_font_t *scaled_font = cairo_scaled_font_create(cairo_font_face, &m, &m, options);
128
129
41.2k
                cairo_text_extents_t extents;
130
41.2k
                cairo_scaled_font_text_extents(scaled_font, "m", &extents);
131
132
41.2k
                cairo_scaled_font_destroy(scaled_font);
133
41.2k
                cairo_font_options_destroy(options);
134
41.2k
                w2 = extents.x_advance;
135
41.2k
            }
136
41.2k
            w3 = std::static_pointer_cast<Gfx8BitFont>(gfxFont)->getWidth(0);
137
41.2k
            if (!gfxFont->isSymbolic() && w2 > 0 && w1 > w3) {
138
                // if real font is substantially narrower than substituted
139
                // font, reduce the font size accordingly
140
26.4k
                if (w1 > 0.01 && w1 < 0.9 * w2) {
141
25.5k
                    w1 /= w2;
142
25.5k
                    return w1;
143
25.5k
                }
144
26.4k
            }
145
41.2k
        }
146
43.8k
    }
147
109k
    return 1.0;
148
135k
}
149
150
//------------------------------------------------------------------------
151
// CairoFreeTypeFont
152
//------------------------------------------------------------------------
153
154
static cairo_user_data_key_t ft_cairo_key;
155
156
// Font resources to be freed when cairo_font_face_t is destroyed
157
struct FreeTypeFontResource
158
{
159
    FT_Face face;
160
    std::vector<unsigned char> font_data;
161
};
162
163
// cairo callback for when cairo_font_face_t is destroyed
164
static void _ft_done_face(void *closure)
165
4.66k
{
166
4.66k
    auto *resource = static_cast<FreeTypeFontResource *>(closure);
167
168
4.66k
    FT_Done_Face(resource->face);
169
4.66k
    delete resource;
170
4.66k
}
171
172
4.86k
CairoFreeTypeFont::CairoFreeTypeFont(Ref refA, cairo_font_face_t *cairo_font_faceA, std::vector<int> &&codeToGIDA, bool substituteA) : CairoFont(refA, cairo_font_faceA, std::move(codeToGIDA), substituteA, true) { }
173
174
CairoFreeTypeFont::~CairoFreeTypeFont() = default;
175
176
// Create a cairo_font_face_t for the given font filename OR font data.
177
std::optional<FreeTypeFontFace> CairoFreeTypeFont::createFreeTypeFontFace(FT_Library lib, const std::string &filename, int faceIndex, std::vector<unsigned char> &&font_data)
178
5.63k
{
179
5.63k
    auto *resource = new FreeTypeFontResource;
180
5.63k
    FreeTypeFontFace font_face;
181
182
5.63k
    if (font_data.empty()) {
183
1.89k
        FT_Error err = ft_new_face_from_file(lib, filename.c_str(), faceIndex, &resource->face);
184
1.89k
        if (err) {
185
0
            delete resource;
186
0
            return {};
187
0
        }
188
3.74k
    } else {
189
3.74k
        resource->font_data = std::move(font_data);
190
3.74k
        FT_Error err = FT_New_Memory_Face(lib, static_cast<FT_Byte *>(resource->font_data.data()), resource->font_data.size(), faceIndex, &resource->face);
191
3.74k
        if (err) {
192
775
            delete resource;
193
775
            return {};
194
775
        }
195
3.74k
    }
196
197
4.86k
    font_face.cairo_font_face = cairo_ft_font_face_create_for_ft_face(resource->face, FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP);
198
4.86k
    if (cairo_font_face_set_user_data(font_face.cairo_font_face, &ft_cairo_key, resource, _ft_done_face)) {
199
0
        cairo_font_face_destroy(font_face.cairo_font_face);
200
0
        _ft_done_face(resource);
201
0
        return {};
202
0
    }
203
204
4.86k
    font_face.face = resource->face;
205
4.86k
    return font_face;
206
4.86k
}
207
208
CairoFreeTypeFont *CairoFreeTypeFont::create(const std::shared_ptr<GfxFont> &gfxFont, XRef *xref, FT_Library lib)
209
9.01k
{
210
9.01k
    std::string fileName;
211
9.01k
    int faceIndex = 0;
212
9.01k
    std::vector<unsigned char> font_data;
213
9.01k
    int i;
214
9.01k
    std::optional<GfxFontLoc> fontLoc;
215
9.01k
    const char *name;
216
9.01k
    std::optional<FreeTypeFontFace> font_face;
217
9.01k
    std::vector<int> codeToGID;
218
9.01k
    bool substitute = false;
219
220
9.01k
    Ref ref = *gfxFont->getID();
221
9.01k
    GfxFontType fontType = gfxFont->getType();
222
223
9.01k
    if (!(fontLoc = gfxFont->locateFont(xref, nullptr))) {
224
3.06k
        const std::optional<std::string> &fontName = gfxFont->getName();
225
3.06k
        error(errSyntaxError, -1, "Couldn't find a font for '{0:s}'", fontName ? fontName->c_str() : "(unnamed)");
226
3.06k
        goto err2;
227
3.06k
    }
228
229
    // embedded font
230
5.94k
    if (fontLoc->locType == gfxFontLocEmbedded) {
231
4.05k
        auto fd = gfxFont->readEmbFontFile(xref);
232
4.05k
        if (!fd || fd->empty()) {
233
49
            goto err2;
234
49
        }
235
4.00k
        font_data = std::move(fd.value());
236
237
        // external font
238
4.00k
    } else { // gfxFontLocExternal
239
1.89k
        fileName = fontLoc->path;
240
1.89k
        fontType = fontLoc->fontType;
241
1.89k
        faceIndex = fontLoc->fontNum;
242
1.89k
        substitute = true;
243
1.89k
    }
244
245
5.90k
    switch (fontType) {
246
1.16k
    case fontType1:
247
1.91k
    case fontType1C:
248
1.97k
    case fontType1COT: {
249
1.97k
        font_face = createFreeTypeFontFace(lib, fileName, faceIndex, std::move(font_data));
250
1.97k
        if (!font_face) {
251
655
            error(errSyntaxError, -1, "could not create type1 face");
252
655
            goto err2;
253
655
        }
254
255
1.32k
        const std::array<const char *, 256> &enc = std::static_pointer_cast<Gfx8BitFont>(gfxFont)->getEncoding();
256
257
1.32k
        codeToGID.resize(256);
258
339k
        for (i = 0; i < 256; ++i) {
259
338k
            codeToGID[i] = 0;
260
338k
            if ((name = enc[i])) {
261
188k
                codeToGID[i] = FT_Get_Name_Index(font_face->face, name);
262
188k
                if (codeToGID[i] == 0) {
263
132k
                    Unicode u;
264
132k
                    u = globalParams->mapNameToUnicodeText(name);
265
132k
                    codeToGID[i] = FT_Get_Char_Index(font_face->face, u);
266
132k
                }
267
188k
                if (codeToGID[i] == 0) {
268
132k
                    name = GfxFont::getAlternateName(name);
269
132k
                    if (name) {
270
1.31k
                        codeToGID[i] = FT_Get_Name_Index(font_face->face, name);
271
1.31k
                    }
272
132k
                }
273
188k
            }
274
338k
        }
275
1.32k
    } break;
276
694
    case fontCIDType2:
277
694
    case fontCIDType2OT:
278
694
        if (std::static_pointer_cast<GfxCIDFont>(gfxFont)->getCIDToGIDLen() > 0) {
279
121
            std::vector<int> src = std::static_pointer_cast<GfxCIDFont>(gfxFont)->getCIDToGID();
280
121
            codeToGID = std::move(src);
281
573
        } else {
282
573
            std::unique_ptr<FoFiTrueType> ff;
283
573
            if (!font_data.empty()) {
284
416
                ff = FoFiTrueType::make(std::span(font_data), faceIndex);
285
416
            } else {
286
157
                ff = FoFiTrueType::load(fileName.c_str(), faceIndex);
287
157
            }
288
573
            if (!ff) {
289
50
                goto err2;
290
50
            }
291
523
            std::vector<int> src = std::static_pointer_cast<GfxCIDFont>(gfxFont)->getCodeToGIDMap(ff.get());
292
523
            codeToGID = std::move(src);
293
523
        }
294
        /* Fall through */
295
3.64k
    case fontTrueType:
296
3.64k
    case fontTrueTypeOT: {
297
3.64k
        std::unique_ptr<FoFiTrueType> ff;
298
3.64k
        if (!font_data.empty()) {
299
1.75k
            ff = FoFiTrueType::make(std::span(font_data), faceIndex);
300
1.89k
        } else {
301
1.89k
            ff = FoFiTrueType::load(fileName.c_str(), faceIndex);
302
1.89k
        }
303
3.64k
        if (!ff) {
304
212
            error(errSyntaxError, -1, "failed to load truetype font");
305
212
            goto err2;
306
212
        }
307
        /* This might be set already for the CIDType2 case */
308
3.43k
        if (fontType == fontTrueType || fontType == fontTrueTypeOT) {
309
2.79k
            std::vector<int> src = std::static_pointer_cast<Gfx8BitFont>(gfxFont)->getCodeToGIDMap(ff.get());
310
2.79k
            codeToGID = std::move(src);
311
2.79k
        }
312
3.43k
        font_face = createFreeTypeFontFace(lib, fileName, faceIndex, std::move(font_data));
313
3.43k
        if (!font_face) {
314
116
            error(errSyntaxError, -1, "could not create truetype face");
315
116
            goto err2;
316
116
        }
317
3.31k
        break;
318
3.43k
    }
319
3.31k
    case fontCIDType0:
320
187
    case fontCIDType0C:
321
187
        font_face = createFreeTypeFontFace(lib, fileName, faceIndex, std::move(font_data));
322
187
        if (!font_face) {
323
4
            error(errSyntaxError, -1, "could not create cid face");
324
4
            goto err2;
325
4
        }
326
183
        break;
327
328
183
    case fontCIDType0COT:
329
41
        if (std::static_pointer_cast<GfxCIDFont>(gfxFont)->getCIDToGIDLen() > 0) {
330
33
            std::vector<int> src = std::static_pointer_cast<GfxCIDFont>(gfxFont)->getCIDToGID();
331
33
            codeToGID = std::move(src);
332
33
        }
333
334
41
        font_face = createFreeTypeFontFace(lib, fileName, faceIndex, std::move(font_data));
335
41
        if (!font_face) {
336
0
            error(errSyntaxError, -1, "could not create cid (OT) face");
337
0
            goto err2;
338
0
        }
339
41
        break;
340
341
41
    default:
342
2
        fprintf(stderr, "font type %d not handled\n", static_cast<int>(fontType));
343
2
        goto err2;
344
0
        break;
345
5.90k
    }
346
347
4.86k
    return new CairoFreeTypeFont(ref, font_face->cairo_font_face, std::move(codeToGID), substitute);
348
349
4.15k
err2:
350
4.15k
    if (fontLoc && fontLoc->locType == gfxFontLocEmbedded) {
351
1.08k
        gfxFont->invalidateEmbeddedFont();
352
1.08k
    }
353
4.15k
    fprintf(stderr, "some font thing failed\n");
354
4.15k
    return nullptr;
355
5.90k
}
356
357
//------------------------------------------------------------------------
358
// CairoType3Font
359
//------------------------------------------------------------------------
360
361
static const cairo_user_data_key_t type3_font_key = { 0 };
362
363
struct type3_font_info_t
364
{
365
166
    type3_font_info_t(const std::shared_ptr<GfxFont> &fontA, PDFDoc *docA, CairoFontEngine *fontEngineA, CairoOutputDev *outputDevA, Gfx *gfxA) : font(fontA), doc(docA), fontEngine(fontEngineA), outputDev(outputDevA), gfx(gfxA) { }
366
367
    std::shared_ptr<GfxFont> font;
368
    PDFDoc *doc;
369
    CairoFontEngine *fontEngine;
370
    CairoOutputDev *outputDev;
371
    Gfx *gfx;
372
};
373
374
static void _free_type3_font_info(void *closure)
375
165
{
376
165
    auto *info = static_cast<type3_font_info_t *>(closure);
377
165
    delete info->gfx;
378
165
    delete info->outputDev;
379
165
    delete info;
380
165
}
381
382
static cairo_status_t _init_type3_glyph(cairo_scaled_font_t *scaled_font, cairo_t * /*cr*/, cairo_font_extents_t *extents)
383
294
{
384
294
    type3_font_info_t *info;
385
386
294
    info = static_cast<type3_font_info_t *>(cairo_font_face_get_user_data(cairo_scaled_font_get_font_face(scaled_font), &type3_font_key));
387
294
    const std::array<double, 4> &mat = info->font->getFontBBox();
388
294
    extents->ascent = mat[3]; /* y2 */
389
294
    extents->descent = -mat[3]; /* -y1 */
390
294
    extents->height = extents->ascent + extents->descent;
391
294
    extents->max_x_advance = mat[2] - mat[1]; /* x2 - x1 */
392
294
    extents->max_y_advance = 0;
393
394
294
    return CAIRO_STATUS_SUCCESS;
395
294
}
396
397
static cairo_status_t _render_type3_glyph(cairo_scaled_font_t *scaled_font, unsigned long glyph, cairo_t *cr, cairo_text_extents_t *metrics, bool color)
398
13.9k
{
399
13.9k
    Dict *charProcs;
400
13.9k
    Object charProc;
401
13.9k
    cairo_matrix_t matrix, invert_y_axis;
402
13.9k
    double wx, wy;
403
13.9k
    type3_font_info_t *info;
404
13.9k
    Gfx *gfx;
405
13.9k
    cairo_status_t status;
406
407
13.9k
    info = static_cast<type3_font_info_t *>(cairo_font_face_get_user_data(cairo_scaled_font_get_font_face(scaled_font), &type3_font_key));
408
409
13.9k
    charProcs = std::static_pointer_cast<Gfx8BitFont>(info->font)->getCharProcs();
410
13.9k
    if (!charProcs) {
411
        // error already logged by Gfx8BitFont constructor
412
0
        return CAIRO_STATUS_SUCCESS;
413
0
    }
414
415
13.9k
    if (glyph >= static_cast<unsigned>(charProcs->getLength())) {
416
0
        error(errSyntaxError, -1, "Type3 character not in /CharProcs");
417
0
        return CAIRO_STATUS_SUCCESS;
418
0
    }
419
420
13.9k
    const std::array<double, 6> &mat = info->font->getFontMatrix();
421
13.9k
    matrix.xx = mat[0];
422
13.9k
    matrix.yx = mat[1];
423
13.9k
    matrix.xy = mat[2];
424
13.9k
    matrix.yy = mat[3];
425
13.9k
    matrix.x0 = mat[4];
426
13.9k
    matrix.y0 = mat[5];
427
13.9k
    cairo_matrix_init_scale(&invert_y_axis, 1, -1);
428
13.9k
    cairo_matrix_multiply(&matrix, &matrix, &invert_y_axis);
429
13.9k
    cairo_transform(cr, &matrix);
430
431
13.9k
    cairo_set_source(cr, cairo_user_scaled_font_get_foreground_marker(scaled_font));
432
433
13.9k
    CairoOutputDev *output_dev = info->outputDev;
434
13.9k
    output_dev->setCairo(cr);
435
436
13.9k
    gfx = info->gfx;
437
13.9k
    gfx->saveState();
438
439
13.9k
    output_dev->startDoc(info->doc, info->fontEngine);
440
13.9k
    output_dev->startType3Render(gfx->getXRef());
441
13.9k
    output_dev->setType3RenderType(color ? CairoOutputDev::Type3RenderColor : CairoOutputDev::Type3RenderMask);
442
13.9k
    charProc = charProcs->getVal(glyph);
443
13.9k
    if (!charProc.isStream()) {
444
426
        error(errSyntaxError, -1, "Type3 character /{0:r} value not a stream", &charProcs->getKey(glyph));
445
426
        return CAIRO_STATUS_SUCCESS;
446
426
    }
447
13.5k
    Object charProcResObject = charProc.getStream()->getDict()->lookup("Resources");
448
13.5k
    if (charProcResObject.isDict()) {
449
14
        gfx->pushResources(charProcResObject.getDict());
450
14
    }
451
13.5k
    gfx->display(&charProc);
452
13.5k
    if (charProcResObject.isDict()) {
453
14
        gfx->popResources();
454
14
    }
455
456
13.5k
    output_dev->getType3GlyphWidth(&wx, &wy);
457
13.5k
    cairo_matrix_transform_distance(&matrix, &wx, &wy);
458
13.5k
    metrics->x_advance = wx;
459
13.5k
    metrics->y_advance = wy;
460
13.5k
    if (output_dev->hasType3GlyphBBox()) {
461
13.0k
        double *bbox = output_dev->getType3GlyphBBox();
462
463
13.0k
        cairo_matrix_transform_point(&matrix, &bbox[0], &bbox[1]);
464
13.0k
        cairo_matrix_transform_point(&matrix, &bbox[2], &bbox[3]);
465
13.0k
        metrics->x_bearing = bbox[0];
466
13.0k
        metrics->y_bearing = bbox[1];
467
13.0k
        metrics->width = bbox[2] - bbox[0];
468
13.0k
        metrics->height = bbox[3] - bbox[1];
469
13.0k
    }
470
13.5k
    gfx->restoreState();
471
472
13.5k
    status = CAIRO_STATUS_SUCCESS;
473
474
    // If this is a render color glyph callback but the Type 3 glyph
475
    // specified non-color, return NOT_IMPLEMENTED. Cairo will then
476
    // call the render non-color glyph callback.
477
13.5k
    if (color && !output_dev->type3GlyphHasColor()) {
478
6.78k
        status = CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED;
479
6.78k
    }
480
481
13.5k
    return status;
482
13.9k
}
483
484
static cairo_status_t _render_type3_color_glyph(cairo_scaled_font_t *scaled_font, unsigned long glyph, cairo_t *cr, cairo_text_extents_t *metrics)
485
7.21k
{
486
7.21k
    return _render_type3_glyph(scaled_font, glyph, cr, metrics, true);
487
7.21k
}
488
489
static cairo_status_t _render_type3_noncolor_glyph(cairo_scaled_font_t *scaled_font, unsigned long glyph, cairo_t *cr, cairo_text_extents_t *metrics)
490
6.78k
{
491
6.78k
    return _render_type3_glyph(scaled_font, glyph, cr, metrics, false);
492
6.78k
}
493
494
CairoType3Font *CairoType3Font::create(const std::shared_ptr<GfxFont> &gfxFont, PDFDoc *doc, CairoFontEngine *fontEngine, bool printing)
495
166
{
496
166
    std::vector<int> codeToGID;
497
166
    const char *name;
498
499
166
    Dict *charProcs = std::static_pointer_cast<Gfx8BitFont>(gfxFont)->getCharProcs();
500
166
    Ref ref = *gfxFont->getID();
501
166
    cairo_font_face_t *font_face = cairo_user_font_face_create();
502
166
    cairo_user_font_face_set_init_func(font_face, _init_type3_glyph);
503
    // When both callbacks are set, Cairo will call the color glyph
504
    // callback first.  If that returns NOT_IMPLEMENTED, Cairo will
505
    // then call the non-color glyph callback.
506
166
    cairo_user_font_face_set_render_color_glyph_func(font_face, _render_type3_color_glyph);
507
166
    cairo_user_font_face_set_render_glyph_func(font_face, _render_type3_noncolor_glyph);
508
509
166
    auto *output_dev = new CairoOutputDev();
510
166
    output_dev->setPrinting(printing);
511
512
166
    Dict *resDict = std::static_pointer_cast<Gfx8BitFont>(gfxFont)->getResources();
513
166
    const std::array<double, 4> &mat = gfxFont->getFontBBox();
514
166
    PDFRectangle box;
515
166
    box.x1 = mat[0];
516
166
    box.y1 = mat[1];
517
166
    box.x2 = mat[2];
518
166
    box.y2 = mat[3];
519
166
    Gfx *gfx = new Gfx(doc, output_dev, resDict, box, nullptr);
520
521
166
    auto *info = new type3_font_info_t(gfxFont, doc, fontEngine, output_dev, gfx);
522
166
    cairo_font_face_set_user_data(font_face, &type3_font_key, reinterpret_cast<void *>(info), _free_type3_font_info);
523
524
166
    const std::array<const char *, 256> &enc = std::static_pointer_cast<Gfx8BitFont>(gfxFont)->getEncoding();
525
166
    codeToGID.resize(256);
526
42.6k
    for (int i = 0; i < 256; ++i) {
527
42.4k
        codeToGID[i] = EMPTY_ARRAY;
528
42.4k
        if (charProcs && (name = enc[i])) {
529
1.22M
            for (int j = 0; j < charProcs->getLength(); j++) {
530
1.19M
                if (charProcs->getKey(j) == name) {
531
6.55k
                    codeToGID[i] = j;
532
6.55k
                }
533
1.19M
            }
534
27.4k
        }
535
42.4k
    }
536
537
166
    return new CairoType3Font(ref, font_face, std::move(codeToGID), printing);
538
166
}
539
540
166
CairoType3Font::CairoType3Font(Ref refA, cairo_font_face_t *cairo_font_faceA, std::vector<int> &&codeToGIDA, bool printingA) : CairoFont(refA, cairo_font_faceA, std::move(codeToGIDA), false, printingA) { }
541
542
CairoType3Font::~CairoType3Font() = default;
543
544
bool CairoType3Font::matches(Ref &other, bool printingA)
545
753
{
546
753
    return (other == ref && printing == printingA);
547
753
}
548
549
//------------------------------------------------------------------------
550
// CairoFontEngine
551
//------------------------------------------------------------------------
552
553
CairoFontEngine::CairoFontEngine(FT_Library libA)
554
22.2k
{
555
22.2k
    lib = libA;
556
22.2k
    fontCache.reserve(cairoFontCacheSize);
557
22.2k
}
558
559
22.2k
CairoFontEngine::~CairoFontEngine() = default;
560
561
std::shared_ptr<CairoFont> CairoFontEngine::getFont(const std::shared_ptr<GfxFont> &gfxFont, PDFDoc *doc, bool printing, XRef *xref)
562
139k
{
563
139k
    std::scoped_lock lock(mutex);
564
139k
    Ref ref = *gfxFont->getID();
565
139k
    std::shared_ptr<CairoFont> font;
566
567
    // Check if font is in the MRU cache, and move it to the end if it is.
568
229k
    for (auto it = fontCache.rbegin(); it != fontCache.rend(); ++it) {
569
220k
        if ((*it)->matches(ref, printing)) {
570
130k
            font = *it;
571
            // move it to the end
572
130k
            if (it != fontCache.rbegin()) {
573
                // https://stackoverflow.com/questions/1830158/how-to-call-erase-with-a-reverse-iterator
574
41.9k
                fontCache.erase(std::next(it).base());
575
41.9k
                fontCache.push_back(font);
576
41.9k
            }
577
130k
            return font;
578
130k
        }
579
220k
    }
580
581
9.18k
    GfxFontType fontType = gfxFont->getType();
582
9.18k
    if (fontType == fontType3) {
583
166
        font = std::shared_ptr<CairoFont>(CairoType3Font::create(gfxFont, doc, this, printing));
584
9.01k
    } else {
585
9.01k
        font = std::shared_ptr<CairoFont>(CairoFreeTypeFont::create(gfxFont, xref, lib));
586
9.01k
    }
587
588
9.18k
    if (font) {
589
5.02k
        if (fontCache.size() == cairoFontCacheSize) {
590
0
            fontCache.erase(fontCache.begin());
591
0
        }
592
5.02k
        fontCache.push_back(font);
593
5.02k
    }
594
9.18k
    return font;
595
139k
}