diff --git a/src/core/textrenderer/qgstextdocument.cpp b/src/core/textrenderer/qgstextdocument.cpp index 69329a4a7676..c12bb93537c6 100644 --- a/src/core/textrenderer/qgstextdocument.cpp +++ b/src/core/textrenderer/qgstextdocument.cpp @@ -53,6 +53,10 @@ QgsTextDocument QgsTextDocument::fromPlainText( const QStringList &lines ) // a html or css tag doesn't mess things up. Instead, Qt will just silently // ignore html attributes it doesn't know about, like this replacement string #define TAB_REPLACEMENT_MARKER " ignore_me_i_am_a_tab " +// when splitting by the tab replacement marker we need to be tolerant to the +// spaces surrounding REPLACEMENT_MARKER being swallowed when multiple consecutive +// tab characters exist +#define TAB_REPLACEMENT_MARKER_RX " ?ignore_me_i_am_a_tab ?" QgsTextDocument QgsTextDocument::fromHtml( const QStringList &lines ) { @@ -73,6 +77,7 @@ QgsTextDocument QgsTextDocument::fromHtml( const QStringList &lines ) // by first replacing it with a string which QTextDocument won't mess with, and then // handle these markers as tab characters in the parsed HTML document. line.replace( QString( '\t' ), QStringLiteral( TAB_REPLACEMENT_MARKER ) ); + const thread_local QRegularExpression sTabReplacementMarkerRx( QStringLiteral( TAB_REPLACEMENT_MARKER_RX ) ); // cheat a little. Qt css requires some properties to have the "px" suffix. But we don't treat these properties // as pixels, because that doesn't scale well with different dpi render targets! So let's instead use just instead treat the suffix as @@ -162,7 +167,7 @@ QgsTextDocument QgsTextDocument::fromHtml( const QStringList &lines ) } splitFragment.setCharacterFormat( newFormat ); - const QStringList tabSplit = splitLine.split( QStringLiteral( TAB_REPLACEMENT_MARKER ) ); + const QStringList tabSplit = splitLine.split( sTabReplacementMarkerRx ); int index = 0; for ( const QString &part : tabSplit ) { @@ -210,7 +215,7 @@ QgsTextDocument QgsTextDocument::fromHtml( const QStringList &lines ) newFormat.overrideWith( blockFormat ); tmpFragment.setCharacterFormat( newFormat ); - const QStringList tabSplit = fragmentText.split( QStringLiteral( TAB_REPLACEMENT_MARKER ) ); + const QStringList tabSplit = fragmentText.split( sTabReplacementMarkerRx ); int index = 0; for ( const QString &part : tabSplit ) { diff --git a/tests/src/python/test_qgstextrenderer.py b/tests/src/python/test_qgstextrenderer.py index fa667ee5d647..f4607a4528db 100644 --- a/tests/src/python/test_qgstextrenderer.py +++ b/tests/src/python/test_qgstextrenderer.py @@ -3656,6 +3656,20 @@ def testDrawTabFixedSize(self): self.checkRender(format, "text_tab_fixed_size", text=["with\ttabs", "a\tb"]) ) + def testDrawTabsMultipleHtmlFixedSize(self): + format = QgsTextFormat() + format.setFont(getTestFont("bold")) + format.setSize(20) + format.setAllowHtmlFormatting(True) + format.setSizeUnit(QgsUnitTypes.RenderUnit.RenderPoints) + format.setTabStopDistance(20) + format.setTabStopDistanceUnit(Qgis.RenderUnit.Millimeters) + self.assertTrue( + self.checkRender( + format, "text_tab_multiple_html", text=["with\t\ttabs", "a\t\tb"] + ) + ) + def testDrawTabPositionsFixedSize(self): format = QgsTextFormat() format.setFont(getTestFont("bold")) diff --git a/tests/testdata/control_images/text_renderer/text_tab_multiple_html/text_tab_multiple_html.png b/tests/testdata/control_images/text_renderer/text_tab_multiple_html/text_tab_multiple_html.png new file mode 100644 index 000000000000..db00646f40dd Binary files /dev/null and b/tests/testdata/control_images/text_renderer/text_tab_multiple_html/text_tab_multiple_html.png differ diff --git a/tests/testdata/control_images/text_renderer/text_tab_multiple_html/text_tab_multiple_html_mask.png b/tests/testdata/control_images/text_renderer/text_tab_multiple_html/text_tab_multiple_html_mask.png new file mode 100644 index 000000000000..9b6b0bc8ae98 Binary files /dev/null and b/tests/testdata/control_images/text_renderer/text_tab_multiple_html/text_tab_multiple_html_mask.png differ