1 /* This file is part of the KDE project
2  * Copyright (C) 2007-2008,2011 Jan Hambrecht <jaham@gmx.net>
3  * Copyright (C) 2008 Rob Buis <buis@kde.org>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public License
16  * along with this library; see the file COPYING.LIB.  If not, write to
17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 #ifndef ARTISTICTEXTSHAPE_H
22 #define ARTISTICTEXTSHAPE_H
23 
24 #include "ArtisticTextRange.h"
25 #include <KoShape.h>
26 #include <KoPostscriptPaintDevice.h>
27 #include <SvgShape.h>
28 #include <QFont>
29 #include <QPainterPath>
30 #include <QVector>
31 
32 class QPainter;
33 class KoPathShape;
34 class ArtisticTextLoadingContext;
35 class SvgGraphicsContext;
36 
37 #define ArtisticTextShapeID "ArtisticText"
38 
39 /// Character position within text shape (range index, range character index)
40 typedef QPair<int, int> CharIndex;
41 
42 class ArtisticTextShape : public KoShape, public SvgShape
43 {
44 public:
45     enum TextAnchor { AnchorStart, AnchorMiddle, AnchorEnd };
46 
47     enum LayoutMode {
48         Straight,    ///< baseline is a straight line
49         OnPath,      ///< baseline is a QPainterPath
50         OnPathShape  ///< baseline is the outline of a path shape
51     };
52 
53     ArtisticTextShape();
54     ~ArtisticTextShape() override;
55 
56     /// reimplemented
57     void paint(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintContext) override;
58     /// reimplemented
59     void saveOdf(KoShapeSavingContext & context) const override;
60     /// reimplemented
61     bool loadOdf( const KoXmlElement & element, KoShapeLoadingContext &context ) override;
62     /// reimplemented
63     QSizeF size() const override;
64     /// reimplemented
65     void setSize( const QSizeF &size ) override;
66     /// reimplemented
67     QPainterPath outline() const override;
68     /// reimplemented from SvgShape
69     bool saveSvg(SvgSavingContext &context) override;
70     /// reimplemented from SvgShape
71     bool loadSvg(const KoXmlElement &element, SvgLoadingContext &context) override;
72 
73     /// Sets the plain text to display
74     void setPlainText(const QString &newText);
75 
76     /// Returns the plain text content
77     QString plainText() const;
78 
79     /// Returns formatted text
80     QList<ArtisticTextRange> text() const;
81 
82     /// Returns if text shape is empty, i.e. no text
83     bool isEmpty() const;
84 
85     /// Clears the text shape
86     void clear();
87 
88     /**
89      * Sets the font used for drawing
90      * Note that it is expected that the font has its point size set
91      * in postscript points.
92      */
93     void setFont(const QFont &font);
94 
95     /**
96      * Sets the font for the specified range of characters
97      * @param charIndex the index of the first character of the range
98      * @param charCount the number of characters of the range
99      * @param font the new font to set
100      */
101     void setFont(int charIndex, int charCount, const QFont &font);
102 
103     /**
104      * Returns the font at the specified character position
105      * If the text shape is empty it will return the default font.
106      * If the character index is smaller than zero it will return the font
107      * of the first character. If the character index is greater than the
108      * last character index it will return the font of the last character.
109      */
110     QFont fontAt(int charIndex) const;
111 
112     /// Returns the default font
113     QFont defaultFont() const;
114 
115     /// Attaches this text shape to the given path shape
116     bool putOnPath(KoPathShape *path);
117 
118     /// Puts the text on the given path, the path is expected to be in document coordinates
119     bool putOnPath(const QPainterPath &path);
120 
121     /// Detaches this text shape from an already attached path shape
122     void removeFromPath();
123 
124     /// Returns if shape is attached to a path shape
125     bool isOnPath() const;
126 
127     /// Sets the offset for for text on path
128     void setStartOffset(qreal offset);
129 
130     /// Returns the start offset for text on path
131     qreal startOffset() const;
132 
133     /**
134      * Returns the y-offset from the top-left corner to the baseline.
135      * This is usable for being able to exactly position the texts baseline.
136      * Note: The value makes only sense for text not attached to a path.
137      */
138     qreal baselineOffset() const;
139 
140     /// Sets the text anchor
141     void setTextAnchor(TextAnchor anchor);
142 
143     /// Returns the actual text anchor
144     TextAnchor textAnchor() const;
145 
146     /// Returns the current layout mode
147     LayoutMode layout() const;
148 
149     /// Returns the baseline path
150     QPainterPath baseline() const;
151 
152     /// Returns a pointer to the shape used as baseline
153     KoPathShape * baselineShape() const;
154 
155     /// Removes a range of text starting from the given character
156     QList<ArtisticTextRange> removeText(int charIndex, int charCount);
157 
158     /// Copies a range of text starting from the given character
159     QList<ArtisticTextRange> copyText(int charIndex, int charCount);
160 
161     /// Adds a range of text at the given index
162     void insertText(int charIndex, const QString &plainText);
163 
164     /// Adds range of text at the given index
165     void insertText(int charIndex, const ArtisticTextRange &textRange);
166 
167     /// Adds ranges of text at the given index
168     void insertText(int charIndex, const QList<ArtisticTextRange> &textRanges);
169 
170     /// Appends plain text to the last text range
171     void appendText(const QString &plainText);
172 
173     /// Appends a single formatted range of text
174     void appendText(const ArtisticTextRange &text);
175 
176     /// Replaces a range of text with the specified text range
177     bool replaceText(int charIndex, int charCount, const ArtisticTextRange &textRange);
178 
179     /// Replaces a range of text with the specified text ranges
180     bool replaceText(int charIndex, int charCount, const QList<ArtisticTextRange> &textRanges);
181 
182     /// Gets the angle of the char with the given index
183     qreal charAngleAt(int charIndex) const;
184 
185     /// Gets the position of the char with the given index in shape coordinates
186     QPointF charPositionAt(int charIndex) const;
187 
188     /// Gets the extents of the char with the given index
189     QRectF charExtentsAt(int charIndex) const;
190 
191     /// Returns index of range and index within range of specified character
192     CharIndex indexOfChar(int charIndex) const;
193 
194     /// reimplemented from KoShape
195     void shapeChanged(ChangeType type, KoShape * shape) override;
196 
197 private:
198     void updateSizeAndPosition( bool global = false );
199     bool pathHasChanged() const;
200     void createOutline();
201 
202     void beginTextUpdate();
203     void finishTextUpdate();
204 
205     /// Calculates abstract character positions in baseline coordinates
206     QVector<QPointF> calculateAbstractCharacterPositions();
207 
208     /// Returns the bounding box for an empty text shape
209     QRectF nullBoundBox() const;
210 
211     /// Saves svg font
212     void saveSvgFont(const QFont &font, SvgSavingContext &context);
213     /// Saves svg text range
214     void saveSvgTextRange(const ArtisticTextRange &range, SvgSavingContext &context, bool saveFont, qreal baselineOffset);
215     /// Parse nested text ranges
216     void parseTextRanges(const KoXmlElement &element, SvgLoadingContext &context, ArtisticTextLoadingContext &textContext);
217     /// Creates text range
218     ArtisticTextRange createTextRange(const QString &text, ArtisticTextLoadingContext &context, SvgGraphicsContext *gc);
219 
220     QList<ArtisticTextRange> m_ranges;
221     KoPostscriptPaintDevice m_paintDevice;
222     KoPathShape * m_path; ///< the path shape we are attached to
223     qreal m_startOffset; ///< the offset from the attached path start point
224     QPointF m_outlineOrigin; ///< the top-left corner of the non-normalized text outline
225     QPainterPath m_outline; ///< the actual text outline
226     QPainterPath m_baseline; ///< the baseline path the text is put on
227     TextAnchor m_textAnchor; ///< the actual text anchor
228     QVector<qreal> m_charOffsets; ///< char positions [0..1] on baseline path
229     QVector<QPointF> m_charPositions; ///< char positions in shape coordinates
230     int m_textUpdateCounter;
231     QFont m_defaultFont;
232     bool m_drawBoundaryLines;
233 };
234 
235 #endif // ARTISTICTEXTSHAPE_H
236