/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 | } |