Krita Source Code Documentation
Loading...
Searching...
No Matches
SvgInlineSizeHelper.h
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2023 Alvin Wong <alvin@alvinhc.com>
3 *
4 * SPDX-License-Identifier: GPL-3.0-or-later
5 */
6
7#ifndef SVG_INLINE_SIZE_HELPER_H
8#define SVG_INLINE_SIZE_HELPER_H
9
10#include "KoSvgText.h"
11#include "KoSvgTextProperties.h"
12#include "KoSvgTextShape.h"
13
14#include <optional>
15
16namespace SvgInlineSizeHelper
17{
18
19[[nodiscard]] static inline double getInlineSizePt(const KoSvgTextShape *const shape)
20{
21 const KoSvgText::AutoValue inlineSizeProp =
23 if (!inlineSizeProp.isAuto) {
24 return inlineSizeProp.customValue;
25 }
26 return 0.0;
27}
28
29enum class VisualAnchor {
31 Mid,
33};
34
35enum class Side {
38};
39
40struct Q_DECL_HIDDEN InlineSizeInfo {
41 double inlineSize;
43 double baseline;
45 double left;
47 double right;
49 double top;
51 double bottom;
56 QTransform editorTransform;
58 QTransform shapeTransform;
59
60 [[nodiscard]] static inline std::optional<InlineSizeInfo> fromShape(KoSvgTextShape *const shape, qreal dashesLength = 36.0)
61 {
62 const double inlineSize = getInlineSizePt(shape);
63 if (inlineSize <= 0) {
64 return {};
65 }
66 KoSvgTextProperties props = shape->propertiesForPos(-1);
67
70 const KoSvgText::Direction direction =
72 const KoSvgText::TextAnchor textAnchor =
74
75 VisualAnchor anchor{};
76 switch (textAnchor) {
78 default:
80 anchor = VisualAnchor::LeftOrTop;
81 } else {
82 anchor = VisualAnchor::RightOrBottom;
83 }
84 break;
86 anchor = VisualAnchor::Mid;
87 break;
90 anchor = VisualAnchor::RightOrBottom;
91 } else {
92 anchor = VisualAnchor::LeftOrTop;
93 }
94 break;
95 }
96 const double xPos = writingMode == KoSvgText::WritingMode::HorizontalTB? shape->initialTextPosition().x(): shape->initialTextPosition().y();
97 const double yPos = writingMode == KoSvgText::WritingMode::HorizontalTB? shape->initialTextPosition().y(): shape->initialTextPosition().x();
98
99 const double baseline = yPos;
100 double left{};
101 double right{};
102 double top{};
103 double bottom{};
104 switch (anchor) {
105 case VisualAnchor::LeftOrTop:
106 left = xPos;
107 right = xPos + inlineSize;
108 break;
109 case VisualAnchor::Mid:
110 left = xPos - inlineSize * 0.5;
111 right = xPos + inlineSize * 0.5;
112 break;
113 case VisualAnchor::RightOrBottom:
114 left = xPos - inlineSize;
115 right = xPos;
116 break;
117 };
118
119 // We piggyback on the shape transformation that we already need to
120 // deal with to also handle the different orientations of writing-mode.
121 // We default to the "default caret size" when the textShape (and thus outline) is empty.
122 QLineF caret;
123 QColor c;
124 shape->cursorForPos(0, caret, c);
125 const QRectF outline = shape->outlineRect().isEmpty()? QRectF(caret.p2(), caret.p1()): shape->outlineRect();
126 QTransform editorTransform;
127 switch (writingMode) {
129 default:
130 top = outline.top();
131 bottom = outline.bottom();
132 break;
134 editorTransform.rotate(90.0);
135 top = -outline.right();
136 bottom = -outline.left();
137 break;
139 editorTransform.rotate(-90.0);
140 editorTransform.scale(-1.0, 1.0);
141 top = outline.left();
142 bottom = outline.right();
143 break;
144 }
145
146 QLineF scale = editorTransform.map(QLineF(0, 0, 0, 1.0));
147 scale = shape->absoluteTransformation().inverted().map(scale);
148 scale = editorTransform.inverted().map(scale);
149
150 InlineSizeInfo ret{inlineSize,
151 baseline,
152 left,
153 right,
154 top,
155 bottom,
156 dashesLength * scale.length(),
157 anchor,
158 editorTransform,
159 shape->absoluteTransformation()};
160 return {ret};
161 }
162
163private:
164 [[nodiscard]] inline QLineF leftLineRaw() const
165 {
166 return {left, top, left, bottom};
167 }
168
169 [[nodiscard]] inline QLineF rightLineRaw() const
170 {
171 return {right, top, right, bottom};
172 }
173
174 [[nodiscard]] inline QRectF boundingRectRaw() const
175 {
176 return {QPointF(left, top), QPointF(right, bottom + dashesLength)};
177 }
178
179 [[nodiscard]] inline QLineF generateDashLine(const QLineF line, const qreal dashLength = 4.0) const
180 {
181 QPointF start = line.p2();
182 QLineF dash = line;
183 dash.setLength(line.length() + dashLength);
184 dash.setP1(start);
185 return dash;
186 }
187
188public:
195 [[nodiscard]] inline QLineF baselineLineLocal() const
196 {
197 return editorTransform.map(QLineF{left, baseline, right, baseline});
198 }
199
206 [[nodiscard]] inline QLineF baselineLine() const
207 {
208 return shapeTransform.map(baselineLineLocal());
209 }
210
211 [[nodiscard]] inline Side endLineSide() const
212 {
213 switch (anchor) {
214 case VisualAnchor::LeftOrTop:
215 case VisualAnchor::Mid:
216 default:
217 return Side::RightOrBottom;
218 case VisualAnchor::RightOrBottom:
219 return Side::LeftOrTop;
220 }
221 }
222
223 [[nodiscard]] inline QLineF endLineLocal() const
224 {
225 switch (endLineSide()) {
226 case Side::LeftOrTop:
227 return editorTransform.map(leftLineRaw());
228 case Side::RightOrBottom:
229 default:
230 return editorTransform.map(rightLineRaw());
231 }
232 }
233
234 [[nodiscard]] inline QLineF endLineDashes() const
235 {
236 switch (endLineSide()) {
237 case Side::LeftOrTop:
238 return generateDashLine(editorTransform.map(leftLineRaw()), dashesLength);
239 case Side::RightOrBottom:
240 default:
241 return generateDashLine(editorTransform.map(rightLineRaw()), dashesLength);
242 }
243 }
244
245 [[nodiscard]] inline QLineF endLine() const
246 {
247 return shapeTransform.map(endLineLocal());
248 }
249
250 [[nodiscard]] inline Side startLineSide() const
251 {
252 switch (anchor) {
253 case VisualAnchor::LeftOrTop:
254 case VisualAnchor::Mid:
255 default:
256 return Side::LeftOrTop;
257 case VisualAnchor::RightOrBottom:
258 return Side::RightOrBottom;
259 }
260 }
261
262 [[nodiscard]] inline QLineF startLineLocal() const
263 {
264 switch (endLineSide()) {
265 case Side::LeftOrTop:
266 return editorTransform.map(rightLineRaw());
267 case Side::RightOrBottom:
268 default:
269 return editorTransform.map(leftLineRaw());
270 }
271 }
272
273 [[nodiscard]] inline QLineF startLineDashes() const
274 {
275 switch (endLineSide()) {
276 case Side::LeftOrTop:
277 return generateDashLine(editorTransform.map(rightLineRaw()), dashesLength);
278 case Side::RightOrBottom:
279 default:
280 return generateDashLine(editorTransform.map(leftLineRaw()), dashesLength);
281 }
282 }
283
284 [[nodiscard]] inline QLineF startLine() const
285 {
286 return shapeTransform.map(startLineLocal());
287 }
288
289 [[nodiscard]] inline QPolygonF endLineGrabRect(double grabThreshold) const
290 {
291 QLineF endLine;
292 switch (endLineSide()) {
293 case Side::LeftOrTop:
294 endLine = leftLineRaw();
295 break;
296 case Side::RightOrBottom:
297 default:
298 endLine = rightLineRaw();
299 break;
300 }
301 const QRectF rect{endLine.x1() - grabThreshold,
302 top - grabThreshold,
303 grabThreshold * 2,
304 bottom - top + grabThreshold * 2};
305 const QPolygonF poly(rect);
306 return shapeTransform.map(editorTransform.map(poly));
307 }
308
309 [[nodiscard]] inline QPolygonF startLineGrabRect(double grabThreshold) const
310 {
311 QLineF startLine;
312 switch (endLineSide()) {
313 case Side::LeftOrTop:
314 startLine = rightLineRaw();
315 break;
316 case Side::RightOrBottom:
317 default:
318 startLine = leftLineRaw();
319 break;
320 }
321 const QRectF rect{startLine.x1() - grabThreshold,
322 top - grabThreshold,
323 grabThreshold * 2,
324 bottom - top + grabThreshold * 2};
325 const QPolygonF poly(rect);
326 return shapeTransform.map(editorTransform.map(poly));
327 }
328
329 [[nodiscard]] inline QRectF boundingRect() const
330 {
331 return shapeTransform.mapRect(editorTransform.mapRect(boundingRectRaw()));
332 }
333};
334
335} // namespace SvgInlineSizeHelper
336
337#endif /* SVG_INLINE_SIZE_HELPER_H */
QTransform absoluteTransformation() const
Definition KoShape.cpp:382
@ TextAnchorId
KoSvgText::TextAnchor.
@ InlineSizeId
KoSvgText::AutoValue.
@ WritingModeId
KoSvgText::WritingMode.
@ DirectionId
KoSvgText::Direction.
QVariant property(PropertyId id, const QVariant &defaultValue=QVariant()) const
QVariant propertyOrDefault(PropertyId id) const
QPointF initialTextPosition
KoSvgTextProperties propertiesForPos(const int pos, bool inherited=false) const
Return the properties at a given position.
QPainterPath cursorForPos(int pos, QLineF &caret, QColor &color, double bidiFlagSize=1.0)
cursorForPos returns the QPainterPath associated with this cursorPosition.
QRectF outlineRect() const override
KoSvgTextProperties textProperties() const
TextAnchor
Where the text is anchored for SVG 1.1 text and 'inline-size'.
Definition KoSvgText.h:79
@ AnchorEnd
Anchor right for LTR, left for RTL.
Definition KoSvgText.h:82
@ AnchorStart
Anchor left for LTR, right for RTL.
Definition KoSvgText.h:80
@ AnchorMiddle
Anchor to the middle.
Definition KoSvgText.h:81
Direction
Base direction used by Bidi algorithm.
Definition KoSvgText.h:48
@ DirectionLeftToRight
Definition KoSvgText.h:49
@ HorizontalTB
Definition KoSvgText.h:38
static double getInlineSizePt(const KoSvgTextShape *const shape)
QTransform editorTransform
Transformation from inline-size editor (writing-mode transformation) to shape.
double dashesLength
Length of the dashes at the end.
static std::optional< InlineSizeInfo > fromShape(KoSvgTextShape *const shape, qreal dashesLength=36.0)
QLineF baselineLineLocal() const
Gets a shape-local line representing the first line baseline. This always goes from left to right by ...
QTransform shapeTransform
Transformation from shape local to document.
double baseline
Baseline coord along the block-flow direction.
double bottom
Bottom coord along the block-flow direction (left for h-rl, right for h-lr)
double left
Left coord (vertical mode) or top coord (horizontal mode)
double right
Right coord (vertical mode) or bottom coord (horizontal mode)
QPolygonF startLineGrabRect(double grabThreshold) const
QPolygonF endLineGrabRect(double grabThreshold) const
double top
Top coord along the block-flow direction (right for h-rl, left for h-lr)
QLineF generateDashLine(const QLineF line, const qreal dashLength=4.0) const
QLineF baselineLine() const
Gets a line representing the first line baseline. This always goes from left to right by the inline-b...