From 37911c1254a71ce0b57dfbbd2a4735159fb0757d Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Mon, 21 Sep 2020 09:50:31 +1000 Subject: [PATCH 1/2] Add method to change capitalization of QgsTextDocument --- .../auto_generated/textrenderer/qgstextblock.sip.in | 7 +++++++ .../auto_generated/textrenderer/qgstextdocument.sip.in | 7 +++++++ .../auto_generated/textrenderer/qgstextfragment.sip.in | 7 +++++++ src/core/textrenderer/qgstextblock.cpp | 8 ++++++++ src/core/textrenderer/qgstextblock.h | 8 ++++++++ src/core/textrenderer/qgstextdocument.cpp | 8 ++++++++ src/core/textrenderer/qgstextdocument.h | 8 ++++++++ src/core/textrenderer/qgstextfragment.cpp | 5 +++++ src/core/textrenderer/qgstextfragment.h | 8 ++++++++ tests/src/python/test_qgstextblock.py | 10 +++++++++- tests/src/python/test_qgstextdocument.py | 8 +++++++- tests/src/python/test_qgstextfragment.py | 8 +++++++- 12 files changed, 89 insertions(+), 3 deletions(-) diff --git a/python/core/auto_generated/textrenderer/qgstextblock.sip.in b/python/core/auto_generated/textrenderer/qgstextblock.sip.in index 497508ca838d..6f34166648ab 100644 --- a/python/core/auto_generated/textrenderer/qgstextblock.sip.in +++ b/python/core/auto_generated/textrenderer/qgstextblock.sip.in @@ -62,6 +62,13 @@ Returns ``True`` if the block is empty. int size() const; %Docstring Returns the number of fragments in the block. +%End + + void applyCapitalization( QgsStringUtils::Capitalization capitalization ); +%Docstring +Applies a ``capitalization`` style to the block's text. + +.. versionadded:: 3.16 %End int __len__() const; diff --git a/python/core/auto_generated/textrenderer/qgstextdocument.sip.in b/python/core/auto_generated/textrenderer/qgstextdocument.sip.in index 435b48a2c7d2..d0d498432edc 100644 --- a/python/core/auto_generated/textrenderer/qgstextdocument.sip.in +++ b/python/core/auto_generated/textrenderer/qgstextdocument.sip.in @@ -113,6 +113,13 @@ argument controls whether the lines should be wrapped to an ideal maximum of ``a if ``False`` then the lines are wrapped to an ideal minimum length of ``autoWrapLength`` characters. %End + void applyCapitalization( QgsStringUtils::Capitalization capitalization ); +%Docstring +Applies a ``capitalization`` style to the document's text. + +.. versionadded:: 3.16 +%End + }; diff --git a/python/core/auto_generated/textrenderer/qgstextfragment.sip.in b/python/core/auto_generated/textrenderer/qgstextfragment.sip.in index 594e41595a95..a9054299ee60 100644 --- a/python/core/auto_generated/textrenderer/qgstextfragment.sip.in +++ b/python/core/auto_generated/textrenderer/qgstextfragment.sip.in @@ -76,6 +76,13 @@ The optional ``scaleFactor`` parameter can specify a font size scaling factor. I QgsTextRenderer.FONT_WORKAROUND_SCALE and then manually calculations based on the resultant font metrics. Failure to do so will result in poor quality text rendering at small font sizes. +%End + + void applyCapitalization( QgsStringUtils::Capitalization capitalization ); +%Docstring +Applies a ``capitalization`` style to the fragment's text. + +.. versionadded:: 3.16 %End }; diff --git a/src/core/textrenderer/qgstextblock.cpp b/src/core/textrenderer/qgstextblock.cpp index 673aebd53245..464bffcf970c 100644 --- a/src/core/textrenderer/qgstextblock.cpp +++ b/src/core/textrenderer/qgstextblock.cpp @@ -56,6 +56,14 @@ int QgsTextBlock::size() const return mFragments.size(); } +void QgsTextBlock::applyCapitalization( QgsStringUtils::Capitalization capitalization ) +{ + for ( QgsTextFragment &fragment : mFragments ) + { + fragment.applyCapitalization( capitalization ); + } +} + const QgsTextFragment &QgsTextBlock::at( int index ) const { return mFragments.at( index ); diff --git a/src/core/textrenderer/qgstextblock.h b/src/core/textrenderer/qgstextblock.h index a879884e2709..18840c35037b 100644 --- a/src/core/textrenderer/qgstextblock.h +++ b/src/core/textrenderer/qgstextblock.h @@ -19,6 +19,7 @@ #include "qgis_sip.h" #include "qgis_core.h" #include "qgstextfragment.h" +#include "qgsstringutils.h" #include /** @@ -78,6 +79,13 @@ class CORE_EXPORT QgsTextBlock */ int size() const; + /** + * Applies a \a capitalization style to the block's text. + * + * \since QGIS 3.16 + */ + void applyCapitalization( QgsStringUtils::Capitalization capitalization ); + #ifdef SIP_RUN int __len__() const; % MethodCode diff --git a/src/core/textrenderer/qgstextdocument.cpp b/src/core/textrenderer/qgstextdocument.cpp index cf25e7ae84cf..0c9f4fbb8739 100644 --- a/src/core/textrenderer/qgstextdocument.cpp +++ b/src/core/textrenderer/qgstextdocument.cpp @@ -187,6 +187,14 @@ void QgsTextDocument::splitLines( const QString &wrapCharacter, int autoWrapLeng } } +void QgsTextDocument::applyCapitalization( QgsStringUtils::Capitalization capitalization ) +{ + for ( QgsTextBlock &block : mBlocks ) + { + block.applyCapitalization( capitalization ); + } +} + ///@cond PRIVATE QVector< QgsTextBlock >::const_iterator QgsTextDocument::begin() const { diff --git a/src/core/textrenderer/qgstextdocument.h b/src/core/textrenderer/qgstextdocument.h index 173bc557af83..43df966f88d8 100644 --- a/src/core/textrenderer/qgstextdocument.h +++ b/src/core/textrenderer/qgstextdocument.h @@ -18,6 +18,7 @@ #include "qgis_sip.h" #include "qgis_core.h" +#include "qgsstringutils.h" #include @@ -137,6 +138,13 @@ class CORE_EXPORT QgsTextDocument */ void splitLines( const QString &wrapCharacter, int autoWrapLength = 0, bool useMaxLineLengthWhenAutoWrapping = true ); + /** + * Applies a \a capitalization style to the document's text. + * + * \since QGIS 3.16 + */ + void applyCapitalization( QgsStringUtils::Capitalization capitalization ); + #ifndef SIP_RUN ///@cond PRIVATE QVector< QgsTextBlock >::const_iterator begin() const; diff --git a/src/core/textrenderer/qgstextfragment.cpp b/src/core/textrenderer/qgstextfragment.cpp index 8029a66c892b..8bd412f1e466 100644 --- a/src/core/textrenderer/qgstextfragment.cpp +++ b/src/core/textrenderer/qgstextfragment.cpp @@ -68,3 +68,8 @@ double QgsTextFragment::horizontalAdvance( const QFont &font, bool fontHasBeenUp } } +void QgsTextFragment::applyCapitalization( QgsStringUtils::Capitalization capitalization ) +{ + mText = QgsStringUtils::capitalize( mText, capitalization ); +} + diff --git a/src/core/textrenderer/qgstextfragment.h b/src/core/textrenderer/qgstextfragment.h index ede938ef2101..71a10f2023dd 100644 --- a/src/core/textrenderer/qgstextfragment.h +++ b/src/core/textrenderer/qgstextfragment.h @@ -19,6 +19,7 @@ #include "qgis_sip.h" #include "qgis_core.h" #include "qgstextcharacterformat.h" +#include "qgsstringutils.h" class QTextFragment; @@ -87,6 +88,13 @@ class CORE_EXPORT QgsTextFragment */ double horizontalAdvance( const QFont &font, bool fontHasBeenUpdatedForFragment = false, double scaleFactor = 1.0 ) const; + /** + * Applies a \a capitalization style to the fragment's text. + * + * \since QGIS 3.16 + */ + void applyCapitalization( QgsStringUtils::Capitalization capitalization ); + private: QString mText; diff --git a/tests/src/python/test_qgstextblock.py b/tests/src/python/test_qgstextblock.py index 4dd73ce3b28f..73ade38cee88 100644 --- a/tests/src/python/test_qgstextblock.py +++ b/tests/src/python/test_qgstextblock.py @@ -16,7 +16,8 @@ from qgis.core import ( QgsTextBlock, - QgsTextFragment + QgsTextFragment, + QgsStringUtils ) from qgis.testing import start_app, unittest @@ -85,6 +86,13 @@ def testClear(self): self.assertEqual(len(block), 0) self.assertTrue(block.empty()) + def testCapitalize(self): + fragment = QgsTextFragment('ludicrous gibs!') + block = QgsTextBlock(fragment) + block.append(QgsTextFragment('another part')) + block.applyCapitalization(QgsStringUtils.TitleCase) + self.assertEqual(block.toPlainText(), 'Ludicrous Gibs!Another Part') + if __name__ == '__main__': unittest.main() diff --git a/tests/src/python/test_qgstextdocument.py b/tests/src/python/test_qgstextdocument.py index 56c6a2b4bc38..a5a65f166c5b 100644 --- a/tests/src/python/test_qgstextdocument.py +++ b/tests/src/python/test_qgstextdocument.py @@ -18,7 +18,8 @@ QgsTextDocument, QgsTextBlock, QgsTextFragment, - QgsTextCharacterFormat + QgsTextCharacterFormat, + QgsStringUtils ) from qgis.testing import start_app, unittest @@ -160,6 +161,11 @@ def testSplitLines(self): self.assertEqual(len(doc[2]), 1) self.assertEqual(doc[2][0].text(), 'red') + def testCapitalize(self): + doc = QgsTextDocument.fromPlainText(['abc def ghi', 'more text', 'another block']) + doc.applyCapitalization(QgsStringUtils.TitleCase) + self.assertEqual(doc.toPlainText(), ['Abc Def Ghi', 'More Text', 'Another Block']) + if __name__ == '__main__': unittest.main() diff --git a/tests/src/python/test_qgstextfragment.py b/tests/src/python/test_qgstextfragment.py index e53f8097d0d2..a54a9585b95a 100644 --- a/tests/src/python/test_qgstextfragment.py +++ b/tests/src/python/test_qgstextfragment.py @@ -16,7 +16,8 @@ from qgis.core import ( QgsTextFragment, - QgsTextCharacterFormat + QgsTextCharacterFormat, + QgsStringUtils ) from qgis.PyQt.QtGui import QColor from qgis.testing import start_app, unittest @@ -49,6 +50,11 @@ def testSetCharacterFormat(self): self.assertTrue(fragment.characterFormat().textColor().isValid()) self.assertEqual(fragment.characterFormat().textColor().name(), '#ff0000') + def testCapitalize(self): + fragment = QgsTextFragment('ludicrous gibs!') + fragment.applyCapitalization(QgsStringUtils.TitleCase) + self.assertEqual(fragment.text(), 'Ludicrous Gibs!') + if __name__ == '__main__': unittest.main() From f32a528cf9fd087b4dd0b64796a45f8c089dd3d9 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Mon, 21 Sep 2020 09:54:47 +1000 Subject: [PATCH 2/2] Correctly apply capitalization setting whenever QgsTextRenderer is used, not just in labeling Fixes #38898 --- src/core/textrenderer/qgstextrenderer.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/core/textrenderer/qgstextrenderer.cpp b/src/core/textrenderer/qgstextrenderer.cpp index e10713fb8ef7..363056858dbb 100644 --- a/src/core/textrenderer/qgstextrenderer.cpp +++ b/src/core/textrenderer/qgstextrenderer.cpp @@ -79,7 +79,8 @@ void QgsTextRenderer::drawText( const QRectF &rect, double rotation, QgsTextRend tmpFormat.updateDataDefinedProperties( context ); tmpFormat = updateShadowPosition( tmpFormat ); - const QgsTextDocument document = format.allowHtmlFormatting() ? QgsTextDocument::fromHtml( textLines ) : QgsTextDocument::fromPlainText( textLines ); + QgsTextDocument document = format.allowHtmlFormatting() ? QgsTextDocument::fromHtml( textLines ) : QgsTextDocument::fromPlainText( textLines ); + document.applyCapitalization( format.capitalization() ); if ( tmpFormat.background().enabled() ) { @@ -101,7 +102,8 @@ void QgsTextRenderer::drawText( QPointF point, double rotation, QgsTextRenderer: tmpFormat.updateDataDefinedProperties( context ); tmpFormat = updateShadowPosition( tmpFormat ); - const QgsTextDocument document = format.allowHtmlFormatting() ? QgsTextDocument::fromHtml( textLines ) : QgsTextDocument::fromPlainText( textLines ); + QgsTextDocument document = format.allowHtmlFormatting() ? QgsTextDocument::fromHtml( textLines ) : QgsTextDocument::fromPlainText( textLines ); + document.applyCapitalization( format.capitalization() ); if ( tmpFormat.background().enabled() ) { @@ -489,14 +491,17 @@ void QgsTextRenderer::drawMask( QgsRenderContext &context, const QgsTextRenderer double QgsTextRenderer::textWidth( const QgsRenderContext &context, const QgsTextFormat &format, const QStringList &textLines, QFontMetricsF * ) { + QgsTextDocument doc; if ( !format.allowHtmlFormatting() ) { - return textWidth( context, format, QgsTextDocument::fromPlainText( textLines ) ); + doc = QgsTextDocument::fromPlainText( textLines ); } else { - return textWidth( context, format, QgsTextDocument::fromHtml( textLines ) ); + doc = QgsTextDocument::fromHtml( textLines ); } + doc.applyCapitalization( format.capitalization() ); + return textWidth( context, format, doc ); } double QgsTextRenderer::textWidth( const QgsRenderContext &context, const QgsTextFormat &format, const QgsTextDocument &document ) @@ -600,8 +605,11 @@ double QgsTextRenderer::textHeight( const QgsRenderContext &context, const QgsTe return height + maxExtension; } -double QgsTextRenderer::textHeight( const QgsRenderContext &context, const QgsTextFormat &format, const QgsTextDocument &document, DrawMode mode ) +double QgsTextRenderer::textHeight( const QgsRenderContext &context, const QgsTextFormat &format, const QgsTextDocument &doc, DrawMode mode ) { + QgsTextDocument document = doc; + document.applyCapitalization( format.capitalization() ); + //calculate max height of text lines const double scaleFactor = ( context.flags() & QgsRenderContext::ApplyScalingWorkaroundForTextRendering ) ? FONT_WORKAROUND_SCALE : 1.0;