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