diff --git a/src/core/textrenderer/qgstextrenderer.cpp b/src/core/textrenderer/qgstextrenderer.cpp index 1ff201f97605..d78adf984816 100644 --- a/src/core/textrenderer/qgstextrenderer.cpp +++ b/src/core/textrenderer/qgstextrenderer.cpp @@ -304,7 +304,11 @@ void QgsTextRenderer::drawDocumentOnLine( const QPolygonF &line, const QgsTextFo if ( placement->graphemePlacement.empty() ) return; - std::vector< QgsTextRenderer::Component > components; + // We may have deliberately skipped over some graphemes during curved text placement (such as zero-width graphemes). + // So we need to use a hash of the original grapheme index to place generated components in, as there may accordingly + // be graphemes which don't result in components, and we can't just blindly assume the component array position + // will match the original grapheme index + QHash< int, QgsTextRenderer::Component > components; components.reserve( placement->graphemePlacement.size() ); for ( const QgsTextRendererUtils::CurvedGraphemePlacement &grapheme : std::as_const( placement->graphemePlacement ) ) { @@ -320,7 +324,7 @@ void QgsTextRenderer::drawDocumentOnLine( const QPolygonF &line, const QgsTextFo component.origin.ry() += verticalOffset * std::sin( grapheme.angle + M_PI_2 ); } - components.emplace_back( component ); + components.insert( grapheme.graphemeIndex, component ); } if ( format.background().enabled() ) diff --git a/tests/src/python/test_qgstextrenderer.py b/tests/src/python/test_qgstextrenderer.py index 2f4ac84819da..72d329c80a8c 100644 --- a/tests/src/python/test_qgstextrenderer.py +++ b/tests/src/python/test_qgstextrenderer.py @@ -4456,6 +4456,42 @@ def testDrawTextOnLineAtStart(self): painter.end() self.assertTrue(self.image_check('text_on_line_at_start', 'text_on_line_at_start', image, 'text_on_line_at_start')) + def testDrawTextOnLineZeroWidthChar(self): + format = QgsTextFormat() + format.setFont(getTestFont('bold')) + format.setSize(16) + format.setSizeUnit(QgsUnitTypes.RenderUnit.RenderPoints) + format.background().setEnabled(True) + + image = QImage(400, 400, QImage.Format.Format_RGB32) + + painter = QPainter() + ms = QgsMapSettings() + ms.setExtent(QgsRectangle(0, 0, 50, 50)) + ms.setOutputSize(image.size()) + context = QgsRenderContext.fromMapSettings(ms) + context.setPainter(painter) + context.setScaleFactor(96 / 25.4) # 96 DPI + context.setFlag(QgsRenderContext.Flag.ApplyScalingWorkaroundForTextRendering, True) + + painter.begin(image) + painter.setRenderHint(QPainter.RenderHint.Antialiasing) + image.fill(QColor(152, 219, 249)) + + painter.setBrush(Qt.BrushStyle.NoBrush) + painter.setPen(QPen(QColor(0, 0, 0))) + + line = QPolygonF([QPointF(50, 200), QPointF(350, 200)]) + painter.drawPolygon(line) + + painter.setBrush(QBrush(QColor(182, 239, 255))) + painter.setPen(Qt.PenStyle.NoPen) + + QgsTextRenderer.drawTextOnLine(line, 'b\r\na', context, format, 0) + + painter.end() + self.assertTrue(self.image_check('text_on_curved_line_zero_width_char', 'text_on_curved_line_zero_width_char', image, 'text_on_curved_line_zero_width_char')) + def testDrawTextOnLineAtOffset(self): format = QgsTextFormat() format.setFont(getTestFont('bold')) diff --git a/tests/testdata/control_images/text_renderer/text_on_curved_line_zero_width_char/text_on_curved_line_zero_width_char.png b/tests/testdata/control_images/text_renderer/text_on_curved_line_zero_width_char/text_on_curved_line_zero_width_char.png new file mode 100644 index 000000000000..23a64474101a Binary files /dev/null and b/tests/testdata/control_images/text_renderer/text_on_curved_line_zero_width_char/text_on_curved_line_zero_width_char.png differ diff --git a/tests/testdata/control_images/text_renderer/text_on_curved_line_zero_width_char/text_on_curved_line_zero_width_char_mask.png b/tests/testdata/control_images/text_renderer/text_on_curved_line_zero_width_char/text_on_curved_line_zero_width_char_mask.png new file mode 100644 index 000000000000..9bf6c04b6f95 Binary files /dev/null and b/tests/testdata/control_images/text_renderer/text_on_curved_line_zero_width_char/text_on_curved_line_zero_width_char_mask.png differ