From 6bffa17cc26c017c49dea714407f9a2bea42b24a Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Mon, 30 Jan 2017 18:43:56 +1000 Subject: [PATCH] Fix failing tests --- scripts/parse_dash_results.py | 5 +++- src/core/composer/qgscomposershape.cpp | 8 ++++-- src/core/composer/qgscomposerutils.cpp | 23 +++++++++++++++--- src/core/composer/qgscomposerutils.h | 2 +- src/core/composer/qgspaperitem.cpp | 9 ++++--- tests/src/python/test_qgsannotation.py | 5 ++-- .../python/test_qgsmapcanvasannotationitem.py | 5 ++-- .../{ => default}/expected_html_nofeature.png | Bin .../travis/expected_html_nofeature.png | Bin 0 -> 7973 bytes .../expected_svg_annotation_mask.png | Bin 0 -> 3207 bytes .../expected_text_annotation.png | Bin .../travis/expected_text_annotation.png | Bin 0 -> 8910 bytes 12 files changed, 43 insertions(+), 14 deletions(-) rename tests/testdata/control_images/annotations/expected_html_nofeature/{ => default}/expected_html_nofeature.png (100%) create mode 100644 tests/testdata/control_images/annotations/expected_html_nofeature/travis/expected_html_nofeature.png create mode 100644 tests/testdata/control_images/annotations/expected_svg_annotation/expected_svg_annotation_mask.png rename tests/testdata/control_images/annotations/expected_text_annotation/{ => default}/expected_text_annotation.png (100%) create mode 100644 tests/testdata/control_images/annotations/expected_text_annotation/travis/expected_text_annotation.png diff --git a/scripts/parse_dash_results.py b/scripts/parse_dash_results.py index bec9a2c5323..1b37a8b67e6 100755 --- a/scripts/parse_dash_results.py +++ b/scripts/parse_dash_results.py @@ -61,12 +61,15 @@ def colorDiff(c1, c2): def imageFromPath(path): - if (path[:7] == 'https://' or path[:7] == 'file://'): + print(path) + if (path[:8] == 'https://' or path[:7] == 'file://'): # fetch remote image + print('fetching remote!') data = urllib.request.urlopen(path).read() image = QImage() image.loadFromData(data) else: + print('using local!') image = QImage(path) return image diff --git a/src/core/composer/qgscomposershape.cpp b/src/core/composer/qgscomposershape.cpp index cb795d91337..40289664723 100644 --- a/src/core/composer/qgscomposershape.cpp +++ b/src/core/composer/qgscomposershape.cpp @@ -83,7 +83,9 @@ void QgsComposerShape::setShapeStyleSymbol( QgsFillSymbol* symbol ) void QgsComposerShape::refreshSymbol() { - mMaxSymbolBleed = QgsSymbolLayerUtils::estimateMaxSymbolBleed( mShapeStyleSymbol, QgsComposerUtils::createRenderContextForComposition( mComposition, nullptr ) ); + QgsRenderContext rc = QgsComposerUtils::createRenderContextForMap( mComposition->referenceMap(), nullptr, mComposition->printResolution() ); + mMaxSymbolBleed = ( 25.4 / mComposition->printResolution() ) * QgsSymbolLayerUtils::estimateMaxSymbolBleed( mShapeStyleSymbol, rc ); + updateBoundingRect(); update(); @@ -102,7 +104,9 @@ void QgsComposerShape::createDefaultShapeStyleSymbol() properties.insert( QStringLiteral( "joinstyle" ), QStringLiteral( "miter" ) ); mShapeStyleSymbol = QgsFillSymbol::createSimple( properties ); - mMaxSymbolBleed = QgsSymbolLayerUtils::estimateMaxSymbolBleed( mShapeStyleSymbol, QgsComposerUtils::createRenderContextForComposition( mComposition, nullptr ) ); + QgsRenderContext rc = QgsComposerUtils::createRenderContextForMap( mComposition->referenceMap(), nullptr, mComposition->printResolution() ); + mMaxSymbolBleed = ( 25.4 / mComposition->printResolution() ) * QgsSymbolLayerUtils::estimateMaxSymbolBleed( mShapeStyleSymbol, rc ); + updateBoundingRect(); emit frameChanged(); diff --git a/src/core/composer/qgscomposerutils.cpp b/src/core/composer/qgscomposerutils.cpp index f4cc5dd8e6f..45cc95294fb 100644 --- a/src/core/composer/qgscomposerutils.cpp +++ b/src/core/composer/qgscomposerutils.cpp @@ -488,15 +488,32 @@ void QgsComposerUtils::drawText( QPainter *painter, const QRectF &rect, const QS painter->restore(); } -QgsRenderContext QgsComposerUtils::createRenderContextForMap( QgsComposerMap* map, QPainter* painter ) +QgsRenderContext QgsComposerUtils::createRenderContextForMap( QgsComposerMap* map, QPainter* painter, double dpi ) { if ( !map ) { - return QgsRenderContext::fromQPainter( painter ); + QgsRenderContext context; + context.setPainter( painter ); + if ( dpi < 0 && painter && painter->device() ) + { + context.setScaleFactor( painter->device()->logicalDpiX() / 25.4 ); + } + else if ( dpi > 0 ) + { + context.setScaleFactor( dpi / 25.4 ); + } + else + { + context.setScaleFactor( 3.465 ); //assume 88 dpi as standard value + } + return context; } // default to 88 dpi if no painter specified - int dpi = ( painter && painter->device() ) ? painter->device()->logicalDpiX() : 88; + if ( dpi < 0 ) + { + dpi = ( painter && painter->device() ) ? painter->device()->logicalDpiX() : 88; + } double dotsPerMM = dpi / 25.4; // get map settings from reference map diff --git a/src/core/composer/qgscomposerutils.h b/src/core/composer/qgscomposerutils.h index 4d4f9e42553..459c2d1626f 100644 --- a/src/core/composer/qgscomposerutils.h +++ b/src/core/composer/qgscomposerutils.h @@ -252,7 +252,7 @@ class CORE_EXPORT QgsComposerUtils * @note added in QGIS 3.0 * @see createRenderContextForComposition() */ - static QgsRenderContext createRenderContextForMap( QgsComposerMap* map, QPainter* painter ); + static QgsRenderContext createRenderContextForMap( QgsComposerMap* map, QPainter* painter, double dpi = -1 ); /** * Creates a render context suitable for the specified composition and QPainter destination. diff --git a/src/core/composer/qgspaperitem.cpp b/src/core/composer/qgspaperitem.cpp index 0dd83f4b58d..5382875ec9c 100644 --- a/src/core/composer/qgspaperitem.cpp +++ b/src/core/composer/qgspaperitem.cpp @@ -201,13 +201,16 @@ void QgsPaperItem::paint( QPainter* painter, const QStyleOptionGraphicsItem* ite void QgsPaperItem::calculatePageMargin() { //get max bleed from symbol - double maxBleed = QgsSymbolLayerUtils::estimateMaxSymbolBleed( mComposition->pageStyleSymbol(), - QgsComposerUtils::createRenderContextForComposition( mComposition, nullptr ) ); + QgsRenderContext rc = QgsComposerUtils::createRenderContextForMap( mComposition->referenceMap(), nullptr, mComposition->printResolution() ); + double maxBleedPixels = QgsSymbolLayerUtils::estimateMaxSymbolBleed( mComposition->pageStyleSymbol(), rc ); //Now subtract 1 pixel to prevent semi-transparent borders at edge of solid page caused by //anti-aliased painting. This may cause a pixel to be cropped from certain edge lines/symbols, //but that can be counteracted by adding a dummy transparent line symbol layer with a wider line width - mPageMargin = maxBleed - ( 25.4 / mComposition->printResolution() ); + maxBleedPixels--; + + double maxBleedMm = ( 25.4 / mComposition->printResolution() ) * maxBleedPixels; + mPageMargin = maxBleedMm; } bool QgsPaperItem::writeXml( QDomElement& elem, QDomDocument & doc ) const diff --git a/tests/src/python/test_qgsannotation.py b/tests/src/python/test_qgsannotation.py index d1f60908e05..56f0c020db7 100644 --- a/tests/src/python/test_qgsannotation.py +++ b/tests/src/python/test_qgsannotation.py @@ -21,6 +21,7 @@ from qgis.core import (QgsTextAnnotation, QgsRenderContext, QgsCoordinateReferenceSystem, QgsRectangle, + QgsMultiRenderChecker, QgsRenderChecker, QgsPoint, QgsVectorLayer, @@ -160,12 +161,12 @@ class TestQgsAnnotation(unittest.TestCase): temp_dir = QDir.tempPath() + '/' file_name = temp_dir + 'annotation_' + name + ".png" image.save(file_name, "PNG") - checker = QgsRenderChecker() + checker = QgsMultiRenderChecker() checker.setControlPathPrefix("annotations") checker.setControlName("expected_" + reference_image) checker.setRenderedImage(file_name) checker.setColorTolerance(2) - result = checker.compareImages(name, 20) + result = checker.runTest(name, 20) self.report += checker.report() print((self.report)) return result diff --git a/tests/src/python/test_qgsmapcanvasannotationitem.py b/tests/src/python/test_qgsmapcanvasannotationitem.py index c1d07fae6cf..b8d775c3e37 100644 --- a/tests/src/python/test_qgsmapcanvasannotationitem.py +++ b/tests/src/python/test_qgsmapcanvasannotationitem.py @@ -21,7 +21,8 @@ from qgis.core import (QgsTextAnnotation, QgsPoint, QgsVectorLayer, QgsFeature, - QgsGeometry) + QgsGeometry, + QgsFillSymbol) from qgis.gui import (QgsMapCanvas, QgsMapCanvasAnnotationItem) @@ -86,7 +87,7 @@ class TestQgsMapCanvasAnnotationItem(unittest.TestCase): a = QgsTextAnnotation() a.setFrameSize(QSizeF(300, 200)) a.setHasFixedMapPosition(False) - a.setFrameBorderWidth(0) + a.setFillSymbol(QgsFillSymbol.createSimple({'color': 'blue', 'width_border': '0'})) canvas = QgsMapCanvas() canvas.setDestinationCrs(QgsCoordinateReferenceSystem(4326)) diff --git a/tests/testdata/control_images/annotations/expected_html_nofeature/expected_html_nofeature.png b/tests/testdata/control_images/annotations/expected_html_nofeature/default/expected_html_nofeature.png similarity index 100% rename from tests/testdata/control_images/annotations/expected_html_nofeature/expected_html_nofeature.png rename to tests/testdata/control_images/annotations/expected_html_nofeature/default/expected_html_nofeature.png diff --git a/tests/testdata/control_images/annotations/expected_html_nofeature/travis/expected_html_nofeature.png b/tests/testdata/control_images/annotations/expected_html_nofeature/travis/expected_html_nofeature.png new file mode 100644 index 0000000000000000000000000000000000000000..d443f6e974ae9d6dda73e1637934fe75edf56b57 GIT binary patch literal 7973 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