diff --git a/python/PyQt6/core/auto_additions/qgstextblockformat.py b/python/PyQt6/core/auto_additions/qgstextblockformat.py new file mode 100644 index 0000000000000..67d60ab395352 --- /dev/null +++ b/python/PyQt6/core/auto_additions/qgstextblockformat.py @@ -0,0 +1,17 @@ +# The following has been generated automatically from src/core/textrenderer/qgstextblockformat.h +# monkey patching scoped based enum +QgsTextBlockFormat.BooleanValue.NotSet.__doc__ = "Property is not set" +QgsTextBlockFormat.BooleanValue.SetTrue.__doc__ = "Property is set and ``True``" +QgsTextBlockFormat.BooleanValue.SetFalse.__doc__ = "Property is set and ``False``" +QgsTextBlockFormat.BooleanValue.__doc__ = """Status values for boolean format properties + +* ``NotSet``: Property is not set +* ``SetTrue``: Property is set and ``True`` +* ``SetFalse``: Property is set and ``False`` + +""" +# -- +try: + QgsTextBlockFormat.__group__ = ['textrenderer'] +except NameError: + pass diff --git a/python/PyQt6/core/auto_generated/textrenderer/qgstextblock.sip.in b/python/PyQt6/core/auto_generated/textrenderer/qgstextblock.sip.in index d42f48a0fd262..cdb9926d9e453 100644 --- a/python/PyQt6/core/auto_generated/textrenderer/qgstextblock.sip.in +++ b/python/PyQt6/core/auto_generated/textrenderer/qgstextblock.sip.in @@ -72,6 +72,24 @@ Returns ``True`` if the block is empty. int size() const; %Docstring Returns the number of fragments in the block. +%End + + const QgsTextBlockFormat &blockFormat() const; +%Docstring +Returns the block formatting for the fragment. + +.. seealso:: :py:func:`setBlockFormat` + +.. versionadded:: 3.40 +%End + + void setBlockFormat( const QgsTextBlockFormat &format ); +%Docstring +Sets the block ``format`` for the fragment. + +.. seealso:: :py:func:`blockFormat` + +.. versionadded:: 3.40 %End void applyCapitalization( Qgis::Capitalization capitalization ); diff --git a/python/PyQt6/core/auto_generated/textrenderer/qgstextblockformat.sip.in b/python/PyQt6/core/auto_generated/textrenderer/qgstextblockformat.sip.in new file mode 100644 index 0000000000000..cc649266a738f --- /dev/null +++ b/python/PyQt6/core/auto_generated/textrenderer/qgstextblockformat.sip.in @@ -0,0 +1,123 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/textrenderer/qgstextblockformat.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.py again * + ************************************************************************/ + + + + + +class QgsTextBlockFormat +{ +%Docstring(signature="appended") +Stores information relating to individual block formatting. + +These options encapsulate formatting options which override the default +settings from a :py:class:`QgsTextFormat` for individual text blocks. + +.. warning:: + + This API is not considered stable and may change in future QGIS versions. + +.. versionadded:: 3.40 +%End + +%TypeHeaderCode +#include "qgstextblockformat.h" +%End + public: + + QgsTextBlockFormat(); + + QgsTextBlockFormat( const QTextBlockFormat &format ); +%Docstring +Constructor for QgsTextBlockFormat, based on the specified QTextBlockFormat ``format``. +%End + + enum class BooleanValue + { + NotSet, + SetTrue, + SetFalse, + }; + + void overrideWith( const QgsTextBlockFormat &other ); +%Docstring +Override all the default/unset properties of the current block format +with the settings from another format. + +This will replace any default/unset existing settings with the +settings from ``other``. + +Any settings which are default/unset in ``other`` will be left unchanged. + +:param other: The format to override with. +%End + + bool hasHorizontalAlignmentSet() const; +%Docstring +Returns ``True`` if the format has an explicit horizontal alignment set. + +If ``False`` is returned then the horizontal alignment will be inherited. + +.. seealso:: :py:func:`setHasHorizontalAlignmentSet` + +.. seealso:: :py:func:`horizontalAlignment` +%End + + void setHasHorizontalAlignmentSet( bool set ); +%Docstring +Sets whether the format has an explicit horizontal alignment ``set``. + +If ``set`` is ``False`` then the horizontal alignment will be inherited. + +.. seealso:: :py:func:`hasHorizontalAlignmentSet` + +.. seealso:: :py:func:`setHorizontalAlignment` +%End + + Qgis::TextHorizontalAlignment horizontalAlignment() const; +%Docstring +Returns the format horizontal alignment. + +This property is only respected if :py:func:`~QgsTextBlockFormat.hasHorizontalAlignmentSet` is ``True``. + +.. seealso:: :py:func:`hasHorizontalAlignmentSet` + +.. seealso:: :py:func:`setHorizontalAlignment` +%End + + void setHorizontalAlignment( Qgis::TextHorizontalAlignment alignment ); +%Docstring +Sets the format horizontal ``alignment``. + +This property is only respected if :py:func:`~QgsTextBlockFormat.hasHorizontalAlignmentSet` is ``True``. + +.. seealso:: :py:func:`hasHorizontalAlignmentSet` + +.. seealso:: :py:func:`horizontalAlignment` +%End + + void updateFontForFormat( QFont &font, const QgsRenderContext &context, double scaleFactor = 1.0 ) const; +%Docstring +Updates the specified ``font`` in place, applying block formatting options which +are applicable on a font level when rendered in the given ``context``. + +The optional ``scaleFactor`` parameter can specify a font size scaling factor. It is recommended to set this to +:py:func:`QgsTextRenderer.calculateScaleFactorForFormat()` 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 + +}; + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/textrenderer/qgstextblockformat.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.py again * + ************************************************************************/ diff --git a/python/PyQt6/core/core_auto.sip b/python/PyQt6/core/core_auto.sip index 853d4d954e9f4..a7ba6b291f9bc 100644 --- a/python/PyQt6/core/core_auto.sip +++ b/python/PyQt6/core/core_auto.sip @@ -726,6 +726,7 @@ %Include auto_generated/textrenderer/qgsfontmanager.sip %Include auto_generated/textrenderer/qgstextbackgroundsettings.sip %Include auto_generated/textrenderer/qgstextblock.sip +%Include auto_generated/textrenderer/qgstextblockformat.sip %Include auto_generated/textrenderer/qgstextbuffersettings.sip %Include auto_generated/textrenderer/qgstextcharacterformat.sip %Include auto_generated/textrenderer/qgstextdocument.sip diff --git a/python/core/auto_additions/qgstextblockformat.py b/python/core/auto_additions/qgstextblockformat.py new file mode 100644 index 0000000000000..67d60ab395352 --- /dev/null +++ b/python/core/auto_additions/qgstextblockformat.py @@ -0,0 +1,17 @@ +# The following has been generated automatically from src/core/textrenderer/qgstextblockformat.h +# monkey patching scoped based enum +QgsTextBlockFormat.BooleanValue.NotSet.__doc__ = "Property is not set" +QgsTextBlockFormat.BooleanValue.SetTrue.__doc__ = "Property is set and ``True``" +QgsTextBlockFormat.BooleanValue.SetFalse.__doc__ = "Property is set and ``False``" +QgsTextBlockFormat.BooleanValue.__doc__ = """Status values for boolean format properties + +* ``NotSet``: Property is not set +* ``SetTrue``: Property is set and ``True`` +* ``SetFalse``: Property is set and ``False`` + +""" +# -- +try: + QgsTextBlockFormat.__group__ = ['textrenderer'] +except NameError: + pass diff --git a/python/core/auto_generated/textrenderer/qgstextblock.sip.in b/python/core/auto_generated/textrenderer/qgstextblock.sip.in index d43a3d3432897..b8bb97b9f67eb 100644 --- a/python/core/auto_generated/textrenderer/qgstextblock.sip.in +++ b/python/core/auto_generated/textrenderer/qgstextblock.sip.in @@ -72,6 +72,24 @@ Returns ``True`` if the block is empty. int size() const; %Docstring Returns the number of fragments in the block. +%End + + const QgsTextBlockFormat &blockFormat() const; +%Docstring +Returns the block formatting for the fragment. + +.. seealso:: :py:func:`setBlockFormat` + +.. versionadded:: 3.40 +%End + + void setBlockFormat( const QgsTextBlockFormat &format ); +%Docstring +Sets the block ``format`` for the fragment. + +.. seealso:: :py:func:`blockFormat` + +.. versionadded:: 3.40 %End void applyCapitalization( Qgis::Capitalization capitalization ); diff --git a/python/core/auto_generated/textrenderer/qgstextblockformat.sip.in b/python/core/auto_generated/textrenderer/qgstextblockformat.sip.in new file mode 100644 index 0000000000000..cc649266a738f --- /dev/null +++ b/python/core/auto_generated/textrenderer/qgstextblockformat.sip.in @@ -0,0 +1,123 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/textrenderer/qgstextblockformat.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.py again * + ************************************************************************/ + + + + + +class QgsTextBlockFormat +{ +%Docstring(signature="appended") +Stores information relating to individual block formatting. + +These options encapsulate formatting options which override the default +settings from a :py:class:`QgsTextFormat` for individual text blocks. + +.. warning:: + + This API is not considered stable and may change in future QGIS versions. + +.. versionadded:: 3.40 +%End + +%TypeHeaderCode +#include "qgstextblockformat.h" +%End + public: + + QgsTextBlockFormat(); + + QgsTextBlockFormat( const QTextBlockFormat &format ); +%Docstring +Constructor for QgsTextBlockFormat, based on the specified QTextBlockFormat ``format``. +%End + + enum class BooleanValue + { + NotSet, + SetTrue, + SetFalse, + }; + + void overrideWith( const QgsTextBlockFormat &other ); +%Docstring +Override all the default/unset properties of the current block format +with the settings from another format. + +This will replace any default/unset existing settings with the +settings from ``other``. + +Any settings which are default/unset in ``other`` will be left unchanged. + +:param other: The format to override with. +%End + + bool hasHorizontalAlignmentSet() const; +%Docstring +Returns ``True`` if the format has an explicit horizontal alignment set. + +If ``False`` is returned then the horizontal alignment will be inherited. + +.. seealso:: :py:func:`setHasHorizontalAlignmentSet` + +.. seealso:: :py:func:`horizontalAlignment` +%End + + void setHasHorizontalAlignmentSet( bool set ); +%Docstring +Sets whether the format has an explicit horizontal alignment ``set``. + +If ``set`` is ``False`` then the horizontal alignment will be inherited. + +.. seealso:: :py:func:`hasHorizontalAlignmentSet` + +.. seealso:: :py:func:`setHorizontalAlignment` +%End + + Qgis::TextHorizontalAlignment horizontalAlignment() const; +%Docstring +Returns the format horizontal alignment. + +This property is only respected if :py:func:`~QgsTextBlockFormat.hasHorizontalAlignmentSet` is ``True``. + +.. seealso:: :py:func:`hasHorizontalAlignmentSet` + +.. seealso:: :py:func:`setHorizontalAlignment` +%End + + void setHorizontalAlignment( Qgis::TextHorizontalAlignment alignment ); +%Docstring +Sets the format horizontal ``alignment``. + +This property is only respected if :py:func:`~QgsTextBlockFormat.hasHorizontalAlignmentSet` is ``True``. + +.. seealso:: :py:func:`hasHorizontalAlignmentSet` + +.. seealso:: :py:func:`horizontalAlignment` +%End + + void updateFontForFormat( QFont &font, const QgsRenderContext &context, double scaleFactor = 1.0 ) const; +%Docstring +Updates the specified ``font`` in place, applying block formatting options which +are applicable on a font level when rendered in the given ``context``. + +The optional ``scaleFactor`` parameter can specify a font size scaling factor. It is recommended to set this to +:py:func:`QgsTextRenderer.calculateScaleFactorForFormat()` 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 + +}; + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/textrenderer/qgstextblockformat.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.py again * + ************************************************************************/ diff --git a/python/core/core_auto.sip b/python/core/core_auto.sip index 853d4d954e9f4..a7ba6b291f9bc 100644 --- a/python/core/core_auto.sip +++ b/python/core/core_auto.sip @@ -726,6 +726,7 @@ %Include auto_generated/textrenderer/qgsfontmanager.sip %Include auto_generated/textrenderer/qgstextbackgroundsettings.sip %Include auto_generated/textrenderer/qgstextblock.sip +%Include auto_generated/textrenderer/qgstextblockformat.sip %Include auto_generated/textrenderer/qgstextbuffersettings.sip %Include auto_generated/textrenderer/qgstextcharacterformat.sip %Include auto_generated/textrenderer/qgstextdocument.sip diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 506745ab98b2a..49f414628430d 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -359,6 +359,7 @@ set(QGIS_CORE_SRCS textrenderer/qgsfontmanager.cpp textrenderer/qgstextbackgroundsettings.cpp textrenderer/qgstextblock.cpp + textrenderer/qgstextblockformat.cpp textrenderer/qgstextbuffersettings.cpp textrenderer/qgstextcharacterformat.cpp textrenderer/qgstextdocument.cpp @@ -1981,6 +1982,7 @@ set(QGIS_CORE_HDRS textrenderer/qgsfontmanager.h textrenderer/qgstextbackgroundsettings.h textrenderer/qgstextblock.h + textrenderer/qgstextblockformat.h textrenderer/qgstextbuffersettings.h textrenderer/qgstextcharacterformat.h textrenderer/qgstextdocument.h diff --git a/src/core/textrenderer/qgstextblock.cpp b/src/core/textrenderer/qgstextblock.cpp index fb90ca5ce191b..b93f3c0af19a5 100644 --- a/src/core/textrenderer/qgstextblock.cpp +++ b/src/core/textrenderer/qgstextblock.cpp @@ -86,6 +86,11 @@ int QgsTextBlock::size() const return mFragments.size(); } +void QgsTextBlock::setBlockFormat( const QgsTextBlockFormat &format ) +{ + mBlockFormat = format; +} + void QgsTextBlock::applyCapitalization( Qgis::Capitalization capitalization ) { for ( QgsTextFragment &fragment : mFragments ) diff --git a/src/core/textrenderer/qgstextblock.h b/src/core/textrenderer/qgstextblock.h index 17cf46f2cbf70..9c5501105c224 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 "qgstextblockformat.h" #include "qgsstringutils.h" #include @@ -89,6 +90,24 @@ class CORE_EXPORT QgsTextBlock */ int size() const; + /** + * Returns the block formatting for the fragment. + * + * \see setBlockFormat() + * + * \since QGIS 3.40 + */ + const QgsTextBlockFormat &blockFormat() const { return mBlockFormat; } + + /** + * Sets the block \a format for the fragment. + * + * \see blockFormat() + * + * \since QGIS 3.40 + */ + void setBlockFormat( const QgsTextBlockFormat &format ); + /** * Applies a \a capitalization style to the block's text. * @@ -154,7 +173,7 @@ class CORE_EXPORT QgsTextBlock private: QVector< QgsTextFragment > mFragments; - + QgsTextBlockFormat mBlockFormat; }; #endif // QGSTEXTBLOCK_H diff --git a/src/core/textrenderer/qgstextblockformat.cpp b/src/core/textrenderer/qgstextblockformat.cpp new file mode 100644 index 0000000000000..b926f33180166 --- /dev/null +++ b/src/core/textrenderer/qgstextblockformat.cpp @@ -0,0 +1,64 @@ +/*************************************************************************** + qgstextblockformat.cpp + ----------------- + begin : September 2024 + copyright : (C) Nyall Dawson + email : nyall dot dawson at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgstextblockformat.h" +#include "qgsrendercontext.h" +#include "qgsfontutils.h" + +#include + + +Qgis::TextHorizontalAlignment convertTextBlockFormatAlign( const QTextBlockFormat &format, bool &set ) +{ + set = format.hasProperty( QTextFormat::BlockAlignment ); + if ( format.alignment() & Qt::AlignLeft ) + { + return Qgis::TextHorizontalAlignment::Left; + } + else if ( format.alignment() & Qt::AlignRight ) + { + return Qgis::TextHorizontalAlignment::Right; + } + else if ( format.alignment() & Qt::AlignHCenter ) + { + return Qgis::TextHorizontalAlignment::Center; + } + else if ( format.alignment() & Qt::AlignJustify) + { + return Qgis::TextHorizontalAlignment::Justify; + } + + set = false; + return Qgis::TextHorizontalAlignment::Left; +} + +QgsTextBlockFormat::QgsTextBlockFormat( const QTextBlockFormat &format ) +{ + mHorizontalAlign = convertTextBlockFormatAlign( format, mHasHorizontalAlignSet ); +} + +void QgsTextBlockFormat::overrideWith( const QgsTextBlockFormat &other ) +{ + if ( mHasHorizontalAlignSet && other.hasHorizontalAlignmentSet() ) + { + mHorizontalAlign = other.mHorizontalAlign; + mHasHorizontalAlignSet = true; + } +} + +void QgsTextBlockFormat::updateFontForFormat( QFont &, const QgsRenderContext &, const double ) const +{ + +} diff --git a/src/core/textrenderer/qgstextblockformat.h b/src/core/textrenderer/qgstextblockformat.h new file mode 100644 index 0000000000000..fb9bae6b9bc87 --- /dev/null +++ b/src/core/textrenderer/qgstextblockformat.h @@ -0,0 +1,131 @@ +/*************************************************************************** + qgstextblockformat.h + ----------------- + begin : September 2024 + copyright : (C) Nyall Dawson + email : nyall dot dawson at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSTEXTBLOCKFORMAT_H +#define QGSTEXTBLOCKFORMAT_H + +#include "qgis_sip.h" +#include "qgis_core.h" +#include "qgis.h" + +#include +#include + +class QTextBlockFormat; +class QgsRenderContext; + +/** + * \class QgsTextBlockFormat + * \ingroup core + * \brief Stores information relating to individual block formatting. + * + * These options encapsulate formatting options which override the default + * settings from a QgsTextFormat for individual text blocks. + * + * \warning This API is not considered stable and may change in future QGIS versions. + * + * \since QGIS 3.40 + */ +class CORE_EXPORT QgsTextBlockFormat +{ + public: + + QgsTextBlockFormat() = default; + + /** + * Constructor for QgsTextBlockFormat, based on the specified QTextBlockFormat \a format. + */ + QgsTextBlockFormat( const QTextBlockFormat &format ); + + //! Status values for boolean format properties + enum class BooleanValue + { + NotSet, //!< Property is not set + SetTrue, //!< Property is set and TRUE + SetFalse, //!< Property is set and FALSE + }; + + /** + * Override all the default/unset properties of the current block format + * with the settings from another format. + * + * This will replace any default/unset existing settings with the + * settings from \a other. + * + * Any settings which are default/unset in \a other will be left unchanged. + * + * \param other The format to override with. + */ + void overrideWith( const QgsTextBlockFormat &other ); + + /** + * Returns TRUE if the format has an explicit horizontal alignment set. + * + * If FALSE is returned then the horizontal alignment will be inherited. + * + * \see setHasHorizontalAlignmentSet() + * \see horizontalAlignment() + */ + bool hasHorizontalAlignmentSet() const { return mHasHorizontalAlignSet; } + + /** + * Sets whether the format has an explicit horizontal alignment \a set. + * + * If \a set is FALSE then the horizontal alignment will be inherited. + * + * \see hasHorizontalAlignmentSet() + * \see setHorizontalAlignment() + */ + void setHasHorizontalAlignmentSet( bool set ) { mHasHorizontalAlignSet = set; } + + /** + * Returns the format horizontal alignment. + * + * This property is only respected if hasHorizontalAlignmentSet() is TRUE. + * + * \see hasHorizontalAlignmentSet() + * \see setHorizontalAlignment() + */ + Qgis::TextHorizontalAlignment horizontalAlignment() const { return mHorizontalAlign; } + + /** + * Sets the format horizontal \a alignment. + * + * This property is only respected if hasHorizontalAlignmentSet() is TRUE. + * + * \see hasHorizontalAlignmentSet() + * \see horizontalAlignment() + */ + void setHorizontalAlignment( Qgis::TextHorizontalAlignment alignment ) { mHorizontalAlign = alignment; } + + /** + * Updates the specified \a font in place, applying block formatting options which + * are applicable on a font level when rendered in the given \a context. + * + * The optional \a scaleFactor parameter can specify a font size scaling factor. It is recommended to set this to + * QgsTextRenderer::calculateScaleFactorForFormat() 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. + */ + void updateFontForFormat( QFont &font, const QgsRenderContext &context, double scaleFactor = 1.0 ) const; + + private: + + bool mHasHorizontalAlignSet = false; + Qgis::TextHorizontalAlignment mHorizontalAlign = Qgis::TextHorizontalAlignment::Left; + +}; + +#endif // QGSTEXTBLOCKFORMAT_H diff --git a/src/core/textrenderer/qgstextdocument.cpp b/src/core/textrenderer/qgstextdocument.cpp index 747b98fca975a..e17f8a0f42892 100644 --- a/src/core/textrenderer/qgstextdocument.cpp +++ b/src/core/textrenderer/qgstextdocument.cpp @@ -117,6 +117,7 @@ QgsTextDocument QgsTextDocument::fromHtml( const QStringList &lines ) auto it = sourceBlock.begin(); QgsTextBlock block; + block.setBlockFormat( QgsTextBlockFormat( sourceBlock.blockFormat() ) ); while ( !it.atEnd() ) { const QTextFragment fragment = it.fragment(); @@ -189,6 +190,7 @@ QgsTextDocument QgsTextDocument::fromHtml( const QStringList &lines ) document.append( block ); block = QgsTextBlock(); + block.setBlockFormat( QgsTextBlockFormat( sourceBlock.blockFormat() ) ); } } else if ( fragmentText.contains( QStringLiteral( TAB_REPLACEMENT_MARKER ) ) ) @@ -296,6 +298,7 @@ void QgsTextDocument::splitLines( const QString &wrapCharacter, int autoWrapLeng for ( const QgsTextBlock &block : prevBlocks ) { QgsTextBlock destinationBlock; + destinationBlock.setBlockFormat( block.blockFormat() ); for ( const QgsTextFragment &fragment : block ) { QStringList thisParts; @@ -338,7 +341,9 @@ void QgsTextDocument::splitLines( const QString &wrapCharacter, int autoWrapLeng destinationBlock.clear(); for ( int i = 1 ; i < thisParts.size() - 1; ++i ) { - append( QgsTextBlock( QgsTextFragment( thisParts.at( i ), fragment.characterFormat() ) ) ); + QgsTextBlock partBlock( QgsTextFragment( thisParts.at( i ), fragment.characterFormat() ) ); + partBlock.setBlockFormat( block.blockFormat() ); + append( partBlock ); } destinationBlock.append( QgsTextFragment( thisParts.at( thisParts.size() - 1 ), fragment.characterFormat() ) ); } diff --git a/src/core/textrenderer/qgstextrenderer.cpp b/src/core/textrenderer/qgstextrenderer.cpp index 1cd3fbba1d309..ab05f28a7db8d 100644 --- a/src/core/textrenderer/qgstextrenderer.cpp +++ b/src/core/textrenderer/qgstextrenderer.cpp @@ -1689,7 +1689,7 @@ void QgsTextRenderer::applyExtraSpacingForLineJustification( QFont &font, double font.setLetterSpacing( QFont::AbsoluteSpacing, prevLetterSpace + extraLetterSpace ); } -void QgsTextRenderer::drawTextInternalHorizontal( QgsRenderContext &context, const QgsTextFormat &format, Qgis::TextComponent drawType, Qgis::TextLayoutMode mode, const Component &component, const QgsTextDocument &document, const QgsTextDocumentMetrics &metrics, double fontScale, Qgis::TextHorizontalAlignment hAlignment, +void QgsTextRenderer::drawTextInternalHorizontal( QgsRenderContext &context, const QgsTextFormat &format, Qgis::TextComponent drawType, Qgis::TextLayoutMode mode, const Component &component, const QgsTextDocument &document, const QgsTextDocumentMetrics &metrics, double fontScale, const Qgis::TextHorizontalAlignment hAlignment, Qgis::TextVerticalAlignment vAlignment, double rotation ) { QPainter *maskPainter = context.maskPainter( context.currentMaskId() ); @@ -1714,8 +1714,6 @@ void QgsTextRenderer::drawTextInternalHorizontal( QgsRenderContext &context, con double verticalAlignOffset = 0; - bool adjustForAlignment = hAlignment != Qgis::TextHorizontalAlignment::Left && ( mode != Qgis::TextLayoutMode::Labeling || textLines.size() > 1 ); - if ( mode == Qgis::TextLayoutMode::Rectangle && vAlignment != Qgis::TextVerticalAlignment::Top ) { const double overallHeight = documentSize.height(); @@ -1737,6 +1735,13 @@ void QgsTextRenderer::drawTextInternalHorizontal( QgsRenderContext &context, con int blockIndex = 0; for ( const QgsTextBlock &block : document ) { + Qgis::TextHorizontalAlignment blockAlignment = hAlignment; + if ( block.blockFormat().hasHorizontalAlignmentSet() ) + blockAlignment = block.blockFormat().horizontalAlignment(); + const bool adjustForAlignment = blockAlignment != Qgis::TextHorizontalAlignment::Left && + ( mode != Qgis::TextLayoutMode::Labeling + || textLines.size() > 1 ); + const bool isFinalLineInParagraph = ( blockIndex == document.size() - 1 ) || document.at( blockIndex + 1 ).toPlainText().trimmed().isEmpty(); @@ -1765,7 +1770,7 @@ void QgsTextRenderer::drawTextInternalHorizontal( QgsRenderContext &context, con if ( adjustForAlignment ) { double labelWidthDiff = 0; - switch ( hAlignment ) + switch ( blockAlignment ) { case Qgis::TextHorizontalAlignment::Center: labelWidthDiff = ( labelWidest - blockWidth ) * 0.5; @@ -1798,7 +1803,7 @@ void QgsTextRenderer::drawTextInternalHorizontal( QgsRenderContext &context, con case Qgis::TextLayoutMode::Point: { - switch ( hAlignment ) + switch ( blockAlignment ) { case Qgis::TextHorizontalAlignment::Right: xMultiLineOffset = labelWidthDiff - labelWidest; diff --git a/tests/src/python/CMakeLists.txt b/tests/src/python/CMakeLists.txt index 40516eb717d06..b71e9d463808c 100644 --- a/tests/src/python/CMakeLists.txt +++ b/tests/src/python/CMakeLists.txt @@ -342,6 +342,7 @@ ADD_PYTHON_TEST(PyQgsTaskManager test_qgstaskmanager.py) ADD_PYTHON_TEST(PyQgsTemporalUtils test_qgstemporalutils.py) ADD_PYTHON_TEST(PyQgsTerrainProvider test_qgsterrainprovider.py) ADD_PYTHON_TEST(PyQgsTextBlock test_qgstextblock.py) +ADD_PYTHON_TEST(PyQgsTextBlockFormat test_qgstextblockformat.py) ADD_PYTHON_TEST(PyQgsTextCharacterFormat test_qgstextcharacterformat.py) ADD_PYTHON_TEST(PyQgsTextDocument test_qgstextdocument.py) ADD_PYTHON_TEST(PyQgsTextFragment test_qgstextfragment.py) diff --git a/tests/src/python/test_qgstextblock.py b/tests/src/python/test_qgstextblock.py index eead7548c6eb3..20a547fe07034 100644 --- a/tests/src/python/test_qgstextblock.py +++ b/tests/src/python/test_qgstextblock.py @@ -17,6 +17,7 @@ QgsStringUtils, QgsTextBlock, QgsTextFragment, + QgsTextBlockFormat, QgsTextCharacterFormat ) @@ -40,6 +41,14 @@ def testConstructors(self): self.assertEqual(block[0].text(), fragment.text()) self.assertEqual(block.toPlainText(), 'ludicrous gibs!') + def test_format(self): + block = QgsTextBlock() + self.assertFalse(block.blockFormat().hasHorizontalAlignmentSet()) + format = QgsTextBlockFormat() + format.setHasHorizontalAlignmentSet(True) + block.setBlockFormat(format) + self.assertTrue(block.blockFormat().hasHorizontalAlignmentSet()) + def testFromPlainText(self): block = QgsTextBlock.fromPlainText('abc def') self.assertEqual(len(block), 1) diff --git a/tests/src/python/test_qgstextblockformat.py b/tests/src/python/test_qgstextblockformat.py new file mode 100644 index 0000000000000..f7bd6b60b3dc6 --- /dev/null +++ b/tests/src/python/test_qgstextblockformat.py @@ -0,0 +1,49 @@ +"""QGIS Unit tests for QgsTextBlockFormat. + +Run with: ctest -V -R QgsTextBlockFormat + +.. note:: This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +""" +from qgis.PyQt.QtGui import QColor +from qgis.core import ( + Qgis, + QgsFontUtils, + QgsRenderContext, + QgsTextBlockFormat, +) +import unittest +from qgis.testing import start_app, QgisTestCase + +start_app() + + +class TestQgsTextBlockFormat(QgisTestCase): + + def setUp(self): + QgsFontUtils.loadStandardTestFonts(['Bold', 'Oblique']) + + def testGettersSetters(self): + format = QgsTextBlockFormat() + self.assertFalse(format.hasHorizontalAlignmentSet()) + self.assertEqual(format.horizontalAlignment(), Qgis.TextHorizontalAlignment.Left) + + format.setHasHorizontalAlignmentSet(True) + self.assertTrue(format.hasHorizontalAlignmentSet()) + format.setHorizontalAlignment(Qgis.TextHorizontalAlignment.Right) + self.assertEqual(format.horizontalAlignment(), Qgis.TextHorizontalAlignment.Right) + + def testUpdateFont(self): + context = QgsRenderContext() + font = QgsFontUtils.getStandardTestFont() + + format = QgsTextBlockFormat() + format.updateFontForFormat(font, context) + + # no effect for now... + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/src/python/test_qgstextdocument.py b/tests/src/python/test_qgstextdocument.py index 7aa20f28287e2..2bd4497482e5b 100644 --- a/tests/src/python/test_qgstextdocument.py +++ b/tests/src/python/test_qgstextdocument.py @@ -82,9 +82,11 @@ def testFromPlainTextWithTabs(self): self.assertEqual(doc[0][5].text(), 'd') def testFromHtml(self): - doc = QgsTextDocument.fromHtml(['abc
def ghi
jkl
', 'b c d', 'e']) + doc = QgsTextDocument.fromHtml(['abc
def ghi
jkl
', 'b c d', 'e']) self.assertEqual(len(doc), 5) self.assertEqual(len(doc[0]), 1) + self.assertTrue(doc[0].blockFormat().hasHorizontalAlignmentSet()) + self.assertEqual(doc[0].blockFormat().horizontalAlignment(), Qgis.TextHorizontalAlignment.Right) self.assertEqual(doc[0][0].text(), 'abc') self.assertEqual(doc[0][0].characterFormat().underline(), QgsTextCharacterFormat.BooleanValue.NotSet) self.assertEqual(doc[0][0].characterFormat().italic(), QgsTextCharacterFormat.BooleanValue.NotSet) diff --git a/tests/src/python/test_qgstextrenderer.py b/tests/src/python/test_qgstextrenderer.py index 1e248395c0d1c..b1706f56a5dd0 100644 --- a/tests/src/python/test_qgstextrenderer.py +++ b/tests/src/python/test_qgstextrenderer.py @@ -3968,6 +3968,39 @@ def testHtmlHeadingsLargerFont(self): '

