1/****************************************************************************
2**
3** Copyright (C) 2018 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include <qdebug.h>
41#include <private/qfontengine_p.h>
42#include <private/qfontengineglyphcache_p.h>
43#include <private/qguiapplication_p.h>
44
45#include <qpa/qplatformfontdatabase.h>
46#include <qpa/qplatformintegration.h>
47
48#include "qbitmap.h"
49#include "qpainter.h"
50#include "qpainterpath.h"
51#include "qvarlengtharray.h"
52#include <qmath.h>
53#include <qendian.h>
54#include <private/qstringiterator_p.h>
55
56#if QT_CONFIG(harfbuzz)
57# include "qharfbuzzng_p.h"
58# include <harfbuzz/hb-ot.h>
59#endif
60#include <private/qharfbuzz_p.h>
61
62#include <algorithm>
63#include <limits.h>
64
65QT_BEGIN_NAMESPACE
66
67static inline bool qtransform_equals_no_translate(const QTransform &a, const QTransform &b)
68{
69 if (a.type() <= QTransform::TxTranslate && b.type() <= QTransform::TxTranslate) {
70 return true;
71 } else {
72 // We always use paths for perspective text anyway, so no
73 // point in checking the full matrix...
74 Q_ASSERT(a.type() < QTransform::TxProject);
75 Q_ASSERT(b.type() < QTransform::TxProject);
76
77 return a.m11() == b.m11()
78 && a.m12() == b.m12()
79 && a.m21() == b.m21()
80 && a.m22() == b.m22();
81 }
82}
83
84template<typename T>
85static inline bool qSafeFromBigEndian(const uchar *source, const uchar *end, T *output)
86{
87 if (source + sizeof(T) > end)
88 return false;
89
90 *output = qFromBigEndian<T>(source);
91 return true;
92}
93
94// Harfbuzz helper functions
95
96#if QT_CONFIG(harfbuzz)
97Q_GLOBAL_STATIC_WITH_ARGS(bool, useHarfbuzzNG,(qgetenv("QT_HARFBUZZ") != "old"))
98
99bool qt_useHarfbuzzNG()
100{
101 return *useHarfbuzzNG();
102}
103#endif
104
105Q_STATIC_ASSERT(sizeof(HB_Glyph) == sizeof(glyph_t));
106Q_STATIC_ASSERT(sizeof(HB_Fixed) == sizeof(QFixed));
107
108static HB_Bool hb_stringToGlyphs(HB_Font font, const HB_UChar16 *string, hb_uint32 length, HB_Glyph *glyphs, hb_uint32 *numGlyphs, HB_Bool rightToLeft)
109{
110 QFontEngine *fe = (QFontEngine *)font->userData;
111
112 const QChar *str = reinterpret_cast<const QChar *>(string);
113
114 QGlyphLayout qglyphs;
115 qglyphs.numGlyphs = *numGlyphs;
116 qglyphs.glyphs = glyphs;
117 int nGlyphs = *numGlyphs;
118 bool result = fe->stringToCMap(str, len: length, glyphs: &qglyphs, nglyphs: &nGlyphs, flags: QFontEngine::GlyphIndicesOnly);
119 *numGlyphs = nGlyphs;
120
121 if (rightToLeft && result && !fe->symbol) {
122 QStringIterator it(str, str + length);
123 while (it.hasNext()) {
124 const uint ucs4 = it.next();
125 const uint mirrored = QChar::mirroredChar(ucs4);
126 if (Q_UNLIKELY(mirrored != ucs4))
127 *glyphs = fe->glyphIndex(ucs4: mirrored);
128 ++glyphs;
129 }
130 }
131
132 return result;
133}
134
135static void hb_getAdvances(HB_Font font, const HB_Glyph *glyphs, hb_uint32 numGlyphs, HB_Fixed *advances, int flags)
136{
137 QFontEngine *fe = (QFontEngine *)font->userData;
138
139 QGlyphLayout qglyphs;
140 qglyphs.numGlyphs = numGlyphs;
141 qglyphs.glyphs = const_cast<glyph_t *>(glyphs);
142 qglyphs.advances = reinterpret_cast<QFixed *>(advances);
143
144 fe->recalcAdvances(&qglyphs, (flags & HB_ShaperFlag_UseDesignMetrics) ? QFontEngine::DesignMetrics : QFontEngine::ShaperFlags{});
145}
146
147static HB_Bool hb_canRender(HB_Font font, const HB_UChar16 *string, hb_uint32 length)
148{
149 QFontEngine *fe = (QFontEngine *)font->userData;
150 return fe->canRender(str: reinterpret_cast<const QChar *>(string), len: length);
151}
152
153static void hb_getGlyphMetrics(HB_Font font, HB_Glyph glyph, HB_GlyphMetrics *metrics)
154{
155 QFontEngine *fe = (QFontEngine *)font->userData;
156 glyph_metrics_t m = fe->boundingBox(glyph);
157 metrics->x = m.x.value();
158 metrics->y = m.y.value();
159 metrics->width = m.width.value();
160 metrics->height = m.height.value();
161 metrics->xOffset = m.xoff.value();
162 metrics->yOffset = m.yoff.value();
163}
164
165static HB_Fixed hb_getFontMetric(HB_Font font, HB_FontMetric metric)
166{
167 if (metric == HB_FontAscent) {
168 QFontEngine *fe = (QFontEngine *)font->userData;
169 return fe->ascent().value();
170 }
171 return 0;
172}
173
174int QFontEngine::getPointInOutline(glyph_t glyph, int flags, quint32 point, QFixed *xpos, QFixed *ypos, quint32 *nPoints)
175{
176 Q_UNUSED(glyph)
177 Q_UNUSED(flags)
178 Q_UNUSED(point)
179 Q_UNUSED(xpos)
180 Q_UNUSED(ypos)
181 Q_UNUSED(nPoints)
182 return Err_Not_Covered;
183}
184
185static HB_Error hb_getPointInOutline(HB_Font font, HB_Glyph glyph, int flags, hb_uint32 point, HB_Fixed *xpos, HB_Fixed *ypos, hb_uint32 *nPoints)
186{
187 QFontEngine *fe = (QFontEngine *)font->userData;
188 return (HB_Error)fe->getPointInOutline(glyph, flags, point, xpos: (QFixed *)xpos, ypos: (QFixed *)ypos, nPoints: (quint32 *)nPoints);
189}
190
191static const HB_FontClass hb_fontClass = {
192 .convertStringToGlyphIndices: hb_stringToGlyphs, .getGlyphAdvances: hb_getAdvances, .canRender: hb_canRender, .getPointInOutline: hb_getPointInOutline,
193 .getGlyphMetrics: hb_getGlyphMetrics, .getFontMetric: hb_getFontMetric
194};
195
196static HB_Error hb_getSFntTable(void *font, HB_Tag tableTag, HB_Byte *buffer, HB_UInt *length)
197{
198 QFontEngine::FaceData *data = (QFontEngine::FaceData *)font;
199 Q_ASSERT(data);
200
201 qt_get_font_table_func_t get_font_table = data->get_font_table;
202 Q_ASSERT(get_font_table);
203
204 if (!get_font_table(data->user_data, tableTag, buffer, length))
205 return HB_Err_Invalid_Argument;
206 return HB_Err_Ok;
207}
208
209static void hb_freeFace(void *face)
210{
211 qHBFreeFace((HB_Face)face);
212}
213
214
215static bool qt_get_font_table_default(void *user_data, uint tag, uchar *buffer, uint *length)
216{
217 QFontEngine *fe = (QFontEngine *)user_data;
218 return fe->getSfntTableData(tag, buffer, length);
219}
220
221
222#ifdef QT_BUILD_INTERNAL
223// for testing purpose only, not thread-safe!
224static QList<QFontEngine *> *enginesCollector = nullptr;
225
226Q_AUTOTEST_EXPORT void QFontEngine_startCollectingEngines()
227{
228 delete enginesCollector;
229 enginesCollector = new QList<QFontEngine *>();
230}
231
232Q_AUTOTEST_EXPORT QList<QFontEngine *> QFontEngine_stopCollectingEngines()
233{
234 Q_ASSERT(enginesCollector);
235 QList<QFontEngine *> ret = *enginesCollector;
236 delete enginesCollector;
237 enginesCollector = nullptr;
238 return ret;
239}
240#endif // QT_BUILD_INTERNAL
241
242
243// QFontEngine
244
245#define kBearingNotInitialized std::numeric_limits<qreal>::max()
246
247QFontEngine::QFontEngine(Type type)
248 : m_type(type), ref(0),
249 font_(),
250 face_(),
251 m_minLeftBearing(kBearingNotInitialized),
252 m_minRightBearing(kBearingNotInitialized)
253{
254 faceData.user_data = this;
255 faceData.get_font_table = qt_get_font_table_default;
256
257 cache_cost = 0;
258 fsType = 0;
259 symbol = false;
260 isSmoothlyScalable = false;
261
262 glyphFormat = Format_None;
263 m_subPixelPositionCount = 0;
264
265#ifdef QT_BUILD_INTERNAL
266 if (enginesCollector)
267 enginesCollector->append(t: this);
268#endif
269}
270
271QFontEngine::~QFontEngine()
272{
273#ifdef QT_BUILD_INTERNAL
274 if (enginesCollector)
275 enginesCollector->removeOne(t: this);
276#endif
277}
278
279QFixed QFontEngine::lineThickness() const
280{
281 // ad hoc algorithm
282 int score = fontDef.weight * fontDef.pixelSize;
283 int lw = score / 700;
284
285 // looks better with thicker line for small pointsizes
286 if (lw < 2 && score >= 1050) lw = 2;
287 if (lw == 0) lw = 1;
288
289 return lw;
290}
291
292QFixed QFontEngine::underlinePosition() const
293{
294 return ((lineThickness() * 2) + 3) / 6;
295}
296
297void *QFontEngine::harfbuzzFont() const
298{
299 Q_ASSERT(type() != QFontEngine::Multi);
300#if QT_CONFIG(harfbuzz)
301 if (qt_useHarfbuzzNG())
302 return hb_qt_font_get_for_engine(fe: const_cast<QFontEngine *>(this));
303#endif
304 if (!font_) {
305 HB_Face hbFace = (HB_Face)harfbuzzFace();
306 if (hbFace->font_for_init) {
307 void *data = hbFace->font_for_init;
308 q_check_ptr(p: qHBLoadFace(face: hbFace));
309 free(ptr: data);
310 }
311
312 HB_FontRec *hbFont = (HB_FontRec *) malloc(size: sizeof(HB_FontRec));
313 Q_CHECK_PTR(hbFont);
314 hbFont->klass = &hb_fontClass;
315 hbFont->userData = const_cast<QFontEngine *>(this);
316
317 qint64 emSquare = emSquareSize().truncate();
318 Q_ASSERT(emSquare == emSquareSize().toInt()); // ensure no truncation
319 if (emSquare == 0)
320 emSquare = 1000; // a fallback value suitable for Type1 fonts
321 hbFont->y_ppem = fontDef.pixelSize;
322 hbFont->x_ppem = fontDef.pixelSize * fontDef.stretch / 100;
323 // same as QFixed(x)/QFixed(emSquare) but without int32 overflow for x
324 hbFont->x_scale = (((qint64)hbFont->x_ppem << 6) * 0x10000L + (emSquare >> 1)) / emSquare;
325 hbFont->y_scale = (((qint64)hbFont->y_ppem << 6) * 0x10000L + (emSquare >> 1)) / emSquare;
326
327 font_ = Holder(hbFont, free);
328 }
329 return font_.get();
330}
331
332void *QFontEngine::harfbuzzFace() const
333{
334 Q_ASSERT(type() != QFontEngine::Multi);
335#if QT_CONFIG(harfbuzz)
336 if (qt_useHarfbuzzNG())
337 return hb_qt_face_get_for_engine(fe: const_cast<QFontEngine *>(this));
338#endif
339 if (!face_) {
340 QFontEngine::FaceData *data = (QFontEngine::FaceData *)malloc(size: sizeof(QFontEngine::FaceData));
341 Q_CHECK_PTR(data);
342 data->user_data = faceData.user_data;
343 data->get_font_table = faceData.get_font_table;
344
345 HB_Face hbFace = qHBNewFace(font: data, tableFunc: hb_getSFntTable);
346 Q_CHECK_PTR(hbFace);
347 hbFace->isSymbolFont = symbol;
348
349 face_ = Holder(hbFace, hb_freeFace);
350 }
351 return face_.get();
352}
353
354bool QFontEngine::supportsScript(QChar::Script script) const
355{
356 if (type() <= QFontEngine::Multi)
357 return true;
358
359 // ### TODO: This only works for scripts that require OpenType. More generally
360 // for scripts that do not require OpenType we should just look at the list of
361 // supported writing systems in the font's OS/2 table.
362 if (!scriptRequiresOpenType(script))
363 return true;
364
365#if QT_CONFIG(harfbuzz)
366 if (qt_useHarfbuzzNG()) {
367#if defined(Q_OS_DARWIN)
368 // in AAT fonts, 'gsub' table is effectively replaced by 'mort'/'morx' table
369 uint lenMort = 0, lenMorx = 0;
370 if (getSfntTableData(MAKE_TAG('m','o','r','t'), 0, &lenMort) || getSfntTableData(MAKE_TAG('m','o','r','x'), 0, &lenMorx))
371 return true;
372#endif
373
374 bool ret = false;
375 if (hb_face_t *face = hb_qt_face_get_for_engine(fe: const_cast<QFontEngine *>(this))) {
376 hb_tag_t script_tag_1, script_tag_2;
377 hb_ot_tags_from_script(script: hb_qt_script_to_script(script), script_tag_1: &script_tag_1, script_tag_2: &script_tag_2);
378
379 unsigned int script_index;
380 ret = hb_ot_layout_table_find_script(face, HB_OT_TAG_GSUB, script_tag: script_tag_1, script_index: &script_index);
381 if (!ret) {
382 ret = hb_ot_layout_table_find_script(face, HB_OT_TAG_GSUB, script_tag: script_tag_2, script_index: &script_index);
383 if (!ret && script_tag_2 != HB_OT_TAG_DEFAULT_SCRIPT)
384 ret = hb_ot_layout_table_find_script(face, HB_OT_TAG_GSUB, HB_OT_TAG_DEFAULT_SCRIPT, script_index: &script_index);
385 }
386 }
387 return ret;
388 }
389#endif
390 HB_Face hbFace = (HB_Face)harfbuzzFace();
391 if (hbFace->font_for_init) {
392 void *data = hbFace->font_for_init;
393 q_check_ptr(p: qHBLoadFace(face: hbFace));
394 free(ptr: data);
395 }
396 return hbFace->supported_scripts[script_to_hbscript(script)];
397}
398
399bool QFontEngine::canRender(const QChar *str, int len) const
400{
401 QStringIterator it(str, str + len);
402 while (it.hasNext()) {
403 if (glyphIndex(ucs4: it.next()) == 0)
404 return false;
405 }
406
407 return true;
408}
409
410glyph_metrics_t QFontEngine::boundingBox(glyph_t glyph, const QTransform &matrix)
411{
412 glyph_metrics_t metrics = boundingBox(glyph);
413
414 if (matrix.type() > QTransform::TxTranslate) {
415 return metrics.transformed(xform: matrix);
416 }
417 return metrics;
418}
419
420QFixed QFontEngine::calculatedCapHeight() const
421{
422 const glyph_t glyph = glyphIndex(ucs4: 'H');
423 glyph_metrics_t bb = const_cast<QFontEngine *>(this)->boundingBox(glyph);
424 return bb.height;
425}
426
427QFixed QFontEngine::xHeight() const
428{
429 const glyph_t glyph = glyphIndex(ucs4: 'x');
430 glyph_metrics_t bb = const_cast<QFontEngine *>(this)->boundingBox(glyph);
431 return bb.height;
432}
433
434QFixed QFontEngine::averageCharWidth() const
435{
436 const glyph_t glyph = glyphIndex(ucs4: 'x');
437 glyph_metrics_t bb = const_cast<QFontEngine *>(this)->boundingBox(glyph);
438 return bb.xoff;
439}
440
441bool QFontEngine::supportsTransformation(const QTransform &transform) const
442{
443 return transform.type() < QTransform::TxProject;
444}
445
446bool QFontEngine::expectsGammaCorrectedBlending() const
447{
448 return true;
449}
450
451void QFontEngine::getGlyphPositions(const QGlyphLayout &glyphs, const QTransform &matrix, QTextItem::RenderFlags flags,
452 QVarLengthArray<glyph_t> &glyphs_out, QVarLengthArray<QFixedPoint> &positions)
453{
454 QFixed xpos;
455 QFixed ypos;
456
457 const bool transform = matrix.m11() != 1.
458 || matrix.m12() != 0.
459 || matrix.m21() != 0.
460 || matrix.m22() != 1.;
461 if (!transform) {
462 xpos = QFixed::fromReal(r: matrix.dx());
463 ypos = QFixed::fromReal(r: matrix.dy());
464 }
465
466 int current = 0;
467 if (flags & QTextItem::RightToLeft) {
468 int i = glyphs.numGlyphs;
469 int totalKashidas = 0;
470 while(i--) {
471 if (glyphs.attributes[i].dontPrint)
472 continue;
473 xpos += glyphs.advances[i] + QFixed::fromFixed(fixed: glyphs.justifications[i].space_18d6);
474 totalKashidas += glyphs.justifications[i].nKashidas;
475 }
476 positions.resize(asize: glyphs.numGlyphs+totalKashidas);
477 glyphs_out.resize(asize: glyphs.numGlyphs+totalKashidas);
478
479 i = 0;
480 while(i < glyphs.numGlyphs) {
481 if (glyphs.attributes[i].dontPrint) {
482 ++i;
483 continue;
484 }
485 xpos -= glyphs.advances[i];
486
487 QFixed gpos_x = xpos + glyphs.offsets[i].x;
488 QFixed gpos_y = ypos + glyphs.offsets[i].y;
489 if (transform) {
490 QPointF gpos(gpos_x.toReal(), gpos_y.toReal());
491 gpos = gpos * matrix;
492 gpos_x = QFixed::fromReal(r: gpos.x());
493 gpos_y = QFixed::fromReal(r: gpos.y());
494 }
495 positions[current].x = gpos_x;
496 positions[current].y = gpos_y;
497 glyphs_out[current] = glyphs.glyphs[i];
498 ++current;
499 if (glyphs.justifications[i].nKashidas) {
500 QChar ch(0x640); // Kashida character
501
502 glyph_t kashidaGlyph = glyphIndex(ucs4: ch.unicode());
503 QFixed kashidaWidth;
504
505 QGlyphLayout g;
506 g.numGlyphs = 1;
507 g.glyphs = &kashidaGlyph;
508 g.advances = &kashidaWidth;
509 recalcAdvances(&g, { });
510
511 for (uint k = 0; k < glyphs.justifications[i].nKashidas; ++k) {
512 xpos -= kashidaWidth;
513
514 QFixed gpos_x = xpos + glyphs.offsets[i].x;
515 QFixed gpos_y = ypos + glyphs.offsets[i].y;
516 if (transform) {
517 QPointF gpos(gpos_x.toReal(), gpos_y.toReal());
518 gpos = gpos * matrix;
519 gpos_x = QFixed::fromReal(r: gpos.x());
520 gpos_y = QFixed::fromReal(r: gpos.y());
521 }
522 positions[current].x = gpos_x;
523 positions[current].y = gpos_y;
524 glyphs_out[current] = kashidaGlyph;
525 ++current;
526 }
527 } else {
528 xpos -= QFixed::fromFixed(fixed: glyphs.justifications[i].space_18d6);
529 }
530 ++i;
531 }
532 } else {
533 positions.resize(asize: glyphs.numGlyphs);
534 glyphs_out.resize(asize: glyphs.numGlyphs);
535 int i = 0;
536 if (!transform) {
537 while (i < glyphs.numGlyphs) {
538 if (!glyphs.attributes[i].dontPrint) {
539 positions[current].x = xpos + glyphs.offsets[i].x;
540 positions[current].y = ypos + glyphs.offsets[i].y;
541 glyphs_out[current] = glyphs.glyphs[i];
542 xpos += glyphs.advances[i] + QFixed::fromFixed(fixed: glyphs.justifications[i].space_18d6);
543 ++current;
544 }
545 ++i;
546 }
547 } else {
548 while (i < glyphs.numGlyphs) {
549 if (!glyphs.attributes[i].dontPrint) {
550 QFixed gpos_x = xpos + glyphs.offsets[i].x;
551 QFixed gpos_y = ypos + glyphs.offsets[i].y;
552 QPointF gpos(gpos_x.toReal(), gpos_y.toReal());
553 gpos = gpos * matrix;
554 positions[current].x = QFixed::fromReal(r: gpos.x());
555 positions[current].y = QFixed::fromReal(r: gpos.y());
556 glyphs_out[current] = glyphs.glyphs[i];
557 xpos += glyphs.advances[i] + QFixed::fromFixed(fixed: glyphs.justifications[i].space_18d6);
558 ++current;
559 }
560 ++i;
561 }
562 }
563 }
564 positions.resize(asize: current);
565 glyphs_out.resize(asize: current);
566 Q_ASSERT(positions.size() == glyphs_out.size());
567}
568
569void QFontEngine::getGlyphBearings(glyph_t glyph, qreal *leftBearing, qreal *rightBearing)
570{
571 glyph_metrics_t gi = boundingBox(glyph);
572 if (leftBearing != nullptr)
573 *leftBearing = gi.leftBearing().toReal();
574 if (rightBearing != nullptr)
575 *rightBearing = gi.rightBearing().toReal();
576}
577
578qreal QFontEngine::minLeftBearing() const
579{
580 if (m_minLeftBearing == kBearingNotInitialized)
581 minRightBearing(); // Initializes both (see below)
582
583 return m_minLeftBearing;
584}
585
586#define q16Dot16ToFloat(i) ((i) / 65536.0)
587
588#define kMinLeftSideBearingOffset 12
589#define kMinRightSideBearingOffset 14
590
591qreal QFontEngine::minRightBearing() const
592{
593 if (m_minRightBearing == kBearingNotInitialized) {
594
595 // Try the 'hhea' font table first, which covers the entire font
596 QByteArray hheaTable = getSfntTable(MAKE_TAG('h', 'h', 'e', 'a'));
597 if (hheaTable.size() >= int(kMinRightSideBearingOffset + sizeof(qint16))) {
598 const uchar *tableData = reinterpret_cast<const uchar *>(hheaTable.constData());
599 Q_ASSERT(q16Dot16ToFloat(qFromBigEndian<quint32>(tableData)) == 1.0);
600
601 qint16 minLeftSideBearing = qFromBigEndian<qint16>(src: tableData + kMinLeftSideBearingOffset);
602 qint16 minRightSideBearing = qFromBigEndian<qint16>(src: tableData + kMinRightSideBearingOffset);
603
604 // The table data is expressed as FUnits, meaning we have to take the number
605 // of units per em into account. Since pixelSize already has taken DPI into
606 // account we can use that directly instead of the point size.
607 int unitsPerEm = emSquareSize().toInt();
608 qreal funitToPixelFactor = fontDef.pixelSize / unitsPerEm;
609
610 // Some fonts on OS X (such as Gurmukhi Sangam MN, Khmer MN, Lao Sangam MN, etc.), have
611 // invalid values for their NBSPACE left bearing, causing the 'hhea' minimum bearings to
612 // be way off. We detect this by assuming that the minimum bearsings are within a certain
613 // range of the em square size.
614 static const int largestValidBearing = 4 * unitsPerEm;
615
616 if (qAbs(t: minLeftSideBearing) < largestValidBearing)
617 m_minLeftBearing = minLeftSideBearing * funitToPixelFactor;
618 if (qAbs(t: minRightSideBearing) < largestValidBearing)
619 m_minRightBearing = minRightSideBearing * funitToPixelFactor;
620 }
621
622 // Fallback in case of missing 'hhea' table (bitmap fonts e.g.) or broken 'hhea' values
623 if (m_minLeftBearing == kBearingNotInitialized || m_minRightBearing == kBearingNotInitialized) {
624
625 // To balance performance and correctness we only look at a subset of the
626 // possible glyphs in the font, based on which characters are more likely
627 // to have a left or right bearing.
628 static const ushort characterSubset[] = {
629 '(', 'C', 'F', 'K', 'V', 'X', 'Y', ']', '_', 'f', 'r', '|',
630 127, 205, 645, 884, 922, 1070, 12386
631 };
632
633 // The font may have minimum bearings larger than 0, so we have to start at the max
634 m_minLeftBearing = m_minRightBearing = std::numeric_limits<qreal>::max();
635
636 for (uint i = 0; i < (sizeof(characterSubset) / sizeof(ushort)); ++i) {
637 const glyph_t glyph = glyphIndex(ucs4: characterSubset[i]);
638 if (!glyph)
639 continue;
640
641 glyph_metrics_t glyphMetrics = const_cast<QFontEngine *>(this)->boundingBox(glyph);
642
643 // Glyphs with no contours shouldn't contribute to bearings
644 if (!glyphMetrics.width || !glyphMetrics.height)
645 continue;
646
647 m_minLeftBearing = qMin(a: m_minLeftBearing, b: glyphMetrics.leftBearing().toReal());
648 m_minRightBearing = qMin(a: m_minRightBearing, b: glyphMetrics.rightBearing().toReal());
649 }
650 }
651
652 if (m_minLeftBearing == kBearingNotInitialized || m_minRightBearing == kBearingNotInitialized)
653 qWarning() << "Failed to compute left/right minimum bearings for" << fontDef.family;
654 }
655
656 return m_minRightBearing;
657}
658
659glyph_metrics_t QFontEngine::tightBoundingBox(const QGlyphLayout &glyphs)
660{
661 glyph_metrics_t overall;
662
663 QFixed ymax = 0;
664 QFixed xmax = 0;
665 for (int i = 0; i < glyphs.numGlyphs; i++) {
666 // If shaping has found this should be ignored, ignore it.
667 if (!glyphs.advances[i] || glyphs.attributes[i].dontPrint)
668 continue;
669 glyph_metrics_t bb = boundingBox(glyph: glyphs.glyphs[i]);
670 QFixed x = overall.xoff + glyphs.offsets[i].x + bb.x;
671 QFixed y = overall.yoff + glyphs.offsets[i].y + bb.y;
672 overall.x = qMin(a: overall.x, b: x);
673 overall.y = qMin(a: overall.y, b: y);
674 xmax = qMax(a: xmax, b: x + bb.width);
675 ymax = qMax(a: ymax, b: y + bb.height);
676 overall.xoff += bb.xoff;
677 overall.yoff += bb.yoff;
678 }
679 overall.height = qMax(a: overall.height, b: ymax - overall.y);
680 overall.width = xmax - overall.x;
681
682 return overall;
683}
684
685
686void QFontEngine::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path,
687 QTextItem::RenderFlags flags)
688{
689 if (!glyphs.numGlyphs)
690 return;
691
692 QVarLengthArray<QFixedPoint> positions;
693 QVarLengthArray<glyph_t> positioned_glyphs;
694 QTransform matrix = QTransform::fromTranslate(dx: x, dy: y);
695 getGlyphPositions(glyphs, matrix, flags, glyphs_out&: positioned_glyphs, positions);
696 addGlyphsToPath(glyphs: positioned_glyphs.data(), positions: positions.data(), nglyphs: positioned_glyphs.size(), path, flags);
697}
698
699#define GRID(x, y) grid[(y)*(w+1) + (x)]
700#define SET(x, y) (*(image_data + (y)*bpl + ((x) >> 3)) & (0x80 >> ((x) & 7)))
701
702enum { EdgeRight = 0x1,
703 EdgeDown = 0x2,
704 EdgeLeft = 0x4,
705 EdgeUp = 0x8
706};
707
708static void collectSingleContour(qreal x0, qreal y0, uint *grid, int x, int y, int w, int h, QPainterPath *path)
709{
710 Q_UNUSED(h);
711
712 path->moveTo(x: x + x0, y: y + y0);
713 while (GRID(x, y)) {
714 if (GRID(x, y) & EdgeRight) {
715 while (GRID(x, y) & EdgeRight) {
716 GRID(x, y) &= ~EdgeRight;
717 ++x;
718 }
719 Q_ASSERT(x <= w);
720 path->lineTo(x: x + x0, y: y + y0);
721 continue;
722 }
723 if (GRID(x, y) & EdgeDown) {
724 while (GRID(x, y) & EdgeDown) {
725 GRID(x, y) &= ~EdgeDown;
726 ++y;
727 }
728 Q_ASSERT(y <= h);
729 path->lineTo(x: x + x0, y: y + y0);
730 continue;
731 }
732 if (GRID(x, y) & EdgeLeft) {
733 while (GRID(x, y) & EdgeLeft) {
734 GRID(x, y) &= ~EdgeLeft;
735 --x;
736 }
737 Q_ASSERT(x >= 0);
738 path->lineTo(x: x + x0, y: y + y0);
739 continue;
740 }
741 if (GRID(x, y) & EdgeUp) {
742 while (GRID(x, y) & EdgeUp) {
743 GRID(x, y) &= ~EdgeUp;
744 --y;
745 }
746 Q_ASSERT(y >= 0);
747 path->lineTo(x: x + x0, y: y + y0);
748 continue;
749 }
750 }
751 path->closeSubpath();
752}
753
754Q_GUI_EXPORT void qt_addBitmapToPath(qreal x0, qreal y0, const uchar *image_data, int bpl, int w, int h, QPainterPath *path)
755{
756 uint *grid = new uint[(w+1)*(h+1)];
757 // set up edges
758 for (int y = 0; y <= h; ++y) {
759 for (int x = 0; x <= w; ++x) {
760 bool topLeft = (x == 0 || y == 0) ? false : SET(x - 1, y - 1);
761 bool topRight = (x == w || y == 0) ? false : SET(x, y - 1);
762 bool bottomLeft = (x == 0 || y == h) ? false : SET(x - 1, y);
763 bool bottomRight = (x == w || y == h) ? false : SET(x, y);
764
765 GRID(x, y) = 0;
766 if ((!topRight) & bottomRight)
767 GRID(x, y) |= EdgeRight;
768 if ((!bottomRight) & bottomLeft)
769 GRID(x, y) |= EdgeDown;
770 if ((!bottomLeft) & topLeft)
771 GRID(x, y) |= EdgeLeft;
772 if ((!topLeft) & topRight)
773 GRID(x, y) |= EdgeUp;
774 }
775 }
776
777 // collect edges
778 for (int y = 0; y < h; ++y) {
779 for (int x = 0; x < w; ++x) {
780 if (!GRID(x, y))
781 continue;
782 // found start of a contour, follow it
783 collectSingleContour(x0, y0, grid, x, y, w, h, path);
784 }
785 }
786 delete [] grid;
787}
788
789#undef GRID
790#undef SET
791
792
793void QFontEngine::addBitmapFontToPath(qreal x, qreal y, const QGlyphLayout &glyphs,
794 QPainterPath *path, QTextItem::RenderFlags flags)
795{
796// TODO what to do with 'flags' ??
797 Q_UNUSED(flags);
798 QFixed advanceX = QFixed::fromReal(r: x);
799 QFixed advanceY = QFixed::fromReal(r: y);
800 for (int i=0; i < glyphs.numGlyphs; ++i) {
801 glyph_metrics_t metrics = boundingBox(glyph: glyphs.glyphs[i]);
802 if (metrics.width.value() == 0 || metrics.height.value() == 0) {
803 advanceX += glyphs.advances[i];
804 continue;
805 }
806 const QImage alphaMask = alphaMapForGlyph(glyphs.glyphs[i]);
807
808 const int w = alphaMask.width();
809 const int h = alphaMask.height();
810 const int srcBpl = alphaMask.bytesPerLine();
811 QImage bitmap;
812 if (alphaMask.depth() == 1) {
813 bitmap = alphaMask;
814 } else {
815 bitmap = QImage(w, h, QImage::Format_Mono);
816 const uchar *imageData = alphaMask.bits();
817 const int destBpl = bitmap.bytesPerLine();
818 uchar *bitmapData = bitmap.bits();
819
820 for (int yi = 0; yi < h; ++yi) {
821 const uchar *src = imageData + yi*srcBpl;
822 uchar *dst = bitmapData + yi*destBpl;
823 for (int xi = 0; xi < w; ++xi) {
824 const int byte = xi / 8;
825 const int bit = xi % 8;
826 if (bit == 0)
827 dst[byte] = 0;
828 if (src[xi])
829 dst[byte] |= 128 >> bit;
830 }
831 }
832 }
833 const uchar *bitmap_data = bitmap.constBits();
834 QFixedPoint offset = glyphs.offsets[i];
835 advanceX += offset.x;
836 advanceY += offset.y;
837 qt_addBitmapToPath(x0: (advanceX + metrics.x).toReal(), y0: (advanceY + metrics.y).toReal(), image_data: bitmap_data, bpl: bitmap.bytesPerLine(), w, h, path);
838 advanceX += glyphs.advances[i];
839 }
840}
841
842void QFontEngine::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nGlyphs,
843 QPainterPath *path, QTextItem::RenderFlags flags)
844{
845 qreal x = positions[0].x.toReal();
846 qreal y = positions[0].y.toReal();
847 QVarLengthGlyphLayoutArray g(nGlyphs);
848
849 for (int i = 0; i < nGlyphs - 1; ++i) {
850 g.glyphs[i] = glyphs[i];
851 g.advances[i] = positions[i + 1].x - positions[i].x;
852 }
853 g.glyphs[nGlyphs - 1] = glyphs[nGlyphs - 1];
854 g.advances[nGlyphs - 1] = QFixed::fromReal(r: maxCharWidth());
855
856 addBitmapFontToPath(x, y, glyphs: g, path, flags);
857}
858
859QImage QFontEngine::alphaMapForGlyph(glyph_t glyph, QFixed /*subPixelPosition*/)
860{
861 // For font engines don't support subpixel positioning
862 return alphaMapForGlyph(glyph);
863}
864
865QImage QFontEngine::alphaMapForGlyph(glyph_t glyph, const QTransform &t)
866{
867 QImage i = alphaMapForGlyph(glyph);
868 if (t.type() > QTransform::TxTranslate)
869 i = i.transformed(matrix: t).convertToFormat(f: QImage::Format_Alpha8);
870 Q_ASSERT(i.depth() <= 8); // To verify that transformed didn't change the format...
871
872 return i;
873}
874
875QImage QFontEngine::alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition, const QTransform &t)
876{
877 if (! supportsSubPixelPositions())
878 return alphaMapForGlyph(glyph, t);
879
880 QImage i = alphaMapForGlyph(glyph, subPixelPosition);
881 if (t.type() > QTransform::TxTranslate)
882 i = i.transformed(matrix: t).convertToFormat(f: QImage::Format_Alpha8);
883 Q_ASSERT(i.depth() <= 8); // To verify that transformed didn't change the format...
884
885 return i;
886}
887
888QImage QFontEngine::alphaRGBMapForGlyph(glyph_t glyph, QFixed /*subPixelPosition*/, const QTransform &t)
889{
890 const QImage alphaMask = alphaMapForGlyph(glyph, t);
891 QImage rgbMask(alphaMask.width(), alphaMask.height(), QImage::Format_RGB32);
892
893 for (int y=0; y<alphaMask.height(); ++y) {
894 uint *dst = (uint *) rgbMask.scanLine(y);
895 const uchar *src = alphaMask.constScanLine(y);
896 for (int x=0; x<alphaMask.width(); ++x) {
897 int val = src[x];
898 dst[x] = qRgb(r: val, g: val, b: val);
899 }
900 }
901
902 return rgbMask;
903}
904
905QImage QFontEngine::bitmapForGlyph(glyph_t, QFixed subPixelPosition, const QTransform&, const QColor &)
906{
907 Q_UNUSED(subPixelPosition);
908
909 return QImage();
910}
911
912QFixed QFontEngine::subPixelPositionForX(QFixed x) const
913{
914 if (m_subPixelPositionCount <= 1 || !supportsSubPixelPositions())
915 return QFixed();
916
917 QFixed subPixelPosition;
918 if (x != 0) {
919 subPixelPosition = x - x.floor();
920 QFixed fraction = (subPixelPosition / QFixed::fromReal(r: 1.0 / m_subPixelPositionCount)).floor();
921
922 // Compensate for precision loss in fixed point to make sure we are always drawing at a subpixel position over
923 // the lower boundary for the selected rasterization by adding 1/64.
924 subPixelPosition = fraction / QFixed(m_subPixelPositionCount) + QFixed::fromReal(r: 0.015625);
925 }
926 return subPixelPosition;
927}
928
929QFontEngine::Glyph *QFontEngine::glyphData(glyph_t, QFixed,
930 QFontEngine::GlyphFormat, const QTransform &)
931{
932 return nullptr;
933}
934
935QImage QFontEngine::alphaMapForGlyph(glyph_t glyph)
936{
937 glyph_metrics_t gm = boundingBox(glyph);
938 int glyph_x = qFloor(v: gm.x.toReal());
939 int glyph_y = qFloor(v: gm.y.toReal());
940 int glyph_width = qCeil(v: (gm.x + gm.width).toReal()) - glyph_x;
941 int glyph_height = qCeil(v: (gm.y + gm.height).toReal()) - glyph_y;
942
943 if (glyph_width <= 0 || glyph_height <= 0)
944 return QImage();
945 QFixedPoint pt;
946 pt.x = -glyph_x;
947 pt.y = -glyph_y; // the baseline
948 QPainterPath path;
949 path.setFillRule(Qt::WindingFill);
950 QImage im(glyph_width, glyph_height, QImage::Format_ARGB32_Premultiplied);
951 im.fill(color: Qt::transparent);
952 QPainter p(&im);
953 p.setRenderHint(hint: QPainter::Antialiasing);
954 addGlyphsToPath(glyphs: &glyph, positions: &pt, nGlyphs: 1, path: &path, flags: { });
955 p.setPen(Qt::NoPen);
956 p.setBrush(Qt::black);
957 p.drawPath(path);
958 p.end();
959
960 QImage alphaMap(im.width(), im.height(), QImage::Format_Alpha8);
961
962 for (int y=0; y<im.height(); ++y) {
963 uchar *dst = (uchar *) alphaMap.scanLine(y);
964 const uint *src = reinterpret_cast<const uint *>(im.constScanLine(y));
965 for (int x=0; x<im.width(); ++x)
966 dst[x] = qAlpha(rgb: src[x]);
967 }
968
969 return alphaMap;
970}
971
972void QFontEngine::removeGlyphFromCache(glyph_t)
973{
974}
975
976QFontEngine::Properties QFontEngine::properties() const
977{
978 Properties p;
979 p.postscriptName
980 = QFontEngine::convertToPostscriptFontFamilyName(fontFamily: fontDef.family.toUtf8())
981 + '-'
982 + QByteArray::number(fontDef.style)
983 + '-'
984 + QByteArray::number(fontDef.weight);
985 p.ascent = ascent();
986 p.descent = descent();
987 p.leading = leading();
988 p.emSquare = p.ascent;
989 p.boundingBox = QRectF(0, -p.ascent.toReal(), maxCharWidth(), (p.ascent + p.descent).toReal());
990 p.italicAngle = 0;
991 p.capHeight = p.ascent;
992 p.lineWidth = lineThickness();
993 return p;
994}
995
996void QFontEngine::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics)
997{
998 *metrics = boundingBox(glyph);
999 QFixedPoint p;
1000 p.x = 0;
1001 p.y = 0;
1002 addGlyphsToPath(glyphs: &glyph, positions: &p, nGlyphs: 1, path, flags: QFlag(0));
1003}
1004
1005/*!
1006 Returns \c true if the font table idetified by \a tag exists in the font;
1007 returns \c false otherwise.
1008
1009 If \a buffer is \nullptr, stores the size of the buffer required for the font table data,
1010 in bytes, in \a length. If \a buffer is not \nullptr and the capacity
1011 of the buffer, passed in \a length, is sufficient to store the font table data,
1012 also copies the font table data to \a buffer.
1013
1014 Note: returning \c false when the font table exists could lead to an undefined behavior.
1015*/
1016bool QFontEngine::getSfntTableData(uint tag, uchar *buffer, uint *length) const
1017{
1018 Q_UNUSED(tag)
1019 Q_UNUSED(buffer)
1020 Q_UNUSED(length)
1021 return false;
1022}
1023
1024QByteArray QFontEngine::getSfntTable(uint tag) const
1025{
1026 QByteArray table;
1027 uint len = 0;
1028 if (!getSfntTableData(tag, buffer: nullptr, length: &len))
1029 return table;
1030 table.resize(size: len);
1031 if (!getSfntTableData(tag, buffer: reinterpret_cast<uchar *>(table.data()), length: &len))
1032 return QByteArray();
1033 return table;
1034}
1035
1036void QFontEngine::clearGlyphCache(const void *context)
1037{
1038 m_glyphCaches.remove(akey: context);
1039}
1040
1041void QFontEngine::setGlyphCache(const void *context, QFontEngineGlyphCache *cache)
1042{
1043 Q_ASSERT(cache);
1044
1045 GlyphCaches &caches = m_glyphCaches[context];
1046 for (auto & e : caches) {
1047 if (cache == e.cache.data())
1048 return;
1049 }
1050
1051 // Limit the glyph caches to 4 per context. This covers all 90 degree rotations,
1052 // and limits memory use when there is continuous or random rotation
1053 if (caches.size() == 4)
1054 caches.pop_back();
1055
1056 GlyphCacheEntry entry;
1057 entry.cache = cache;
1058 caches.push_front(x: entry);
1059
1060}
1061
1062QFontEngineGlyphCache *QFontEngine::glyphCache(const void *context,
1063 GlyphFormat format,
1064 const QTransform &transform,
1065 const QColor &color) const
1066{
1067 const QHash<const void*, GlyphCaches>::const_iterator caches = m_glyphCaches.constFind(akey: context);
1068 if (caches == m_glyphCaches.cend())
1069 return nullptr;
1070
1071 for (auto &e : *caches) {
1072 QFontEngineGlyphCache *cache = e.cache.data();
1073 if (format == cache->glyphFormat()
1074 && (format != Format_ARGB || color == cache->color())
1075 && qtransform_equals_no_translate(a: cache->m_transform, b: transform)) {
1076 return cache;
1077 }
1078 }
1079
1080 return nullptr;
1081}
1082
1083static inline QFixed kerning(int left, int right, const QFontEngine::KernPair *pairs, int numPairs)
1084{
1085 uint left_right = (left << 16) + right;
1086
1087 left = 0, right = numPairs - 1;
1088 while (left <= right) {
1089 int middle = left + ( ( right - left ) >> 1 );
1090
1091 if(pairs[middle].left_right == left_right)
1092 return pairs[middle].adjust;
1093
1094 if (pairs[middle].left_right < left_right)
1095 left = middle + 1;
1096 else
1097 right = middle - 1;
1098 }
1099 return 0;
1100}
1101
1102void QFontEngine::doKerning(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const
1103{
1104 int numPairs = kerning_pairs.size();
1105 if(!numPairs)
1106 return;
1107
1108 const KernPair *pairs = kerning_pairs.constData();
1109
1110 if (flags & DesignMetrics) {
1111 for(int i = 0; i < glyphs->numGlyphs - 1; ++i)
1112 glyphs->advances[i] += kerning(left: glyphs->glyphs[i], right: glyphs->glyphs[i+1] , pairs, numPairs);
1113 } else {
1114 for(int i = 0; i < glyphs->numGlyphs - 1; ++i)
1115 glyphs->advances[i] += qRound(f: kerning(left: glyphs->glyphs[i], right: glyphs->glyphs[i+1] , pairs, numPairs));
1116 }
1117}
1118
1119void QFontEngine::loadKerningPairs(QFixed scalingFactor)
1120{
1121 kerning_pairs.clear();
1122
1123 QByteArray tab = getSfntTable(MAKE_TAG('k', 'e', 'r', 'n'));
1124 if (tab.isEmpty())
1125 return;
1126
1127 const uchar *table = reinterpret_cast<const uchar *>(tab.constData());
1128 const uchar *end = table + tab.size();
1129
1130 quint16 version;
1131 if (!qSafeFromBigEndian(source: table, end, output: &version))
1132 return;
1133
1134 if (version != 0) {
1135// qDebug("wrong version");
1136 return;
1137 }
1138
1139 quint16 numTables;
1140 if (!qSafeFromBigEndian(source: table + 2, end, output: &numTables))
1141 return;
1142
1143 {
1144 int offset = 4;
1145 for(int i = 0; i < numTables; ++i) {
1146 const uchar *header = table + offset;
1147
1148 quint16 version;
1149 if (!qSafeFromBigEndian(source: header, end, output: &version))
1150 goto end;
1151
1152 quint16 length;
1153 if (!qSafeFromBigEndian(source: header + 2, end, output: &length))
1154 goto end;
1155
1156 quint16 coverage;
1157 if (!qSafeFromBigEndian(source: header + 4, end, output: &coverage))
1158 goto end;
1159
1160// qDebug("subtable: version=%d, coverage=%x",version, coverage);
1161 if(version == 0 && coverage == 0x0001) {
1162 if (offset + length > tab.size()) {
1163// qDebug("length ouf ot bounds");
1164 goto end;
1165 }
1166 const uchar *data = table + offset + 6;
1167
1168 quint16 nPairs;
1169 if (!qSafeFromBigEndian(source: data, end, output: &nPairs))
1170 goto end;
1171
1172 if(nPairs * 6 + 8 > length - 6) {
1173// qDebug("corrupt table!");
1174 // corrupt table
1175 goto end;
1176 }
1177
1178 int off = 8;
1179 for(int i = 0; i < nPairs; ++i) {
1180 QFontEngine::KernPair p;
1181
1182 quint16 tmp;
1183 if (!qSafeFromBigEndian(source: data + off, end, output: &tmp))
1184 goto end;
1185
1186 p.left_right = uint(tmp) << 16;
1187 if (!qSafeFromBigEndian(source: data + off + 2, end, output: &tmp))
1188 goto end;
1189
1190 p.left_right |= tmp;
1191
1192 if (!qSafeFromBigEndian(source: data + off + 4, end, output: &tmp))
1193 goto end;
1194
1195 p.adjust = QFixed(int(short(tmp))) / scalingFactor;
1196 kerning_pairs.append(t: p);
1197 off += 6;
1198 }
1199 }
1200 offset += length;
1201 }
1202 }
1203end:
1204 std::sort(first: kerning_pairs.begin(), last: kerning_pairs.end());
1205// for (int i = 0; i < kerning_pairs.count(); ++i)
1206// qDebug() << 'i' << i << "left_right" << Qt::hex << kerning_pairs.at(i).left_right;
1207}
1208
1209
1210int QFontEngine::glyphCount() const
1211{
1212 QByteArray maxpTable = getSfntTable(MAKE_TAG('m', 'a', 'x', 'p'));
1213 if (maxpTable.size() < 6)
1214 return 0;
1215
1216 const uchar *source = reinterpret_cast<const uchar *>(maxpTable.constData() + 4);
1217 const uchar *end = source + maxpTable.size();
1218
1219 quint16 count = 0;
1220 qSafeFromBigEndian(source, end, output: &count);
1221 return count;
1222}
1223
1224Qt::HANDLE QFontEngine::handle() const
1225{
1226 return nullptr;
1227}
1228
1229const uchar *QFontEngine::getCMap(const uchar *table, uint tableSize, bool *isSymbolFont, int *cmapSize)
1230{
1231 const uchar *header = table;
1232 const uchar *endPtr = table + tableSize;
1233
1234 // version check
1235 quint16 version;
1236 if (!qSafeFromBigEndian(source: header, end: endPtr, output: &version) || version != 0)
1237 return nullptr;
1238
1239 quint16 numTables;
1240 if (!qSafeFromBigEndian(source: header + 2, end: endPtr, output: &numTables))
1241 return nullptr;
1242
1243 const uchar *maps = table + 4;
1244
1245 enum {
1246 Invalid,
1247 AppleRoman,
1248 Symbol,
1249 Unicode11,
1250 Unicode,
1251 MicrosoftUnicode,
1252 MicrosoftUnicodeExtended
1253 };
1254
1255 int symbolTable = -1;
1256 int tableToUse = -1;
1257 int score = Invalid;
1258 for (int n = 0; n < numTables; ++n) {
1259 quint16 platformId;
1260 if (!qSafeFromBigEndian(source: maps + 8 * n, end: endPtr, output: &platformId))
1261 return nullptr;
1262
1263 quint16 platformSpecificId = 0;
1264 if (!qSafeFromBigEndian(source: maps + 8 * n + 2, end: endPtr, output: &platformSpecificId))
1265 return nullptr;
1266
1267 switch (platformId) {
1268 case 0: // Unicode
1269 if (score < Unicode &&
1270 (platformSpecificId == 0 ||
1271 platformSpecificId == 2 ||
1272 platformSpecificId == 3)) {
1273 tableToUse = n;
1274 score = Unicode;
1275 } else if (score < Unicode11 && platformSpecificId == 1) {
1276 tableToUse = n;
1277 score = Unicode11;
1278 }
1279 break;
1280 case 1: // Apple
1281 if (score < AppleRoman && platformSpecificId == 0) { // Apple Roman
1282 tableToUse = n;
1283 score = AppleRoman;
1284 }
1285 break;
1286 case 3: // Microsoft
1287 switch (platformSpecificId) {
1288 case 0:
1289 symbolTable = n;
1290 if (score < Symbol) {
1291 tableToUse = n;
1292 score = Symbol;
1293 }
1294 break;
1295 case 1:
1296 if (score < MicrosoftUnicode) {
1297 tableToUse = n;
1298 score = MicrosoftUnicode;
1299 }
1300 break;
1301 case 0xa:
1302 if (score < MicrosoftUnicodeExtended) {
1303 tableToUse = n;
1304 score = MicrosoftUnicodeExtended;
1305 }
1306 break;
1307 default:
1308 break;
1309 }
1310 default:
1311 break;
1312 }
1313 }
1314 if(tableToUse < 0)
1315 return nullptr;
1316
1317resolveTable:
1318 *isSymbolFont = (symbolTable > -1);
1319
1320 quint32 unicode_table = 0;
1321 if (!qSafeFromBigEndian(source: maps + 8 * tableToUse + 4, end: endPtr, output: &unicode_table))
1322 return nullptr;
1323
1324 if (!unicode_table)
1325 return nullptr;
1326
1327 // get the header of the unicode table
1328 header = table + unicode_table;
1329
1330 quint16 format;
1331 if (!qSafeFromBigEndian(source: header, end: endPtr, output: &format))
1332 return nullptr;
1333
1334 quint32 length;
1335 if (format < 8) {
1336 quint16 tmp;
1337 if (!qSafeFromBigEndian(source: header + 2, end: endPtr, output: &tmp))
1338 return nullptr;
1339 length = tmp;
1340 } else {
1341 if (!qSafeFromBigEndian(source: header + 4, end: endPtr, output: &length))
1342 return nullptr;
1343 }
1344
1345 if (table + unicode_table + length > endPtr)
1346 return nullptr;
1347 *cmapSize = length;
1348
1349 // To support symbol fonts that contain a unicode table for the symbol area
1350 // we check the cmap tables and fall back to symbol font unless that would
1351 // involve losing information from the unicode table
1352 if (symbolTable > -1 && ((score == Unicode) || (score == Unicode11))) {
1353 const uchar *selectedTable = table + unicode_table;
1354
1355 // Check that none of the latin1 range are in the unicode table
1356 bool unicodeTableHasLatin1 = false;
1357 for (int uc=0x00; uc<0x100; ++uc) {
1358 if (getTrueTypeGlyphIndex(cmap: selectedTable, cmapSize: length, unicode: uc) != 0) {
1359 unicodeTableHasLatin1 = true;
1360 break;
1361 }
1362 }
1363
1364 // Check that at least one symbol char is in the unicode table
1365 bool unicodeTableHasSymbols = false;
1366 if (!unicodeTableHasLatin1) {
1367 for (int uc=0xf000; uc<0xf100; ++uc) {
1368 if (getTrueTypeGlyphIndex(cmap: selectedTable, cmapSize: length, unicode: uc) != 0) {
1369 unicodeTableHasSymbols = true;
1370 break;
1371 }
1372 }
1373 }
1374
1375 // Fall back to symbol table
1376 if (!unicodeTableHasLatin1 && unicodeTableHasSymbols) {
1377 tableToUse = symbolTable;
1378 score = Symbol;
1379 goto resolveTable;
1380 }
1381 }
1382
1383 return table + unicode_table;
1384}
1385
1386quint32 QFontEngine::getTrueTypeGlyphIndex(const uchar *cmap, int cmapSize, uint unicode)
1387{
1388 const uchar *end = cmap + cmapSize;
1389 quint16 format;
1390 if (!qSafeFromBigEndian(source: cmap, end, output: &format))
1391 return 0;
1392
1393 if (format == 0) {
1394 const uchar *ptr = cmap + 6 + unicode;
1395 if (unicode < 256 && ptr < end)
1396 return quint32(*ptr);
1397 } else if (format == 4) {
1398 /* some fonts come with invalid cmap tables, where the last segment
1399 specified end = start = rangeoffset = 0xffff, delta = 0x0001
1400 Since 0xffff is never a valid Unicode char anyway, we just get rid of the issue
1401 by returning 0 for 0xffff
1402 */
1403 if(unicode >= 0xffff)
1404 return 0;
1405
1406 quint16 segCountX2;
1407 if (!qSafeFromBigEndian(source: cmap + 6, end, output: &segCountX2))
1408 return 0;
1409
1410 const unsigned char *ends = cmap + 14;
1411
1412 int i = 0;
1413 for (; i < segCountX2/2; ++i) {
1414 quint16 codePoint;
1415 if (!qSafeFromBigEndian(source: ends + 2 * i, end, output: &codePoint))
1416 return 0;
1417 if (codePoint >= unicode)
1418 break;
1419 }
1420
1421 const unsigned char *idx = ends + segCountX2 + 2 + 2*i;
1422
1423 quint16 startIndex;
1424 if (!qSafeFromBigEndian(source: idx, end, output: &startIndex))
1425 return 0;
1426 if (startIndex > unicode)
1427 return 0;
1428
1429 idx += segCountX2;
1430
1431 quint16 tmp;
1432 if (!qSafeFromBigEndian(source: idx, end, output: &tmp))
1433 return 0;
1434 qint16 idDelta = qint16(tmp);
1435
1436 idx += segCountX2;
1437
1438 quint16 idRangeoffset_t;
1439 if (!qSafeFromBigEndian(source: idx, end, output: &idRangeoffset_t))
1440 return 0;
1441
1442 quint16 glyphIndex;
1443 if (idRangeoffset_t) {
1444 quint16 id;
1445 if (!qSafeFromBigEndian(source: idRangeoffset_t + 2 * (unicode - startIndex) + idx, end, output: &id))
1446 return 0;
1447
1448 if (id)
1449 glyphIndex = (idDelta + id) % 0x10000;
1450 else
1451 glyphIndex = 0;
1452 } else {
1453 glyphIndex = (idDelta + unicode) % 0x10000;
1454 }
1455 return glyphIndex;
1456 } else if (format == 6) {
1457 quint16 tableSize;
1458 if (!qSafeFromBigEndian(source: cmap + 2, end, output: &tableSize))
1459 return 0;
1460
1461 quint16 firstCode6;
1462 if (!qSafeFromBigEndian(source: cmap + 6, end, output: &firstCode6))
1463 return 0;
1464 if (unicode < firstCode6)
1465 return 0;
1466
1467 quint16 entryCount6;
1468 if (!qSafeFromBigEndian(source: cmap + 8, end, output: &entryCount6))
1469 return 0;
1470 if (entryCount6 * 2 + 10 > tableSize)
1471 return 0;
1472
1473 quint16 sentinel6 = firstCode6 + entryCount6;
1474 if (unicode >= sentinel6)
1475 return 0;
1476
1477 quint16 entryIndex6 = unicode - firstCode6;
1478
1479 quint16 index = 0;
1480 qSafeFromBigEndian(source: cmap + 10 + (entryIndex6 * 2), end, output: &index);
1481 return index;
1482 } else if (format == 12) {
1483 quint32 nGroups;
1484 if (!qSafeFromBigEndian(source: cmap + 12, end, output: &nGroups))
1485 return 0;
1486
1487 cmap += 16; // move to start of groups
1488
1489 int left = 0, right = nGroups - 1;
1490 while (left <= right) {
1491 int middle = left + ( ( right - left ) >> 1 );
1492
1493 quint32 startCharCode;
1494 if (!qSafeFromBigEndian(source: cmap + 12 * middle, end, output: &startCharCode))
1495 return 0;
1496
1497 if(unicode < startCharCode)
1498 right = middle - 1;
1499 else {
1500 quint32 endCharCode;
1501 if (!qSafeFromBigEndian(source: cmap + 12 * middle + 4, end, output: &endCharCode))
1502 return 0;
1503
1504 if (unicode <= endCharCode) {
1505 quint32 index;
1506 if (!qSafeFromBigEndian(source: cmap + 12 * middle + 8, end, output: &index))
1507 return 0;
1508
1509 return index + unicode - startCharCode;
1510 }
1511 left = middle + 1;
1512 }
1513 }
1514 } else {
1515 qDebug(msg: "cmap table of format %d not implemented", format);
1516 }
1517
1518 return 0;
1519}
1520
1521QByteArray QFontEngine::convertToPostscriptFontFamilyName(const QByteArray &family)
1522{
1523 QByteArray f = family;
1524 f.replace(before: ' ', c: "");
1525 f.replace(before: '(', c: "");
1526 f.replace(before: ')', c: "");
1527 f.replace(before: '<', c: "");
1528 f.replace(before: '>', c: "");
1529 f.replace(before: '[', c: "");
1530 f.replace(before: ']', c: "");
1531 f.replace(before: '{', c: "");
1532 f.replace(before: '}', c: "");
1533 f.replace(before: '/', c: "");
1534 f.replace(before: '%', c: "");
1535 return f;
1536}
1537
1538// Allow font engines (e.g. Windows) that can not reliably create
1539// outline paths for distance-field rendering to switch the scene
1540// graph over to native text rendering.
1541bool QFontEngine::hasUnreliableGlyphOutline() const
1542{
1543 // Color glyphs (Emoji) are generally not suited for outlining
1544 return glyphFormat == QFontEngine::Format_ARGB;
1545}
1546
1547QFixed QFontEngine::lastRightBearing(const QGlyphLayout &glyphs, bool round)
1548{
1549 if (glyphs.numGlyphs >= 1) {
1550 glyph_t glyph = glyphs.glyphs[glyphs.numGlyphs - 1];
1551 glyph_metrics_t gi = boundingBox(glyph);
1552 if (gi.isValid())
1553 return round ? qRound(f: gi.rightBearing()) : gi.rightBearing();
1554 }
1555 return 0;
1556}
1557
1558
1559QFontEngine::GlyphCacheEntry::GlyphCacheEntry()
1560{
1561}
1562
1563QFontEngine::GlyphCacheEntry::GlyphCacheEntry(const GlyphCacheEntry &o)
1564 : cache(o.cache)
1565{
1566}
1567
1568QFontEngine::GlyphCacheEntry::~GlyphCacheEntry()
1569{
1570}
1571
1572QFontEngine::GlyphCacheEntry &QFontEngine::GlyphCacheEntry::operator=(const GlyphCacheEntry &o)
1573{
1574 cache = o.cache;
1575 return *this;
1576}
1577
1578// ------------------------------------------------------------------
1579// The box font engine
1580// ------------------------------------------------------------------
1581
1582QFontEngineBox::QFontEngineBox(int size)
1583 : QFontEngine(Box),
1584 _size(size)
1585{
1586 cache_cost = sizeof(QFontEngineBox);
1587}
1588
1589QFontEngineBox::QFontEngineBox(Type type, int size)
1590 : QFontEngine(type),
1591 _size(size)
1592{
1593 cache_cost = sizeof(QFontEngineBox);
1594}
1595
1596QFontEngineBox::~QFontEngineBox()
1597{
1598}
1599
1600glyph_t QFontEngineBox::glyphIndex(uint ucs4) const
1601{
1602 Q_UNUSED(ucs4)
1603 return 1;
1604}
1605
1606bool QFontEngineBox::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QFontEngine::ShaperFlags flags) const
1607{
1608 Q_ASSERT(glyphs->numGlyphs >= *nglyphs);
1609 if (*nglyphs < len) {
1610 *nglyphs = len;
1611 return false;
1612 }
1613
1614 int ucs4Length = 0;
1615 QStringIterator it(str, str + len);
1616 while (it.hasNext()) {
1617 it.advance();
1618 glyphs->glyphs[ucs4Length++] = 1;
1619 }
1620
1621 *nglyphs = ucs4Length;
1622 glyphs->numGlyphs = ucs4Length;
1623
1624 if (!(flags & GlyphIndicesOnly))
1625 recalcAdvances(glyphs, flags);
1626
1627 return true;
1628}
1629
1630void QFontEngineBox::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags) const
1631{
1632 for (int i = 0; i < glyphs->numGlyphs; i++)
1633 glyphs->advances[i] = _size;
1634}
1635
1636void QFontEngineBox::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags)
1637{
1638 if (!glyphs.numGlyphs)
1639 return;
1640
1641 QVarLengthArray<QFixedPoint> positions;
1642 QVarLengthArray<glyph_t> positioned_glyphs;
1643 QTransform matrix = QTransform::fromTranslate(dx: x, dy: y - _size);
1644 getGlyphPositions(glyphs, matrix, flags, glyphs_out&: positioned_glyphs, positions);
1645
1646 QSize s(_size - 3, _size - 3);
1647 for (int k = 0; k < positions.size(); k++)
1648 path->addRect(rect: QRectF(positions[k].toPointF(), s));
1649}
1650
1651glyph_metrics_t QFontEngineBox::boundingBox(const QGlyphLayout &glyphs)
1652{
1653 glyph_metrics_t overall;
1654 overall.width = _size*glyphs.numGlyphs;
1655 overall.height = _size;
1656 overall.xoff = overall.width;
1657 return overall;
1658}
1659
1660void QFontEngineBox::draw(QPaintEngine *p, qreal x, qreal y, const QTextItemInt &ti)
1661{
1662 if (!ti.glyphs.numGlyphs)
1663 return;
1664
1665 // any fixes here should probably also be done in QPaintEnginePrivate::drawBoxTextItem
1666 QSize s(_size - 3, _size - 3);
1667
1668 QVarLengthArray<QFixedPoint> positions;
1669 QVarLengthArray<glyph_t> glyphs;
1670 QTransform matrix = QTransform::fromTranslate(dx: x, dy: y - _size);
1671 ti.fontEngine->getGlyphPositions(glyphs: ti.glyphs, matrix, flags: ti.flags, glyphs_out&: glyphs, positions);
1672 if (glyphs.size() == 0)
1673 return;
1674
1675
1676 QPainter *painter = p->painter();
1677 painter->save();
1678 painter->setBrush(Qt::NoBrush);
1679 QPen pen = painter->pen();
1680 pen.setWidthF(lineThickness().toReal());
1681 painter->setPen(pen);
1682 for (int k = 0; k < positions.size(); k++)
1683 painter->drawRect(rect: QRectF(positions[k].toPointF(), s));
1684 painter->restore();
1685}
1686
1687glyph_metrics_t QFontEngineBox::boundingBox(glyph_t)
1688{
1689 return glyph_metrics_t(0, -_size, _size, _size, _size, 0);
1690}
1691
1692QFontEngine *QFontEngineBox::cloneWithSize(qreal pixelSize) const
1693{
1694 QFontEngineBox *fe = new QFontEngineBox(pixelSize);
1695 return fe;
1696}
1697
1698QFixed QFontEngineBox::ascent() const
1699{
1700 return _size;
1701}
1702
1703QFixed QFontEngineBox::capHeight() const
1704{
1705 return _size;
1706}
1707
1708QFixed QFontEngineBox::descent() const
1709{
1710 return 0;
1711}
1712
1713QFixed QFontEngineBox::leading() const
1714{
1715 QFixed l = _size * QFixed::fromReal(r: qreal(0.15));
1716 return l.ceil();
1717}
1718
1719qreal QFontEngineBox::maxCharWidth() const
1720{
1721 return _size;
1722}
1723
1724bool QFontEngineBox::canRender(const QChar *, int) const
1725{
1726 return true;
1727}
1728
1729QImage QFontEngineBox::alphaMapForGlyph(glyph_t)
1730{
1731 QImage image(_size, _size, QImage::Format_Alpha8);
1732 image.fill(pixel: 0);
1733
1734 // FIXME: use qpainter
1735 for (int i=2; i <= _size-3; ++i) {
1736 image.setPixel(x: i, y: 2, index_or_rgb: 255);
1737 image.setPixel(x: i, y: _size-3, index_or_rgb: 255);
1738 image.setPixel(x: 2, y: i, index_or_rgb: 255);
1739 image.setPixel(x: _size-3, y: i, index_or_rgb: 255);
1740 }
1741 return image;
1742}
1743
1744// ------------------------------------------------------------------
1745// Multi engine
1746// ------------------------------------------------------------------
1747
1748uchar QFontEngineMulti::highByte(glyph_t glyph)
1749{ return glyph >> 24; }
1750
1751// strip high byte from glyph
1752static inline glyph_t stripped(glyph_t glyph)
1753{ return glyph & 0x00ffffff; }
1754
1755QFontEngineMulti::QFontEngineMulti(QFontEngine *engine, int script, const QStringList &fallbackFamilies)
1756 : QFontEngine(Multi),
1757 m_fallbackFamilies(fallbackFamilies),
1758 m_script(script),
1759 m_fallbackFamiliesQueried(!m_fallbackFamilies.isEmpty())
1760{
1761 Q_ASSERT(engine && engine->type() != QFontEngine::Multi);
1762
1763 if (m_fallbackFamilies.isEmpty()) {
1764 // defer obtaining the fallback families until loadEngine(1)
1765 m_fallbackFamilies << QString();
1766 }
1767
1768 m_engines.resize(asize: m_fallbackFamilies.size() + 1);
1769
1770 engine->ref.ref();
1771 m_engines[0] = engine;
1772
1773 fontDef = engine->fontDef;
1774 cache_cost = engine->cache_cost;
1775}
1776
1777QFontEngineMulti::~QFontEngineMulti()
1778{
1779 for (int i = 0; i < m_engines.size(); ++i) {
1780 QFontEngine *fontEngine = m_engines.at(i);
1781 if (fontEngine && !fontEngine->ref.deref())
1782 delete fontEngine;
1783 }
1784}
1785
1786QStringList qt_fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script);
1787
1788void QFontEngineMulti::ensureFallbackFamiliesQueried()
1789{
1790 QFont::StyleHint styleHint = QFont::StyleHint(fontDef.styleHint);
1791 if (styleHint == QFont::AnyStyle && fontDef.fixedPitch)
1792 styleHint = QFont::TypeWriter;
1793
1794 setFallbackFamiliesList(qt_fallbacksForFamily(family: fontDef.family, style: QFont::Style(fontDef.style), styleHint, script: QChar::Script(m_script)));
1795}
1796
1797void QFontEngineMulti::setFallbackFamiliesList(const QStringList &fallbackFamilies)
1798{
1799 Q_ASSERT(!m_fallbackFamiliesQueried);
1800
1801 m_fallbackFamilies = fallbackFamilies;
1802 if (m_fallbackFamilies.isEmpty()) {
1803 // turns out we lied about having any fallback at all
1804 Q_ASSERT(m_engines.size() == 2); // see c-tor for details
1805 QFontEngine *engine = m_engines.at(i: 0);
1806 engine->ref.ref();
1807 m_engines[1] = engine;
1808 m_fallbackFamilies << fontDef.family;
1809 } else {
1810 m_engines.resize(asize: m_fallbackFamilies.size() + 1);
1811 }
1812
1813 m_fallbackFamiliesQueried = true;
1814}
1815
1816void QFontEngineMulti::ensureEngineAt(int at)
1817{
1818 if (!m_fallbackFamiliesQueried && at > 0)
1819 ensureFallbackFamiliesQueried();
1820 Q_ASSERT(at < m_engines.size());
1821 if (!m_engines.at(i: at)) {
1822 QFontEngine *engine = loadEngine(at);
1823 if (!engine)
1824 engine = new QFontEngineBox(fontDef.pixelSize);
1825 Q_ASSERT(engine && engine->type() != QFontEngine::Multi);
1826 engine->ref.ref();
1827 m_engines[at] = engine;
1828 }
1829}
1830
1831QFontEngine *QFontEngineMulti::loadEngine(int at)
1832{
1833 QFontDef request(fontDef);
1834 request.styleStrategy |= QFont::NoFontMerging;
1835 request.family = fallbackFamilyAt(at: at - 1);
1836 request.families = QStringList(request.family);
1837
1838 // At this point, the main script of the text has already been considered
1839 // when fetching the list of fallback families from the database, and the
1840 // info about the actual script of the characters may have been discarded,
1841 // so we do not check for writing system support, but instead just load
1842 // the family indiscriminately.
1843 if (QFontEngine *engine = QFontDatabase::findFont(request, script: QChar::Script_Common)) {
1844 engine->fontDef.weight = request.weight;
1845 if (request.style > QFont::StyleNormal)
1846 engine->fontDef.style = request.style;
1847 return engine;
1848 }
1849
1850 return nullptr;
1851}
1852
1853glyph_t QFontEngineMulti::glyphIndex(uint ucs4) const
1854{
1855 glyph_t glyph = engine(at: 0)->glyphIndex(ucs4);
1856 if (glyph == 0
1857 && ucs4 != QChar::LineSeparator
1858 && ucs4 != QChar::LineFeed
1859 && ucs4 != QChar::CarriageReturn
1860 && ucs4 != QChar::ParagraphSeparator) {
1861 if (!m_fallbackFamiliesQueried)
1862 const_cast<QFontEngineMulti *>(this)->ensureFallbackFamiliesQueried();
1863 for (int x = 1, n = qMin(a: m_engines.size(), b: 256); x < n; ++x) {
1864 QFontEngine *engine = m_engines.at(i: x);
1865 if (!engine) {
1866 if (!shouldLoadFontEngineForCharacter(at: x, ucs4))
1867 continue;
1868 const_cast<QFontEngineMulti *>(this)->ensureEngineAt(at: x);
1869 engine = m_engines.at(i: x);
1870 }
1871 Q_ASSERT(engine != nullptr);
1872 if (engine->type() == Box)
1873 continue;
1874
1875 glyph = engine->glyphIndex(ucs4);
1876 if (glyph != 0) {
1877 // set the high byte to indicate which engine the glyph came from
1878 glyph |= (x << 24);
1879 break;
1880 }
1881 }
1882 }
1883
1884 return glyph;
1885}
1886
1887bool QFontEngineMulti::stringToCMap(const QChar *str, int len,
1888 QGlyphLayout *glyphs, int *nglyphs,
1889 QFontEngine::ShaperFlags flags) const
1890{
1891 if (!engine(at: 0)->stringToCMap(str, len, glyphs, nglyphs, flags))
1892 return false;
1893
1894 int glyph_pos = 0;
1895 QStringIterator it(str, str + len);
1896
1897 int lastFallback = -1;
1898 while (it.hasNext()) {
1899 const uint ucs4 = it.peekNext();
1900
1901 // If we applied a fallback font to previous glyph, and the current is either
1902 // ZWJ or ZWNJ, we should also try applying the same fallback font to that, in order
1903 // to get the correct shaping rules applied.
1904 if (lastFallback >= 0 && (ucs4 == QChar(0x200d) || ucs4 == QChar(0x200c))) {
1905 QFontEngine *engine = m_engines.at(i: lastFallback);
1906 glyph_t glyph = engine->glyphIndex(ucs4);
1907 if (glyph != 0) {
1908 glyphs->glyphs[glyph_pos] = glyph;
1909 if (!(flags & GlyphIndicesOnly)) {
1910 QGlyphLayout g = glyphs->mid(position: glyph_pos, n: 1);
1911 engine->recalcAdvances(&g, flags);
1912 }
1913
1914 // set the high byte to indicate which engine the glyph came from
1915 glyphs->glyphs[glyph_pos] |= (lastFallback << 24);
1916 } else {
1917 lastFallback = -1;
1918 }
1919 } else {
1920 lastFallback = -1;
1921 }
1922
1923 if (glyphs->glyphs[glyph_pos] == 0
1924 && ucs4 != QChar::LineSeparator
1925 && ucs4 != QChar::LineFeed
1926 && ucs4 != QChar::CarriageReturn
1927 && ucs4 != QChar::ParagraphSeparator) {
1928 if (!m_fallbackFamiliesQueried)
1929 const_cast<QFontEngineMulti *>(this)->ensureFallbackFamiliesQueried();
1930 for (int x = 1, n = qMin(a: m_engines.size(), b: 256); x < n; ++x) {
1931 QFontEngine *engine = m_engines.at(i: x);
1932 if (!engine) {
1933 if (!shouldLoadFontEngineForCharacter(at: x, ucs4))
1934 continue;
1935 const_cast<QFontEngineMulti *>(this)->ensureEngineAt(at: x);
1936 engine = m_engines.at(i: x);
1937 if (!engine)
1938 continue;
1939 }
1940 Q_ASSERT(engine != nullptr);
1941 if (engine->type() == Box)
1942 continue;
1943
1944 glyph_t glyph = engine->glyphIndex(ucs4);
1945 if (glyph != 0) {
1946 glyphs->glyphs[glyph_pos] = glyph;
1947 if (!(flags & GlyphIndicesOnly)) {
1948 QGlyphLayout g = glyphs->mid(position: glyph_pos, n: 1);
1949 engine->recalcAdvances(&g, flags);
1950 }
1951
1952 lastFallback = x;
1953
1954 // set the high byte to indicate which engine the glyph came from
1955 glyphs->glyphs[glyph_pos] |= (x << 24);
1956 break;
1957 }
1958 }
1959 }
1960
1961 it.advance();
1962 ++glyph_pos;
1963 }
1964
1965 *nglyphs = glyph_pos;
1966 glyphs->numGlyphs = glyph_pos;
1967
1968 return true;
1969}
1970
1971bool QFontEngineMulti::shouldLoadFontEngineForCharacter(int at, uint ucs4) const
1972{
1973 Q_UNUSED(at);
1974 Q_UNUSED(ucs4);
1975 return true;
1976}
1977
1978glyph_metrics_t QFontEngineMulti::boundingBox(const QGlyphLayout &glyphs)
1979{
1980 if (glyphs.numGlyphs <= 0)
1981 return glyph_metrics_t();
1982
1983 glyph_metrics_t overall;
1984
1985 int which = highByte(glyph: glyphs.glyphs[0]);
1986 int start = 0;
1987 int end, i;
1988 for (end = 0; end < glyphs.numGlyphs; ++end) {
1989 const int e = highByte(glyph: glyphs.glyphs[end]);
1990 if (e == which)
1991 continue;
1992
1993 // set the high byte to zero
1994 for (i = start; i < end; ++i)
1995 glyphs.glyphs[i] = stripped(glyph: glyphs.glyphs[i]);
1996
1997 // merge the bounding box for this run
1998 const glyph_metrics_t gm = engine(at: which)->boundingBox(glyphs: glyphs.mid(position: start, n: end - start));
1999
2000 overall.x = qMin(a: overall.x, b: gm.x);
2001 overall.y = qMin(a: overall.y, b: gm.y);
2002 overall.width = overall.xoff + gm.width;
2003 overall.height = qMax(a: overall.height + overall.y, b: gm.height + gm.y) -
2004 qMin(a: overall.y, b: gm.y);
2005 overall.xoff += gm.xoff;
2006 overall.yoff += gm.yoff;
2007
2008 // reset the high byte for all glyphs
2009 const int hi = which << 24;
2010 for (i = start; i < end; ++i)
2011 glyphs.glyphs[i] = hi | glyphs.glyphs[i];
2012
2013 // change engine
2014 start = end;
2015 which = e;
2016 }
2017
2018 // set the high byte to zero
2019 for (i = start; i < end; ++i)
2020 glyphs.glyphs[i] = stripped(glyph: glyphs.glyphs[i]);
2021
2022 // merge the bounding box for this run
2023 const glyph_metrics_t gm = engine(at: which)->boundingBox(glyphs: glyphs.mid(position: start, n: end - start));
2024
2025 overall.x = qMin(a: overall.x, b: gm.x);
2026 overall.y = qMin(a: overall.y, b: gm.y);
2027 overall.width = overall.xoff + gm.width;
2028 overall.height = qMax(a: overall.height + overall.y, b: gm.height + gm.y) -
2029 qMin(a: overall.y, b: gm.y);
2030 overall.xoff += gm.xoff;
2031 overall.yoff += gm.yoff;
2032
2033 // reset the high byte for all glyphs
2034 const int hi = which << 24;
2035 for (i = start; i < end; ++i)
2036 glyphs.glyphs[i] = hi | glyphs.glyphs[i];
2037
2038 return overall;
2039}
2040
2041void QFontEngineMulti::getGlyphBearings(glyph_t glyph, qreal *leftBearing, qreal *rightBearing)
2042{
2043 int which = highByte(glyph);
2044 ensureEngineAt(at: which);
2045 engine(at: which)->getGlyphBearings(glyph: stripped(glyph), leftBearing, rightBearing);
2046}
2047
2048void QFontEngineMulti::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs,
2049 QPainterPath *path, QTextItem::RenderFlags flags)
2050{
2051 if (glyphs.numGlyphs <= 0)
2052 return;
2053
2054 int which = highByte(glyph: glyphs.glyphs[0]);
2055 int start = 0;
2056 int end, i;
2057 if (flags & QTextItem::RightToLeft) {
2058 for (int gl = 0; gl < glyphs.numGlyphs; gl++)
2059 x += glyphs.advances[gl].toReal();
2060 }
2061 for (end = 0; end < glyphs.numGlyphs; ++end) {
2062 const int e = highByte(glyph: glyphs.glyphs[end]);
2063 if (e == which)
2064 continue;
2065
2066 if (flags & QTextItem::RightToLeft) {
2067 for (i = start; i < end; ++i)
2068 x -= glyphs.advances[i].toReal();
2069 }
2070
2071 // set the high byte to zero
2072 for (i = start; i < end; ++i)
2073 glyphs.glyphs[i] = stripped(glyph: glyphs.glyphs[i]);
2074 engine(at: which)->addOutlineToPath(x, y, glyphs: glyphs.mid(position: start, n: end - start), path, flags);
2075 // reset the high byte for all glyphs and update x and y
2076 const int hi = which << 24;
2077 for (i = start; i < end; ++i)
2078 glyphs.glyphs[i] = hi | glyphs.glyphs[i];
2079
2080 if (!(flags & QTextItem::RightToLeft)) {
2081 for (i = start; i < end; ++i)
2082 x += glyphs.advances[i].toReal();
2083 }
2084
2085 // change engine
2086 start = end;
2087 which = e;
2088 }
2089
2090 if (flags & QTextItem::RightToLeft) {
2091 for (i = start; i < end; ++i)
2092 x -= glyphs.advances[i].toReal();
2093 }
2094
2095 // set the high byte to zero
2096 for (i = start; i < end; ++i)
2097 glyphs.glyphs[i] = stripped(glyph: glyphs.glyphs[i]);
2098
2099 engine(at: which)->addOutlineToPath(x, y, glyphs: glyphs.mid(position: start, n: end - start), path, flags);
2100
2101 // reset the high byte for all glyphs
2102 const int hi = which << 24;
2103 for (i = start; i < end; ++i)
2104 glyphs.glyphs[i] = hi | glyphs.glyphs[i];
2105}
2106
2107void QFontEngineMulti::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const
2108{
2109 if (glyphs->numGlyphs <= 0)
2110 return;
2111
2112 int which = highByte(glyph: glyphs->glyphs[0]);
2113 int start = 0;
2114 int end, i;
2115 for (end = 0; end < glyphs->numGlyphs; ++end) {
2116 const int e = highByte(glyph: glyphs->glyphs[end]);
2117 if (e == which)
2118 continue;
2119
2120 // set the high byte to zero
2121 for (i = start; i < end; ++i)
2122 glyphs->glyphs[i] = stripped(glyph: glyphs->glyphs[i]);
2123
2124 QGlyphLayout offs = glyphs->mid(position: start, n: end - start);
2125 engine(at: which)->recalcAdvances(&offs, flags);
2126
2127 // reset the high byte for all glyphs and update x and y
2128 const int hi = which << 24;
2129 for (i = start; i < end; ++i)
2130 glyphs->glyphs[i] = hi | glyphs->glyphs[i];
2131
2132 // change engine
2133 start = end;
2134 which = e;
2135 }
2136
2137 // set the high byte to zero
2138 for (i = start; i < end; ++i)
2139 glyphs->glyphs[i] = stripped(glyph: glyphs->glyphs[i]);
2140
2141 QGlyphLayout offs = glyphs->mid(position: start, n: end - start);
2142 engine(at: which)->recalcAdvances(&offs, flags);
2143
2144 // reset the high byte for all glyphs
2145 const int hi = which << 24;
2146 for (i = start; i < end; ++i)
2147 glyphs->glyphs[i] = hi | glyphs->glyphs[i];
2148}
2149
2150void QFontEngineMulti::doKerning(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const
2151{
2152 if (glyphs->numGlyphs <= 0)
2153 return;
2154
2155 int which = highByte(glyph: glyphs->glyphs[0]);
2156 int start = 0;
2157 int end, i;
2158 for (end = 0; end < glyphs->numGlyphs; ++end) {
2159 const int e = highByte(glyph: glyphs->glyphs[end]);
2160 if (e == which)
2161 continue;
2162
2163 // set the high byte to zero
2164 for (i = start; i < end; ++i)
2165 glyphs->glyphs[i] = stripped(glyph: glyphs->glyphs[i]);
2166
2167 QGlyphLayout offs = glyphs->mid(position: start, n: end - start);
2168 engine(at: which)->doKerning(glyphs: &offs, flags);
2169
2170 // reset the high byte for all glyphs and update x and y
2171 const int hi = which << 24;
2172 for (i = start; i < end; ++i)
2173 glyphs->glyphs[i] = hi | glyphs->glyphs[i];
2174
2175 // change engine
2176 start = end;
2177 which = e;
2178 }
2179
2180 // set the high byte to zero
2181 for (i = start; i < end; ++i)
2182 glyphs->glyphs[i] = stripped(glyph: glyphs->glyphs[i]);
2183
2184 QGlyphLayout offs = glyphs->mid(position: start, n: end - start);
2185 engine(at: which)->doKerning(glyphs: &offs, flags);
2186
2187 // reset the high byte for all glyphs
2188 const int hi = which << 24;
2189 for (i = start; i < end; ++i)
2190 glyphs->glyphs[i] = hi | glyphs->glyphs[i];
2191}
2192
2193glyph_metrics_t QFontEngineMulti::boundingBox(glyph_t glyph)
2194{
2195 const int which = highByte(glyph);
2196 return engine(at: which)->boundingBox(glyph: stripped(glyph));
2197}
2198
2199QFixed QFontEngineMulti::ascent() const
2200{ return engine(at: 0)->ascent(); }
2201
2202QFixed QFontEngineMulti::capHeight() const
2203{ return engine(at: 0)->capHeight(); }
2204
2205QFixed QFontEngineMulti::descent() const
2206{ return engine(at: 0)->descent(); }
2207
2208QFixed QFontEngineMulti::leading() const
2209{
2210 return engine(at: 0)->leading();
2211}
2212
2213QFixed QFontEngineMulti::xHeight() const
2214{
2215 return engine(at: 0)->xHeight();
2216}
2217
2218QFixed QFontEngineMulti::averageCharWidth() const
2219{
2220 return engine(at: 0)->averageCharWidth();
2221}
2222
2223QFixed QFontEngineMulti::lineThickness() const
2224{
2225 return engine(at: 0)->lineThickness();
2226}
2227
2228QFixed QFontEngineMulti::underlinePosition() const
2229{
2230 return engine(at: 0)->underlinePosition();
2231}
2232
2233qreal QFontEngineMulti::maxCharWidth() const
2234{
2235 return engine(at: 0)->maxCharWidth();
2236}
2237
2238qreal QFontEngineMulti::minLeftBearing() const
2239{
2240 return engine(at: 0)->minLeftBearing();
2241}
2242
2243qreal QFontEngineMulti::minRightBearing() const
2244{
2245 return engine(at: 0)->minRightBearing();
2246}
2247
2248bool QFontEngineMulti::canRender(const QChar *string, int len) const
2249{
2250 if (engine(at: 0)->canRender(str: string, len))
2251 return true;
2252
2253 int nglyphs = len;
2254
2255 QVarLengthArray<glyph_t> glyphs(nglyphs);
2256
2257 QGlyphLayout g;
2258 g.numGlyphs = nglyphs;
2259 g.glyphs = glyphs.data();
2260 if (!stringToCMap(str: string, len, glyphs: &g, nglyphs: &nglyphs, flags: GlyphIndicesOnly))
2261 Q_UNREACHABLE();
2262
2263 for (int i = 0; i < nglyphs; i++) {
2264 if (glyphs[i] == 0)
2265 return false;
2266 }
2267
2268 return true;
2269}
2270
2271/* Implement alphaMapForGlyph() which is called by QPA Windows code.
2272 * Ideally, that code should be fixed to correctly handle QFontEngineMulti. */
2273
2274QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph)
2275{
2276 const int which = highByte(glyph);
2277 return engine(at: which)->alphaMapForGlyph(glyph: stripped(glyph));
2278}
2279
2280QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition)
2281{
2282 const int which = highByte(glyph);
2283 return engine(at: which)->alphaMapForGlyph(glyph: stripped(glyph), subPixelPosition);
2284}
2285
2286QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph, const QTransform &t)
2287{
2288 const int which = highByte(glyph);
2289 return engine(at: which)->alphaMapForGlyph(glyph: stripped(glyph), t);
2290}
2291
2292QImage QFontEngineMulti::alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition, const QTransform &t)
2293{
2294 const int which = highByte(glyph);
2295 return engine(at: which)->alphaMapForGlyph(glyph: stripped(glyph), subPixelPosition, t);
2296}
2297
2298QImage QFontEngineMulti::alphaRGBMapForGlyph(glyph_t glyph, QFixed subPixelPosition, const QTransform &t)
2299{
2300 const int which = highByte(glyph);
2301 return engine(at: which)->alphaRGBMapForGlyph(glyph: stripped(glyph), subPixelPosition, t);
2302}
2303
2304/*
2305 This is used indirectly by Qt WebKit when using QTextLayout::setRawFont
2306
2307 The purpose of this is to provide the necessary font fallbacks when drawing complex
2308 text. Since Qt WebKit ends up repeatedly creating QTextLayout instances and passing them
2309 the same raw font over and over again, we want to cache the corresponding multi font engine
2310 as it may contain fallback font engines already.
2311*/
2312QFontEngine *QFontEngineMulti::createMultiFontEngine(QFontEngine *fe, int script)
2313{
2314 QFontEngine *engine = nullptr;
2315 QFontCache::Key key(fe->fontDef, script, /*multi = */true);
2316 QFontCache *fc = QFontCache::instance();
2317 // We can't rely on the fontDef (and hence the cache Key)
2318 // alone to distinguish webfonts, since these should not be
2319 // accidentally shared, even if the resulting fontcache key
2320 // is strictly identical. See:
2321 // http://www.w3.org/TR/css3-fonts/#font-face-rule
2322 const bool faceIsLocal = !fe->faceId().filename.isEmpty();
2323 QFontCache::EngineCache::Iterator it = fc->engineCache.find(akey: key),
2324 end = fc->engineCache.end();
2325 while (it != end && it.key() == key) {
2326 Q_ASSERT(it.value().data->type() == QFontEngine::Multi);
2327 QFontEngineMulti *cachedEngine = static_cast<QFontEngineMulti *>(it.value().data);
2328 if (fe == cachedEngine->engine(at: 0) || (faceIsLocal && fe->faceId().filename == cachedEngine->engine(at: 0)->faceId().filename)) {
2329 engine = cachedEngine;
2330 fc->updateHitCountAndTimeStamp(value&: it.value());
2331 break;
2332 }
2333 ++it;
2334 }
2335 if (!engine) {
2336 engine = QGuiApplicationPrivate::instance()->platformIntegration()->fontDatabase()->fontEngineMulti(fontEngine: fe, script: QChar::Script(script));
2337 fc->insertEngine(key, engine, /* insertMulti */ !faceIsLocal);
2338 }
2339 Q_ASSERT(engine);
2340 return engine;
2341}
2342
2343QTestFontEngine::QTestFontEngine(int size)
2344 : QFontEngineBox(TestFontEngine, size)
2345{}
2346
2347QT_END_NAMESPACE
2348

source code of qtbase/src/gui/text/qfontengine.cpp