QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgstextdocument.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgstextdocument.cpp
3 -----------------
4 begin : May 2020
5 copyright : (C) Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include "qgstextdocument.h"
17#include "qgis.h"
18#include "qgsstringutils.h"
19#include "qgstextblock.h"
20#include "qgstextfragment.h"
21
22#include <QTextDocument>
23#include <QTextBlock>
24
25
27
29
31{
32 mBlocks.append( block );
33}
34
36{
37 mBlocks.append( QgsTextBlock( fragment ) );
38}
39
41{
42 QgsTextDocument document;
43 document.reserve( lines.size() );
44 for ( const QString &line : lines )
45 document.append( QgsTextBlock( QgsTextFragment( line ) ) );
46 return document;
47}
48
49QgsTextDocument QgsTextDocument::fromHtml( const QStringList &lines )
50{
51
52 QgsTextDocument document;
53
54 document.reserve( lines.size() );
55
56 for ( const QString &line : std::as_const( lines ) )
57 {
58 // QTextDocument is a very heavy way of parsing HTML + css (it's heavily geared toward an editable text document,
59 // and includes a LOT of calculations we don't need, when all we're after is a HTML + CSS style parser).
60 // TODO - try to find an alternative library we can use here
61
62 QTextDocument sourceDoc;
63
64 sourceDoc.setHtml( line );
65
66 QTextBlock sourceBlock = sourceDoc.firstBlock();
67
68 while ( true )
69 {
70 auto it = sourceBlock.begin();
71 QgsTextBlock block;
72 while ( !it.atEnd() )
73 {
74 const QTextFragment fragment = it.fragment();
75 if ( fragment.isValid() )
76 {
77 // Search for line breaks in the fragment
78 if ( fragment.text().contains( QStringLiteral( "\u2028" ) ) )
79 {
80
81 // Flush last block
82 if ( !block.empty() )
83 {
84 document.append( block );
85 block.clear();
86 }
87
88 // Split fragment text into lines
89 const QStringList splitLines = fragment.text().split( QStringLiteral( "\u2028" ), Qt::SplitBehaviorFlags::SkipEmptyParts );
90
91 for ( const QString &splitLine : std::as_const( splitLines ) )
92 {
93 QgsTextBlock splitLineBlock;
94
95 QgsTextFragment splitFragment( fragment );
96 splitFragment.setText( splitLine );
97
98 const QgsTextCharacterFormat *previousFormat = nullptr;
99
100 // If the splitLine is not the first, inherit style from previous fragment
101 if ( splitLine != splitLines.first() && document.size() > 0 )
102 {
103 previousFormat = &document.at( document.size() - 1 ).at( 0 ).characterFormat();
104 }
105
106 if ( previousFormat )
107 {
108 // Apply overrides from previous fragment
109 QgsTextCharacterFormat newFormat { splitFragment.characterFormat() };
110 newFormat.overrideWith( *previousFormat );
111 splitFragment.setCharacterFormat( newFormat );
112 }
113
114 splitLineBlock.append( splitFragment );
115 document.append( splitLineBlock );
116
117 }
118 }
119 else
120 {
121 block.append( QgsTextFragment( fragment ) );
122 }
123 }
124 it++;
125 }
126
127 if ( !block.empty() )
128 document.append( block );
129
130 sourceBlock = sourceBlock.next();
131 if ( !sourceBlock.isValid() )
132 break;
133 }
134 }
135
136 return document;
137}
138
140{
141 mBlocks.append( block );
142}
143
145{
146 mBlocks.push_back( block );
147}
148
150{
151 mBlocks.reserve( count );
152}
153
155{
156 return mBlocks.at( i );
157}
158
160{
161 return mBlocks[i];
162}
163
165{
166 return mBlocks.size();
167}
168
170{
171 QStringList textLines;
172 textLines.reserve( mBlocks.size() );
173 for ( const QgsTextBlock &block : mBlocks )
174 {
175 QString line;
176 for ( const QgsTextFragment &fragment : block )
177 {
178 line.append( fragment.text() );
179 }
180 textLines << line;
181 }
182 return textLines;
183}
184
185void QgsTextDocument::splitLines( const QString &wrapCharacter, int autoWrapLength, bool useMaxLineLengthWhenAutoWrapping )
186{
187 const QVector< QgsTextBlock > prevBlocks = mBlocks;
188 mBlocks.clear();
189 mBlocks.reserve( prevBlocks.size() );
190 for ( const QgsTextBlock &block : prevBlocks )
191 {
192 QgsTextBlock destinationBlock;
193 for ( const QgsTextFragment &fragment : block )
194 {
195 QStringList thisParts;
196 if ( !wrapCharacter.isEmpty() && wrapCharacter != QLatin1String( "\n" ) )
197 {
198 //wrap on both the wrapchr and new line characters
199 const QStringList lines = fragment.text().split( wrapCharacter );
200 for ( const QString &line : lines )
201 {
202 thisParts.append( line.split( '\n' ) );
203 }
204 }
205 else
206 {
207 thisParts = fragment.text().split( '\n' );
208 }
209
210 // apply auto wrapping to each manually created line
211 if ( autoWrapLength != 0 )
212 {
213 QStringList autoWrappedLines;
214 autoWrappedLines.reserve( thisParts.count() );
215 for ( const QString &line : std::as_const( thisParts ) )
216 {
217 autoWrappedLines.append( QgsStringUtils::wordWrap( line, autoWrapLength, useMaxLineLengthWhenAutoWrapping ).split( '\n' ) );
218 }
219 thisParts = autoWrappedLines;
220 }
221
222 if ( thisParts.empty() )
223 continue;
224 else if ( thisParts.size() == 1 )
225 destinationBlock.append( fragment );
226 else
227 {
228 if ( !thisParts.at( 0 ).isEmpty() )
229 destinationBlock.append( QgsTextFragment( thisParts.at( 0 ), fragment.characterFormat() ) );
230
231 append( destinationBlock );
232 destinationBlock.clear();
233 for ( int i = 1 ; i < thisParts.size() - 1; ++i )
234 {
235 append( QgsTextBlock( QgsTextFragment( thisParts.at( i ), fragment.characterFormat() ) ) );
236 }
237 destinationBlock.append( QgsTextFragment( thisParts.at( thisParts.size() - 1 ), fragment.characterFormat() ) );
238 }
239 }
240 append( destinationBlock );
241 }
242}
243
245{
246 for ( QgsTextBlock &block : mBlocks )
247 {
248 block.applyCapitalization( capitalization );
249 }
250}
251
253QVector< QgsTextBlock >::const_iterator QgsTextDocument::begin() const
254{
255 return mBlocks.begin();
256}
257
258QVector< QgsTextBlock >::const_iterator QgsTextDocument::end() const
259{
260 return mBlocks.end();
261}
Capitalization
String capitalization options.
Definition: qgis.h:2747
static QString wordWrap(const QString &string, int length, bool useMaxLineLength=true, const QString &customDelimiter=QString())
Automatically wraps a string by inserting new line characters at appropriate locations in the string.
Represents a block of text consisting of one or more QgsTextFragment objects.
Definition: qgstextblock.h:36
void clear()
Clears the block, removing all its contents.
void append(const QgsTextFragment &fragment)
Appends a fragment to the block.
const QgsTextFragment & at(int index) const
Returns the fragment at the specified index.
bool empty() const
Returns true if the block is empty.
Stores information relating to individual character formatting.
void overrideWith(const QgsTextCharacterFormat &other)
Override all the default/unset properties of the current character format with the settings from anot...
Represents a document consisting of one or more QgsTextBlock objects.
void splitLines(const QString &wrapCharacter, int autoWrapLength=0, bool useMaxLineLengthWhenAutoWrapping=true)
Splits lines of text in the document to separate lines, using a specified wrap character (wrapCharact...
QgsTextBlock & operator[](int index)
Returns the block at the specified index.
const QgsTextBlock & at(int index) const
Returns the block at the specified index.
void reserve(int count)
Reserves the specified count of blocks for optimised block appending.
QStringList toPlainText() const
Returns a list of plain text lines of text representing the document.
int size() const
Returns the number of blocks in the document.
static QgsTextDocument fromHtml(const QStringList &lines)
Constructor for QgsTextDocument consisting of a set of HTML formatted lines.
static QgsTextDocument fromPlainText(const QStringList &lines)
Constructor for QgsTextDocument consisting of a set of plain text lines.
void append(const QgsTextBlock &block)
Appends a block to the document.
void applyCapitalization(Qgis::Capitalization capitalization)
Applies a capitalization style to the document's text.
Stores a fragment of text along with formatting overrides to be used when rendering the fragment.
void setText(const QString &text)
Sets the text content of the fragment.
void setCharacterFormat(const QgsTextCharacterFormat &format)
Sets the character format for the fragment.
const QgsTextCharacterFormat & characterFormat() const
Returns the character formatting for the fragment.