h1

h2

h3

h4

h5
h6
'], point=QPointF(10, 350)) + def testHtmlAlignmentLeftBase(self): + format = QgsTextFormat() + format.setFont(getTestFont('bold')) + format.setSize(30) + format.setSizeUnit(QgsUnitTypes.RenderUnit.RenderPoints) + format.setColor(QColor(255, 0, 0)) + format.setAllowHtmlFormatting(True) + assert self.checkRender(format, 'html_align_rect_left_base', None, text=[ + '

Test some text

Short

test

test

center
'], + rect=QRectF(10, 10, 300, 300)) + + def testHtmlAlignmentRightBase(self): + format = QgsTextFormat() + format.setFont(getTestFont('bold')) + format.setSize(30) + format.setSizeUnit(QgsUnitTypes.RenderUnit.RenderPoints) + format.setColor(QColor(255, 0, 0)) + format.setAllowHtmlFormatting(True) + assert self.checkRender(format, 'html_align_rect_right_base', None, text=[ + '

Test some text

Short

test

test

center
'], + rect=QRectF(10, 10, 300, 300), alignment=Qgis.TextHorizontalAlignment.Right) + + def testHtmlAlignmentCenterBase(self): + format = QgsTextFormat() + format.setFont(getTestFont('bold')) + format.setSize(30) + format.setSizeUnit(QgsUnitTypes.RenderUnit.RenderPoints) + format.setColor(QColor(255, 0, 0)) + format.setAllowHtmlFormatting(True) + assert self.checkRender(format, 'html_align_rect_center_base', None, text=[ + '

