Skip to content

Commit

Permalink
Prepare framework for text renderer vertical alignment
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Nov 14, 2022
1 parent 680f28f commit 79b809a
Show file tree
Hide file tree
Showing 8 changed files with 198 additions and 2 deletions.
9 changes: 8 additions & 1 deletion python/core/auto_additions/qgis.py
Expand Up @@ -1525,10 +1525,17 @@
QgsTextRenderer.AlignBottom = Qgis.TextVerticalAlignment.Bottom
QgsTextRenderer.AlignBottom.is_monkey_patched = True
QgsTextRenderer.AlignBottom.__doc__ = "Align to bottom"
Qgis.TextVerticalAlignment.__doc__ = 'Text vertical alignment.\n\n.. note::\n\n Prior to QGIS 3.28 this was available as :py:class:`QgsTextRenderer`.VAlignment\n\n.. versionadded:: 3.28\n\n' + '* ``AlignTop``: ' + Qgis.TextVerticalAlignment.Top.__doc__ + '\n' + '* ``AlignVCenter``: ' + Qgis.TextVerticalAlignment.VerticalCenter.__doc__ + '\n' + '* ``AlignBottom``: ' + Qgis.TextVerticalAlignment.Bottom.__doc__
Qgis.TextVerticalAlignment.__doc__ = 'Text vertical alignment.\n\nThis enum controls vertical alignment of text in a predefined rectangular\nbounding box. See also Qgis.TextCharacterVerticalAlignment.\n\n.. note::\n\n Prior to QGIS 3.28 this was available as :py:class:`QgsTextRenderer`.VAlignment\n\n.. versionadded:: 3.28\n\n' + '* ``AlignTop``: ' + Qgis.TextVerticalAlignment.Top.__doc__ + '\n' + '* ``AlignVCenter``: ' + Qgis.TextVerticalAlignment.VerticalCenter.__doc__ + '\n' + '* ``AlignBottom``: ' + Qgis.TextVerticalAlignment.Bottom.__doc__
# --
Qgis.TextVerticalAlignment.baseClass = Qgis
# monkey patching scoped based enum
Qgis.TextCharacterVerticalAlignment.Normal.__doc__ = "Adjacent characters are positioned in the standard way for text in the writing system in use"
Qgis.TextCharacterVerticalAlignment.SuperScript.__doc__ = "Characters are placed above the base line for normal text."
Qgis.TextCharacterVerticalAlignment.SubScript.__doc__ = "Characters are placed below the base line for normal text."
Qgis.TextCharacterVerticalAlignment.__doc__ = 'Text vertical alignment for characters.\n\nThis enum controls vertical alignment of individual characters within a block\nof text.\n\n.. versionadded:: 3.30\n\n' + '* ``Normal``: ' + Qgis.TextCharacterVerticalAlignment.Normal.__doc__ + '\n' + '* ``SuperScript``: ' + Qgis.TextCharacterVerticalAlignment.SuperScript.__doc__ + '\n' + '* ``SubScript``: ' + Qgis.TextCharacterVerticalAlignment.SubScript.__doc__
# --
Qgis.TextCharacterVerticalAlignment.baseClass = Qgis
# monkey patching scoped based enum
Qgis.RenderSubcomponentProperty.Generic.__doc__ = "Generic subcomponent property"
Qgis.RenderSubcomponentProperty.ShadowOffset.__doc__ = "Shadow offset"
Qgis.RenderSubcomponentProperty.BlurSize.__doc__ = "Blur size"
Expand Down
7 changes: 7 additions & 0 deletions python/core/auto_generated/qgis.sip.in
Expand Up @@ -1005,6 +1005,13 @@ The development version
Bottom,
};

enum class TextCharacterVerticalAlignment
{
Normal,
SuperScript,
SubScript,
};

