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