Coverage Report

Created: 2026-01-25 07:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/qtsvg/src/svg/qsvgfont.cpp
Line
Count
Source
1
// Copyright (C) 2016 The Qt Company Ltd.
2
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4
#include "qsvgfont_p.h"
5
6
#include "qpainter.h"
7
#include "qpen.h"
8
#include "qdebug.h"
9
10
QT_BEGIN_NAMESPACE
11
12
QSvgGlyph::QSvgGlyph(const QString &unicode, const QPainterPath &path, qreal horizAdvX)
13
0
    : m_unicode(unicode), m_path(path), m_horizAdvX(horizAdvX)
14
0
{
15
16
0
}
17
18
19
QSvgFont::QSvgFont(qreal horizAdvX)
20
0
    : m_horizAdvX(horizAdvX)
21
0
{
22
0
}
23
24
25
QString QSvgFont::familyName() const
26
0
{
27
0
    return m_familyName;
28
0
}
29
30
31
void QSvgFont::addGlyph(const QString &unicode, const QPainterPath &path, qreal horizAdvX)
32
0
{
33
0
    m_glyphs.emplaceBack(unicode, path, (horizAdvX == -1) ? m_horizAdvX : horizAdvX);
34
0
}
35
36
bool QSvgFont::addMissingGlyph(const QPainterPath &path, qreal horizAdvX)
37
0
{
38
0
    if (m_missingGlyph) {
39
0
        qWarning("The font already has a 'missing-glyph' element.");
40
0
        return false;
41
0
    }
42
0
    m_missingGlyph.reset(new QSvgGlyph(QChar(), path, (horizAdvX == -1) ? m_horizAdvX : horizAdvX));
43
0
    return true;
44
0
}
45
46
47
void QSvgFont::draw(QPainter *p, const QPointF &point, const QList<const QSvgGlyph *> &glyphs,
48
                    qreal pixelSize, Qt::Alignment alignment) const
49
0
{
50
0
    draw_helper(p, point, glyphs, pixelSize, alignment, nullptr);
51
0
}
52
53
QRectF QSvgFont::boundingRect(QPainter *p, const QPointF &point,
54
                              const QList<const QSvgGlyph *> &glyphs,
55
                              qreal pixelSize, Qt::Alignment alignment) const
56
0
{
57
0
    QRectF bounds;
58
0
    draw_helper(p, point, glyphs, pixelSize, alignment, &bounds);
59
0
    return bounds;
60
0
}
61
62
// text must not be empty
63
char32_t firstUcs4(QStringView text)
64
0
{
65
0
    if (text.size() > 1 && text.at(0).isHighSurrogate() && text.at(1).isLowSurrogate())
66
0
        return QChar::surrogateToUcs4(text.at(0), text.at(1));
67
68
0
    return text.first().unicode();
69
0
}
70
71
// returns a pointer to the first QSvgGlyph to be used in the text
72
// or nullptr if none can be found
73
const QSvgGlyph *QSvgFont::findFirstGlyphFor(QStringView text) const
74
0
{
75
0
    const auto possibleIndices = m_possibleGlyphIndicesForChar.value(firstUcs4(text));
76
0
    for (const qsizetype i : possibleIndices) {
77
0
        const QSvgGlyph &currentGlyph = m_glyphs.at(i);
78
0
        if (text.startsWith(currentGlyph.m_unicode)) {
79
0
            return &currentGlyph;
80
0
        }
81
0
    }
82
83
0
    for (; m_firstUnscannedGlyphIdx < m_glyphs.size(); ++m_firstUnscannedGlyphIdx) {
84
0
        const QSvgGlyph &currentGlyph = m_glyphs.at(m_firstUnscannedGlyphIdx);
85
0
        m_possibleGlyphIndicesForChar[firstUcs4(currentGlyph.m_unicode)].push_back(m_firstUnscannedGlyphIdx);
86
0
        if (text.startsWith(currentGlyph.m_unicode)) {
87
0
            ++m_firstUnscannedGlyphIdx;
88
0
            return &currentGlyph;
89
0
        }
90
0
    }
91
0
    return nullptr;
92
0
}
93
94
QList<const QSvgGlyph *> QSvgFont::toGlyphs(QStringView text) const
95
0
{
96
0
    QList<const QSvgGlyph *> glyphs;
97
0
    glyphs.reserve(text.length());
98
0
    while (text.length()) {
99
0
        const QSvgGlyph *foundGlyph = findFirstGlyphFor(text);
100
0
        if (foundGlyph) {
101
0
            glyphs.append(foundGlyph);
102
0
            text.slice(foundGlyph->m_unicode.length());
103
0
        } else {
104
0
            if (m_missingGlyph)
105
0
                glyphs.append(m_missingGlyph.get());
106
0
            if (text.size() > 1
107
0
                && text.at(0).isHighSurrogate() && text.at(1).isLowSurrogate()) {
108
0
                text.slice(2);
109
0
            } else {
110
0
                text.slice(1);
111
0
            }
112
0
        }
113
0
    }
114
0
    return glyphs;
115
0
}
116
117
void QSvgFont::draw_helper(QPainter *p, const QPointF &point,
118
                           const QList<const QSvgGlyph *> &glyphs, qreal pixelSize,
119
                           Qt::Alignment alignment, QRectF *boundingRect) const
120
0
{
121
0
    const bool isPainting = (boundingRect == nullptr);
122
123
0
    p->save();
124
0
    p->translate(point);
125
0
    p->scale(pixelSize / m_unitsPerEm, -pixelSize / m_unitsPerEm);
126
127
    // Calculate the text width to be used for alignment
128
0
    int textWidth = 0;
129
0
    for (const auto *glyph : glyphs)
130
0
        textWidth += static_cast<int>(glyph->m_horizAdvX);
131
132
0
    QPoint alignmentOffset(0, 0);
133
0
    if (alignment == Qt::AlignHCenter) {
134
0
        alignmentOffset.setX(-textWidth / 2);
135
0
    } else if (alignment == Qt::AlignRight) {
136
0
        alignmentOffset.setX(-textWidth);
137
0
    }
138
139
0
    p->translate(alignmentOffset);
140
141
    // since in SVG the embedded font ain't really a path
142
    // the outline has got to stay untransformed...
143
0
    qreal penWidth = p->pen().widthF();
144
0
    penWidth /= (pixelSize/m_unitsPerEm);
145
0
    QPen pen = p->pen();
146
0
    pen.setWidthF(penWidth);
147
0
    p->setPen(pen);
148
149
0
    for (const auto *glyph : glyphs) {
150
0
        if (isPainting)
151
0
            p->drawPath(glyph->m_path);
152
153
0
        if (boundingRect) {
154
0
            QPainterPathStroker stroker;
155
0
            stroker.setWidth(penWidth);
156
0
            stroker.setJoinStyle(p->pen().joinStyle());
157
0
            stroker.setMiterLimit(p->pen().miterLimit());
158
0
            QPainterPath stroke = stroker.createStroke(glyph->m_path);
159
0
            *boundingRect |= p->transform().map(stroke).boundingRect();
160
0
        }
161
162
0
        p->translate(glyph->m_horizAdvX, 0);
163
0
    }
164
165
0
    p->restore();
166
0
}
167
168
void QSvgFont::setFamilyName(const QString &name)
169
0
{
170
0
    m_familyName = name;
171
0
}
172
173
void QSvgFont::setUnitsPerEm(qreal upem)
174
0
{
175
0
    m_unitsPerEm = upem;
176
0
}
177
178
QT_END_NAMESPACE