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"
17#include "KoShapeFactoryBase.h"
18#include "KoShapeRegistry.h"
19#include "KoToolBase.h"
20#include "KoViewConverter.h"
21#include "KoSnapGuide.h"
26#include "kis_global.h"
27#include "kundo2command.h"
28
29#include <KoPathShape.h>
30#include <KoPathSegment.h>
31
34 , m_dragStart(clicked)
35 , m_dragEnd(clicked)
36 , m_flowShape(shape)
37{
38 KoSvgTextProperties properties = tool->propertiesForNewText();
40 const KoSvgText::FontMetrics fontMetrics = properties.metrics(true);
41 const qreal ftMultiplier = properties.fontSize().value / fontMetrics.fontSize;
42 const double lineHeight = (fontMetrics.lineGap+fontMetrics.ascender+fontMetrics.descender)*ftMultiplier;
43 m_minSizeInline = {lineHeight, lineHeight};
44}
45
46void SvgCreateTextStrategy::paint(QPainter &painter, const KoViewConverter &converter)
47{
48 const QTransform originalPainterTransform = painter.transform();
49 painter.setTransform(converter.documentToView(), true);
50 KisHandlePainterHelper handlePainter(&painter, originalPainterTransform, 0.0, decorationThickness());
51
52 const QPolygonF poly(QRectF(m_dragStart, m_dragEnd));
54 handlePainter.drawRubberLine(poly);
55}
56
57void SvgCreateTextStrategy::handleMouseMove(const QPointF &mouseLocation, Qt::KeyboardModifiers modifiers)
58{
59 m_dragEnd = this->tool()->canvas()->snapGuide()->snap(mouseLocation, modifiers);
60 m_modifiers = modifiers;
61 const QRectF updateRect = QRectF(m_dragStart, m_dragEnd).normalized();
62 tool()->canvas()->updateCanvas(kisGrowRect(updateRect, 100));
63}
64
66{
67 SvgTextTool *const tool = qobject_cast<SvgTextTool *>(this->tool());
68
69 QRectF rectangle = QRectF(m_dragStart, m_dragEnd).normalized();
70
71 KoSvgTextProperties properties = tool->propertiesForNewText();
72 KoSvgTextProperties resolvedProperties = properties;
73 resolvedProperties.inheritFrom(KoSvgTextProperties::defaultProperties(), true);
74
75 const KoSvgText::FontMetrics fontMetrics = properties.metrics(true);
76 const qreal ftMultiplier = resolvedProperties.fontSize().value / fontMetrics.fontSize;
77 double ascender = fontMetrics.ascender;
78 ascender += fontMetrics.lineGap/2;
79 ascender *= ftMultiplier;
80 const double lineHeight = m_minSizeInline.width();
82
83 bool unwrappedText = m_modifiers.testFlag(Qt::ControlModifier);
84 if (rectangle.width() < m_minSizeInline.width() && rectangle.height() < m_minSizeInline.height()) {
85 unwrappedText = true;
86 }
87 if (!unwrappedText) {
89 val.isAuto = false;
90 val.customValue = writingMode == KoSvgText::HorizontalTB? rectangle.width(): rectangle.height();
91 properties.setProperty(KoSvgTextProperties::InlineSizeId, QVariant::fromValue(val));
92 }
93 if (writingMode != KoSvgText::HorizontalTB) {
95 }
96 // Ensure white space is set to pre-wrap if unspecified.
99 }
102 }
103
104 KoShapeFactoryBase *factory = KoShapeRegistry::instance()->value("KoSvgTextShapeID");
105 KoProperties *params = new KoProperties();//Fill these with "svgText", "defs" and "shapeRect"
106 params->setProperty("defs", QVariant(tool->generateDefs(properties)));
107
108 QPointF origin = rectangle.topLeft();
109
110 {
113
114 if (writingMode == KoSvgText::HorizontalTB) {
115 origin.setY(rectangle.top() + ascender);
116 if (halign == KoSvgText::AnchorMiddle) {
117 origin.setX(rectangle.center().x());
118 } else if ((halign == KoSvgText::AnchorEnd && !isRtl) || (halign == KoSvgText::AnchorStart && isRtl)) {
119 origin.setX(rectangle.right());
120 }
121 } else {
122 if (writingMode == KoSvgText::VerticalRL) {
123 origin.setX(rectangle.right() - (lineHeight*0.5));
124 } else {
125 origin.setX(rectangle.left() + (lineHeight*0.5));
126 }
127
128 if (halign == KoSvgText::AnchorMiddle) {
129 origin.setY(rectangle.center().y());
130 } else if (halign == KoSvgText::AnchorEnd) {
131 origin.setY(rectangle.bottom());
132 }
133 }
134 }
135 if (!rectangle.contains(origin) && unwrappedText) {
136 origin = writingMode == KoSvgText::HorizontalTB? QPointF(origin.x(), rectangle.bottom()): QPointF(rectangle.center().x(), origin.y());
137 }
138 params->setProperty("shapeRect", QVariant(rectangle));
139 params->setProperty("origin", QVariant(origin));
140
141 KoSvgTextShape *textShape = dynamic_cast<KoSvgTextShape *>(factory->createShape( params, tool->canvas()->shapeController()->resourceManager()));
142
145 textShape->setBackground(properties.background());
146 }
148 textShape->setStroke(properties.stroke());
149 }
150
151 KUndo2Command *parentCommand = new KUndo2Command();
152
153 new KoKeepShapesSelectedCommand(tool->koSelection()->selectedShapes(), {}, tool->canvas()->selectedShapesProxy(), false, parentCommand);
154
155 KUndo2Command *cmd = tool->canvas()->shapeController()->addShape(textShape, 0, parentCommand);
156 parentCommand->setText(cmd->text());
157
158 if (m_flowShape) {
160
161 KoPathShape *path = dynamic_cast<KoPathShape*>(m_flowShape);
162 KoPathSegment segment;
163 if(path) {
164 segment = path->segmentAtPoint(m_dragStart, tool->handleGrabRect(m_dragStart));
165 }
166 if (segment.isValid()) {
167 int pos = textShape->posForIndex(textShape->plainText().size());
168 new KoSvgTextSetTextPathOnRangeCommand(textShape, m_flowShape, 0, pos, parentCommand);
169
171 const qreal grab = tool->grabSensitivityInPt();
172 QList<KoPathSegment> segments = path->segmentsAt(path->outlineRect().adjusted(-grab, -grab, grab, grab));
173 Q_FOREACH(KoPathSegment s, segments) {
174 if (s == segment) {
175 info.startOffset += (segment.nearestPoint(path->documentToShape(m_dragStart))*segment.length());
176 break;
177 }
178 info.startOffset += s.length();
179 qDebug() << info.startOffset << s.length();
180 }
181 qDebug() << "setting path at..." << info.startOffset << segments.size();
182 new SvgTextPathInfoChangeCommand(textShape, 2, info, parentCommand);
183 } else {
184 new KoSvgTextAddShapeCommand(textShape, m_flowShape, true, parentCommand);
185 }
186 }
187
188 new KoKeepShapesSelectedCommand({}, {textShape}, tool->canvas()->selectedShapesProxy(), true, parentCommand);
189 tool->canvas()->snapGuide()->reset();
190
191 return parentCommand;
192}
193
195{
196 tool()->canvas()->snapGuide()->reset();
197 const QRectF updateRect = QRectF(m_dragStart, m_dragEnd).normalized();
198 tool()->canvas()->updateCanvas(updateRect);
199}
200
201void SvgCreateTextStrategy::finishInteraction(Qt::KeyboardModifiers modifiers)
202{
203 m_modifiers = modifiers;
204}
205
207{
208 QRectF rectangle = QRectF(m_dragStart, m_dragEnd).normalized();
209 return (rectangle.width() >= m_minSizeInline.width() || rectangle.height() >= m_minSizeInline.height()) && !m_modifiers.testFlag(Qt::ControlModifier);
210}
211
213{
214 return (m_flowShape)? true: false;
215}
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()
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)
virtual KoShape * createShape(const KoProperties *params, KoDocumentResourceManager *documentResources=0) const
static KoShapeRegistry * instance()
QPointF absolutePosition(KoFlake::AnchorPosition anchor=KoFlake::Center) const
Definition KoShape.cpp:573
virtual void setPosition(const QPointF &position)
Set the position of the shape in pt.
Definition KoShape.cpp:267
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
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 setProperty(PropertyId id, const QVariant &value)
QVariant propertyOrDefault(PropertyId id) const
KoSvgText::CssLengthPercentage fontSize() const
void inheritFrom(const KoSvgTextProperties &parentProperties, bool resolve=false)
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