From de20bca5580b31a03a3c9a04a3a89a17b07422f1 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Wed, 6 Oct 2021 13:19:06 +1000 Subject: [PATCH] Fix offset background when drawing rotated text using QgsTextRenderer --- src/core/textrenderer/qgstextrenderer.cpp | 10 ++++++++ tests/src/python/test_qgstextrenderer.py | 22 ++++++++++++++++++ .../background_rect_buffer_rotated_text.png | Bin 0 -> 6461 bytes .../background_rect_fixed_rotated_text.png | Bin 0 -> 6242 bytes 4 files changed, 32 insertions(+) create mode 100644 tests/testdata/control_images/text_renderer/background_rect_buffer_rotated_text/background_rect_buffer_rotated_text.png create mode 100644 tests/testdata/control_images/text_renderer/background_rect_fixed_rotated_text/background_rect_fixed_rotated_text.png diff --git a/src/core/textrenderer/qgstextrenderer.cpp b/src/core/textrenderer/qgstextrenderer.cpp index d7faaee4f992..4c9c794adc94 100644 --- a/src/core/textrenderer/qgstextrenderer.cpp +++ b/src/core/textrenderer/qgstextrenderer.cpp @@ -768,6 +768,7 @@ void QgsTextRenderer::drawBackground( QgsRenderContext &context, QgsTextRenderer // shared calculations between shapes and SVG // configure angles, set component rotation and rotationOffset + const double originAdjustRotationRadians = -component.rotation; if ( background.rotationType() != QgsTextBackgroundSettings::RotationFixed ) { component.rotation = -( component.rotation * 180 / M_PI ); // RotationSync @@ -834,6 +835,15 @@ void QgsTextRenderer::drawBackground( QgsRenderContext &context, QgsTextRenderer component.origin.y() - height / 2.0 + originAdjust ); break; } + + // apply rotation to center point + if ( !qgsDoubleNear( originAdjustRotationRadians, 0 ) ) + { + const double dx = component.center.x() - component.origin.x(); + const double dy = component.center.y() - component.origin.y(); + component.center.setX( component.origin.x() + ( std::cos( originAdjustRotationRadians ) * dx - std::sin( originAdjustRotationRadians ) * dy ) ); + component.center.setY( component.origin.y() + ( std::sin( originAdjustRotationRadians ) * dx + std::cos( originAdjustRotationRadians ) * dy ) ); + } break; } diff --git a/tests/src/python/test_qgstextrenderer.py b/tests/src/python/test_qgstextrenderer.py index 9c90b38b2d24..724b963c9d8e 100644 --- a/tests/src/python/test_qgstextrenderer.py +++ b/tests/src/python/test_qgstextrenderer.py @@ -1522,6 +1522,28 @@ def testDrawBackgroundRectangleFixedSizeMapUnits(self): format.background().setSizeUnit(QgsUnitTypes.RenderMapUnits) assert self.checkRender(format, 'background_rect_mapunits', QgsTextRenderer.Background) + def testDrawBackgroundRectangleFixedSizeWithRotatedText(self): + format = QgsTextFormat() + format.setFont(getTestFont('bold')) + format.setSize(40) + format.background().setEnabled(True) + format.background().setType(QgsTextBackgroundSettings.ShapeRectangle) + format.background().setSize(QSizeF(20, 20)) + format.background().setSizeType(QgsTextBackgroundSettings.SizeFixed) + format.background().setSizeUnit(QgsUnitTypes.RenderMillimeters) + assert self.checkRenderPoint(format, 'background_rect_fixed_rotated_text', angle=3.141 / 4) + + def testDrawBackgroundRectangleBufferSizeWithRotatedText(self): + format = QgsTextFormat() + format.setFont(getTestFont('bold')) + format.setSize(40) + format.background().setEnabled(True) + format.background().setType(QgsTextBackgroundSettings.ShapeRectangle) + format.background().setSize(QSizeF(2, 3)) + format.background().setSizeType(QgsTextBackgroundSettings.SizeBuffer) + format.background().setSizeUnit(QgsUnitTypes.RenderMillimeters) + assert self.checkRenderPoint(format, 'background_rect_buffer_rotated_text', angle=3.141 / 4) + def testDrawBackgroundRectangleMultilineFixedSizeMapUnits(self): format = QgsTextFormat() format.setFont(getTestFont('bold')) diff --git a/tests/testdata/control_images/text_renderer/background_rect_buffer_rotated_text/background_rect_buffer_rotated_text.png b/tests/testdata/control_images/text_renderer/background_rect_buffer_rotated_text/background_rect_buffer_rotated_text.png new file mode 100644 index 0000000000000000000000000000000000000000..1ac392fa010353ddc0bae80408d469279eb7dfdb GIT binary patch literal 6461 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 F{{smHAb literal 0 HcmV?d00001 diff --git a/tests/testdata/control_images/text_renderer/background_rect_fixed_rotated_text/background_rect_fixed_rotated_text.png b/tests/testdata/control_images/text_renderer/background_rect_fixed_rotated_text/background_rect_fixed_rotated_text.png new file mode 100644 index 0000000000000000000000000000000000000000..c70bcf52e65ddf0d4f536ec297042425f0ad09e4 GIT binary patch literal 6242 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