Branch data Line data Source code
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 : : #include <QTextDocument> 22 : : #include <QTextBlock> 23 : : 24 : 0 : QgsTextDocument::~QgsTextDocument() = default; 25 : : 26 : 0 : QgsTextDocument::QgsTextDocument() = default; 27 : : 28 : 0 : QgsTextDocument::QgsTextDocument( const QgsTextBlock &block ) 29 : : { 30 : 0 : mBlocks.append( block ); 31 : 0 : } 32 : : 33 : 0 : QgsTextDocument::QgsTextDocument( const QgsTextFragment &fragment ) 34 : : { 35 : 0 : mBlocks.append( QgsTextBlock( fragment ) ); 36 : 0 : } 37 : : 38 : 0 : QgsTextDocument QgsTextDocument::fromPlainText( const QStringList &lines ) 39 : : { 40 : 0 : QgsTextDocument document; 41 : 0 : document.reserve( lines.size() ); 42 : 0 : for ( const QString &line : lines ) 43 : 0 : document.append( QgsTextBlock( QgsTextFragment( line ) ) ); 44 : 0 : return document; 45 : 0 : } 46 : : 47 : 0 : QgsTextDocument QgsTextDocument::fromHtml( const QStringList &lines ) 48 : : { 49 : 0 : QgsTextDocument document; 50 : : 51 : 0 : document.reserve( lines.size() ); 52 : 0 : for ( const QString &line : lines ) 53 : : { 54 : : // QTextDocument is a very heavy way of parsing HTML + css (it's heavily geared toward an editable text document, 55 : : // and includes a LOT of calculations we don't need, when all we're after is a HTML + CSS style parser). 56 : : // TODO - try to find an alternative library we can use here 57 : 0 : QTextDocument sourceDoc; 58 : 0 : sourceDoc.setHtml( line ); 59 : : 60 : 0 : QTextBlock sourceBlock = sourceDoc.firstBlock(); 61 : 0 : while ( true ) 62 : : { 63 : 0 : auto it = sourceBlock.begin(); 64 : 0 : QgsTextBlock block; 65 : 0 : while ( !it.atEnd() ) 66 : : { 67 : 0 : const QTextFragment fragment = it.fragment(); 68 : 0 : if ( fragment.isValid() ) 69 : : { 70 : 0 : block.append( QgsTextFragment( fragment ) ); 71 : 0 : } 72 : 0 : it++; 73 : : } 74 : 0 : if ( !block.empty() ) 75 : 0 : document.append( block ); 76 : : 77 : 0 : sourceBlock = sourceBlock.next(); 78 : 0 : if ( !sourceBlock.isValid() ) 79 : 0 : break; 80 : 0 : } 81 : 0 : } 82 : 0 : return document; 83 : 0 : } 84 : : 85 : 0 : void QgsTextDocument::append( const QgsTextBlock &block ) 86 : : { 87 : 0 : mBlocks.append( block ); 88 : 0 : } 89 : : 90 : 0 : void QgsTextDocument::append( QgsTextBlock &&block ) 91 : : { 92 : 0 : mBlocks.push_back( block ); 93 : 0 : } 94 : : 95 : 0 : void QgsTextDocument::reserve( int count ) 96 : : { 97 : 0 : mBlocks.reserve( count ); 98 : 0 : } 99 : : 100 : 0 : const QgsTextBlock &QgsTextDocument::at( int i ) const 101 : : { 102 : 0 : return mBlocks.at( i ); 103 : : } 104 : : 105 : 0 : QgsTextBlock &QgsTextDocument::operator[]( int i ) 106 : : { 107 : 0 : return mBlocks[i]; 108 : : } 109 : : 110 : 0 : int QgsTextDocument::size() const 111 : : { 112 : 0 : return mBlocks.size(); 113 : : } 114 : : 115 : 0 : QStringList QgsTextDocument::toPlainText() const 116 : : { 117 : 0 : QStringList textLines; 118 : 0 : textLines.reserve( mBlocks.size() ); 119 : 0 : for ( const QgsTextBlock &block : mBlocks ) 120 : : { 121 : 0 : QString line; 122 : 0 : for ( const QgsTextFragment &fragment : block ) 123 : : { 124 : 0 : line.append( fragment.text() ); 125 : : } 126 : 0 : textLines << line; 127 : 0 : } 128 : 0 : return textLines; 129 : 0 : } 130 : : 131 : 0 : void QgsTextDocument::splitLines( const QString &wrapCharacter, int autoWrapLength, bool useMaxLineLengthWhenAutoWrapping ) 132 : : { 133 : 0 : const QVector< QgsTextBlock > prevBlocks = mBlocks; 134 : 0 : mBlocks.clear(); 135 : 0 : mBlocks.reserve( prevBlocks.size() ); 136 : 0 : for ( const QgsTextBlock &block : prevBlocks ) 137 : : { 138 : 0 : QgsTextBlock destinationBlock; 139 : 0 : for ( const QgsTextFragment &fragment : block ) 140 : : { 141 : 0 : QStringList thisParts; 142 : 0 : if ( !wrapCharacter.isEmpty() && wrapCharacter != QLatin1String( "\n" ) ) 143 : : { 144 : : //wrap on both the wrapchr and new line characters 145 : 0 : const QStringList lines = fragment.text().split( wrapCharacter ); 146 : 0 : for ( const QString &line : lines ) 147 : : { 148 : 0 : thisParts.append( line.split( '\n' ) ); 149 : : } 150 : 0 : } 151 : : else 152 : : { 153 : 0 : thisParts = fragment.text().split( '\n' ); 154 : : } 155 : : 156 : : // apply auto wrapping to each manually created line 157 : 0 : if ( autoWrapLength != 0 ) 158 : : { 159 : 0 : QStringList autoWrappedLines; 160 : 0 : autoWrappedLines.reserve( thisParts.count() ); 161 : 0 : for ( const QString &line : std::as_const( thisParts ) ) 162 : : { 163 : 0 : autoWrappedLines.append( QgsStringUtils::wordWrap( line, autoWrapLength, useMaxLineLengthWhenAutoWrapping ).split( '\n' ) ); 164 : : } 165 : 0 : thisParts = autoWrappedLines; 166 : 0 : } 167 : : 168 : 0 : if ( thisParts.empty() ) 169 : 0 : continue; 170 : 0 : else if ( thisParts.size() == 1 ) 171 : 0 : destinationBlock.append( fragment ); 172 : : else 173 : : { 174 : 0 : if ( !thisParts.at( 0 ).isEmpty() ) 175 : 0 : destinationBlock.append( QgsTextFragment( thisParts.at( 0 ), fragment.characterFormat() ) ); 176 : : 177 : 0 : append( destinationBlock ); 178 : 0 : destinationBlock.clear(); 179 : 0 : for ( int i = 1 ; i < thisParts.size() - 1; ++i ) 180 : : { 181 : 0 : append( QgsTextBlock( QgsTextFragment( thisParts.at( i ), fragment.characterFormat() ) ) ); 182 : 0 : } 183 : 0 : destinationBlock.append( QgsTextFragment( thisParts.at( thisParts.size() - 1 ), fragment.characterFormat() ) ); 184 : : } 185 : 0 : } 186 : 0 : append( destinationBlock ); 187 : 0 : } 188 : 0 : } 189 : : 190 : 0 : void QgsTextDocument::applyCapitalization( QgsStringUtils::Capitalization capitalization ) 191 : : { 192 : 0 : for ( QgsTextBlock &block : mBlocks ) 193 : : { 194 : 0 : block.applyCapitalization( capitalization ); 195 : : } 196 : 0 : } 197 : : 198 : : ///@cond PRIVATE 199 : 0 : QVector< QgsTextBlock >::const_iterator QgsTextDocument::begin() const 200 : : { 201 : 0 : return mBlocks.begin(); 202 : : } 203 : : 204 : 0 : QVector< QgsTextBlock >::const_iterator QgsTextDocument::end() const 205 : : { 206 : 0 : return mBlocks.end(); 207 : : } 208 : : ///@endcond