Branch data Line data Source code
1 : : /*************************************************************************** 2 : : qgstextlabelfeature.cpp 3 : : --------------------- 4 : : begin : December 2015 5 : : copyright : (C) 2015 by Martin Dobias 6 : : email : wonder dot sk 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 "qgstextlabelfeature.h" 17 : : 18 : : #include "qgsgeometry.h" 19 : : #include "qgspallabeling.h" 20 : : #include "qgsmaptopixel.h" 21 : : #include "pal/feature.h" 22 : : #include "qgstextcharacterformat.h" 23 : : #include "qgstextfragment.h" 24 : : #include "qgstextblock.h" 25 : : 26 : 0 : QgsTextLabelFeature::QgsTextLabelFeature( QgsFeatureId id, geos::unique_ptr geometry, QSizeF size ) 27 : 0 : : QgsLabelFeature( id, std::move( geometry ), size ) 28 : 0 : { 29 : 0 : mDefinedFont = QFont(); 30 : 0 : } 31 : : 32 : 0 : QgsTextLabelFeature::~QgsTextLabelFeature() = default; 33 : : 34 : 0 : QString QgsTextLabelFeature::text( int partId ) const 35 : : { 36 : 0 : if ( partId == -1 ) 37 : 0 : return mLabelText; 38 : : else 39 : 0 : return mTextMetrics->grapheme( partId ); 40 : 0 : } 41 : : 42 : 0 : QgsTextCharacterFormat QgsTextLabelFeature::characterFormat( int partId ) const 43 : : { 44 : 0 : return mTextMetrics.has_value() ? mTextMetrics->graphemeFormat( partId ) : QgsTextCharacterFormat(); 45 : : } 46 : : 47 : 0 : bool QgsTextLabelFeature::hasCharacterFormat( int partId ) const 48 : : { 49 : 0 : return mTextMetrics.has_value() && partId < mTextMetrics->graphemeFormatCount(); 50 : : } 51 : : 52 : 0 : void QgsTextLabelFeature::setFontMetrics( const QFontMetricsF &metrics ) 53 : : { 54 : 0 : mFontMetrics = metrics; // duplicate metrics for when drawing label 55 : 0 : } 56 : : 57 : 0 : QgsPrecalculatedTextMetrics QgsTextLabelFeature::calculateTextMetrics( const QgsMapToPixel *xform, const QFontMetricsF &fontMetrics, double letterSpacing, double wordSpacing, const QString &text, QgsTextDocument *document ) 58 : : { 59 : : // create label info! 60 : 0 : const double mapScale = xform->mapUnitsPerPixel(); 61 : 0 : const double characterHeight = mapScale * fontMetrics.height(); 62 : 0 : QStringList graphemes; 63 : 0 : QVector< QgsTextCharacterFormat > graphemeFormats; 64 : : 65 : 0 : if ( document ) 66 : : { 67 : 0 : for ( const QgsTextBlock &block : std::as_const( *document ) ) 68 : : { 69 : 0 : for ( const QgsTextFragment &fragment : block ) 70 : : { 71 : 0 : const QStringList fragmentGraphemes = QgsPalLabeling::splitToGraphemes( fragment.text() ); 72 : 0 : for ( const QString &grapheme : fragmentGraphemes ) 73 : : { 74 : 0 : graphemes.append( grapheme ); 75 : 0 : graphemeFormats.append( fragment.characterFormat() ); 76 : : } 77 : 0 : } 78 : : } 79 : 0 : } 80 : : else 81 : : { 82 : : //split string by valid grapheme boundaries - required for certain scripts (see #6883) 83 : 0 : graphemes = QgsPalLabeling::splitToGraphemes( text ); 84 : : } 85 : : 86 : 0 : QVector< double > characterWidths( graphemes.count() ); 87 : 0 : for ( int i = 0; i < graphemes.count(); i++ ) 88 : : { 89 : : // reconstruct how Qt creates word spacing, then adjust per individual stored character 90 : : // this will allow PAL to create each candidate width = character width + correct spacing 91 : : 92 : 0 : qreal wordSpaceFix = qreal( 0.0 ); 93 : 0 : if ( graphemes[i] == QLatin1String( " " ) ) 94 : : { 95 : : // word spacing only gets added once at end of consecutive run of spaces, see QTextEngine::shapeText() 96 : 0 : int nxt = i + 1; 97 : 0 : wordSpaceFix = ( nxt < graphemes.count() && graphemes[nxt] != QLatin1String( " " ) ) ? wordSpacing : qreal( 0.0 ); 98 : 0 : } 99 : : // this workaround only works for clusters with a single character. Not sure how it should be handled 100 : : // with multi-character clusters. 101 : 0 : if ( graphemes[i].length() == 1 && 102 : 0 : !qgsDoubleNear( fontMetrics.horizontalAdvance( QString( graphemes[i].at( 0 ) ) ), fontMetrics.horizontalAdvance( graphemes[i].at( 0 ) ) + letterSpacing ) ) 103 : : { 104 : : // word spacing applied when it shouldn't be 105 : 0 : wordSpaceFix -= wordSpacing; 106 : 0 : } 107 : : 108 : 0 : const double charWidth = fontMetrics.horizontalAdvance( QString( graphemes[i] ) ) + wordSpaceFix; 109 : 0 : characterWidths[i] = mapScale * charWidth; 110 : 0 : } 111 : : 112 : 0 : QgsPrecalculatedTextMetrics res( graphemes, characterHeight, std::move( characterWidths ) ); 113 : 0 : res.setGraphemeFormats( graphemeFormats ); 114 : 0 : return res; 115 : 0 : } 116 : : 117 : 0 : QgsTextDocument QgsTextLabelFeature::document() const 118 : : { 119 : 0 : return mDocument; 120 : : } 121 : : 122 : 0 : void QgsTextLabelFeature::setDocument( const QgsTextDocument &document ) 123 : : { 124 : 0 : mDocument = document; 125 : 0 : }