enum class RenderSubcomponentProperty
{
Generic,
Expand Down
Expand Up @@ -188,6 +188,58 @@ Returns whether the format has overline enabled.
Sets whether the format has overline ``enabled``.

.. seealso:: :py:func:`overline`
%End

bool hasVerticalAlignmentSet() const;
%Docstring
Returns ``True`` if the format has an explicit vertical alignment set.

If ``False`` is returned then the vertical alignment will be inherited.

.. seealso:: :py:func:`setHasVerticalAlignmentSet`

.. seealso:: :py:func:`verticalAlignment`

.. versionadded:: 3.30
%End

void setHasVerticalAlignmentSet( bool set );
%Docstring
Sets whether the format has an explicit vertical alignment ``set``.

If ``set`` is ``False`` then the vertical alignment will be inherited.

.. seealso:: :py:func:`hasVerticalAlignmentSet`

.. seealso:: :py:func:`setVerticalAlignment`

.. versionadded:: 3.30
%End

Qgis::TextCharacterVerticalAlignment verticalAlignment() const;
%Docstring
Returns the format vertical alignment.

This property is only respected if :py:func:`~QgsTextCharacterFormat.hasVerticalAlignmentSet` is ``True``.

.. seealso:: :py:func:`hasVerticalAlignmentSet`

.. seealso:: :py:func:`setVerticalAlignment`

.. versionadded:: 3.30
%End

void setVerticalAlignment( Qgis::TextCharacterVerticalAlignment alignment );
%Docstring
Sets the format vertical ``alignment``.

This property is only respected if :py:func:`~QgsTextCharacterFormat.hasVerticalAlignmentSet` is ``True``.

.. seealso:: :py:func:`hasVerticalAlignmentSet`

.. seealso:: :py:func:`verticalAlignment`

.. versionadded:: 3.30
%End

void updateFontForFormat( QFont &font, const QgsRenderContext &context, double scaleFactor = 1.0 ) const;
Expand Down
19 changes: 19 additions & 0 deletions src/core/qgis.h
Expand Up @@ -1668,6 +1668,9 @@ class CORE_EXPORT Qgis
/**
* Text vertical alignment.
*
* This enum controls vertical alignment of text in a predefined rectangular
* bounding box. See also Qgis::TextCharacterVerticalAlignment.
*
* \note Prior to QGIS 3.28 this was available as QgsTextRenderer::VAlignment
*
* \since QGIS 3.28
Expand All @@ -1680,6 +1683,22 @@ class CORE_EXPORT Qgis
};
Q_ENUM( TextVerticalAlignment )

/**
* Text vertical alignment for characters.
*
* This enum controls vertical alignment of individual characters within a block
* of text.
*
* \since QGIS 3.30
*/
enum class TextCharacterVerticalAlignment : int
{
Normal, //!< Adjacent characters are positioned in the standard way for text in the writing system in use
SuperScript, //!< Characters are placed above the base line for normal text.
SubScript, //!< Characters are placed below the base line for normal text.
};
Q_ENUM( TextCharacterVerticalAlignment )

/**
* Rendering subcomponent properties.
*
Expand Down
25 changes: 24 additions & 1 deletion src/core/textrenderer/qgstextcharacterformat.cpp
Expand Up @@ -19,6 +19,29 @@

#include <QTextCharFormat>

Qgis::TextCharacterVerticalAlignment convertTextCharFormatVAlign( const QTextCharFormat &format, bool &set )
{
set = format.hasProperty( QTextFormat::TextVerticalAlignment );
switch ( format.verticalAlignment() )
{
case QTextCharFormat::AlignNormal:
return Qgis::TextCharacterVerticalAlignment::Normal;
case QTextCharFormat::AlignSuperScript:
return Qgis::TextCharacterVerticalAlignment::SuperScript;
case QTextCharFormat::AlignSubScript:
return Qgis::TextCharacterVerticalAlignment::SubScript;

// not yet supported
case QTextCharFormat::AlignMiddle:
case QTextCharFormat::AlignTop:
case QTextCharFormat::AlignBottom:
case QTextCharFormat::AlignBaseline:
set = false;
return Qgis::TextCharacterVerticalAlignment::Normal;
}
BUILTIN_UNREACHABLE
}

QgsTextCharacterFormat::QgsTextCharacterFormat( const QTextCharFormat &format )
: mTextColor( format.hasProperty( QTextFormat::ForegroundBrush ) ? format.foreground().color() : QColor() )
, mFontWeight( format.hasProperty( QTextFormat::FontWeight ) ? format.fontWeight() : -1 )
Expand All @@ -30,7 +53,7 @@ QgsTextCharacterFormat::QgsTextCharacterFormat( const QTextCharFormat &format )
, mUnderline( format.hasProperty( QTextFormat::FontUnderline ) ? ( format.fontUnderline() ? BooleanValue::SetTrue : BooleanValue::SetFalse ) : BooleanValue::NotSet )
, mOverline( format.hasProperty( QTextFormat::FontOverline ) ? ( format.fontOverline() ? BooleanValue::SetTrue : BooleanValue::SetFalse ) : BooleanValue::NotSet )
{

mVerticalAlign = convertTextCharFormatVAlign( format, mHasVerticalAlignSet );
}

QColor QgsTextCharacterFormat::textColor() const
Expand Down
53 changes: 53 additions & 0 deletions src/core/textrenderer/qgstextcharacterformat.h
Expand Up @@ -18,6 +18,7 @@

#include "qgis_sip.h"
#include "qgis_core.h"
#include "qgis.h"

#include <QFont>
#include <QColor>
Expand Down Expand Up @@ -194,6 +195,54 @@ class CORE_EXPORT QgsTextCharacterFormat
*/
void setOverline( BooleanValue enabled );

/**
* Returns TRUE if the format has an explicit vertical alignment set.
*
* If FALSE is returned then the vertical alignment will be inherited.
*
* \see setHasVerticalAlignmentSet()
* \see verticalAlignment()
*
* \since QGIS 3.30
*/
bool hasVerticalAlignmentSet() const { return mHasVerticalAlignSet; }

/**
* Sets whether the format has an explicit vertical alignment \a set.
*
* If \a set is FALSE then the vertical alignment will be inherited.
*
* \see hasVerticalAlignmentSet()
* \see setVerticalAlignment()
*
* \since QGIS 3.30
*/
void setHasVerticalAlignmentSet( bool set ) { mHasVerticalAlignSet = set; }

/**
* Returns the format vertical alignment.
*
* This property is only respected if hasVerticalAlignmentSet() is TRUE.
*
* \see hasVerticalAlignmentSet()
* \see setVerticalAlignment()
*
* \since QGIS 3.30
*/
Qgis::TextCharacterVerticalAlignment verticalAlignment() const { return mVerticalAlign; }

/**
* Sets the format vertical \a alignment.
*
* This property is only respected if hasVerticalAlignmentSet() is TRUE.
*
* \see hasVerticalAlignmentSet()
* \see verticalAlignment()
*
* \since QGIS 3.30
*/
void setVerticalAlignment( Qgis::TextCharacterVerticalAlignment alignment ) { mVerticalAlign = alignment; }

/**
* Updates the specified \a font in place, applying character formatting options which
* are applicable on a font level when rendered in the given \a context.
Expand All @@ -213,6 +262,10 @@ class CORE_EXPORT QgsTextCharacterFormat
BooleanValue mItalic = BooleanValue::NotSet;
double mFontPointSize = -1;
QString mFontFamily;

bool mHasVerticalAlignSet = false;
Qgis::TextCharacterVerticalAlignment mVerticalAlign = Qgis::TextCharacterVerticalAlignment::Normal;

BooleanValue mStrikethrough = BooleanValue::NotSet;
BooleanValue mUnderline = BooleanValue::NotSet;
BooleanValue mOverline = BooleanValue::NotSet;
Expand Down
8 changes: 8 additions & 0 deletions tests/src/python/test_qgstextcharacterformat.py
Expand Up @@ -15,6 +15,7 @@
import qgis # NOQA

from qgis.core import (
Qgis,
QgsFontUtils,
QgsTextCharacterFormat,
QgsRenderContext
Expand All @@ -39,6 +40,8 @@ def testGettersSetters(self):
self.assertEqual(format.overline(), QgsTextCharacterFormat.BooleanValue.NotSet)
self.assertEqual(format.fontPointSize(), -1)
self.assertFalse(format.family())
self.assertFalse(format.hasVerticalAlignmentSet())
self.assertEqual(format.verticalAlignment(), Qgis.TextCharacterVerticalAlignment.Normal)

format.setTextColor(QColor(255, 0, 0))
self.assertTrue(format.textColor().isValid())
Expand All @@ -59,6 +62,11 @@ def testGettersSetters(self):
format.setFamily('comic sans')
self.assertEqual(format.family(), 'comic sans')

format.setHasVerticalAlignmentSet(True)
self.assertTrue(format.hasVerticalAlignmentSet())
format.setVerticalAlignment(Qgis.TextCharacterVerticalAlignment.SuperScript)
self.assertEqual(format.verticalAlignment(), Qgis.TextCharacterVerticalAlignment.SuperScript)

def testUpdateFont(self):
context = QgsRenderContext()
font = QgsFontUtils.getStandardTestFont()
Expand Down
27 changes: 27 additions & 0 deletions tests/src/python/test_qgstextdocument.py
Expand Up @@ -15,6 +15,7 @@
import qgis # NOQA

from qgis.core import (
Qgis,
QgsTextDocument,
QgsTextBlock,
QgsTextFragment,
Expand Down Expand Up @@ -67,6 +68,7 @@ def testFromHtml(self):
self.assertFalse(doc[0][0].characterFormat().family())
self.assertEqual(doc[0][0].characterFormat().fontPointSize(), -1)
self.assertFalse(doc[0][0].characterFormat().textColor().isValid())
self.assertFalse(doc[0][0].characterFormat().hasVerticalAlignmentSet())
self.assertEqual(len(doc[1]), 2)
self.assertEqual(doc[1][0].text(), 'def')
self.assertEqual(doc[1][0].characterFormat().underline(), QgsTextCharacterFormat.BooleanValue.SetTrue)
Expand All @@ -75,20 +77,45 @@ def testFromHtml(self):
self.assertEqual(doc[1][0].characterFormat().family(), 'Serif')
self.assertEqual(doc[1][0].characterFormat().textColor().name(), '#ff0000')
self.assertEqual(doc[1][0].characterFormat().fontPointSize(), 15)
self.assertFalse(doc[1][0].characterFormat().hasVerticalAlignmentSet())
self.assertEqual(doc[1][1].text(), ' ghi')
self.assertEqual(doc[1][1].characterFormat().underline(), QgsTextCharacterFormat.BooleanValue.NotSet)
self.assertEqual(doc[1][1].characterFormat().italic(), QgsTextCharacterFormat.BooleanValue.NotSet)
self.assertEqual(doc[1][1].characterFormat().fontWeight(), -1)
self.assertFalse(doc[1][1].characterFormat().family())
self.assertEqual(doc[1][1].characterFormat().textColor().name(), '#ff0000')
self.assertEqual(doc[1][1].characterFormat().fontPointSize(), -1)
self.assertFalse(doc[1][1].characterFormat().hasVerticalAlignmentSet())
self.assertEqual(len(doc[2]), 1)
self.assertEqual(doc[2][0].text(), 'jkl')
self.assertEqual(len(doc[3]), 1)
self.assertEqual(doc[3][0].text(), 'b c d')
self.assertEqual(len(doc[4]), 1)
self.assertEqual(doc[4][0].text(), 'e')

def testFromHtmlVerticalAlignment(self):
doc = QgsTextDocument.fromHtml(['abc<div style="color: red"><sub>def<b>extra</b></sub> ghi</div><sup>sup</sup><span style="vertical-align: sub">css</span>'])
self.assertEqual(len(doc), 3)
self.assertEqual(len(doc[0]), 1)
self.assertEqual(doc[0][0].text(), 'abc')
self.assertFalse(doc[0][0].characterFormat().hasVerticalAlignmentSet())
self.assertEqual(len(doc[1]), 3)
self.assertEqual(doc[1][0].text(), 'def')
self.assertTrue(doc[1][0].characterFormat().hasVerticalAlignmentSet())
self.assertEqual(doc[1][0].characterFormat().verticalAlignment(), Qgis.TextCharacterVerticalAlignment.SubScript)
self.assertEqual(doc[1][1].text(), 'extra')
self.assertTrue(doc[1][1].characterFormat().hasVerticalAlignmentSet())
self.assertEqual(doc[1][1].characterFormat().verticalAlignment(), Qgis.TextCharacterVerticalAlignment.SubScript)
self.assertEqual(doc[1][2].text(), ' ghi')
self.assertFalse(doc[1][2].characterFormat().hasVerticalAlignmentSet())
self.assertEqual(len(doc[2]), 2)
self.assertEqual(doc[2][0].text(), 'sup')
self.assertTrue(doc[2][0].characterFormat().hasVerticalAlignmentSet())
self.assertEqual(doc[2][0].characterFormat().verticalAlignment(), Qgis.TextCharacterVerticalAlignment.SuperScript)
self.assertEqual(doc[2][1].text(), 'css')
self.assertTrue(doc[2][1].characterFormat().hasVerticalAlignmentSet())
self.assertEqual(doc[2][1].characterFormat().verticalAlignment(), Qgis.TextCharacterVerticalAlignment.SubScript)

def testAppend(self):
doc = QgsTextDocument()
self.assertEqual(len(doc), 0)
Expand Down

0 comments on commit 79b809a

Please sign in to comment.