/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 ¤tGlyph = m_glyphs.at(i); |
78 | 0 | if (text.startsWith(currentGlyph.m_unicode)) { |
79 | 0 | return ¤tGlyph; |
80 | 0 | } |
81 | 0 | } |
82 | | |
83 | 0 | for (; m_firstUnscannedGlyphIdx < m_glyphs.size(); ++m_firstUnscannedGlyphIdx) { |
84 | 0 | const QSvgGlyph ¤tGlyph = 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 ¤tGlyph; |
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 |