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 : : 33 : 0 : QgsTextLabelFeature::~QgsTextLabelFeature() 34 : 0 : { 35 : 0 : delete mFontMetrics; 36 : 0 : } 37 : : 38 : : 39 : 0 : QString QgsTextLabelFeature::text( int partId ) const 40 : : { 41 : 0 : if ( partId == -1 ) 42 : 0 : return mLabelText; 43 : : else 44 : 0 : return mClusters.at( partId ); 45 : 0 : } 46 : : 47 : 0 : QgsTextCharacterFormat QgsTextLabelFeature::characterFormat( int partId ) const 48 : : { 49 : 0 : return mCharacterFormats.value( partId ); 50 : : } 51 : : 52 : 0 : bool QgsTextLabelFeature::hasCharacterFormat( int partId ) const 53 : : { 54 : 0 : return partId < mCharacterFormats.size(); 55 : : } 56 : : 57 : 0 : void QgsTextLabelFeature::calculateInfo( bool curvedLabeling, QFontMetricsF *fm, const QgsMapToPixel *xform, double maxinangle, double maxoutangle, QgsTextDocument *document ) 58 : : { 59 : 0 : if ( mInfo ) 60 : 0 : return; 61 : : 62 : 0 : mFontMetrics = new QFontMetricsF( *fm ); // duplicate metrics for when drawing label 63 : : 64 : 0 : qreal letterSpacing = mDefinedFont.letterSpacing(); 65 : 0 : qreal wordSpacing = mDefinedFont.wordSpacing(); 66 : : 67 : : // max angle between curved label characters (20.0/-20.0 was default in QGIS <= 1.8) 68 : 0 : if ( maxinangle < 20.0 ) 69 : 0 : maxinangle = 20.0; 70 : 0 : if ( 60.0 < maxinangle ) 71 : 0 : maxinangle = 60.0; 72 : 0 : if ( maxoutangle > -20.0 ) 73 : 0 : maxoutangle = -20.0; 74 : 0 : if ( -95.0 > maxoutangle ) 75 : 0 : maxoutangle = -95.0; 76 : : 77 : : // create label info! 78 : 0 : double mapScale = xform->mapUnitsPerPixel(); 79 : 0 : double labelHeight = mapScale * fm->height(); 80 : : 81 : : // mLetterSpacing/mWordSpacing = 0.0 is default for non-curved labels 82 : : // (non-curved spacings handled by Qt in QgsPalLayerSettings/QgsPalLabeling) 83 : : qreal charWidth; 84 : : qreal wordSpaceFix; 85 : : 86 : 0 : if ( document && curvedLabeling ) 87 : : { 88 : 0 : for ( const QgsTextBlock &block : std::as_const( *document ) ) 89 : : { 90 : 0 : for ( const QgsTextFragment &fragment : block ) 91 : : { 92 : 0 : const QStringList graphemes = QgsPalLabeling::splitToGraphemes( fragment.text() ); 93 : 0 : for ( const QString &grapheme : graphemes ) 94 : : { 95 : 0 : mClusters.append( grapheme ); 96 : 0 : mCharacterFormats.append( fragment.characterFormat() ); 97 : : } 98 : 0 : } 99 : : } 100 : 0 : } 101 : : else 102 : : { 103 : : //split string by valid grapheme boundaries - required for certain scripts (see #6883) 104 : 0 : mClusters = QgsPalLabeling::splitToGraphemes( mLabelText ); 105 : : } 106 : : 107 : 0 : mInfo = new pal::LabelInfo( mClusters.count(), labelHeight, maxinangle, maxoutangle ); 108 : 0 : for ( int i = 0; i < mClusters.count(); i++ ) 109 : : { 110 : : // reconstruct how Qt creates word spacing, then adjust per individual stored character 111 : : // this will allow PAL to create each candidate width = character width + correct spacing 112 : 0 : charWidth = fm->horizontalAdvance( mClusters[i] ); 113 : 0 : if ( curvedLabeling ) 114 : : { 115 : 0 : wordSpaceFix = qreal( 0.0 ); 116 : 0 : if ( mClusters[i] == QLatin1String( " " ) ) 117 : : { 118 : : // word spacing only gets added once at end of consecutive run of spaces, see QTextEngine::shapeText() 119 : 0 : int nxt = i + 1; 120 : 0 : wordSpaceFix = ( nxt < mClusters.count() && mClusters[nxt] != QLatin1String( " " ) ) ? wordSpacing : qreal( 0.0 ); 121 : 0 : } 122 : : // this workaround only works for clusters with a single character. Not sure how it should be handled 123 : : // with multi-character clusters. 124 : 0 : if ( mClusters[i].length() == 1 && 125 : 0 : !qgsDoubleNear( fm->horizontalAdvance( QString( mClusters[i].at( 0 ) ) ), fm->horizontalAdvance( mClusters[i].at( 0 ) ) + letterSpacing ) ) 126 : : { 127 : : // word spacing applied when it shouldn't be 128 : 0 : wordSpaceFix -= wordSpacing; 129 : 0 : } 130 : : 131 : 0 : charWidth = fm->horizontalAdvance( QString( mClusters[i] ) ) + wordSpaceFix; 132 : 0 : } 133 : : 134 : 0 : double labelWidth = mapScale * charWidth; 135 : 0 : mInfo->char_info[i].width = labelWidth; 136 : 0 : } 137 : 0 : } 138 : : 139 : 0 : QgsTextDocument QgsTextLabelFeature::document() const 140 : : { 141 : 0 : return mDocument; 142 : : } 143 : : 144 : 0 : void QgsTextLabelFeature::setDocument( const QgsTextDocument &document ) 145 : : { 146 : 0 : mDocument = document; 147 : 0 : }