Krita Source Code Documentation
Loading...
Searching...
No Matches
SvgCreateTextStrategy.cpp
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
8#include "SvgTextTool.h"
9
10#include <KoFontRegistry.h>
11
13#include "KoCanvasBase.h"
14#include "KoProperties.h"
15#include "KoSelection.h"
16#include "KoShapeController.h"
18#include "KoShapeFactoryBase.h"
19#include "KoShapeRegistry.h"
20#include "KoToolBase.h"
21#include "KoViewConverter.h"
22#include "KoSnapGuide.h"
27#include "kis_global.h"
28#include "kis_shape_layer.h"
29#include "kundo2command.h"
30
31#include <KoPathShape.h>
32#include <KoPathSegment.h>
33
36 , m_dragStart(clicked)
37 , m_dragEnd(clicked)
38 , m_flowShape(shape)
39{
40 KoSvgTextProperties properties = tool->propertiesForNewText();
42 const KoSvgText::FontMetrics fontMetrics = properties.metrics(true);
43 const qreal ftMultiplier = properties.fontSize().value / fontMetrics.fontSize;
44 const double lineHeight = (fontMetrics.lineGap+fontMetrics.ascender+fontMetrics.descender)*ftMultiplier;
45 m_minSizeInline = {lineHeight, lineHeight};
46}
47
48void SvgCreateTextStrategy::paint(QPainter &painter, const KoViewConverter &converter)
49{
50 const QTransform originalPainterTransform = painter.transform();
51 painter.setTransform(converter.documentToView(), true);
52 KisHandlePainterHelper handlePainter(&painter, originalPainterTransform, 0.0, decorationThickness());
53
54 const QPolygonF poly(QRectF(m_dragStart, m_dragEnd));
56 handlePainter.drawRubberLine(poly);
57}
58
59void SvgCreateTextStrategy::handleMouseMove(const QPointF &mouseLocation, Qt::KeyboardModifiers modifiers)
60{
61 m_dragEnd = this->tool()->canvas()->snapGuide()->snap(mouseLocation, modifiers);
62 m_modifiers = modifiers;
63 const QRectF updateRect = QRectF(m_dragStart, m_dragEnd).normalized();
64 tool()->canvas()->updateCanvas(kisGrowRect(updateRect, 100));
65}
66
68{
69 SvgTextTool *const tool = qobject_cast<SvgTextTool *>(this->tool());
70
71 QRectF rectangle = QRectF(m_dragStart, m_dragEnd).normalized();
72
73 KoSvgTextProperties properties = tool->propertiesForNewText();
74 KoSvgTextProperties resolvedProperties = properties;
75 resolvedProperties.inheritFrom(KoSvgTextProperties::defaultProperties(), true);
76
77 const KoSvgText::FontMetrics fontMetrics = properties.metrics(true);
78 const qreal ftMultiplier = resolvedProperties.fontSize().value / fontMetrics.fontSize;
79 double ascender = fontMetrics.ascender;
80 ascender += fontMetrics.lineGap/2;
81 ascender *= ftMultiplier;
82 const double lineHeight = m_minSizeInline.width();
84
85 bool unwrappedText = m_modifiers.testFlag(Qt::ControlModifier) || m_flowShape;
86 if (rectangle.width() < m_minSizeInline.width() && rectangle.height() < m_minSizeInline.height()) {
87 unwrappedText = true;
88 }
89 if (!unwrappedText) {
91 val.isAuto = false;
92 val.customValue = writingMode == KoSvgText::HorizontalTB? rectangle.width(): rectangle.height();
93 properties.setProperty(KoSvgTextProperties::InlineSizeId, QVariant::fromValue(val));
94 } else {
95 // We explicitely remove the inline size, because it could've been inside the properties by a different method.
97 }
98 if (writingMode != KoSvgText::HorizontalTB) {
100 }
101 // Ensure white space is set to pre-wrap if unspecified.
104 }
107 }
108
109 KoShapeFactoryBase *factory = KoShapeRegistry::instance()->value("KoSvgTextShapeID");
110 KoProperties *params = new KoProperties();//Fill these with "svgText", "defs" and "shapeRect"
111 params->setProperty("defs", QVariant(tool->generateDefs(properties)));
112
113 QPointF origin = rectangle.topLeft();
114
115 {
118
119 if (writingMode == KoSvgText::HorizontalTB) {
120 origin.setY(rectangle.top() + ascender);
121 if (halign == KoSvgText::AnchorMiddle) {
122 origin.setX(rectangle.center().x());
123 } else if ((halign == KoSvgText::AnchorEnd && !isRtl) || (halign == KoSvgText::AnchorStart && isRtl)) {
124 origin.setX(rectangle.right());
125 }
126 } else {
127 if (writingMode == KoSvgText::VerticalRL) {
128 origin.setX(rectangle.right() - (lineHeight*0.5));
129 } else {
130 origin.setX(rectangle.left() + (lineHeight*0.5));
131 }
132
133 if (halign == KoSvgText::AnchorMiddle) {
134 origin.setY(rectangle.center().y());
135 } else if (halign == KoSvgText::AnchorEnd) {
136 origin.setY(rectangle.bottom());
137 }
138 }
139 }
140 if (!rectangle.contains(origin) && unwrappedText) {
141 origin = writingMode == KoSvgText::HorizontalTB? QPointF(origin.x(), rectangle.bottom()): QPointF(rectangle.center().x(), origin.y());
142 }
143 params->setProperty("shapeRect", QVariant(rectangle));
144 params->setProperty("origin", QVariant(origin));
145
146 KoSvgTextShape *textShape = dynamic_cast<KoSvgTextShape *>(factory->createShape( params, tool->canvas()->shapeController()->resourceManager()));
147
150 textShape->setBackground(properties.background());
151 }
153 textShape->setStroke(properties.stroke());
154 }
155
156 KUndo2Command *parentCommand = new KUndo2Command();
157
158 new KoKeepShapesSelectedCommand(tool->koSelection()->selectedShapes(), {}, tool->canvas()->selectedShapesProxy(), false, parentCommand);
159
160 KoShapeContainer* parent = tool->canvas()->shapeController()->documentBase()->createParentForShapes({textShape}, false, parentCommand);
161 KUndo2Command *cmd = tool->canvas()->shapeController()->addShape(textShape, parent, parentCommand);
162
163 parentCommand->setText(cmd->text());
164
165 if (m_flowShape) {
167
168 KoPathShape *path = dynamic_cast<KoPathShape*>(m_flowShape);
169 KoPathSegment segment;
170 if(path) {
171 segment = path->segmentAtPoint(m_dragStart, tool->handleGrabRect(m_dragStart));
172 }
173 if (segment.isValid()) {
174 int pos = textShape->posForIndex(textShape->plainText().size());
175 new KoSvgTextSetTextPathOnRangeCommand(textShape, m_flowShape, 0, pos, parentCommand);
176
178 const qreal grab = tool->grabSensitivityInPt();
179 QList<KoPathSegment> segments = path->segmentsAt(path->outlineRect().adjusted(-grab, -grab, grab, grab));
180 Q_FOREACH(KoPathSegment s, segments) {
181 if (s == segment) {
182 info.startOffset += (segment.nearestPoint(path->documentToShape(m_dragStart))*segment.length());
183 break;
184 }
185 info.startOffset += s.length();
186 qDebug() << info.startOffset << s.length();
187 }
188 new KoSvgTextPathInfoChangeCommand(textShape, 2, info, parentCommand);
189 } else {
190 new KoSvgTextAddShapeCommand(textShape, m_flowShape, true, parentCommand);
191 }
192 }
193
194 {
195 KoSelectedShapesProxy *finalSelectionProxy = tool->canvas()->selectedShapesProxy();
196 if (parent) {
197 KisShapeLayer *layer = dynamic_cast<KisShapeLayer *>(parent);
198 if (layer) {
199 finalSelectionProxy = layer->selectedShapesProxy();
200 }
201 }
202 new KoKeepShapesSelectedCommand({}, {textShape}, finalSelectionProxy, true, parentCommand);
203 }
204
205 tool->canvas()->snapGuide()->reset();
206
207 return parentCommand;
208}
209
211{
212 tool()->canvas()->snapGuide()->reset();
213 const QRectF updateRect = QRectF(m_dragStart, m_dragEnd).normalized();
214 tool()->canvas()->updateCanvas(updateRect);
215}
216
217void SvgCreateTextStrategy::finishInteraction(Qt::KeyboardModifiers modifiers)
218{
219 m_modifiers = modifiers;
220}
221
223{
224 QRectF rectangle = QRectF(m_dragStart, m_dragEnd).normalized();
225 return (rectangle.width() >= m_minSizeInline.width() || rectangle.height() >= m_minSizeInline.height()) && !m_modifiers.testFlag(Qt::ControlModifier);
226}
227
229{
230 return (m_flowShape)? true: false;
231}
void setText(const KUndo2MagicString &text)
KUndo2MagicString text() const
The KisHandlePainterHelper class is a special helper for painting handles around objects....
void setHandleStyle(const KisHandleStyle &style)
void drawRubberLine(const QPolygonF &poly)
static KisHandleStyle & primarySelection()
KoSelectedShapesProxy * selectedShapesProxy()
selectedShapesProxy
KoSnapGuide * snapGuide
QPointer< KoShapeController > shapeController
virtual void updateCanvas(const QRectF &rc)=0
virtual KoSelectedShapesProxy * selectedShapesProxy() const =0
selectedShapesProxy() is a special interface for keeping a persistent connections to selectionChanged...
const T value(const QString &id) const
A KoPathSegment consist of two neighboring KoPathPoints.
qreal length(qreal error=0.005) const
qreal nearestPoint(const QPointF &point) const
bool isValid() const
Returns if segment is valid, e.g. has two valid points.
The position of a path point within a path shape.
Definition KoPathShape.h:63
void setProperty(const QString &name, const QVariant &value)
The KoSelectedShapesProxy class is a special interface of KoCanvasBase to have a stable connection to...
virtual KoShape * createShape(const KoProperties *params, KoDocumentResourceManager *documentResources=0) const
static KoShapeRegistry * instance()
QPointF absolutePosition(KoFlake::AnchorPosition anchor=KoFlake::Center) const
Definition KoShape.cpp:568
virtual void setPosition(const QPointF &position)
Set the position of the shape in pt.
Definition KoShape.cpp:268
void reset()
Resets the snap guide.
QPointF snap(const QPointF &mousePosition, Qt::KeyboardModifiers modifiers)
snaps the mouse position, returns if mouse was snapped
@ TextAnchorId
KoSvgText::TextAnchor.
@ InlineSizeId
KoSvgText::AutoValue.
@ TextOrientationId
KoSvgText::TextOrientation.
@ TextCollapseId
KoSvgText::TextSpaceCollapse.
@ StrokeId
KoSvgText::StrokeProperty.
@ FillId
KoSvgText::BackgroundProperty.
@ WritingModeId
KoSvgText::WritingMode.
@ DirectionId
KoSvgText::Direction.
@ TextWrapId
KoSvgText::TextWrap.
QSharedPointer< KoShapeBackground > background() const
void removeProperty(PropertyId id)
KoShapeStrokeModelSP stroke() const
KoSvgText::FontMetrics metrics(const bool withResolvedLineHeight=true, const bool offsetByBaseline=false) const
metrics Return the metrics of the first available font.
static const KoSvgTextProperties & defaultProperties()
bool hasProperty(PropertyId id) const
void inheritFrom(const KoSvgTextProperties &parentProperties, bool resolve=false, bool onlyFontAndLineHeight=false)
void setProperty(PropertyId id, const QVariant &value)
QVariant propertyOrDefault(PropertyId id) const
KoSvgText::CssLengthPercentage fontSize() const
void setBackground(QSharedPointer< KoShapeBackground > background) override
int posForIndex(int index, bool firstIndex=false, bool skipSynthetic=false) const
posForIndex Get the cursor position for a given index in a string.
void setStroke(KoShapeStrokeModelSP stroke) override
KoCanvasBase * canvas() const
Returns the canvas the tool is working on.
QRectF handleGrabRect(const QPointF &position) const
virtual QPointF documentToView(const QPointF &documentPoint) const
void handleMouseMove(const QPointF &mouseLocation, Qt::KeyboardModifiers modifiers) override
KUndo2Command * createCommand() override
Qt::KeyboardModifiers m_modifiers
void finishInteraction(Qt::KeyboardModifiers modifiers) override
SvgCreateTextStrategy(SvgTextTool *tool, const QPointF &clicked, KoShape *shape=nullptr)
void paint(QPainter &painter, const KoViewConverter &converter) override
T kisGrowRect(const T &rect, U offset)
Definition kis_global.h:186
@ TopLeft
Definition KoFlake.h:86
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
@ DirectionRightToLeft
Definition KoSvgText.h:50
@ HorizontalTB
Definition KoSvgText.h:38
@ OrientationUpright
Set all characters upright.
Definition KoSvgText.h:73
@ Preserve
Do not collapse any space.
Definition KoSvgText.h:99
The FontMetrics class A class to keep track of a variety of font metrics. Note that values are in Fre...
Definition KoSvgText.h:327
qint32 lineGap
additional linegap between consecutive lines.
Definition KoSvgText.h:341
qint32 fontSize
Currently set size, CSS unit 'em'.
Definition KoSvgText.h:329
qint32 descender
distance for origin to bottom.
Definition KoSvgText.h:340
qint32 ascender
distance from origin to top.
Definition KoSvgText.h:339