Test some text

Short

test

test

center
'], + rect=QRectF(10, 10, 300, 300), alignment=Qgis.TextHorizontalAlignment.Center) + def testHtmlSuperSubscript(self): format = QgsTextFormat() format.setFont(getTestFont('bold')) diff --git a/tests/testdata/control_images/text_renderer/html_align_rect_center_base/html_align_rect_center_base.png b/tests/testdata/control_images/text_renderer/html_align_rect_center_base/html_align_rect_center_base.png new file mode 100644 index 0000000000000..cd10b591688bf Binary files /dev/null and b/tests/testdata/control_images/text_renderer/html_align_rect_center_base/html_align_rect_center_base.png differ diff --git a/tests/testdata/control_images/text_renderer/html_align_rect_left_base/html_align_rect_left_base.png b/tests/testdata/control_images/text_renderer/html_align_rect_left_base/html_align_rect_left_base.png new file mode 100644 index 0000000000000..72467d4ff7be1 Binary files /dev/null and b/tests/testdata/control_images/text_renderer/html_align_rect_left_base/html_align_rect_left_base.png differ diff --git a/tests/testdata/control_images/text_renderer/html_align_rect_right_base/html_align_rect_right_base.png b/tests/testdata/control_images/text_renderer/html_align_rect_right_base/html_align_rect_right_base.png new file mode 100644 index 0000000000000..76f0e85868204 Binary files /dev/null and b/tests/testdata/control_images/text_renderer/html_align_rect_right_base/html_align_rect_right_base.png differ