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 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, 2023 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//
40// To see a description of the changes please see the Changelog file that
41// came with your tarball or type make ChangeLog if you are building from git
42//
43//========================================================================
44
45#include <config.h>
46
47#include <cstring>
48#include <fstream>
49#include "CairoFontEngine.h"
50#include "CairoOutputDev.h"
51#include "GlobalParams.h"
52#include <fofi/FoFiTrueType.h>
53#include <fofi/FoFiType1C.h>
54#include "goo/ft_utils.h"
55#include "goo/gfile.h"
56#include "Error.h"
57#include "XRef.h"
58#include "Gfx.h"
59#include "Page.h"
60
61//------------------------------------------------------------------------
62// CairoFont
63//------------------------------------------------------------------------
64
65CairoFont::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)
66{
67 codeToGID = std::move(codeToGIDA);
68}
69
70CairoFont::~CairoFont()
71{
72 cairo_font_face_destroy(font_face: cairo_font_face);
73}
74
75bool CairoFont::matches(Ref &other, bool printingA)
76{
77 return (other == ref);
78}
79
80cairo_font_face_t *CairoFont::getFontFace()
81{
82 return cairo_font_face;
83}
84
85unsigned long CairoFont::getGlyph(CharCode code, const Unicode *u, int uLen)
86{
87 FT_UInt gid;
88
89 if (code < codeToGID.size()) {
90 gid = (FT_UInt)codeToGID[code];
91 } else {
92 gid = (FT_UInt)code;
93 }
94 return gid;
95}
96
97double CairoFont::getSubstitutionCorrection(const std::shared_ptr<GfxFont> &gfxFont)
98{
99 double w1, w2, w3;
100 CharCode code;
101 const char *name;
102
103 // for substituted fonts: adjust the font matrix -- compare the
104 // width of 'm' in the original font and the substituted font
105 if (isSubstitute() && !gfxFont->isCIDFont()) {
106 for (code = 0; code < 256; ++code) {
107 if ((name = std::static_pointer_cast<Gfx8BitFont>(r: gfxFont)->getCharName(code)) && name[0] == 'm' && name[1] == '\0') {
108 break;
109 }
110 }
111 if (code < 256) {
112 w1 = std::static_pointer_cast<Gfx8BitFont>(r: gfxFont)->getWidth(c: code);
113 {
114 cairo_matrix_t m;
115 cairo_matrix_init_identity(matrix: &m);
116 cairo_font_options_t *options = cairo_font_options_create();
117 cairo_font_options_set_hint_style(options, hint_style: CAIRO_HINT_STYLE_NONE);
118 cairo_font_options_set_hint_metrics(options, hint_metrics: CAIRO_HINT_METRICS_OFF);
119 cairo_scaled_font_t *scaled_font = cairo_scaled_font_create(font_face: cairo_font_face, font_matrix: &m, ctm: &m, options);
120
121 cairo_text_extents_t extents;
122 cairo_scaled_font_text_extents(scaled_font, utf8: "m", extents: &extents);
123
124 cairo_scaled_font_destroy(scaled_font);
125 cairo_font_options_destroy(options);
126 w2 = extents.x_advance;
127 }
128 w3 = std::static_pointer_cast<Gfx8BitFont>(r: gfxFont)->getWidth(c: 0);
129 if (!gfxFont->isSymbolic() && w2 > 0 && w1 > w3) {
130 // if real font is substantially narrower than substituted
131 // font, reduce the font size accordingly
132 if (w1 > 0.01 && w1 < 0.9 * w2) {
133 w1 /= w2;
134 return w1;
135 }
136 }
137 }
138 }
139 return 1.0;
140}
141
142//------------------------------------------------------------------------
143// CairoFreeTypeFont
144//------------------------------------------------------------------------
145
146static cairo_user_data_key_t ft_cairo_key;
147
148// Font resources to be freed when cairo_font_face_t is destroyed
149struct FreeTypeFontResource
150{
151 FT_Face face;
152 std::vector<unsigned char> font_data;
153};
154
155// cairo callback for when cairo_font_face_t is destroyed
156static void _ft_done_face(void *closure)
157{
158 FreeTypeFontResource *resource = (FreeTypeFontResource *)closure;
159
160 FT_Done_Face(face: resource->face);
161 delete resource;
162}
163
164CairoFreeTypeFont::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) { }
165
166CairoFreeTypeFont::~CairoFreeTypeFont() { }
167
168// Create a cairo_font_face_t for the given font filename OR font data.
169static std::optional<FreeTypeFontFace> createFreeTypeFontFace(FT_Library lib, const std::string &filename, std::vector<unsigned char> &&font_data)
170{
171 FreeTypeFontResource *resource = new FreeTypeFontResource;
172 FreeTypeFontFace font_face;
173
174 if (font_data.empty()) {
175 FT_Error err = ft_new_face_from_file(library: lib, filename_utf8: filename.c_str(), face_index: 0, aface: &resource->face);
176 if (err) {
177 delete resource;
178 return {};
179 }
180 } else {
181 resource->font_data = std::move(font_data);
182 FT_Error err = FT_New_Memory_Face(library: lib, file_base: (FT_Byte *)resource->font_data.data(), file_size: resource->font_data.size(), face_index: 0, aface: &resource->face);
183 if (err) {
184 delete resource;
185 return {};
186 }
187 }
188
189 font_face.cairo_font_face = cairo_ft_font_face_create_for_ft_face(face: resource->face, FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP);
190 if (cairo_font_face_set_user_data(font_face: font_face.cairo_font_face, key: &ft_cairo_key, user_data: resource, destroy: _ft_done_face)) {
191 cairo_font_face_destroy(font_face: font_face.cairo_font_face);
192 _ft_done_face(closure: resource);
193 return {};
194 }
195
196 font_face.face = resource->face;
197 return font_face;
198}
199
200// Create a cairo_font_face_t for the given font filename OR font data. First checks if external font
201// is in the cache.
202std::optional<FreeTypeFontFace> CairoFreeTypeFont::getFreeTypeFontFace(CairoFontEngine *fontEngine, FT_Library lib, const std::string &filename, std::vector<unsigned char> &&font_data)
203{
204 if (font_data.empty()) {
205 return fontEngine->getExternalFontFace(ftlib: lib, filename);
206 }
207
208 return createFreeTypeFontFace(lib, filename, font_data: std::move(font_data));
209}
210
211CairoFreeTypeFont *CairoFreeTypeFont::create(const std::shared_ptr<GfxFont> &gfxFont, XRef *xref, FT_Library lib, CairoFontEngine *fontEngine, bool useCIDs)
212{
213 std::string fileName;
214 std::vector<unsigned char> font_data;
215 int i, n;
216 std::optional<GfxFontLoc> fontLoc;
217 char **enc;
218 const char *name;
219 FoFiType1C *ff1c;
220 std::optional<FreeTypeFontFace> font_face;
221 std::vector<int> codeToGID;
222 bool substitute = false;
223
224 Ref ref = *gfxFont->getID();
225 Ref embFontID = Ref::INVALID();
226 gfxFont->getEmbeddedFontID(embID: &embFontID);
227 GfxFontType fontType = gfxFont->getType();
228
229 if (!(fontLoc = gfxFont->locateFont(xref, ps: nullptr))) {
230 error(category: errSyntaxError, pos: -1, msg: "Couldn't find a font for '{0:s}'", gfxFont->getName() ? gfxFont->getName()->c_str() : "(unnamed)");
231 goto err2;
232 }
233
234 // embedded font
235 if (fontLoc->locType == gfxFontLocEmbedded) {
236 auto fd = gfxFont->readEmbFontFile(xref);
237 if (!fd || fd->empty()) {
238 goto err2;
239 }
240 font_data = std::move(fd.value());
241
242 // external font
243 } else { // gfxFontLocExternal
244 fileName = fontLoc->path;
245 fontType = fontLoc->fontType;
246 substitute = true;
247 }
248
249 switch (fontType) {
250 case fontType1:
251 case fontType1C:
252 case fontType1COT:
253 font_face = getFreeTypeFontFace(fontEngine, lib, filename: fileName, font_data: std::move(font_data));
254 if (!font_face) {
255 error(category: errSyntaxError, pos: -1, msg: "could not create type1 face");
256 goto err2;
257 }
258
259 enc = std::static_pointer_cast<Gfx8BitFont>(r: gfxFont)->getEncoding();
260
261 codeToGID.resize(new_size: 256);
262 for (i = 0; i < 256; ++i) {
263 codeToGID[i] = 0;
264 if ((name = enc[i])) {
265 codeToGID[i] = FT_Get_Name_Index(face: font_face->face, glyph_name: (char *)name);
266 if (codeToGID[i] == 0) {
267 Unicode u;
268 u = globalParams->mapNameToUnicodeText(charName: name);
269 codeToGID[i] = FT_Get_Char_Index(face: font_face->face, charcode: u);
270 }
271 if (codeToGID[i] == 0) {
272 name = GfxFont::getAlternateName(name);
273 if (name) {
274 codeToGID[i] = FT_Get_Name_Index(face: font_face->face, glyph_name: (char *)name);
275 }
276 }
277 }
278 }
279 break;
280 case fontCIDType2:
281 case fontCIDType2OT:
282 if (std::static_pointer_cast<GfxCIDFont>(r: gfxFont)->getCIDToGID()) {
283 n = std::static_pointer_cast<GfxCIDFont>(r: gfxFont)->getCIDToGIDLen();
284 if (n) {
285 const int *src = std::static_pointer_cast<GfxCIDFont>(r: gfxFont)->getCIDToGID();
286 codeToGID.reserve(n: n);
287 codeToGID.insert(position: codeToGID.begin(), first: src, last: src + n);
288 }
289 } else {
290 std::unique_ptr<FoFiTrueType> ff;
291 if (!font_data.empty()) {
292 ff = FoFiTrueType::make(fileA: font_data.data(), lenA: font_data.size());
293 } else {
294 ff = FoFiTrueType::load(fileName: fileName.c_str());
295 }
296 if (!ff) {
297 goto err2;
298 }
299 int *src = std::static_pointer_cast<GfxCIDFont>(r: gfxFont)->getCodeToGIDMap(ff: ff.get(), codeToGIDLen: &n);
300 codeToGID.reserve(n: n);
301 codeToGID.insert(position: codeToGID.begin(), first: src, last: src + n);
302 gfree(p: src);
303 }
304 /* Fall through */
305 case fontTrueType:
306 case fontTrueTypeOT: {
307 std::unique_ptr<FoFiTrueType> ff;
308 if (!font_data.empty()) {
309 ff = FoFiTrueType::make(fileA: font_data.data(), lenA: font_data.size());
310 } else {
311 ff = FoFiTrueType::load(fileName: fileName.c_str());
312 }
313 if (!ff) {
314 error(category: errSyntaxError, pos: -1, msg: "failed to load truetype font\n");
315 goto err2;
316 }
317 /* This might be set already for the CIDType2 case */
318 if (fontType == fontTrueType || fontType == fontTrueTypeOT) {
319 int *src = std::static_pointer_cast<Gfx8BitFont>(r: gfxFont)->getCodeToGIDMap(ff: ff.get());
320 codeToGID.reserve(n: 256);
321 codeToGID.insert(position: codeToGID.begin(), first: src, last: src + 256);
322 gfree(p: src);
323 }
324 font_face = getFreeTypeFontFace(fontEngine, lib, filename: fileName, font_data: std::move(font_data));
325 if (!font_face) {
326 error(category: errSyntaxError, pos: -1, msg: "could not create truetype face\n");
327 goto err2;
328 }
329 break;
330 }
331 case fontCIDType0:
332 case fontCIDType0C:
333 if (!useCIDs) {
334 if (!font_data.empty()) {
335 ff1c = FoFiType1C::make(fileA: font_data.data(), lenA: font_data.size());
336 } else {
337 ff1c = FoFiType1C::load(fileName: fileName.c_str());
338 }
339 if (ff1c) {
340 int *src = ff1c->getCIDToGIDMap(nCIDs: &n);
341 codeToGID.reserve(n: n);
342 codeToGID.insert(position: codeToGID.begin(), first: src, last: src + n);
343 gfree(p: src);
344 delete ff1c;
345 }
346 }
347
348 font_face = getFreeTypeFontFace(fontEngine, lib, filename: fileName, font_data: std::move(font_data));
349 if (!font_face) {
350 error(category: errSyntaxError, pos: -1, msg: "could not create cid face\n");
351 goto err2;
352 }
353 break;
354
355 case fontCIDType0COT:
356 if (std::static_pointer_cast<GfxCIDFont>(r: gfxFont)->getCIDToGID()) {
357 n = std::static_pointer_cast<GfxCIDFont>(r: gfxFont)->getCIDToGIDLen();
358 if (n) {
359 const int *src = std::static_pointer_cast<GfxCIDFont>(r: gfxFont)->getCIDToGID();
360 codeToGID.reserve(n: n);
361 codeToGID.insert(position: codeToGID.begin(), first: src, last: src + n);
362 }
363 }
364
365 if (codeToGID.empty()) {
366 if (!useCIDs) {
367 std::unique_ptr<FoFiTrueType> ff;
368 if (!font_data.empty()) {
369 ff = FoFiTrueType::make(fileA: font_data.data(), lenA: font_data.size());
370 } else {
371 ff = FoFiTrueType::load(fileName: fileName.c_str());
372 }
373 if (ff) {
374 if (ff->isOpenTypeCFF()) {
375 int *src = ff->getCIDToGIDMap(nCIDs: &n);
376 codeToGID.reserve(n: n);
377 codeToGID.insert(position: codeToGID.begin(), first: src, last: src + n);
378 gfree(p: src);
379 }
380 }
381 }
382 }
383 font_face = getFreeTypeFontFace(fontEngine, lib, filename: fileName, font_data: std::move(font_data));
384 if (!font_face) {
385 error(category: errSyntaxError, pos: -1, msg: "could not create cid (OT) face\n");
386 goto err2;
387 }
388 break;
389
390 default:
391 fprintf(stderr, format: "font type %d not handled\n", (int)fontType);
392 goto err2;
393 break;
394 }
395
396 return new CairoFreeTypeFont(ref, font_face->cairo_font_face, std::move(codeToGID), substitute);
397
398err2:
399 fprintf(stderr, format: "some font thing failed\n");
400 return nullptr;
401}
402
403//------------------------------------------------------------------------
404// CairoType3Font
405//------------------------------------------------------------------------
406
407static const cairo_user_data_key_t type3_font_key = { .unused: 0 };
408
409typedef struct _type3_font_info
410{
411 _type3_font_info(const std::shared_ptr<GfxFont> &fontA, PDFDoc *docA, CairoFontEngine *fontEngineA, CairoOutputDev *outputDevA, Gfx *gfxA) : font(fontA), doc(docA), fontEngine(fontEngineA), outputDev(outputDevA), gfx(gfxA) { }
412
413 std::shared_ptr<GfxFont> font;
414 PDFDoc *doc;
415 CairoFontEngine *fontEngine;
416 CairoOutputDev *outputDev;
417 Gfx *gfx;
418} type3_font_info_t;
419
420static void _free_type3_font_info(void *closure)
421{
422 type3_font_info_t *info = (type3_font_info_t *)closure;
423 delete info->gfx;
424 delete info->outputDev;
425 delete info;
426}
427
428static cairo_status_t _init_type3_glyph(cairo_scaled_font_t *scaled_font, cairo_t *cr, cairo_font_extents_t *extents)
429{
430 type3_font_info_t *info;
431
432 info = (type3_font_info_t *)cairo_font_face_get_user_data(font_face: cairo_scaled_font_get_font_face(scaled_font), key: &type3_font_key);
433 const double *mat = info->font->getFontBBox();
434 extents->ascent = mat[3]; /* y2 */
435 extents->descent = -mat[3]; /* -y1 */
436 extents->height = extents->ascent + extents->descent;
437 extents->max_x_advance = mat[2] - mat[1]; /* x2 - x1 */
438 extents->max_y_advance = 0;
439
440 return CAIRO_STATUS_SUCCESS;
441}
442
443static 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)
444{
445 Dict *charProcs;
446 Object charProc;
447 cairo_matrix_t matrix, invert_y_axis;
448 const double *mat;
449 double wx, wy;
450 type3_font_info_t *info;
451 Gfx *gfx;
452 cairo_status_t status;
453
454 info = (type3_font_info_t *)cairo_font_face_get_user_data(font_face: cairo_scaled_font_get_font_face(scaled_font), key: &type3_font_key);
455
456 charProcs = std::static_pointer_cast<Gfx8BitFont>(r: info->font)->getCharProcs();
457 if (!charProcs) {
458 return CAIRO_STATUS_USER_FONT_ERROR;
459 }
460
461 if ((int)glyph >= charProcs->getLength()) {
462 return CAIRO_STATUS_USER_FONT_ERROR;
463 }
464
465 mat = info->font->getFontMatrix();
466 matrix.xx = mat[0];
467 matrix.yx = mat[1];
468 matrix.xy = mat[2];
469 matrix.yy = mat[3];
470 matrix.x0 = mat[4];
471 matrix.y0 = mat[5];
472 cairo_matrix_init_scale(matrix: &invert_y_axis, sx: 1, sy: -1);
473 cairo_matrix_multiply(result: &matrix, a: &matrix, b: &invert_y_axis);
474 cairo_transform(cr, matrix: &matrix);
475
476#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 18, 0)
477 cairo_set_source(cr, cairo_user_scaled_font_get_foreground_marker(scaled_font));
478#endif
479
480 CairoOutputDev *output_dev = info->outputDev;
481 output_dev->setCairo(cr);
482
483 gfx = info->gfx;
484 gfx->saveState();
485
486 output_dev->startDoc(docA: info->doc, fontEngine: info->fontEngine);
487 output_dev->startType3Render(state: gfx->getState(), xref: gfx->getXRef());
488 output_dev->setType3RenderType(color ? CairoOutputDev::Type3RenderColor : CairoOutputDev::Type3RenderMask);
489 charProc = charProcs->getVal(i: glyph);
490 if (!charProc.isStream()) {
491 return CAIRO_STATUS_USER_FONT_ERROR;
492 }
493 Object charProcResObject = charProc.streamGetDict()->lookup(key: "Resources");
494 if (charProcResObject.isDict()) {
495 gfx->pushResources(resDict: charProcResObject.getDict());
496 }
497 gfx->display(obj: &charProc);
498 if (charProcResObject.isDict()) {
499 gfx->popResources();
500 }
501
502 output_dev->getType3GlyphWidth(wx: &wx, wy: &wy);
503 cairo_matrix_transform_distance(matrix: &matrix, dx: &wx, dy: &wy);
504 metrics->x_advance = wx;
505 metrics->y_advance = wy;
506 if (output_dev->hasType3GlyphBBox()) {
507 double *bbox = output_dev->getType3GlyphBBox();
508
509 cairo_matrix_transform_point(matrix: &matrix, x: &bbox[0], y: &bbox[1]);
510 cairo_matrix_transform_point(matrix: &matrix, x: &bbox[2], y: &bbox[3]);
511 metrics->x_bearing = bbox[0];
512 metrics->y_bearing = bbox[1];
513 metrics->width = bbox[2] - bbox[0];
514 metrics->height = bbox[3] - bbox[1];
515 }
516
517 status = CAIRO_STATUS_SUCCESS;
518
519 // If this is a render color glyph callback but the Type 3 glyph
520 // specified non-color, return NOT_IMPLEMENTED. Cairo will then
521 // call the render non-color glyph callback.
522 if (color && !output_dev->type3GlyphHasColor()) {
523 status = CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED;
524 }
525
526 return status;
527}
528
529#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 18, 0)
530static cairo_status_t _render_type3_color_glyph(cairo_scaled_font_t *scaled_font, unsigned long glyph, cairo_t *cr, cairo_text_extents_t *metrics)
531{
532 return _render_type3_glyph(scaled_font, glyph, cr, metrics, true);
533}
534#endif
535
536static cairo_status_t _render_type3_noncolor_glyph(cairo_scaled_font_t *scaled_font, unsigned long glyph, cairo_t *cr, cairo_text_extents_t *metrics)
537{
538 return _render_type3_glyph(scaled_font, glyph, cr, metrics, color: false);
539}
540
541CairoType3Font *CairoType3Font::create(const std::shared_ptr<GfxFont> &gfxFont, PDFDoc *doc, CairoFontEngine *fontEngine, bool printing, XRef *xref)
542{
543 std::vector<int> codeToGID;
544 char *name;
545 const double *mat;
546
547 Dict *charProcs = std::static_pointer_cast<Gfx8BitFont>(r: gfxFont)->getCharProcs();
548 Ref ref = *gfxFont->getID();
549 cairo_font_face_t *font_face = cairo_user_font_face_create();
550 cairo_user_font_face_set_init_func(font_face, init_func: _init_type3_glyph);
551#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 18, 0)
552 // When both callbacks are set, Cairo will call the color glyph
553 // callback first. If that returns NOT_IMPLEMENTED, Cairo will
554 // then call the non-color glyph callback.
555 cairo_user_font_face_set_render_color_glyph_func(font_face, _render_type3_color_glyph);
556#endif
557 cairo_user_font_face_set_render_glyph_func(font_face, render_glyph_func: _render_type3_noncolor_glyph);
558
559 CairoOutputDev *output_dev = new CairoOutputDev();
560 output_dev->setPrinting(printing);
561
562 Dict *resDict = std::static_pointer_cast<Gfx8BitFont>(r: gfxFont)->getResources();
563 mat = gfxFont->getFontBBox();
564 PDFRectangle box;
565 box.x1 = mat[0];
566 box.y1 = mat[1];
567 box.x2 = mat[2];
568 box.y2 = mat[3];
569 Gfx *gfx = new Gfx(doc, output_dev, resDict, &box, nullptr);
570
571 type3_font_info_t *info = new type3_font_info_t(gfxFont, doc, fontEngine, output_dev, gfx);
572 cairo_font_face_set_user_data(font_face, key: &type3_font_key, user_data: (void *)info, destroy: _free_type3_font_info);
573
574 char **enc = std::static_pointer_cast<Gfx8BitFont>(r: gfxFont)->getEncoding();
575 codeToGID.resize(new_size: 256);
576 for (int i = 0; i < 256; ++i) {
577 codeToGID[i] = 0;
578 if (charProcs && (name = enc[i])) {
579 for (int j = 0; j < charProcs->getLength(); j++) {
580 if (strcmp(s1: name, s2: charProcs->getKey(i: j)) == 0) {
581 codeToGID[i] = j;
582 }
583 }
584 }
585 }
586
587 return new CairoType3Font(ref, font_face, std::move(codeToGID), printing, xref);
588}
589
590CairoType3Font::CairoType3Font(Ref refA, cairo_font_face_t *cairo_font_faceA, std::vector<int> &&codeToGIDA, bool printingA, XRef *xref) : CairoFont(refA, cairo_font_faceA, std::move(codeToGIDA), false, printingA) { }
591
592CairoType3Font::~CairoType3Font() { }
593
594bool CairoType3Font::matches(Ref &other, bool printingA)
595{
596 return (other == ref && printing == printingA);
597}
598
599//------------------------------------------------------------------------
600// CairoFontEngine
601//------------------------------------------------------------------------
602
603std::unordered_map<std::string, FreeTypeFontFace> CairoFontEngine::fontFileCache;
604std::recursive_mutex CairoFontEngine::fontFileCacheMutex;
605
606CairoFontEngine::CairoFontEngine(FT_Library libA)
607{
608 lib = libA;
609 fontCache.reserve(n: cairoFontCacheSize);
610
611 FT_Int major, minor, patch;
612 // as of FT 2.1.8, CID fonts are indexed by CID instead of GID
613 FT_Library_Version(library: lib, amajor: &major, aminor: &minor, apatch: &patch);
614 useCIDs = major > 2 || (major == 2 && (minor > 1 || (minor == 1 && patch > 7)));
615}
616
617CairoFontEngine::~CairoFontEngine() { }
618
619std::shared_ptr<CairoFont> CairoFontEngine::getFont(const std::shared_ptr<GfxFont> &gfxFont, PDFDoc *doc, bool printing, XRef *xref)
620{
621 std::scoped_lock lock(mutex);
622 Ref ref = *gfxFont->getID();
623 std::shared_ptr<CairoFont> font;
624
625 // Check if font is in the MRU cache, and move it to the end if it is.
626 for (auto it = fontCache.rbegin(); it != fontCache.rend(); ++it) {
627 if ((*it)->matches(other&: ref, printingA: printing)) {
628 font = *it;
629 // move it to the end
630 if (it != fontCache.rbegin()) {
631 // https://stackoverflow.com/questions/1830158/how-to-call-erase-with-a-reverse-iterator
632 fontCache.erase(position: std::next(x: it).base());
633 fontCache.push_back(x: font);
634 }
635 return font;
636 }
637 }
638
639 GfxFontType fontType = gfxFont->getType();
640 if (fontType == fontType3) {
641 font = std::shared_ptr<CairoFont>(CairoType3Font::create(gfxFont, doc, fontEngine: this, printing, xref));
642 } else {
643 font = std::shared_ptr<CairoFont>(CairoFreeTypeFont::create(gfxFont, xref, lib, fontEngine: this, useCIDs));
644 }
645
646 if (font) {
647 if (fontCache.size() == cairoFontCacheSize) {
648 fontCache.erase(position: fontCache.begin());
649 }
650 fontCache.push_back(x: font);
651 }
652 return font;
653}
654
655std::optional<FreeTypeFontFace> CairoFontEngine::getExternalFontFace(FT_Library ftlib, const std::string &filename)
656{
657 std::scoped_lock lock(fontFileCacheMutex);
658
659 auto it = fontFileCache.find(x: filename);
660 if (it != fontFileCache.end()) {
661 FreeTypeFontFace font = it->second;
662 cairo_font_face_reference(font_face: font.cairo_font_face);
663 return font;
664 }
665
666 std::optional<FreeTypeFontFace> font_face = createFreeTypeFontFace(lib: ftlib, filename, font_data: {});
667 if (font_face) {
668 cairo_font_face_reference(font_face: font_face->cairo_font_face);
669 fontFileCache[filename] = *font_face;
670 }
671
672 it = fontFileCache.begin();
673 while (it != fontFileCache.end()) {
674 if (cairo_font_face_get_reference_count(font_face: it->second.cairo_font_face) == 1) {
675 cairo_font_face_destroy(font_face: it->second.cairo_font_face);
676 it = fontFileCache.erase(position: it);
677 } else {
678 ++it;
679 }
680 }
681
682 return font_face;
683}
684

source code of poppler/poppler/CairoFontEngine.cc