1 /***************************************************************************
2                          testqgslayoutshapes.cpp
3                          ----------------------
4     begin                : October 2017
5     copyright            : (C) 2017 by Nyall Dawson
6     email                : nyall dot dawson at gmail.com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17 
18 #include "qgsapplication.h"
19 #include "qgslayout.h"
20 #include "qgsmultirenderchecker.h"
21 #include "qgslayoutitemshape.h"
22 #include "qgsmapsettings.h"
23 #include "qgsproject.h"
24 #include "qgssymbol.h"
25 #include "qgssinglesymbolrenderer.h"
26 #include "qgsfillsymbollayer.h"
27 #include "qgsreadwritecontext.h"
28 #include <QObject>
29 #include "qgstest.h"
30 #include <QColor>
31 #include <QPainter>
32 
33 class TestQgsLayoutShapes : public QObject
34 {
35     Q_OBJECT
36 
37   public:
38     TestQgsLayoutShapes() = default;
39 
40   private slots:
41     void initTestCase();// will be called before the first testfunction is executed.
42     void cleanupTestCase();// will be called after the last testfunction was executed.
43     void init();// will be called before each testfunction is executed.
44     void cleanup();// will be called after every testfunction.
45     void rectangle(); //test if rectangle shape is functioning
46     void triangle(); //test if triangle shape is functioning
47     void ellipse(); //test if ellipse shape is functioning
48     void roundedRectangle(); //test if rounded rectangle shape is functioning
49     void symbol(); //test if styling shapes via symbol is working
50     void readWriteXml();
51     void bounds();
52     void shapeRotation();
53 
54   private:
55 
56     QString mReport;
57 };
58 
initTestCase()59 void TestQgsLayoutShapes::initTestCase()
60 {
61   QgsApplication::init();
62   QgsApplication::initQgis();
63 
64   mReport = QStringLiteral( "<h1>Composer Shape Tests</h1>\n" );
65 }
66 
cleanupTestCase()67 void TestQgsLayoutShapes::cleanupTestCase()
68 {
69   QString myReportFile = QDir::tempPath() + "/qgistest.html";
70   QFile myFile( myReportFile );
71   if ( myFile.open( QIODevice::WriteOnly | QIODevice::Append ) )
72   {
73     QTextStream myQTextStream( &myFile );
74     myQTextStream << mReport;
75     myFile.close();
76   }
77   QgsApplication::exitQgis();
78 }
79 
init()80 void TestQgsLayoutShapes::init()
81 {
82 
83 }
84 
cleanup()85 void TestQgsLayoutShapes::cleanup()
86 {
87 
88 }
89 
rectangle()90 void TestQgsLayoutShapes::rectangle()
91 {
92   QgsProject p;
93   QgsLayout l( &p );
94   l.initializeDefaults();
95 
96   QgsLayoutItemShape *shape = new QgsLayoutItemShape( &l );
97   shape->attemptMove( QgsLayoutPoint( 20, 20 ) );
98   shape->attemptResize( QgsLayoutSize( 150, 100 ) );
99 
100 
101   QgsSimpleFillSymbolLayer *simpleFill = new QgsSimpleFillSymbolLayer();
102   std::unique_ptr< QgsFillSymbol > fillSymbol( new QgsFillSymbol() );
103   fillSymbol->changeSymbolLayer( 0, simpleFill );
104   simpleFill->setColor( QColor( 255, 150, 0 ) );
105   simpleFill->setStrokeColor( QColor( 0, 0, 0 ) );
106   simpleFill->setStrokeWidth( 0.3 );
107   simpleFill->setPenJoinStyle( Qt::MiterJoin );
108   shape->setSymbol( fillSymbol.get() );
109 
110   l.addLayoutItem( shape );
111 
112   QgsLayoutChecker checker( QStringLiteral( "composershapes_rectangle" ), &l );
113   checker.setControlPathPrefix( QStringLiteral( "composer_shapes" ) );
114   QVERIFY( checker.testLayout( mReport ) );
115 }
116 
triangle()117 void TestQgsLayoutShapes::triangle()
118 {
119   QgsProject p;
120   QgsLayout l( &p );
121   l.initializeDefaults();
122 
123   QgsLayoutItemShape *shape = new QgsLayoutItemShape( &l );
124   shape->setShapeType( QgsLayoutItemShape::Triangle );
125   shape->attemptMove( QgsLayoutPoint( 20, 20 ) );
126   shape->attemptResize( QgsLayoutSize( 150, 100 ) );
127 
128 
129   QgsSimpleFillSymbolLayer *simpleFill = new QgsSimpleFillSymbolLayer();
130   std::unique_ptr< QgsFillSymbol > fillSymbol( new QgsFillSymbol() );
131   fillSymbol->changeSymbolLayer( 0, simpleFill );
132   simpleFill->setColor( QColor( 255, 150, 0 ) );
133   simpleFill->setStrokeColor( QColor( 0, 0, 0 ) );
134   simpleFill->setStrokeWidth( 0.3 );
135   simpleFill->setPenJoinStyle( Qt::MiterJoin );
136   shape->setSymbol( fillSymbol.get() );
137 
138   l.addLayoutItem( shape );
139 
140   QgsLayoutChecker checker( QStringLiteral( "composershapes_triangle" ), &l );
141   checker.setControlPathPrefix( QStringLiteral( "composer_shapes" ) );
142   QVERIFY( checker.testLayout( mReport ) );
143 }
144 
ellipse()145 void TestQgsLayoutShapes::ellipse()
146 {
147   QgsProject p;
148   QgsLayout l( &p );
149   l.initializeDefaults();
150 
151   QgsLayoutItemShape *shape = new QgsLayoutItemShape( &l );
152   shape->setShapeType( QgsLayoutItemShape::Ellipse );
153   shape->attemptMove( QgsLayoutPoint( 20, 20 ) );
154   shape->attemptResize( QgsLayoutSize( 150, 100 ) );
155 
156   QgsSimpleFillSymbolLayer *simpleFill = new QgsSimpleFillSymbolLayer();
157   std::unique_ptr< QgsFillSymbol > fillSymbol( new QgsFillSymbol() );
158   fillSymbol->changeSymbolLayer( 0, simpleFill );
159   simpleFill->setColor( QColor( 255, 150, 0 ) );
160   simpleFill->setStrokeColor( QColor( 0, 0, 0 ) );
161   simpleFill->setStrokeWidth( 0.3 );
162   simpleFill->setPenJoinStyle( Qt::MiterJoin );
163   shape->setSymbol( fillSymbol.get() );
164 
165   l.addLayoutItem( shape );
166 
167   QgsLayoutChecker checker( QStringLiteral( "composershapes_ellipse" ), &l );
168   checker.setControlPathPrefix( QStringLiteral( "composer_shapes" ) );
169   QVERIFY( checker.testLayout( mReport ) );
170 }
171 
roundedRectangle()172 void TestQgsLayoutShapes::roundedRectangle()
173 {
174   QgsProject p;
175   QgsLayout l( &p );
176   l.initializeDefaults();
177 
178   QgsLayoutItemShape *shape = new QgsLayoutItemShape( &l );
179   shape->attemptMove( QgsLayoutPoint( 20, 20 ) );
180   shape->attemptResize( QgsLayoutSize( 150, 100 ) );
181 
182   QgsSimpleFillSymbolLayer *simpleFill = new QgsSimpleFillSymbolLayer();
183   std::unique_ptr< QgsFillSymbol > fillSymbol( new QgsFillSymbol() );
184   fillSymbol->changeSymbolLayer( 0, simpleFill );
185   simpleFill->setColor( QColor( 255, 150, 0 ) );
186   simpleFill->setStrokeColor( QColor( 0, 0, 0 ) );
187   simpleFill->setStrokeWidth( 0.3 );
188   simpleFill->setPenJoinStyle( Qt::MiterJoin );
189   shape->setSymbol( fillSymbol.get() );
190 
191   l.addLayoutItem( shape );
192 
193   shape->setCornerRadius( QgsLayoutMeasurement( 30 ) );
194 
195   QgsLayoutChecker checker( QStringLiteral( "composershapes_roundedrect" ), &l );
196   checker.setControlPathPrefix( QStringLiteral( "composer_shapes" ) );
197   QVERIFY( checker.testLayout( mReport ) );
198 }
199 
symbol()200 void TestQgsLayoutShapes::symbol()
201 {
202   QgsProject p;
203   QgsLayout l( &p );
204   l.initializeDefaults();
205 
206   QgsLayoutItemShape *shape = new QgsLayoutItemShape( &l );
207   shape->attemptMove( QgsLayoutPoint( 20, 20 ) );
208   shape->attemptResize( QgsLayoutSize( 150, 100 ) );
209 
210   //setup simple fill
211   QgsSimpleFillSymbolLayer *simpleFill = new QgsSimpleFillSymbolLayer();
212   QgsFillSymbol *fillSymbol = new QgsFillSymbol();
213   fillSymbol->changeSymbolLayer( 0, simpleFill );
214   simpleFill->setColor( Qt::green );
215   simpleFill->setStrokeColor( Qt::yellow );
216   simpleFill->setStrokeWidth( 6 );
217   shape->setSymbol( fillSymbol );
218   delete fillSymbol;
219 
220   l.addLayoutItem( shape );
221   QgsLayoutChecker checker( QStringLiteral( "composershapes_symbol" ), &l );
222   checker.setControlPathPrefix( QStringLiteral( "composer_shapes" ) );
223   QVERIFY( checker.testLayout( mReport ) );
224 }
225 
readWriteXml()226 void TestQgsLayoutShapes::readWriteXml()
227 {
228   QgsProject p;
229   QgsLayout l( &p );
230   std::unique_ptr< QgsLayoutItemShape > shape = qgis::make_unique< QgsLayoutItemShape >( &l );
231   shape->setShapeType( QgsLayoutItemShape::Triangle );
232   QgsSimpleFillSymbolLayer *simpleFill = new QgsSimpleFillSymbolLayer();
233   QgsFillSymbol *fillSymbol = new QgsFillSymbol();
234   fillSymbol->changeSymbolLayer( 0, simpleFill );
235   simpleFill->setColor( Qt::green );
236   simpleFill->setStrokeColor( Qt::yellow );
237   simpleFill->setStrokeWidth( 6 );
238   shape->setSymbol( fillSymbol );
239   delete fillSymbol;
240 
241   //save original item to xml
242   QDomImplementation DomImplementation;
243   QDomDocumentType documentType =
244     DomImplementation.createDocumentType(
245       QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ), QStringLiteral( "SYSTEM" ) );
246   QDomDocument doc( documentType );
247   QDomElement rootNode = doc.createElement( QStringLiteral( "qgis" ) );
248 
249   shape->writeXml( rootNode, doc, QgsReadWriteContext() );
250 
251   //create new item and restore settings from xml
252   std::unique_ptr< QgsLayoutItemShape > copy = qgis::make_unique< QgsLayoutItemShape >( &l );
253   QVERIFY( copy->readXml( rootNode.firstChildElement(), doc, QgsReadWriteContext() ) );
254   QCOMPARE( copy->shapeType(), QgsLayoutItemShape::Triangle );
255   QCOMPARE( copy->symbol()->symbolLayer( 0 )->color().name(), QStringLiteral( "#00ff00" ) );
256   QCOMPARE( copy->symbol()->symbolLayer( 0 )->strokeColor().name(), QStringLiteral( "#ffff00" ) );
257 }
258 
bounds()259 void TestQgsLayoutShapes::bounds()
260 {
261   QgsProject p;
262   QgsLayout l( &p );
263   std::unique_ptr< QgsLayoutItemShape > shape = qgis::make_unique< QgsLayoutItemShape >( &l );
264   shape->attemptMove( QgsLayoutPoint( 20, 20 ) );
265   shape->attemptResize( QgsLayoutSize( 150, 100 ) );
266 
267   QgsSimpleFillSymbolLayer *simpleFill = new QgsSimpleFillSymbolLayer();
268   QgsFillSymbol *fillSymbol = new QgsFillSymbol();
269   fillSymbol->changeSymbolLayer( 0, simpleFill );
270   simpleFill->setColor( Qt::green );
271   simpleFill->setStrokeColor( Qt::yellow );
272   simpleFill->setStrokeWidth( 6 );
273   shape->setSymbol( fillSymbol );
274   delete fillSymbol;
275 
276   // scene bounding rect should include symbol outline
277   QRectF bounds = shape->sceneBoundingRect();
278   QCOMPARE( bounds.left(), 17.0 );
279   QCOMPARE( bounds.right(), 173.0 );
280   QCOMPARE( bounds.top(), 17.0 );
281   QCOMPARE( bounds.bottom(), 123.0 );
282 
283   // rectWithFrame should include symbol outline too
284   bounds = shape->rectWithFrame();
285   QCOMPARE( bounds.left(), -3.0 );
286   QCOMPARE( bounds.right(), 153.0 );
287   QCOMPARE( bounds.top(), -3.0 );
288   QCOMPARE( bounds.bottom(), 103.0 );
289 }
290 
shapeRotation()291 void TestQgsLayoutShapes::shapeRotation()
292 {
293   QgsProject p;
294   QgsLayout l( &p );
295   l.initializeDefaults();
296 
297   QgsLayoutItemShape *shape = new QgsLayoutItemShape( &l );
298   shape->attemptSetSceneRect( QRectF( 70, 70, 150, 100 ) );
299   shape->setItemRotation( 45 );
300 
301   //setup simple fill
302   QgsSimpleFillSymbolLayer *simpleFill = new QgsSimpleFillSymbolLayer();
303   QgsFillSymbol *fillSymbol = new QgsFillSymbol();
304   fillSymbol->changeSymbolLayer( 0, simpleFill );
305   simpleFill->setColor( QColor( 255, 150, 0 ) );
306   simpleFill->setStrokeColor( Qt::black );
307   //simpleFill->setStrokeColor( Qt::yellow );
308   //simpleFill->setStrokeWidth( 6 );
309   shape->setSymbol( fillSymbol );
310   delete fillSymbol;
311 
312   l.addLayoutItem( shape );
313   QgsLayoutChecker checker( QStringLiteral( "composerrotation_shape" ), &l );
314   checker.setControlPathPrefix( QStringLiteral( "composer_items" ) );
315   QVERIFY( checker.testLayout( mReport ) );
316 }
317 
318 QGSTEST_MAIN( TestQgsLayoutShapes )
319 #include "testqgslayoutshapes.moc"
320