Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-CLA-applies
4 : *
5 : * MuseScore Studio
6 : * Music Composition & Notation
7 : *
8 : * Copyright (C) 2024 MuseScore Limited and others
9 : *
10 : * This program is free software: you can redistribute it and/or modify
11 : * it under the terms of the GNU General Public License version 3 as
12 : * published by the Free Software Foundation.
13 : *
14 : * This program is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU General Public License
20 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 : */
22 : #include "fontsengine.h"
23 :
24 : #include "ifontface.h"
25 : #ifdef MUSE_MODULE_DRAW_USE_FONTFACE_FT
26 : #include "fontfaceft.h"
27 : #endif
28 : #ifdef MUSE_MODULE_DRAW_USE_FONTFACE_XT
29 : #include "fontfacext.h"
30 : #endif
31 : #include "fontfacedu.h"
32 :
33 : #include "log.h"
34 :
35 : using namespace muse;
36 : using namespace muse::draw;
37 :
38 208 : static bool isFtx(const ByteArray& data)
39 : {
40 : #ifdef MUSE_MODULE_DRAW_USE_FONTFACE_XT
41 208 : return FontFaceXT::isFtx(data);
42 : #else
43 : UNUSED(data);
44 : return false;
45 : #endif
46 : }
47 :
48 : static const double DEFAULT_PIXEL_SIZE = 100.0;
49 : static const double LOADED_PIXEL_SIZE = 200.0;
50 : static const double FONT_METRICS_DPI = 1200.0;
51 : static constexpr double PPI = 72.0;
52 :
53 785 : static int fontMetricsPixelSize(const Font& f)
54 : {
55 785 : if (f.pixelSize() > 0) {
56 0 : return f.pixelSize();
57 : }
58 :
59 785 : double pixelSize = f.pointSizeF() * FONT_METRICS_DPI / PPI;
60 785 : return static_cast<int>(std::round(pixelSize));
61 : }
62 :
63 785 : static FaceKey faceKeyForMetricsFont(const Font& f)
64 : {
65 785 : return FaceKey(dataKeyForFont(f), f.type(), fontMetricsPixelSize(f));
66 : }
67 :
68 278 : static inline RectF fromFBBox(const FBBox& bb, double scale)
69 : {
70 556 : return RectF(from_f26d6(bb.left()) * scale, from_f26d6(bb.top()) * scale,
71 834 : from_f26d6(bb.width()) * scale, from_f26d6(bb.height()) * scale);
72 : }
73 :
74 : static inline RectF scaleRect(const RectF& r, double scale)
75 : {
76 : return RectF(r.x() * scale, r.y() * scale, r.width() * scale, r.height() * scale);
77 : }
78 :
79 21 : static const IFontFace* findSubtitutionFont(char32_t ch, const std::vector<IFontFace*>& subtitutionFaces)
80 : {
81 21 : const IFontFace* founded = nullptr;
82 22 : for (const IFontFace* subFace : subtitutionFaces) {
83 1 : if (subFace->glyphIndex(ch) != 0) {
84 1 : founded = subFace;
85 : }
86 : }
87 21 : return founded;
88 : }
89 :
90 318 : static bool fitsQtGlyphCacheMetrics(const FBBox& bbox, f26dot6_t advance)
91 : {
92 318 : return (bbox.width() >> 6) <= 0xFF && (bbox.height() >> 6) <= 0xFF && advance <= 0x7FFF;
93 : }
94 :
95 20 : static bool isZeroAdvanceChar(char32_t ch)
96 : {
97 20 : return ch == U'\n' || ch == U'\r';
98 : }
99 :
100 20 : static bool isZeroAdvanceText(const char32_t* text, int length)
101 : {
102 38 : for (int i = 0; i < length; ++i) {
103 20 : if (!isZeroAdvanceChar(text[i])) {
104 2 : return false;
105 : }
106 : }
107 18 : return true;
108 : }
109 :
110 682 : bool FontsEngine::RequireFace::isSymbolMode() const
111 : {
112 682 : return face ? face->isSymbolMode() : false;
113 : }
114 :
115 674 : double FontsEngine::RequireFace::pixelScale() const
116 : {
117 674 : return pixelScaleFor(face);
118 : }
119 :
120 791 : double FontsEngine::RequireFace::pixelScaleFor(const IFontFace* loadedFace) const
121 : {
122 791 : if (!loadedFace) {
123 0 : return 0.0;
124 : }
125 :
126 791 : return static_cast<double>(requireKey.pixelSize) / static_cast<double>(loadedFace->key().pixelSize);
127 : }
128 :
129 23 : FontsEngine::~FontsEngine()
130 : {
131 23 : clearLoadedFaces();
132 23 : }
133 :
134 23 : void FontsEngine::init()
135 : {
136 23 : m_renderCache.init();
137 :
138 23 : fontsDatabase()->changed().onNotify(this, [this]() {
139 7 : clearLoadedFaces();
140 7 : });
141 23 : }
142 :
143 0 : void FontsEngine::setRenderCacheDirPath(const io::path_t& path, const std::string& revision)
144 : {
145 0 : m_renderCache.setCacheDirPath(path, revision);
146 0 : }
147 :
148 30 : void FontsEngine::clearLoadedFaces()
149 : {
150 133 : for (RequireFace* f : m_requiredFaces) {
151 103 : delete f;
152 : }
153 30 : m_requiredFaces.clear();
154 :
155 134 : for (IFontFace* f : m_loadedFaces) {
156 104 : delete f;
157 : }
158 30 : m_loadedFaces.clear();
159 30 : }
160 :
161 6 : double FontsEngine::lineSpacing(const Font& f) const
162 : {
163 6 : RequireFace* rf = fontFace(f);
164 6 : IF_ASSERT_FAILED(rf && rf->face) {
165 0 : return 0.0;
166 : }
167 :
168 6 : return from_f26d6(rf->face->leading() + rf->face->ascent() + rf->face->descent()) * rf->pixelScale();
169 : }
170 :
171 6 : double FontsEngine::xHeight(const Font& f) const
172 : {
173 6 : RequireFace* rf = fontFace(f);
174 6 : IF_ASSERT_FAILED(rf && rf->face) {
175 0 : return 0.0;
176 : }
177 :
178 6 : return from_f26d6(rf->face->xHeight()) * rf->pixelScale();
179 : }
180 :
181 6 : double FontsEngine::height(const Font& f) const
182 : {
183 6 : RequireFace* rf = fontFace(f);
184 6 : IF_ASSERT_FAILED(rf && rf->face) {
185 0 : return 0.0;
186 : }
187 :
188 6 : return from_f26d6(rf->face->ascent() + rf->face->descent()) * rf->pixelScale();
189 : }
190 :
191 0 : double FontsEngine::capHeight(const Font& f) const
192 : {
193 0 : RequireFace* rf = fontFace(f);
194 0 : IF_ASSERT_FAILED(rf && rf->face) {
195 0 : return 0.0;
196 : }
197 :
198 0 : return from_f26d6(rf->face->capHeight()) * rf->pixelScale();
199 : }
200 :
201 12 : double FontsEngine::ascent(const Font& f) const
202 : {
203 12 : RequireFace* rf = fontFace(f);
204 12 : IF_ASSERT_FAILED(rf && rf->face) {
205 0 : return 0.0;
206 : }
207 :
208 12 : return from_f26d6(rf->face->ascent()) * rf->pixelScale();
209 : }
210 :
211 6 : double FontsEngine::descent(const Font& f) const
212 : {
213 6 : RequireFace* rf = fontFace(f);
214 6 : IF_ASSERT_FAILED(rf && rf->face) {
215 0 : return 0.0;
216 : }
217 :
218 6 : return from_f26d6(rf->face->descent()) * rf->pixelScale();
219 : }
220 :
221 6 : double FontsEngine::underlinePos(const Font& f) const
222 : {
223 6 : RequireFace* rf = fontFace(f);
224 6 : IF_ASSERT_FAILED(rf && rf->face) {
225 0 : return 0.0;
226 : }
227 6 : return from_f26d6(rf->face->underlinePos()) * rf->pixelScale();
228 : }
229 :
230 6 : double FontsEngine::lineWidth(const Font& f) const
231 : {
232 6 : RequireFace* rf = fontFace(f);
233 6 : IF_ASSERT_FAILED(rf && rf->face) {
234 0 : return 1.0;
235 : }
236 6 : return from_f26d6(rf->face->lineWidth()) * rf->pixelScale();
237 : }
238 :
239 6 : double FontsEngine::strikeOutPos(const Font& f) const
240 : {
241 6 : return ascent(f) / 3.0;
242 : }
243 :
244 0 : bool FontsEngine::inFont(const Font& f, char32_t ucs4) const
245 : {
246 0 : RequireFace* rf = fontFace(f);
247 0 : IF_ASSERT_FAILED(rf && rf->face) {
248 0 : return false;
249 : }
250 :
251 0 : return rf->face->glyphIndex(ucs4) != 0;
252 : }
253 :
254 348 : double FontsEngine::horizontalAdvance(const Font& f, const char32_t& ch) const
255 : {
256 348 : RequireFace* rf = fontFace(f);
257 348 : IF_ASSERT_FAILED(rf && rf->face) {
258 0 : return 0.0;
259 : }
260 :
261 348 : glyph_idx_t glyphIdx = rf->face->glyphIndex(ch);
262 348 : return from_f26d6(rf->face->glyphAdvance(glyphIdx)) * rf->pixelScale();
263 : }
264 :
265 111 : double FontsEngine::horizontalAdvance(const Font& f, const std::u32string& text) const
266 : {
267 111 : if (text.empty()) {
268 0 : return 0.0;
269 : }
270 :
271 111 : RequireFace* rf = fontFace(f);
272 111 : IF_ASSERT_FAILED(rf && rf->face) {
273 0 : return 0.0;
274 : }
275 :
276 111 : double advance = 0.0;
277 :
278 111 : TextBlock textBlock;
279 111 : textBlock.text = &text[0];
280 111 : textBlock.lenght = static_cast<int>(text.size());
281 :
282 111 : std::vector<FontFaceTextBlock> fontFaceBlocks = splitTextByFontFaces(rf, textBlock);
283 234 : for (const FontFaceTextBlock& ffBlock : fontFaceBlocks) {
284 123 : const IFontFace* fontFace = ffBlock.face;
285 123 : if (!fontFace) {
286 8 : if (isZeroAdvanceText(ffBlock.text.text, ffBlock.text.lenght)) {
287 6 : continue;
288 : }
289 :
290 2 : fontFace = rf->face;
291 : }
292 :
293 117 : const double pixelScale = rf->pixelScaleFor(fontFace);
294 117 : std::vector<ShapedGlyph> glyphs = fontFace->glyphs(ffBlock.text.text, ffBlock.text.lenght);
295 498 : for (const ShapedGlyph& g : glyphs) {
296 381 : advance += from_f26d6(g.x_advance) * pixelScale;
297 : }
298 117 : }
299 :
300 111 : return advance;
301 111 : }
302 :
303 118 : RectF FontsEngine::boundingRect(const Font& f, const char32_t& ch) const
304 : {
305 118 : RequireFace* rf = fontFace(f, f.type() == Font::Type::MusicSymbol);
306 118 : IF_ASSERT_FAILED(rf && rf->face) {
307 0 : return RectF();
308 : }
309 :
310 118 : glyph_idx_t glyphIdx = rf->face->glyphIndex(ch);
311 118 : return fromFBBox(rf->face->glyphBbox(glyphIdx), rf->pixelScale());
312 : }
313 :
314 48 : RectF FontsEngine::boundingRect(const Font& f, const std::u32string& text) const
315 : {
316 48 : if (text.empty()) {
317 0 : return RectF();
318 : }
319 :
320 48 : RequireFace* rf = fontFace(f);
321 48 : IF_ASSERT_FAILED(rf && rf->face) {
322 0 : return RectF();
323 : }
324 :
325 48 : FBBox rect; // f26dot6_t units
326 48 : rect.setY(-rf->face->ascent());
327 48 : rect.setHeight(rf->face->ascent() + rf->face->descent());
328 :
329 48 : f26dot6_t xOffset = 0;
330 48 : f26dot6_t xmax = 0;
331 48 : f26dot6_t ymax = 0;
332 48 : bool hasGlyph = false;
333 :
334 48 : TextBlock textBlock;
335 48 : textBlock.text = &text[0];
336 48 : textBlock.lenght = static_cast<int>(text.size());
337 :
338 48 : std::vector<FontFaceTextBlock> fontFaceBlocks = splitTextByFontFaces(rf, textBlock);
339 108 : for (const FontFaceTextBlock& ffBlock : fontFaceBlocks) {
340 60 : const IFontFace* fontFace = ffBlock.face;
341 60 : if (!fontFace) {
342 6 : if (isZeroAdvanceText(ffBlock.text.text, ffBlock.text.lenght)) {
343 6 : continue;
344 : }
345 :
346 0 : fontFace = rf->face;
347 : }
348 :
349 54 : std::vector<ShapedGlyph> glyphs = fontFace->glyphs(ffBlock.text.text, ffBlock.text.lenght);
350 :
351 372 : for (const ShapedGlyph& g : glyphs) {
352 318 : FBBox bbox = fontFace->glyphBbox(g.idx);
353 318 : f26dot6_t x = xOffset + bbox.x();
354 318 : f26dot6_t y = bbox.y();
355 :
356 318 : bool useCachedGlyphMetrics = fitsQtGlyphCacheMetrics(bbox, fontFace->glyphAdvance(g.idx));
357 318 : f26dot6_t glyphRight = useCachedGlyphMetrics ? ((x + 63) & -64) + bbox.width() : x + bbox.width();
358 318 : f26dot6_t glyphBottom = useCachedGlyphMetrics ? ((y + 63) & -64) + bbox.height() : y + bbox.height();
359 :
360 318 : if (!hasGlyph) {
361 48 : rect.setX(x);
362 48 : xmax = glyphRight;
363 48 : ymax = glyphBottom;
364 48 : hasGlyph = true;
365 : } else {
366 270 : rect.setX(std::min(rect.x(), x));
367 270 : xmax = std::max(xmax, glyphRight);
368 270 : ymax = std::max(ymax, glyphBottom);
369 : }
370 :
371 318 : rect.setY(std::min(rect.y(), y));
372 318 : xOffset += g.x_advance;
373 : }
374 54 : }
375 :
376 48 : if (!hasGlyph) {
377 0 : rect.setX(0);
378 0 : rect.setWidth(0);
379 0 : return fromFBBox(rect, rf->pixelScale());
380 : }
381 :
382 48 : rect.setWidth(xmax - rect.x());
383 48 : rect.setHeight(std::max(rect.height(), ymax - rect.y()));
384 :
385 48 : return fromFBBox(rect, rf->pixelScale());
386 48 : }
387 :
388 112 : RectF FontsEngine::tightBoundingRect(const Font& f, const std::u32string& text) const
389 : {
390 112 : if (text.empty()) {
391 0 : return RectF();
392 : }
393 :
394 112 : RequireFace* rf = fontFace(f);
395 112 : IF_ASSERT_FAILED(rf && rf->face) {
396 0 : return RectF();
397 : }
398 :
399 112 : FBBox rect; // f26dot6_t units
400 112 : f26dot6_t xOffset = 0;
401 112 : f26dot6_t xmax = 0;
402 112 : f26dot6_t ymax = 0;
403 112 : bool hasGlyph = false;
404 :
405 112 : TextBlock textBlock;
406 112 : textBlock.text = &text[0];
407 112 : textBlock.lenght = static_cast<int>(text.size());
408 :
409 112 : std::vector<FontFaceTextBlock> fontFaceBlocks = splitTextByFontFaces(rf, textBlock);
410 236 : for (const FontFaceTextBlock& ffBlock : fontFaceBlocks) {
411 124 : const IFontFace* fontFace = ffBlock.face;
412 124 : if (!fontFace) {
413 6 : if (isZeroAdvanceText(ffBlock.text.text, ffBlock.text.lenght)) {
414 6 : continue;
415 : }
416 :
417 0 : fontFace = rf->face;
418 : }
419 :
420 118 : std::vector<ShapedGlyph> glyphs = fontFace->glyphs(ffBlock.text.text, ffBlock.text.lenght);
421 :
422 560 : for (const ShapedGlyph& g : glyphs) {
423 442 : FBBox bbox = fontFace->glyphBbox(g.idx);
424 442 : f26dot6_t x = xOffset + bbox.x();
425 442 : f26dot6_t y = bbox.y();
426 :
427 442 : f26dot6_t glyphRight = ((x + 63) & -64) + bbox.width();
428 442 : f26dot6_t glyphBottom = ((y + 63) & -64) + bbox.height();
429 :
430 442 : if (!hasGlyph) {
431 112 : rect.setX(x);
432 112 : rect.setY(y);
433 112 : xmax = glyphRight;
434 112 : ymax = glyphBottom;
435 112 : hasGlyph = true;
436 : } else {
437 330 : rect.setX(std::min(rect.x(), x));
438 330 : rect.setY(std::min(rect.y(), y));
439 330 : xmax = std::max(xmax, glyphRight);
440 330 : ymax = std::max(ymax, glyphBottom);
441 : }
442 :
443 442 : xOffset += g.x_advance;
444 : }
445 118 : }
446 :
447 112 : if (!hasGlyph) {
448 0 : return RectF();
449 : }
450 :
451 112 : rect.setWidth(xmax - rect.x());
452 112 : rect.setHeight(ymax - rect.y());
453 :
454 112 : return fromFBBox(rect, rf->pixelScale());
455 112 : }
456 :
457 0 : std::vector<GlyphImage> FontsEngine::render(const Font& f, const std::u32string& text) const
458 : {
459 : //! NOTE for rendering, all fonts, including symbols fonts, are processed as text
460 0 : RequireFace* rf = fontFace(f);
461 0 : IF_ASSERT_FAILED(rf && rf->face) {
462 0 : return std::vector<GlyphImage>();
463 : }
464 :
465 0 : std::vector<GlyphImage> images;
466 :
467 0 : if (text.empty()) {
468 0 : return images;
469 : }
470 :
471 : #ifndef MUSE_MODULE_DRAW_USE_QTTEXTDRAW
472 : double glyphLeft = 0;
473 :
474 : TextBlock textBlock;
475 : textBlock.text = &text[0];
476 : textBlock.lenght = static_cast<int>(text.size());
477 :
478 : std::vector<FontFaceTextBlock> fontFaceBlocks = splitTextByFontFaces(rf, textBlock);
479 : for (const FontFaceTextBlock& ffBlock : fontFaceBlocks) {
480 : const IFontFace* layoutFace = ffBlock.face;
481 : if (!layoutFace) {
482 : if (isZeroAdvanceText(ffBlock.text.text, ffBlock.text.lenght)) {
483 : continue;
484 : }
485 :
486 : layoutFace = rf->face;
487 : }
488 :
489 : const IFontFace* sdfFace = sdfFontFaceFor(layoutFace);
490 : if (!sdfFace) {
491 : //! NOTE Fall back to the layout face itself rather than dropping this run's glyphs
492 : sdfFace = layoutFace;
493 : }
494 :
495 : double layoutScale = rf->pixelScaleFor(layoutFace);
496 : double sdfToLayoutScale = rf->pixelScaleFor(sdfFace);
497 : std::vector<ShapedGlyph> glyphs = layoutFace->glyphs(ffBlock.text.text, ffBlock.text.lenght);
498 :
499 : for (const ShapedGlyph& g : glyphs) {
500 : if (!layoutFace->glyphBbox(g.idx).isEmpty()) {
501 : GlyphImage image = m_renderCache.load(sdfFace->key(), g.idx);
502 : if (image.isNull()) {
503 : image = sdfFace->glyphImage(g.idx);
504 : m_renderCache.store(sdfFace->key(), g.idx, image);
505 : }
506 :
507 : image.rect = scaleRect(image.rect, sdfToLayoutScale);
508 : image.rect.translate(glyphLeft, 0);
509 :
510 : images.push_back(std::move(image));
511 : }
512 :
513 : glyphLeft += from_f26d6(g.x_advance) * layoutScale;
514 : }
515 : }
516 : #endif
517 :
518 0 : return images;
519 0 : }
520 :
521 0 : void FontsEngine::setFontFaceFactory(const FontFaceFactory& f)
522 : {
523 0 : m_fontFaceFactory = f;
524 0 : }
525 :
526 104 : IFontFace* FontsEngine::createFontFace(const FontData& fontData, Font::Type type) const
527 : {
528 104 : const FontDataKey& dataKey = fontData.key;
529 104 : if (m_fontFaceFactory) {
530 0 : return m_fontFaceFactory(dataKey, type);
531 : }
532 :
533 104 : IFontFace* origin = nullptr;
534 104 : if (isFtx(fontData.data)) {
535 : #ifdef MUSE_MODULE_DRAW_USE_FONTFACE_XT
536 0 : origin = new FontFaceXT();
537 : #else
538 : LOGE() << "XT font face backend is disabled: " << dataKey.family().id();
539 : return nullptr;
540 : #endif
541 : } else {
542 : #ifdef MUSE_MODULE_DRAW_USE_FONTFACE_FT
543 104 : origin = new FontFaceFT();
544 : #else
545 : LOGE() << "FreeType font face backend is disabled: " << dataKey.family().id();
546 : return nullptr;
547 : #endif
548 : }
549 :
550 104 : return new FontFaceDU(origin);
551 : }
552 :
553 104 : IFontFace* FontsEngine::fontFaceByActualDataKey(const FontDataKey& actualDataKey, Font::Type type, int loadedPixelSize,
554 : bool isSymbolMode) const
555 : {
556 345 : for (IFontFace* face : m_loadedFaces) {
557 481 : if (face->key().dataKey == actualDataKey && face->key().type == type && face->key().pixelSize == loadedPixelSize
558 481 : && face->isSymbolMode() == isSymbolMode) {
559 0 : return face;
560 : }
561 : }
562 :
563 104 : FontData fontData = fontsDatabase()->fontData(actualDataKey, type);
564 104 : if (!fontData.valid()) {
565 0 : LOGE() << "font data is empty: " << actualDataKey.family().id();
566 0 : return nullptr;
567 : }
568 :
569 104 : FaceKey loadedKey;
570 104 : loadedKey.dataKey = fontData.key;
571 104 : loadedKey.type = type;
572 104 : loadedKey.pixelSize = loadedPixelSize;
573 :
574 104 : IFontFace* face = createFontFace(fontData, type);
575 104 : IF_ASSERT_FAILED(face) {
576 0 : return nullptr;
577 : }
578 :
579 104 : if (!face->load(loadedKey, fontData, isSymbolMode)) {
580 0 : LOGE() << "failed load font face: " << fontData.key.family().id();
581 0 : delete face;
582 0 : return nullptr;
583 : }
584 :
585 104 : m_loadedFaces.push_back(face);
586 104 : return face;
587 104 : }
588 :
589 0 : IFontFace* FontsEngine::sdfFontFaceFor(const IFontFace* layoutFace) const
590 : {
591 0 : if (!layoutFace) {
592 0 : return nullptr;
593 : }
594 :
595 0 : return fontFaceByActualDataKey(layoutFace->key().dataKey, layoutFace->key().type, static_cast<int>(LOADED_PIXEL_SIZE),
596 0 : layoutFace->isSymbolMode());
597 : }
598 :
599 785 : FontsEngine::RequireFace* FontsEngine::fontFace(const Font& f, bool isSymbolMode) const
600 : {
601 : //! NOTE This font is required
602 785 : FaceKey requireKey = faceKeyForMetricsFont(f);
603 :
604 : //! NOTE If pixelSize is not set, then specify the default
605 : //! (this is the default pixelSize in Qt)
606 785 : if (!(requireKey.pixelSize > 0)) {
607 0 : requireKey.pixelSize = DEFAULT_PIXEL_SIZE;
608 : }
609 :
610 : //! NOTE At the moment, in some cases, the type may not be specified,
611 : //! so set as Text
612 785 : if (requireKey.type == Font::Type::Undefined || requireKey.type == Font::Type::Unknown) {
613 0 : requireKey.type = Font::Type::Text;
614 : }
615 :
616 : //! NOTE We are looking for the require font we need among the previously loaded ones
617 2450 : for (RequireFace* face : m_requiredFaces) {
618 2347 : if (face->requireKey == requireKey && face->isSymbolMode() == isSymbolMode) {
619 682 : return face;
620 : }
621 : }
622 :
623 : //! Let's find out which real font will be used
624 : //! (for example, if there is no required one)
625 103 : FontDataKey actualDataKey = fontsDatabase()->actualFont(requireKey.dataKey, requireKey.type);
626 :
627 103 : const int loadedPixelSize = isFtx(fontsDatabase()->fontData(actualDataKey, requireKey.type).data)
628 103 : ? static_cast<int>(LOADED_PIXEL_SIZE)
629 103 : : requireKey.pixelSize;
630 103 : IFontFace* face = fontFaceByActualDataKey(actualDataKey, requireKey.type, loadedPixelSize, isSymbolMode);
631 103 : if (!face) {
632 0 : return nullptr;
633 : }
634 :
635 : //! If we didn't find it, we create a new require font
636 103 : RequireFace* newFont = new RequireFace();
637 103 : newFont->requireKey = requireKey;
638 103 : newFont->actualDataKey = actualDataKey;
639 103 : newFont->face = face;
640 :
641 103 : auto subtitutionFontDataKeys = fontsDatabase()->substitutionFonts(requireKey.dataKey);
642 104 : for (const FontDataKey& dataKey : subtitutionFontDataKeys) {
643 1 : const FontDataKey actualSubtitutionDataKey = fontsDatabase()->actualFont(dataKey, requireKey.type);
644 1 : const int subtitutionLoadedPixelSize = isFtx(fontsDatabase()->fontData(actualSubtitutionDataKey, requireKey.type).data)
645 1 : ? static_cast<int>(LOADED_PIXEL_SIZE) : requireKey.pixelSize;
646 1 : IFontFace* subtitutionFace = fontFaceByActualDataKey(actualSubtitutionDataKey, requireKey.type, subtitutionLoadedPixelSize,
647 1 : isSymbolMode);
648 1 : if (subtitutionFace) {
649 1 : newFont->subtitutionFaces.push_back(subtitutionFace);
650 : }
651 1 : }
652 :
653 103 : m_requiredFaces.push_back(newFont);
654 :
655 103 : return newFont;
656 785 : }
657 :
658 271 : std::vector<FontsEngine::FontFaceTextBlock> FontsEngine::splitTextByFontFaces(const RequireFace* rf, const TextBlock& text) const
659 : {
660 271 : std::vector<FontFaceTextBlock> textBlocks;
661 :
662 271 : FontFaceTextBlock txtBlock;
663 271 : txtBlock.text.text = &text.text[0];
664 271 : const IFontFace* current = nullptr;
665 1430 : for (int i = 0; i < text.lenght; ++i) {
666 1159 : const IFontFace* newFace = nullptr;
667 1159 : if (rf->face->glyphIndex(text.text[i]) != 0) {
668 1138 : newFace = rf->face;
669 : } else {
670 21 : newFace = findSubtitutionFont(text.text[i], rf->subtitutionFaces);
671 : }
672 :
673 1159 : if (txtBlock.text.lenght > 0 && newFace != current) {
674 36 : txtBlock.face = current;
675 36 : textBlocks.push_back(txtBlock);
676 36 : txtBlock.text.text = &text.text[i];
677 36 : txtBlock.text.lenght = 0;
678 : }
679 :
680 1159 : current = newFace;
681 1159 : ++txtBlock.text.lenght;
682 :
683 1159 : if (i == (text.lenght - 1)) {
684 271 : txtBlock.face = current;
685 271 : textBlocks.push_back(txtBlock);
686 271 : txtBlock.text.text = nullptr;
687 271 : txtBlock.text.lenght = 0;
688 : }
689 : }
690 :
691 271 : return textBlocks;
692 0 : }
|