QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
qgsdxfexport.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsdxfexport.cpp
3  ----------------
4  begin : September 2013
5  copyright : (C) 2013 by Marco Hugentobler
6  email : marco at sourcepole dot ch
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 // Specs:
19 // AutoCAD 2000: http://www.autodesk.com/techpubs/autocad/acad2000/dxf/
20 // AutoCAD 2002: http://www.autodesk.com/techpubs/autocad/dxf/dxf2002.pdf
21 // AutoCAD 2004: http://atrey.karlin.mff.cuni.cz/projekty/vrr/doc/dxf14.pdf
22 // AutoCAD 2006: http://images.autodesk.com/adsk/files/dxf_format.pdf
23 // AutoCAD 2008: http://images.autodesk.com/adsk/files/acad_dxf0.pdf
24 // AutoCAD 2009: http://images.autodesk.com/adsk/files/acad_dxf.pdf
25 // AutoCAD 2011: http://images.autodesk.com/adsk/files/acad_dxf2.pdf
26 // AutoCAD 2012: http://images.autodesk.com/adsk/files/autocad_2012_pdf_dxf-reference_enu.pdf
27 // AutoCAD 2014: http://images.autodesk.com/adsk/files/autocad_2014_pdf_dxf_reference_enu.pdf
28 
29 #include "qgsdxfexport.h"
31 #include "qgsgeometrycollection.h"
32 #include "qgscurvepolygon.h"
33 #include "qgscompoundcurve.h"
34 #include "qgscircularstring.h"
35 #include "qgslinestring.h"
36 #include "qgsvectordataprovider.h"
37 #include "qgspointxy.h"
38 #include "qgsproject.h"
39 #include "qgsrenderer.h"
40 #include "qgssymbollayer.h"
41 #include "qgsfillsymbollayer.h"
42 #include "qgsfeatureiterator.h"
43 #include "qgslinesymbollayer.h"
44 #include "qgsvectorlayer.h"
45 #include "qgsunittypes.h"
46 #include "qgstextlabelfeature.h"
47 #include "qgslogger.h"
48 #include "qgsmaplayerstyle.h"
51 #include "qgsdxfexport_p.h"
52 
53 #include "qgswkbtypes.h"
54 #include "qgspoint.h"
55 #include "qgsgeos.h"
56 
57 #include "pal/feature.h"
58 #include "pal/pointset.h"
59 #include "pal/labelposition.h"
60 
61 #include <QIODevice>
62 #include <QTextCodec>
63 
64 QgsDxfExport::QgsDxfExport() = default;
65 
67 {
68  qDeleteAll( mJobs );
69 }
70 
72 {
73  mMapSettings = settings;
74 }
75 
76 void QgsDxfExport::setFlags( QgsDxfExport::Flags flags )
77 {
78  mFlags = flags;
79 }
80 
81 QgsDxfExport::Flags QgsDxfExport::flags() const
82 {
83  return mFlags;
84 }
85 
86 void QgsDxfExport::addLayers( const QList<DxfLayer> &layers )
87 {
88  QList<QgsMapLayer *> layerList;
89 
90  mLayerNameAttribute.clear();
91 
92  for ( const DxfLayer &dxfLayer : layers )
93  {
94  layerList << dxfLayer.layer();
95  if ( dxfLayer.layerOutputAttributeIndex() >= 0 )
96  mLayerNameAttribute.insert( dxfLayer.layer()->id(), dxfLayer.layerOutputAttributeIndex() );
97  }
98 
99  mMapSettings.setLayers( layerList );
100 }
101 
102 void QgsDxfExport::writeGroup( int code, int i )
103 {
104  writeGroupCode( code );
105  writeInt( i );
106 }
107 
108 void QgsDxfExport::writeGroup( int code, double d )
109 {
110  writeGroupCode( code );
111  writeDouble( d );
112 }
113 
114 void QgsDxfExport::writeGroup( int code, const QString &s )
115 {
116  writeGroupCode( code );
117  writeString( s );
118 }
119 
120 void QgsDxfExport::writeGroup( int code, const QgsPoint &p )
121 {
122  writeGroup( code + 10, p.x() );
123  writeGroup( code + 20, p.y() );
124  if ( !mForce2d && p.is3D() && std::isfinite( p.z() ) )
125  writeGroup( code + 30, p.z() );
126 }
127 
128 void QgsDxfExport::writeGroup( const QColor &color, int exactMatchCode, int rgbCode, int transparencyCode )
129 {
130  int minDistAt = -1;
131  int minDist = std::numeric_limits<int>::max();
132 
133  for ( int i = 1; i < static_cast< int >( sizeof( sDxfColors ) / sizeof( *sDxfColors ) ) && minDist > 0; ++i )
134  {
135  int dist = color_distance( color.rgba(), i );
136  if ( dist >= minDist )
137  continue;
138 
139  minDistAt = i;
140  minDist = dist;
141  }
142 
143  if ( minDist == 0 && minDistAt != 7 )
144  {
145  // exact full opaque match, not black/white
146  writeGroup( exactMatchCode, minDistAt );
147  if ( color.alpha() == 255 )
148  return;
149  }
150 
151  int c = ( color.red() & 0xff ) * 0x10000 + ( color.green() & 0xff ) * 0x100 + ( color.blue() & 0xff );
152  writeGroup( rgbCode, c );
153  if ( transparencyCode != -1 && color.alpha() < 255 )
154  writeGroup( transparencyCode, 0x2000000 | color.alpha() );
155 }
156 
158 {
159  mTextStream << QStringLiteral( "%1\n" ).arg( code, 3, 10, QChar( ' ' ) );
160 }
161 
163 {
164  mTextStream << QStringLiteral( "%1\n" ).arg( i, 6, 10, QChar( ' ' ) );
165 }
166 
168 {
169  QString s( qgsDoubleToString( d ) );
170  if ( !s.contains( '.' ) )
171  s += QLatin1String( ".0" );
172  mTextStream << s << '\n';
173 }
174 
175 void QgsDxfExport::writeString( const QString &s )
176 {
177  mTextStream << s << '\n';
178 }
179 
180 QgsDxfExport::ExportResult QgsDxfExport::writeToFile( QIODevice *d, const QString &encoding )
181 {
182  if ( !d )
183  {
185  }
186 
187  if ( !d->isOpen() && !d->open( QIODevice::WriteOnly | QIODevice::Truncate ) )
188  {
190  }
191 
192  mTextStream.setDevice( d );
193  mTextStream.setCodec( encoding.toLocal8Bit() );
194 
195  if ( mCrs.isValid() )
196  mMapSettings.setDestinationCrs( mCrs );
197 
198  if ( mExtent.isEmpty() )
199  {
200  const QList< QgsMapLayer * > layers = mMapSettings.layers();
201  for ( QgsMapLayer *ml : layers )
202  {
203  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml );
204  if ( !vl )
205  continue;
206 
207  QgsRectangle layerExtent = vl->extent();
208  if ( layerExtent.isEmpty() )
209  continue;
210 
211  layerExtent = mMapSettings.layerToMapCoordinates( vl, layerExtent );
212 
213  if ( mExtent.isEmpty() )
214  {
215  mExtent = layerExtent;
216  }
217  else
218  {
219  mExtent.combineExtentWith( layerExtent );
220  }
221  }
222  }
223 
224  if ( mExtent.isEmpty() )
226 
228  mMapSettings.setExtent( mExtent );
229 
230  int dpi = 96;
231  mFactor = 1000 * dpi / mSymbologyScale / 25.4 * QgsUnitTypes::fromUnitToUnitFactor( mapUnits, QgsUnitTypes::DistanceMeters );
232  mMapSettings.setOutputSize( QSize( mExtent.width() * mFactor, mExtent.height() * mFactor ) );
233  mMapSettings.setOutputDpi( dpi );
234 
235  writeHeader( dxfEncoding( encoding ) );
236  prepareRenderers();
237  writeTables();
238  writeBlocks();
239  writeEntities();
240  writeEndFile();
241  stopRenderers();
242 
243  return ExportResult::Success;
244 }
245 
247 {
248  return mMapUnits;
249 }
250 
251 void QgsDxfExport::writeHeader( const QString &codepage )
252 {
253  writeGroup( 999, QStringLiteral( "DXF created from QGIS" ) );
254 
255  startSection();
256  writeGroup( 2, QStringLiteral( "HEADER" ) );
257 
258  // ACADVER
259  writeGroup( 9, QStringLiteral( "$ACADVER" ) );
260  writeGroup( 1, QStringLiteral( "AC1015" ) );
261 
262  // EXTMIN
263  writeGroup( 9, QStringLiteral( "$EXTMIN" ) );
264  writeGroup( 0, QgsPoint( QgsWkbTypes::PointZ, mExtent.xMinimum(), mExtent.yMinimum(), 0.0 ) );
265 
266  // EXTMAX
267  writeGroup( 9, QStringLiteral( "$EXTMAX" ) );
268  writeGroup( 0, QgsPoint( QgsWkbTypes::PointZ, mExtent.xMaximum(), mExtent.yMaximum(), 0.0 ) );
269 
270  // Global linetype scale
271  writeGroup( 9, QStringLiteral( "$LTSCALE" ) );
272  writeGroup( 40, 1.0 );
273 
274  // Point display mode (33 = circle)
275  writeGroup( 9, QStringLiteral( "$PDMODE" ) );
276  writeGroup( 70, 33 );
277 
278  // Point display size
279  writeGroup( 9, QStringLiteral( "$PDSIZE" ) );
280  writeGroup( 40, 1 );
281 
282  // Controls paper space linetype scaling (1 = No special linetype scaling, 0 = Viewport scaling governs linetype scaling)
283  writeGroup( 9, QStringLiteral( "$PSLTSCALE" ) );
284  writeGroup( 70, 0 );
285 
286  writeGroup( 9, QStringLiteral( "$HANDSEED" ) );
287  writeGroup( 5, DXF_HANDMAX );
288 
289  writeGroup( 9, QStringLiteral( "$DWGCODEPAGE" ) );
290  writeGroup( 3, codepage );
291 
292  endSection();
293 }
294 
295 int QgsDxfExport::writeHandle( int code, int handle )
296 {
297  if ( handle == 0 )
298  handle = mNextHandleId++;
299 
300  Q_ASSERT_X( handle < DXF_HANDMAX, "QgsDxfExport::writeHandle(int, int)", "DXF handle too large" );
301 
302  writeGroup( code, QString::number( handle, 16 ) );
303  return handle;
304 }
305 
306 void QgsDxfExport::writeTables()
307 {
308  startSection();
309  writeGroup( 2, QStringLiteral( "TABLES" ) );
310 
311  // Iterate through all layers and get symbol layer pointers
312  QgsRenderContext context = renderContext();
313  QList< QPair< QgsSymbolLayer *, QgsSymbol * > > slList;
314  if ( mSymbologyExport != NoSymbology )
315  {
316  slList = symbolLayers( context );
317  }
318 
319  // Line types
320  mLineStyles.clear();
321  writeGroup( 0, QStringLiteral( "TABLE" ) );
322  writeGroup( 2, QStringLiteral( "LTYPE" ) );
323  writeHandle();
324  writeGroup( 100, QStringLiteral( "AcDbSymbolTable" ) );
325  writeGroup( 70, nLineTypes( slList ) + 5 );
326 
327  writeDefaultLinetypes();
328 
329  // Add custom linestyles
330  for ( const auto &symbolLayer : qgis::as_const( slList ) )
331  {
332  writeSymbolLayerLinetype( symbolLayer.first );
333  }
334 
335  writeGroup( 0, QStringLiteral( "ENDTAB" ) );
336 
337  // BLOCK_RECORD
338  writeGroup( 0, QStringLiteral( "TABLE" ) );
339  writeGroup( 2, QStringLiteral( "BLOCK_RECORD" ) );
340  writeHandle();
341 
342  writeGroup( 100, QStringLiteral( "AcDbSymbolTable" ) );
343  writeGroup( 70, 0 );
344 
345  const QStringList blockStrings = QStringList() << QStringLiteral( "*Model_Space" ) << QStringLiteral( "*Paper_Space" ) << QStringLiteral( "*Paper_Space0" );
346  for ( const QString &block : blockStrings )
347  {
348  writeGroup( 0, QStringLiteral( "BLOCK_RECORD" ) );
349  mBlockHandles.insert( block, writeHandle() );
350  writeGroup( 100, QStringLiteral( "AcDbSymbolTableRecord" ) );
351  writeGroup( 100, QStringLiteral( "AcDbBlockTableRecord" ) );
352  writeGroup( 2, block );
353  }
354 
355  int i = 0;
356  for ( const auto &symbolLayer : qgis::as_const( slList ) )
357  {
358  QgsMarkerSymbolLayer *ml = dynamic_cast< QgsMarkerSymbolLayer *>( symbolLayer.first );
359  if ( !ml )
360  continue;
361 
362  if ( hasDataDefinedProperties( ml, symbolLayer.second ) )
363  continue;
364 
365  QString name = QStringLiteral( "symbolLayer%1" ).arg( i++ );
366  writeGroup( 0, QStringLiteral( "BLOCK_RECORD" ) );
367  mBlockHandles.insert( name, writeHandle() );
368  writeGroup( 100, QStringLiteral( "AcDbSymbolTableRecord" ) );
369  writeGroup( 100, QStringLiteral( "AcDbBlockTableRecord" ) );
370  writeGroup( 2, name );
371  }
372 
373  writeGroup( 0, QStringLiteral( "ENDTAB" ) );
374 
375  // APPID
376  writeGroup( 0, QStringLiteral( "TABLE" ) );
377  writeGroup( 2, QStringLiteral( "APPID" ) );
378  writeHandle();
379  writeGroup( 100, QStringLiteral( "AcDbSymbolTable" ) );
380  writeGroup( 70, 1 );
381  writeGroup( 0, QStringLiteral( "APPID" ) );
382  writeHandle();
383  writeGroup( 100, QStringLiteral( "AcDbSymbolTableRecord" ) );
384  writeGroup( 100, QStringLiteral( "AcDbRegAppTableRecord" ) );
385  writeGroup( 2, QStringLiteral( "ACAD" ) );
386  writeGroup( 70, 0 );
387  writeGroup( 0, QStringLiteral( "ENDTAB" ) );
388 
389  // VIEW
390  writeGroup( 0, QStringLiteral( "TABLE" ) );
391  writeGroup( 2, QStringLiteral( "VIEW" ) );
392  writeHandle();
393  writeGroup( 100, QStringLiteral( "AcDbSymbolTable" ) );
394  writeGroup( 70, 0 );
395  writeGroup( 0, QStringLiteral( "ENDTAB" ) );
396 
397  // UCS
398  writeGroup( 0, QStringLiteral( "TABLE" ) );
399  writeGroup( 2, QStringLiteral( "UCS" ) );
400  writeHandle();
401  writeGroup( 100, QStringLiteral( "AcDbSymbolTable" ) );
402  writeGroup( 70, 0 );
403  writeGroup( 0, QStringLiteral( "ENDTAB" ) );
404 
405  // VPORT
406  writeGroup( 0, QStringLiteral( "TABLE" ) );
407  writeGroup( 2, QStringLiteral( "VPORT" ) );
408  writeHandle();
409  writeGroup( 100, QStringLiteral( "AcDbSymbolTable" ) );
410 
411  writeGroup( 0, QStringLiteral( "VPORT" ) );
412  writeHandle();
413  writeGroup( 100, QStringLiteral( "AcDbSymbolTableRecord" ) );
414  writeGroup( 100, QStringLiteral( "AcDbViewportTableRecord" ) );
415  writeGroup( 2, QStringLiteral( "*ACTIVE" ) );
416  writeGroup( 70, 0 ); // flags
417  writeGroup( 0, QgsPoint( 0.0, 0.0 ) ); // lower left
418  writeGroup( 1, QgsPoint( 1.0, 1.0 ) ); // upper right
419  writeGroup( 2, QgsPoint( 0.0, 0.0 ) ); // view center point
420  writeGroup( 3, QgsPoint( 0.0, 0.0 ) ); // snap base point
421  writeGroup( 4, QgsPoint( 1.0, 1.0 ) ); // snap spacing
422  writeGroup( 5, QgsPoint( 1.0, 1.0 ) ); // grid spacing
423  writeGroup( 6, QgsPoint( QgsWkbTypes::PointZ, 0.0, 0.0, 1.0 ) ); // view direction from target point
424  writeGroup( 7, QgsPoint( mExtent.center() ) ); // view target point
425  writeGroup( 40, mExtent.height() ); // view height
426  writeGroup( 41, mExtent.width() / mExtent.height() ); // view aspect ratio
427  writeGroup( 42, 50.0 ); // lens length
428  writeGroup( 43, 0.0 ); // front clipping plane
429  writeGroup( 44, 0.0 ); // back clipping plane
430  writeGroup( 50, 0.0 ); // snap rotation
431  writeGroup( 51, 0.0 ); // view twist angle
432  writeGroup( 71, 0 ); // view mode (0 = deactivates)
433  writeGroup( 72, 100 ); // circle zoom percent
434  writeGroup( 73, 1 ); // fast zoom setting
435  writeGroup( 74, 1 ); // UCSICON setting
436  writeGroup( 75, 0 ); // snapping off
437  writeGroup( 76, 0 ); // grid off
438  writeGroup( 77, 0 ); // snap style
439  writeGroup( 78, 0 ); // snap isopair
440  writeGroup( 281, 0 ); // render mode (0 = 2D optimized)
441  writeGroup( 65, 1 ); // value of UCSVP for this viewport
442  writeGroup( 100, QgsPoint( QgsWkbTypes::PointZ, 0.0, 0.0, 0.0 ) );// UCS origin
443  writeGroup( 101, QgsPoint( QgsWkbTypes::PointZ, 1.0, 0.0, 0.0 ) );// UCS x axis
444  writeGroup( 102, QgsPoint( QgsWkbTypes::PointZ, 0.0, 1.0, 0.0 ) );// UCS y axis
445  writeGroup( 79, 0 ); // Orthographic type of UCS (0 = UCS is not orthographic)
446  writeGroup( 146, 0.0 ); // Elevation
447 
448  writeGroup( 70, 0 );
449  writeGroup( 0, QStringLiteral( "ENDTAB" ) );
450 
451  // DIMSTYLE
452  writeGroup( 0, QStringLiteral( "TABLE" ) );
453  writeGroup( 2, QStringLiteral( "DIMSTYLE" ) );
454  writeHandle();
455  writeGroup( 100, QStringLiteral( "AcDbSymbolTable" ) );
456  writeGroup( 100, QStringLiteral( "AcDbDimStyleTable" ) );
457  writeGroup( 70, 0 );
458  writeGroup( 0, QStringLiteral( "ENDTAB" ) );
459 
460  QSet<QString> layerNames;
461  const QList< QgsMapLayer * > layers = mMapSettings.layers();
462  for ( QgsMapLayer *ml : layers )
463  {
464  if ( !layerIsScaleBasedVisible( ml ) )
465  continue;
466 
467  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml );
468  if ( !vl )
469  continue;
470 
471  int attrIdx = mLayerNameAttribute.value( vl->id(), -1 );
472  if ( attrIdx < 0 )
473  {
474  layerNames << dxfLayerName( layerName( vl ) );
475  }
476  else
477  {
478  const QSet<QVariant> values = vl->uniqueValues( attrIdx );
479  for ( const QVariant &v : values )
480  {
481  layerNames << dxfLayerName( v.toString() );
482  }
483  }
484  }
485 
486  // Layers
487  // TODO: iterate features of all layer to produce a data-defined layer list
488  writeGroup( 0, QStringLiteral( "TABLE" ) );
489  writeGroup( 2, QStringLiteral( "LAYER" ) );
490  writeHandle();
491  writeGroup( 100, QStringLiteral( "AcDbSymbolTable" ) );
492  writeGroup( 70, layerNames.size() + 1 );
493 
494  writeGroup( 0, QStringLiteral( "LAYER" ) );
495  writeHandle();
496  writeGroup( 100, QStringLiteral( "AcDbSymbolTableRecord" ) );
497  writeGroup( 100, QStringLiteral( "AcDbLayerTableRecord" ) );
498  writeGroup( 2, QStringLiteral( "0" ) );
499  writeGroup( 70, 64 );
500  writeGroup( 62, 1 );
501  writeGroup( 6, QStringLiteral( "CONTINUOUS" ) );
503 
504  for ( const QString &layerName : qgis::as_const( layerNames ) )
505  {
506  writeGroup( 0, QStringLiteral( "LAYER" ) );
507  writeHandle();
508  writeGroup( 100, QStringLiteral( "AcDbSymbolTableRecord" ) );
509  writeGroup( 100, QStringLiteral( "AcDbLayerTableRecord" ) );
510  writeGroup( 2, layerName );
511  writeGroup( 70, 64 );
512  writeGroup( 62, 1 );
513  writeGroup( 6, QStringLiteral( "CONTINUOUS" ) );
515  }
516  writeGroup( 0, QStringLiteral( "ENDTAB" ) );
517 
518  // Text styles
519  writeGroup( 0, QStringLiteral( "TABLE" ) );
520  writeGroup( 2, QStringLiteral( "STYLE" ) );
521  writeHandle();
522  writeGroup( 100, QStringLiteral( "AcDbSymbolTable" ) );
523  writeGroup( 70, 1 );
524 
525  // Provide only standard font for the moment
526  writeGroup( 0, QStringLiteral( "STYLE" ) );
527  writeHandle();
528  writeGroup( 100, QStringLiteral( "AcDbSymbolTableRecord" ) );
529  writeGroup( 100, QStringLiteral( "AcDbTextStyleTableRecord" ) );
530  writeGroup( 2, QStringLiteral( "STANDARD" ) );
531  writeGroup( 70, 64 );
532  writeGroup( 40, 0.0 );
533  writeGroup( 41, 1.0 );
534  writeGroup( 50, 0.0 );
535  writeGroup( 71, 0 );
536  writeGroup( 42, 5.0 );
537  writeGroup( 3, QStringLiteral( "romans.shx" ) );
538  writeGroup( 4, QString() );
539 
540  writeGroup( 0, QStringLiteral( "ENDTAB" ) );
541 
542  endSection();
543 }
544 
545 void QgsDxfExport::writeBlocks()
546 {
547  startSection();
548  writeGroup( 2, QStringLiteral( "BLOCKS" ) );
549 
550  static const QStringList blockStrings = QStringList() << QStringLiteral( "*Model_Space" ) << QStringLiteral( "*Paper_Space" ) << QStringLiteral( "*Paper_Space0" );
551  for ( const QString &block : blockStrings )
552  {
553  writeGroup( 0, QStringLiteral( "BLOCK" ) );
554  writeHandle();
555  writeGroup( 330, QString::number( mBlockHandles[ block ], 16 ) );
556  writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
557  writeGroup( 8, QStringLiteral( "0" ) );
558  writeGroup( 100, QStringLiteral( "AcDbBlockBegin" ) );
559  writeGroup( 2, block );
560  writeGroup( 70, 0 );
561  writeGroup( 0, QgsPoint( QgsWkbTypes::PointZ, 0.0, 0.0, 0.0 ) );
562  writeGroup( 3, block );
563  writeGroup( 1, QString() );
564  writeGroup( 0, QStringLiteral( "ENDBLK" ) );
565  writeHandle();
566  writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
567  writeGroup( 8, QStringLiteral( "0" ) );
568  writeGroup( 100, QStringLiteral( "AcDbBlockEnd" ) );
569  }
570 
571  QgsRenderContext ct = renderContext();
572 
573  // Iterate through all layers and get symbol layer pointers
574  QList< QPair< QgsSymbolLayer *, QgsSymbol * > > slList;
575  if ( mSymbologyExport != NoSymbology )
576  {
577  slList = symbolLayers( ct );
578  }
579 
580  for ( const auto &symbolLayer : qgis::as_const( slList ) )
581  {
582  QgsMarkerSymbolLayer *ml = dynamic_cast< QgsMarkerSymbolLayer *>( symbolLayer.first );
583  if ( !ml )
584  continue;
585 
586  // if point symbol layer and no data defined properties: write block
587  QgsSymbolRenderContext ctx( ct, QgsUnitTypes::RenderMapUnits, symbolLayer.second->opacity(), false, symbolLayer.second->renderHints(), nullptr );
588 
589  // markers with data defined properties are inserted inline
590  if ( hasDataDefinedProperties( ml, symbolLayer.second ) )
591  {
592  continue;
593  }
594 
595  QString block( QStringLiteral( "symbolLayer%1" ).arg( mBlockCounter++ ) );
596  mBlockHandle = QString::number( mBlockHandles[ block ], 16 );
597 
598  writeGroup( 0, QStringLiteral( "BLOCK" ) );
599  writeHandle();
600  writeGroup( 330, mBlockHandle );
601  writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
602  writeGroup( 8, QStringLiteral( "0" ) );
603  writeGroup( 100, QStringLiteral( "AcDbBlockBegin" ) );
604  writeGroup( 2, block );
605  writeGroup( 70, 0 );
606 
607  // x/y/z coordinates of reference point
608  // todo: consider anchor point
609  // double size = ml->size();
610  // size *= mapUnitScaleFactor( mSymbologyScale, ml->sizeUnit(), mMapUnits );
611  writeGroup( 0, QgsPoint( QgsWkbTypes::PointZ, 0.0, 0.0, 0.0 ) );
612  writeGroup( 3, block );
613  writeGroup( 1, QString() );
614 
615  // maplayer 0 -> block receives layer from INSERT statement
616  ml->writeDxf( *this, mapUnitScaleFactor( mSymbologyScale, ml->sizeUnit(), mMapUnits, ctx.renderContext().mapToPixel().mapUnitsPerPixel() ), QStringLiteral( "0" ), ctx );
617 
618  writeGroup( 0, QStringLiteral( "ENDBLK" ) );
619  writeHandle();
620  writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
621  writeGroup( 8, QStringLiteral( "0" ) );
622  writeGroup( 100, QStringLiteral( "AcDbBlockEnd" ) );
623 
624  mPointSymbolBlocks.insert( ml, block );
625  }
626  endSection();
627 }
628 
629 
630 void QgsDxfExport::writeEntities()
631 {
632  startSection();
633  writeGroup( 2, QStringLiteral( "ENTITIES" ) );
634 
635  mBlockHandle = QString::number( mBlockHandles[ QStringLiteral( "*Model_Space" )], 16 );
636 
637  // iterate through the maplayers
638  for ( DxfLayerJob *job : qgis::as_const( mJobs ) )
639  {
640  QgsSymbolRenderContext sctx( mRenderContext, QgsUnitTypes::RenderMillimeters, 1.0, false, QgsSymbol::RenderHints(), nullptr );
641 
642  if ( mSymbologyExport == QgsDxfExport::SymbolLayerSymbology &&
643  ( job->renderer->capabilities() & QgsFeatureRenderer::SymbolLevels ) &&
644  job->renderer->usingSymbolLevels() )
645  {
646  writeEntitiesSymbolLevels( job );
647 
648  continue;
649  }
650 
651  QgsCoordinateTransform ct( mMapSettings.destinationCrs(), job->crs, mMapSettings.transformContext() );
652 
653  QgsFeatureRequest request = QgsFeatureRequest().setSubsetOfAttributes( job->attributes, job->fields ).setExpressionContext( job->renderContext.expressionContext() );
654  request.setFilterRect( ct.transform( mExtent ) );
655 
656  QgsFeatureIterator featureIt = job->featureSource.getFeatures( request );
657 
658  QgsFeature fet;
659  while ( featureIt.nextFeature( fet ) )
660  {
661  mRenderContext.expressionContext().setFeature( fet );
662  QString lName( dxfLayerName( job->splitLayerAttribute.isNull() ? job->layerTitle : fet.attribute( job->splitLayerAttribute ).toString() ) );
663 
664  sctx.setFeature( &fet );
665 
666  if ( !job->renderer->willRenderFeature( fet, mRenderContext ) )
667  continue;
668 
669  if ( mSymbologyExport == NoSymbology )
670  {
671  addFeature( sctx, ct, lName, nullptr, nullptr ); // no symbology at all
672  }
673  else
674  {
675  const QgsSymbolList symbolList = job->renderer->symbolsForFeature( fet, mRenderContext );
676  bool hasSymbology = symbolList.size() > 0;
677 
678  if ( hasSymbology && mSymbologyExport == QgsDxfExport::SymbolLayerSymbology ) // symbol layer symbology, but layer does not use symbol levels
679  {
680  for ( QgsSymbol *symbol : symbolList )
681  {
682  const QgsSymbolLayerList symbolLayers = symbol->symbolLayers();
683  for ( QgsSymbolLayer *symbolLayer : symbolLayers )
684  {
685  if ( !symbolLayer )
686  continue;
687 
688  bool isGeometryGenerator = ( symbolLayer->layerType() == QLatin1String( "GeometryGenerator" ) );
689  if ( isGeometryGenerator )
690  {
691  addGeometryGeneratorSymbolLayer( sctx, ct, lName, symbolLayer, true );
692  }
693  else
694  {
695  addFeature( sctx, ct, lName, symbolLayer, symbol );
696  }
697  }
698  }
699  }
700  else if ( hasSymbology )
701  {
702  // take first symbollayer from first symbol
703  QgsSymbol *s = symbolList.first();
704  if ( !s || s->symbolLayerCount() < 1 )
705  {
706  continue;
707  }
708 
709  if ( s->symbolLayer( 0 )->layerType() == QLatin1String( "GeometryGenerator" ) )
710  {
711  addGeometryGeneratorSymbolLayer( sctx, ct, lName, s->symbolLayer( 0 ), false );
712  }
713  else
714  {
715  addFeature( sctx, ct, lName, s->symbolLayer( 0 ), s );
716  }
717  }
718 
719  if ( job->labelProvider )
720  {
721  job->labelProvider->registerFeature( fet, mRenderContext );
723  registerDxfLayer( job->featureSource.id(), fet.id(), lName );
725  }
726  else if ( job->ruleBasedLabelProvider )
727  {
728  job->ruleBasedLabelProvider->registerFeature( fet, mRenderContext );
730  registerDxfLayer( job->featureSource.id(), fet.id(), lName );
732  }
733  }
734  }
735  }
736 
737  QImage image( 10, 10, QImage::Format_ARGB32_Premultiplied );
738  image.setDotsPerMeterX( 96 / 25.4 * 1000 );
739  image.setDotsPerMeterY( 96 / 25.4 * 1000 );
740  QPainter painter( &image );
741  mRenderContext.setPainter( &painter );
742 
743  mRenderContext.labelingEngine()->run( mRenderContext );
744 
745  endSection();
746 }
747 
748 void QgsDxfExport::prepareRenderers()
749 {
750  Q_ASSERT( mJobs.empty() ); // If this fails, stopRenderers() was not called after the last job
751 
752  mRenderContext = QgsRenderContext();
753  mRenderContext.setRendererScale( mSymbologyScale );
754  mRenderContext.setExtent( mExtent );
755 
756  mRenderContext.setScaleFactor( 96.0 / 25.4 );
757  mRenderContext.setMapToPixel( QgsMapToPixel( 1.0 / mFactor, mExtent.center().x(), mExtent.center().y(), mExtent.width() * mFactor,
758  mExtent.height() * mFactor, 0 ) );
759 
762 
763  mLabelingEngine = qgis::make_unique<QgsDefaultLabelingEngine>();
764  mLabelingEngine->setMapSettings( mMapSettings );
765  mRenderContext.setLabelingEngine( mLabelingEngine.get() );
766 
767  const QList< QgsMapLayer * > layers = mMapSettings.layers();
768  for ( QgsMapLayer *ml : layers )
769  {
770  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml );
771  if ( !vl )
772  continue;
773 
774  if ( !vl->renderer() )
775  continue;
776 
777  if ( !layerIsScaleBasedVisible( vl ) )
778  continue;
779 
780  QString splitLayerAttribute;
781  int splitLayerAttributeIndex = mLayerNameAttribute.value( vl->id(), -1 );
782  const QgsFields fields = vl->fields();
783  if ( splitLayerAttributeIndex >= 0 && splitLayerAttributeIndex < fields.size() )
784  splitLayerAttribute = fields.at( splitLayerAttributeIndex ).name();
785  DxfLayerJob *job = new DxfLayerJob( vl, mMapSettings.layerStyleOverrides().value( vl->id() ), mRenderContext, this, splitLayerAttribute );
786  mJobs.append( job );
787  }
788 }
789 
790 void QgsDxfExport::writeEntitiesSymbolLevels( DxfLayerJob *job )
791 {
792  QHash< QgsSymbol *, QList<QgsFeature> > features;
793 
794  QgsRenderContext ctx = renderContext();
795  const QList<QgsExpressionContextScope *> scopes = job->renderContext.expressionContext().scopes();
796  for ( QgsExpressionContextScope *scope : scopes )
798  QgsSymbolRenderContext sctx( ctx, QgsUnitTypes::RenderMillimeters, 1.0, false, QgsSymbol::RenderHints(), nullptr );
799 
800  // get iterator
801  QgsFeatureRequest req;
802  req.setSubsetOfAttributes( job->renderer->usedAttributes( ctx ), job->featureSource.fields() );
803  QgsCoordinateTransform ct( mMapSettings.destinationCrs(), job->crs, mMapSettings.transformContext() );
804  req.setFilterRect( ct.transform( mExtent ) );
805 
806  QgsFeatureIterator fit = job->featureSource.getFeatures( req );
807 
808  // fetch features
809  QgsFeature fet;
810  QgsSymbol *featureSymbol = nullptr;
811  while ( fit.nextFeature( fet ) )
812  {
813  ctx.expressionContext().setFeature( fet );
814  featureSymbol = job->renderer->symbolForFeature( fet, ctx );
815  if ( !featureSymbol )
816  {
817  continue;
818  }
819 
820  QHash< QgsSymbol *, QList<QgsFeature> >::iterator it = features.find( featureSymbol );
821  if ( it == features.end() )
822  {
823  it = features.insert( featureSymbol, QList<QgsFeature>() );
824  }
825  it.value().append( fet );
826  }
827 
828  // find out order
829  QgsSymbolLevelOrder levels;
830  const QgsSymbolList symbols = job->renderer->symbols( ctx );
831  for ( QgsSymbol *symbol : symbols )
832  {
833  for ( int j = 0; j < symbol->symbolLayerCount(); j++ )
834  {
835  int level = symbol->symbolLayer( j )->renderingPass();
836  if ( level < 0 || level >= 1000 ) // ignore invalid levels
837  continue;
838  QgsSymbolLevelItem item( symbol, j );
839  while ( level >= levels.count() ) // append new empty levels
840  levels.append( QgsSymbolLevel() );
841  levels[level].append( item );
842  }
843  }
844 
845  // export symbol layers and symbology
846  for ( const QgsSymbolLevel &level : qgis::as_const( levels ) )
847  {
848  for ( const QgsSymbolLevelItem &item : level )
849  {
850  QHash< QgsSymbol *, QList<QgsFeature> >::iterator levelIt = features.find( item.symbol() );
851  if ( levelIt == features.end() )
852  {
853  continue;
854  }
855 
856  int llayer = item.layer();
857  const QList<QgsFeature> &featureList = levelIt.value();
858  for ( const QgsFeature &feature : featureList )
859  {
860  sctx.setFeature( &feature );
861  addFeature( sctx, ct, job->layerName, levelIt.key()->symbolLayer( llayer ), levelIt.key() );
862  }
863  }
864  }
865 }
866 
867 void QgsDxfExport::stopRenderers()
868 {
869  qDeleteAll( mJobs );
870  mJobs.clear();
871 }
872 
873 void QgsDxfExport::writeEndFile()
874 {
875  mTextStream << DXF_TRAILER;
876 
877  writeGroup( 0, QStringLiteral( "EOF" ) );
878 }
879 
880 void QgsDxfExport::startSection()
881 {
882  writeGroup( 0, QStringLiteral( "SECTION" ) );
883 }
884 
885 void QgsDxfExport::endSection()
886 {
887  writeGroup( 0, QStringLiteral( "ENDSEC" ) );
888 }
889 
890 void QgsDxfExport::writePoint( const QgsPoint &pt, const QString &layer, const QColor &color, QgsSymbolRenderContext &ctx, const QgsSymbolLayer *symbolLayer, const QgsSymbol *symbol, double angle )
891 {
892 #if 0
893  // debug: draw rectangle for debugging
894  const QgsMarkerSymbolLayer *msl = dynamic_cast< const QgsMarkerSymbolLayer * >( symbolLayer );
895  if ( msl )
896  {
897  double halfSize = msl->size() * mapUnitScaleFactor( mSymbologyScale,
898  msl->sizeUnit(), mMapUnits ) / 2.0;
899  writeGroup( 0, "SOLID" );
900  writeGroup( 8, layer );
901  writeGroup( 62, 1 );
902  writeGroup( 0, QgsPoint( QgsWkbTypes::PointZ, pt.x() - halfSize, pt.y() - halfSize ) );
903  writeGroup( 1, QgsPoint( QgsWkbTypes::PointZ, pt.x() + halfSize, pt.y() - halfSize ) );
904  writeGroup( 2, QgsPoint( QgsWkbTypes::PointZ, pt.x() - halfSize, pt.y() + halfSize ) );
905  writeGroup( 3, QgsPoint( QgsWkbTypes::PointZ, pt.x() + halfSize, pt.y() + halfSize ) );
906  }
907 #endif // 0
908 
909  // insert block or write point directly?
910  QHash< const QgsSymbolLayer *, QString >::const_iterator blockIt = mPointSymbolBlocks.constFind( symbolLayer );
911  if ( !symbolLayer || blockIt == mPointSymbolBlocks.constEnd() )
912  {
913  // write symbol directly here
914  const QgsMarkerSymbolLayer *msl = dynamic_cast< const QgsMarkerSymbolLayer * >( symbolLayer );
915  if ( msl && symbol )
916  {
917  if ( msl->writeDxf( *this, mapUnitScaleFactor( mSymbologyScale, msl->sizeUnit(), mMapUnits, ctx.renderContext().mapToPixel().mapUnitsPerPixel() ), layer, ctx, QPointF( pt.x(), pt.y() ) ) )
918  {
919  return;
920  }
921  }
922  writePoint( layer, color, pt ); // write default point symbol
923  }
924  else
925  {
926  // insert block reference
927  writeGroup( 0, QStringLiteral( "INSERT" ) );
928  writeHandle();
929  writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
930  writeGroup( 100, QStringLiteral( "AcDbBlockReference" ) );
931  writeGroup( 8, layer );
932  writeGroup( 2, blockIt.value() ); // Block name
933  writeGroup( 50, angle ); // angle
934  writeGroup( 0, pt ); // Insertion point (in OCS)
935  }
936 }
937 
938 void QgsDxfExport::writePolyline( const QgsPointSequence &line, const QString &layer, const QString &lineStyleName, const QColor &color, double width )
939 {
940  int n = line.size();
941  if ( n == 0 )
942  {
943  QgsDebugMsg( QStringLiteral( "writePolyline: empty line layer=%1 lineStyleName=%2" ).arg( layer, lineStyleName ) );
944  return;
945  }
946 
947  if ( n < 2 )
948  {
949  QgsDebugMsg( QStringLiteral( "writePolyline: line too short layer=%1 lineStyleName=%2" ).arg( layer, lineStyleName ) );
950  return;
951  }
952 
953  if ( mForce2d || !line.at( 0 ).is3D() )
954  {
955  bool polygon = line[0] == line[ line.size() - 1 ];
956  if ( polygon )
957  --n;
958 
959  writeGroup( 0, QStringLiteral( "LWPOLYLINE" ) );
960  writeHandle();
961  writeGroup( 8, layer );
962  writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
963  writeGroup( 100, QStringLiteral( "AcDbPolyline" ) );
964  writeGroup( 6, lineStyleName );
965  writeGroup( color );
966 
967  writeGroup( 90, n );
968  writeGroup( 70, polygon ? 1 : 0 );
969  writeGroup( 43, width );
970 
971  for ( int i = 0; i < n; i++ )
972  writeGroup( 0, line[i] );
973  }
974  else
975  {
976  writeGroup( 0, QStringLiteral( "POLYLINE" ) );
977  int plHandle = writeHandle();
978  writeGroup( 330, mBlockHandle );
979  writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
980  writeGroup( 8, layer );
981  writeGroup( 6, lineStyleName );
982  writeGroup( color );
983  writeGroup( 100, QStringLiteral( "AcDb3dPolyline" ) );
984  writeGroup( 0, QgsPoint( QgsWkbTypes::PointZ, 0.0, 0.0, 0.0 ) );
985  writeGroup( 70, 8 );
986 
987  for ( int i = 0; i < n; i++ )
988  {
989  writeGroup( 0, QStringLiteral( "VERTEX" ) );
990  writeHandle();
991  writeGroup( 330, plHandle );
992  writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
993  writeGroup( 8, layer );
994  writeGroup( color );
995  writeGroup( 100, QStringLiteral( "AcDbVertex" ) );
996  writeGroup( 100, QStringLiteral( "AcDb3dPolylineVertex" ) );
997  writeGroup( 0, line[i] );
998  writeGroup( 70, 32 );
999  }
1000 
1001  writeGroup( 0, QStringLiteral( "SEQEND" ) );
1002  writeHandle();
1003  writeGroup( 330, plHandle );
1004  writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
1005  writeGroup( 8, layer );
1006  writeGroup( color );
1007  }
1008 }
1009 
1010 void QgsDxfExport::appendCurve( const QgsCurve &c, QVector<QgsPoint> &points, QVector<double> &bulges )
1011 {
1012  switch ( QgsWkbTypes::flatType( c.wkbType() ) )
1013  {
1015  appendLineString( *dynamic_cast<const QgsLineString *>( &c ), points, bulges );
1016  break;
1017 
1019  appendCircularString( *dynamic_cast<const QgsCircularString *>( &c ), points, bulges );
1020  break;
1021 
1023  appendCompoundCurve( *dynamic_cast<const QgsCompoundCurve *>( &c ), points, bulges );
1024  break;
1025 
1026  default:
1027  QgsDebugMsg( QStringLiteral( "Unexpected curve type %1" ).arg( c.wktTypeStr() ) );
1028  break;
1029  }
1030 }
1031 
1032 void QgsDxfExport::appendLineString( const QgsLineString &ls, QVector<QgsPoint> &points, QVector<double> &bulges )
1033 {
1034  for ( int i = 0; i < ls.numPoints(); i++ )
1035  {
1036  const QgsPoint &p = ls.pointN( i );
1037  if ( !points.isEmpty() && points.last() == p )
1038  continue;
1039 
1040  points << p;
1041  bulges << 0.0;
1042  }
1043 }
1044 
1045 void QgsDxfExport::appendCircularString( const QgsCircularString &cs, QVector<QgsPoint> &points, QVector<double> &bulges )
1046 {
1047  for ( int i = 0; i < cs.numPoints() - 2; i += 2 )
1048  {
1049  const QgsPoint &p1 = cs.pointN( i );
1050  const QgsPoint &p2 = cs.pointN( i + 1 );
1051  const QgsPoint &p3 = cs.pointN( i + 2 );
1052 
1053  if ( points.isEmpty() || points.last() != p1 )
1054  points << p1;
1055  else if ( !bulges.isEmpty() )
1056  bulges.removeLast();
1057 
1058  double a = ( M_PI - ( p1 - p2 ).angle() + ( p3 - p2 ).angle() ) / 2.0;
1059  bulges << sin( a ) / cos( a );
1060 
1061  points << p3;
1062  bulges << 0.0;
1063  }
1064 }
1065 
1066 void QgsDxfExport::appendCompoundCurve( const QgsCompoundCurve &cc, QVector<QgsPoint> &points, QVector<double> &bulges )
1067 {
1068  for ( int i = 0; i < cc.nCurves(); i++ )
1069  {
1070  const QgsCurve *c = cc.curveAt( i );
1071  Q_ASSERT( c );
1072  appendCurve( *c, points, bulges );
1073  }
1074 }
1075 
1076 void QgsDxfExport::writePolyline( const QgsCurve &curve, const QString &layer, const QString &lineStyleName, const QColor &color, double width )
1077 {
1078  int n = curve.numPoints();
1079  if ( n == 0 )
1080  {
1081  QgsDebugMsg( QStringLiteral( "writePolyline: empty line layer=%1 lineStyleName=%2" ).arg( layer, lineStyleName ) );
1082  return;
1083  }
1084 
1085  if ( n < 2 )
1086  {
1087  QgsDebugMsg( QStringLiteral( "writePolyline: line too short layer=%1 lineStyleName=%2" ).arg( layer, lineStyleName ) );
1088  return;
1089  }
1090 
1091  QVector<QgsPoint> points;
1092  QVector<double> bulges;
1093  appendCurve( curve, points, bulges );
1094 
1095  if ( mForce2d || !curve.is3D() )
1096  {
1097  writeGroup( 0, QStringLiteral( "LWPOLYLINE" ) );
1098  writeHandle();
1099  writeGroup( 8, layer );
1100  writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
1101  writeGroup( 100, QStringLiteral( "AcDbPolyline" ) );
1102  writeGroup( 6, lineStyleName );
1103  writeGroup( color );
1104 
1105  writeGroup( 90, points.size() );
1106  QgsDxfExport::DxfPolylineFlags polylineFlags;
1107  if ( curve.isClosed() )
1108  polylineFlags.setFlag( QgsDxfExport::DxfPolylineFlag::Closed );
1109  if ( curve.hasCurvedSegments() )
1110  polylineFlags.setFlag( QgsDxfExport::DxfPolylineFlag::Curve );
1111 
1112  // Might need to conditional once this feature is implemented
1113  // https://github.com/qgis/QGIS/issues/32468
1114  polylineFlags.setFlag( QgsDxfExport::DxfPolylineFlag::ContinuousPattern );
1115 
1116  writeGroup( 70, static_cast<int>( polylineFlags ) );
1117  writeGroup( 43, width );
1118 
1119  for ( int i = 0; i < points.size(); i++ )
1120  {
1121  writeGroup( 0, points[i] );
1122  if ( bulges[i] != 0.0 )
1123  writeGroup( 42, bulges[i] );
1124  }
1125  }
1126  else
1127  {
1128  writeGroup( 0, QStringLiteral( "POLYLINE" ) );
1129  int plHandle = writeHandle();
1130  writeGroup( 330, mBlockHandle );
1131  writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
1132  writeGroup( 8, layer );
1133  writeGroup( 6, lineStyleName );
1134  writeGroup( color );
1135  writeGroup( 100, QStringLiteral( "AcDb3dPolyline" ) );
1136  writeGroup( 0, QgsPoint( QgsWkbTypes::PointZ, 0.0, 0.0, 0.0 ) );
1137  writeGroup( 70, 8 );
1138 
1139  for ( int i = 0; i < points.size(); i++ )
1140  {
1141  writeGroup( 0, QStringLiteral( "VERTEX" ) );
1142  writeHandle();
1143  writeGroup( 330, plHandle );
1144  writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
1145  writeGroup( 8, layer );
1146  writeGroup( color );
1147  writeGroup( 100, QStringLiteral( "AcDbVertex" ) );
1148  writeGroup( 100, QStringLiteral( "AcDb3dPolylineVertex" ) );
1149  writeGroup( 0, points[i] );
1150  if ( bulges[i] != 0.0 )
1151  writeGroup( 42, bulges[i] );
1152  writeGroup( 70, 32 );
1153  }
1154 
1155  writeGroup( 0, QStringLiteral( "SEQEND" ) );
1156  writeHandle();
1157  writeGroup( 330, plHandle );
1158  writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
1159  writeGroup( 8, layer );
1160  writeGroup( color );
1161  }
1162 }
1163 
1164 void QgsDxfExport::writePolygon( const QgsRingSequence &polygon, const QString &layer, const QString &hatchPattern, const QColor &color )
1165 {
1166  writeGroup( 0, QStringLiteral( "HATCH" ) ); // Entity type
1167  writeHandle();
1168  writeGroup( 330, mBlockHandle );
1169  writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
1170  writeGroup( 8, layer ); // Layer name
1171  writeGroup( color ); // Color
1172  writeGroup( 100, QStringLiteral( "AcDbHatch" ) );
1173 
1174  writeGroup( 0, QgsPoint( QgsWkbTypes::PointZ, 0.0, 0.0, 0.0 ) ); // Elevation point (in OCS)
1175  writeGroup( 200, QgsPoint( QgsWkbTypes::PointZ, 0.0, 0.0, 1.0 ) );
1176 
1177  writeGroup( 2, hatchPattern ); // Hatch pattern name
1178  writeGroup( 70, hatchPattern == QLatin1String( "SOLID" ) ); // Solid fill flag (solid fill = 1; pattern fill = 0)
1179  writeGroup( 71, 0 ); // Associativity flag (associative = 1; non-associative = 0)
1180 
1181  writeGroup( 91, polygon.size() ); // Number of boundary paths (loops)
1182  for ( int i = 0; i < polygon.size(); ++i )
1183  {
1184  writeGroup( 92, 2 ); // Boundary path type flag (bit coded): 0 = Default; 1 = External; 2 = Polyline 4 = Derived; 8 = Textbox; 16 = Outermost
1185  writeGroup( 72, 0 ); // Has bulge flag
1186  writeGroup( 73, 1 ); // Is closed flag
1187  writeGroup( 93, polygon[i].size() ); // Number of edges in this boundary path (only if boundary is not a polyline)
1188 
1189  for ( int j = 0; j < polygon[i].size(); ++j )
1190  {
1191  writeGroup( 0, polygon[i][j] ); // Vertex location (in OCS)
1192  }
1193 
1194  writeGroup( 97, 0 ); // Number of source boundary objects
1195  }
1196 
1197  writeGroup( 75, 0 ); // Hatch style: 0 = Hatch "odd parity" area (Normal style), 1 = Hatch outermost area only (Outer style), 2 = Hatch through entire area (Ignore style)
1198  writeGroup( 76, 1 ); // Hatch pattern type: 0 = User-defined; 1 = Predefined; 2 = Custom
1199 
1200  writeGroup( 98, 0 ); // Number of seed points
1201 }
1202 
1203 void QgsDxfExport::writePolygon( const QgsCurvePolygon &polygon, const QString &layer, const QString &hatchPattern, const QColor &color )
1204 {
1205  writeGroup( 0, QStringLiteral( "HATCH" ) ); // Entity type
1206  writeHandle();
1207  writeGroup( 330, mBlockHandle );
1208  writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
1209  writeGroup( 8, layer ); // Layer name
1210  writeGroup( color ); // Color
1211  writeGroup( 100, QStringLiteral( "AcDbHatch" ) );
1212 
1213  writeGroup( 0, QgsPoint( QgsWkbTypes::PointZ, 0.0, 0.0, 0.0 ) ); // Elevation point (in OCS)
1214  writeGroup( 200, QgsPoint( QgsWkbTypes::PointZ, 0.0, 0.0, 1.0 ) );
1215 
1216  writeGroup( 2, hatchPattern ); // Hatch pattern name
1217  writeGroup( 70, hatchPattern == QLatin1String( "SOLID" ) ); // Solid fill flag (solid fill = 1; pattern fill = 0)
1218  writeGroup( 71, 0 ); // Associativity flag (associative = 1; non-associative = 0)
1219 
1220  QVector<QVector<QgsPoint>> points;
1221  QVector<QVector<double>> bulges;
1222 
1223  points << QVector<QgsPoint>();
1224  bulges << QVector<double>();
1225  appendCurve( *polygon.exteriorRing(), points.last(), bulges.last() );
1226 
1227  for ( int i = 0; i < polygon.numInteriorRings(); i++ )
1228  {
1229  points << QVector<QgsPoint>();
1230  bulges << QVector<double>();
1231  appendCurve( *polygon.interiorRing( i ), points.last(), bulges.last() );
1232  }
1233 
1234  bool hasBulges = false;
1235  for ( int i = 0; i < points.size() && !hasBulges; ++i )
1236  for ( int j = 0; j < points[i].size() && !hasBulges; ++j )
1237  hasBulges = bulges[i][j] != 0.0;
1238 
1239  writeGroup( 91, points.size() ); // Number of boundary paths (loops)
1240 
1241  for ( int i = 0; i < points.size(); ++i )
1242  {
1243  writeGroup( 92, 2 ); // Boundary path type flag (bit coded): 0 = Default; 1 = External; 2 = Polyline 4 = Derived; 8 = Textbox; 16 = Outermost
1244  writeGroup( 72, hasBulges ? 1 : 0 ); // Has bulge flag
1245  writeGroup( 73, 1 ); // Is closed flag
1246  writeGroup( 93, points[i].size() ); // Number of edges in this boundary path (only if boundary is not a polyline)
1247 
1248  for ( int j = 0; j < points[i].size(); ++j )
1249  {
1250  writeGroup( 0, points[i][j] ); // Vertex location (in OCS)
1251  if ( hasBulges )
1252  writeGroup( 42, bulges[i][j] );
1253  }
1254 
1255  writeGroup( 97, 0 ); // Number of source boundary objects
1256  }
1257 
1258  writeGroup( 75, 0 ); // Hatch style: 0 = Hatch "odd parity" area (Normal style), 1 = Hatch outermost area only (Outer style), 2 = Hatch through entire area (Ignore style)
1259  writeGroup( 76, 1 ); // Hatch pattern type: 0 = User-defined; 1 = Predefined; 2 = Custom
1260 
1261  writeGroup( 98, 0 ); // Number of seed points
1262 }
1263 
1264 void QgsDxfExport::writeLine( const QgsPoint &pt1, const QgsPoint &pt2, const QString &layer, const QString &lineStyleName, const QColor &color, double width )
1265 {
1266  writePolyline( QgsPointSequence() << pt1 << pt2, layer, lineStyleName, color, width );
1267 }
1268 
1269 void QgsDxfExport::writeText( const QString &layer, const QString &text, pal::LabelPosition *label, const QgsPalLayerSettings &layerSettings, const QgsExpressionContext &expressionContext )
1270 {
1271 
1272  double lblX = label->getX();
1273  double lblY = label->getY();
1274 
1275  QgsLabelFeature *labelFeature = label->getFeaturePart()->feature();
1276 
1277  HAlign hali = HAlign::Undefined;
1278  VAlign vali = VAlign::Undefined;
1279 
1280  const QgsPropertyCollection &props = layerSettings.dataDefinedProperties();
1281 
1282  if ( layerSettings.placement == QgsPalLayerSettings::Placement::OverPoint )
1283  {
1284  lblX = labelFeature->anchorPosition().x();
1285  lblY = labelFeature->anchorPosition().y();
1286 
1287  QgsPalLayerSettings::QuadrantPosition offsetQuad = layerSettings.quadOffset;
1288 
1290  {
1291  const QVariant exprVal = props.value( QgsPalLayerSettings::OffsetQuad, expressionContext );
1292  if ( exprVal.isValid() )
1293  {
1294  offsetQuad = static_cast<QgsPalLayerSettings::QuadrantPosition>( exprVal.toInt() );
1295  }
1296  }
1297 
1298  switch ( offsetQuad )
1299  {
1300  case QgsPalLayerSettings::QuadrantPosition::QuadrantAboveLeft:
1301  hali = HAlign::HRight;
1302  vali = VAlign::VBottom;
1303  break;
1304  case QgsPalLayerSettings::QuadrantPosition::QuadrantAbove:
1305  hali = HAlign::HCenter;
1306  vali = VAlign::VBottom;
1307  break;
1308  case QgsPalLayerSettings::QuadrantPosition::QuadrantAboveRight:
1309  hali = HAlign::HLeft;
1310  vali = VAlign::VBottom;
1311  break;
1312  case QgsPalLayerSettings::QuadrantPosition::QuadrantLeft:
1313  hali = HAlign::HRight;
1314  vali = VAlign::VMiddle;
1315  break;
1316  case QgsPalLayerSettings::QuadrantPosition::QuadrantOver:
1317  hali = HAlign::HCenter;
1318  vali = VAlign::VMiddle;
1319  break;
1320  case QgsPalLayerSettings::QuadrantPosition::QuadrantRight:
1321  hali = HAlign::HLeft;
1322  vali = VAlign::VMiddle;
1323  break;
1324  case QgsPalLayerSettings::QuadrantPosition::QuadrantBelowLeft:
1325  hali = HAlign::HRight;
1326  vali = VAlign::VTop;
1327  break;
1328  case QgsPalLayerSettings::QuadrantPosition::QuadrantBelow:
1329  hali = HAlign::HCenter;
1330  vali = VAlign::VTop;
1331  break;
1332  case QgsPalLayerSettings::QuadrantPosition::QuadrantBelowRight:
1333  hali = HAlign::HLeft;
1334  vali = VAlign::VTop;
1335  break;
1336  default: // OverHali
1337  hali = HAlign::HCenter;
1338  vali = VAlign::VTop;
1339  break;
1340  }
1341  }
1342 
1343  if ( props.isActive( QgsPalLayerSettings::Hali ) )
1344  {
1345  lblX = labelFeature->anchorPosition().x();
1346  lblY = labelFeature->anchorPosition().y();
1347 
1348  hali = HAlign::HLeft;
1349  QVariant exprVal = props.value( QgsPalLayerSettings::Hali, expressionContext );
1350  if ( exprVal.isValid() )
1351  {
1352  const QString haliString = exprVal.toString();
1353  if ( haliString.compare( QLatin1String( "Center" ), Qt::CaseInsensitive ) == 0 )
1354  {
1355  hali = HAlign::HCenter;
1356  }
1357  else if ( haliString.compare( QLatin1String( "Right" ), Qt::CaseInsensitive ) == 0 )
1358  {
1359  hali = HAlign::HRight;
1360  }
1361  }
1362  }
1363 
1364  //vertical alignment
1365  if ( props.isActive( QgsPalLayerSettings::Vali ) )
1366  {
1367  vali = VAlign::VBottom;
1368  QVariant exprVal = props.value( QgsPalLayerSettings::Vali, expressionContext );
1369  if ( exprVal.isValid() )
1370  {
1371  const QString valiString = exprVal.toString();
1372  if ( valiString.compare( QLatin1String( "Bottom" ), Qt::CaseInsensitive ) != 0 )
1373  {
1374  if ( valiString.compare( QLatin1String( "Base" ), Qt::CaseInsensitive ) == 0 )
1375  {
1376  vali = VAlign::VBaseLine;
1377  }
1378  else if ( valiString.compare( QLatin1String( "Half" ), Qt::CaseInsensitive ) == 0 )
1379  {
1380  vali = VAlign::VMiddle;
1381  }
1382  else //'Cap' or 'Top'
1383  {
1384  vali = VAlign::VTop;
1385  }
1386  }
1387  }
1388  }
1389 
1390  writeText( layer, text, QgsPoint( lblX, lblY ), label->getHeight(), label->getAlpha() * 180.0 / M_PI, layerSettings.format().color(), hali, vali );
1391 }
1392 
1393 void QgsDxfExport::writePoint( const QString &layer, const QColor &color, const QgsPoint &pt )
1394 {
1395  writeGroup( 0, QStringLiteral( "POINT" ) );
1396  writeHandle();
1397  writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
1398  writeGroup( 100, QStringLiteral( "AcDbPoint" ) );
1399  writeGroup( 8, layer );
1400  writeGroup( color );
1401  writeGroup( 0, pt );
1402 }
1403 
1404 void QgsDxfExport::writeFilledCircle( const QString &layer, const QColor &color, const QgsPoint &pt, double radius )
1405 {
1406  writeGroup( 0, QStringLiteral( "HATCH" ) ); // Entity type
1407  writeHandle();
1408  writeGroup( 330, mBlockHandle );
1409  writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
1410  writeGroup( 8, layer ); // Layer name
1411  writeGroup( color ); // Color (0 by block, 256 by layer)
1412  writeGroup( 100, QStringLiteral( "AcDbHatch" ) );
1413 
1414  writeGroup( 0, QgsPoint( QgsWkbTypes::PointZ, 0.0, 0.0, 0.0 ) ); // Elevation point (in OCS)
1415  writeGroup( 200, QgsPoint( QgsWkbTypes::PointZ, 0.0, 0.0, 1.0 ) );
1416 
1417  writeGroup( 2, QStringLiteral( "SOLID" ) ); // Hatch pattern name
1418  writeGroup( 70, 1 ); // Solid fill flag (solid fill = 1; pattern fill = 0)
1419  writeGroup( 71, 0 ); // Associativity flag (associative = 1; non-associative = 0)
1420 
1421  writeGroup( 91, 1 ); // Number of boundary paths (loops)
1422 
1423  writeGroup( 92, 3 ); // Boundary path type flag (bit coded): 0 = Default; 1 = External; 2 = Polyline 4 = Derived; 8 = Textbox; 16 = Outermost
1424  writeGroup( 72, 1 );
1425  writeGroup( 73, 1 ); // Is closed flag
1426  writeGroup( 93, 2 ); // Number of polyline vertices
1427 
1428  writeGroup( 0, QgsPoint( QgsWkbTypes::Point, pt.x() - radius, pt.y() ) );
1429  writeGroup( 42, 1.0 );
1430 
1431  writeGroup( 0, QgsPoint( QgsWkbTypes::Point, pt.x() + radius, pt.y() ) );
1432  writeGroup( 42, 1.0 );
1433 
1434  writeGroup( 97, 0 ); // Number of source boundary objects
1435 
1436  writeGroup( 75, 0 ); // Hatch style: 0 = Hatch "odd parity" area (Normal style), 1 = Hatch outermost area only (Outer style), 2 = Hatch through entire area (Ignore style)
1437  writeGroup( 76, 1 ); // Hatch pattern type: 0 = User-defined; 1 = Predefined; 2 = Custom
1438  writeGroup( 98, 0 ); // Number of seed points
1439 }
1440 
1441 void QgsDxfExport::writeCircle( const QString &layer, const QColor &color, const QgsPoint &pt, double radius, const QString &lineStyleName, double width )
1442 {
1443  writeGroup( 0, QStringLiteral( "LWPOLYLINE" ) );
1444  writeHandle();
1445  writeGroup( 330, mBlockHandle );
1446  writeGroup( 8, layer );
1447  writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
1448  writeGroup( 100, QStringLiteral( "AcDbPolyline" ) );
1449  writeGroup( 6, lineStyleName );
1450  writeGroup( color );
1451 
1452  writeGroup( 90, 2 );
1453 
1454  writeGroup( 70, 1 );
1455  writeGroup( 43, width );
1456 
1457  writeGroup( 0, QgsPoint( pt.x() - radius, pt.y() ) );
1458  writeGroup( 42, 1.0 );
1459  writeGroup( 0, QgsPoint( pt.x() + radius, pt.y() ) );
1460  writeGroup( 42, 1.0 );
1461 }
1462 
1463 void QgsDxfExport::writeText( const QString &layer, const QString &text, const QgsPoint &pt, double size, double angle, const QColor &color, HAlign hali, VAlign vali )
1464 {
1465  writeGroup( 0, QStringLiteral( "TEXT" ) );
1466  writeHandle();
1467  writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
1468  // writeGroup( 6, "Continuous" ); // Line style
1469  // writeGroup( 370, 18 ); // Line weight
1470  writeGroup( 100, QStringLiteral( "AcDbText" ) );
1471  writeGroup( 8, layer );
1472  writeGroup( color );
1473  writeGroup( 0, pt );
1474  if ( hali != HAlign::Undefined || vali != VAlign::Undefined )
1475  writeGroup( 1, pt ); // Second alignment point
1476  writeGroup( 40, size );
1477  writeGroup( 1, text );
1478  writeGroup( 50, fmod( angle, 360 ) );
1479  if ( hali != HAlign::Undefined )
1480  writeGroup( 72, static_cast<int>( hali ) );
1481  writeGroup( 7, QStringLiteral( "STANDARD" ) ); // so far only support for standard font
1482  writeGroup( 100, QStringLiteral( "AcDbText" ) );
1483  if ( vali != VAlign::Undefined )
1484  {
1485  writeGroup( 73, static_cast<int>( vali ) );
1486  }
1487 }
1488 
1489 void QgsDxfExport::writeMText( const QString &layer, const QString &text, const QgsPoint &pt, double width, double angle, const QColor &color )
1490 {
1491  if ( !mTextStream.codec()->canEncode( text ) )
1492  {
1493  // TODO return error
1494  QgsDebugMsg( QStringLiteral( "could not encode:%1" ).arg( text ) );
1495  return;
1496  }
1497 
1498  writeGroup( 0, QStringLiteral( "MTEXT" ) );
1499  writeHandle();
1500  writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
1501  writeGroup( 100, QStringLiteral( "AcDbMText" ) );
1502  writeGroup( 8, layer );
1503  writeGroup( color );
1504 
1505  writeGroup( 0, pt );
1506 
1507  QString t( text );
1508  while ( t.length() > 250 )
1509  {
1510  writeGroup( 3, t.left( 250 ) );
1511  t = t.mid( 250 );
1512  }
1513  writeGroup( 1, t );
1514 
1515  writeGroup( 50, angle ); // Rotation angle in radians
1516  writeGroup( 41, width * 1.1 ); // Reference rectangle width
1517 
1518  // Attachment point:
1519  // 1 2 3
1520  // 4 5 6
1521  // 7 8 9
1522  writeGroup( 71, 7 );
1523 
1524  writeGroup( 7, QStringLiteral( "STANDARD" ) ); // so far only support for standard font
1525 }
1526 
1527 void QgsDxfExport::addFeature( QgsSymbolRenderContext &ctx, const QgsCoordinateTransform &ct, const QString &layer, const QgsSymbolLayer *symbolLayer, const QgsSymbol *symbol )
1528 {
1529  const QgsFeature *fet = ctx.feature();
1530  if ( !fet )
1531  return;
1532 
1533  if ( !fet->hasGeometry() )
1534  return;
1535 
1536  QgsGeometry geom( fet->geometry() );
1537  if ( ct.isValid() )
1538  {
1539  geom.transform( ct );
1540  }
1541 
1542  QgsWkbTypes::Type geometryType = geom.wkbType();
1543 
1544  QColor penColor;
1545  QColor brushColor;
1546  if ( mSymbologyExport != NoSymbology && symbolLayer )
1547  {
1548  penColor = colorFromSymbolLayer( symbolLayer, ctx );
1549  brushColor = symbolLayer->dxfBrushColor( ctx );
1550  }
1551 
1552  Qt::PenStyle penStyle( Qt::SolidLine );
1553  Qt::BrushStyle brushStyle( Qt::NoBrush );
1554  double width = -1;
1555  double offset = 0.0;
1556  double angle = 0.0;
1557  if ( mSymbologyExport != NoSymbology && symbolLayer )
1558  {
1559  width = symbolLayer->dxfWidth( *this, ctx );
1560  offset = symbolLayer->dxfOffset( *this, ctx );
1561  angle = symbolLayer->dxfAngle( ctx );
1562  penStyle = symbolLayer->dxfPenStyle();
1563  brushStyle = symbolLayer->dxfBrushStyle();
1564 
1565  if ( qgsDoubleNear( offset, 0.0 ) )
1566  offset = 0.0;
1567  }
1568 
1569  QString lineStyleName = QStringLiteral( "CONTINUOUS" );
1570  if ( mSymbologyExport != NoSymbology )
1571  {
1572  lineStyleName = lineStyleFromSymbolLayer( symbolLayer );
1573  }
1574 
1575  // single point
1576  if ( QgsWkbTypes::flatType( geometryType ) == QgsWkbTypes::Point )
1577  {
1578  writePoint( geom.constGet()->coordinateSequence().at( 0 ).at( 0 ).at( 0 ), layer, penColor, ctx, symbolLayer, symbol, angle );
1579  return;
1580  }
1581 
1582  if ( QgsWkbTypes::flatType( geometryType ) == QgsWkbTypes::MultiPoint )
1583  {
1584  const QgsCoordinateSequence &cs = geom.constGet()->coordinateSequence();
1585  for ( int i = 0; i < cs.size(); i++ )
1586  {
1587  writePoint( cs.at( i ).at( 0 ).at( 0 ), layer, penColor, ctx, symbolLayer, symbol, angle );
1588  }
1589  return;
1590  }
1591 
1592  if ( penStyle != Qt::NoPen )
1593  {
1594  const QgsAbstractGeometry *sourceGeom = geom.constGet();
1595  std::unique_ptr< QgsAbstractGeometry > tempGeom;
1596 
1597  switch ( QgsWkbTypes::flatType( geometryType ) )
1598  {
1602  {
1603  if ( !qgsDoubleNear( offset, 0.0 ) )
1604  {
1605  QgsGeos geos( sourceGeom );
1606  tempGeom.reset( geos.offsetCurve( offset, 0, GEOSBUF_JOIN_MITRE, 2.0 ) ); //#spellok
1607  if ( tempGeom )
1608  sourceGeom = tempGeom.get();
1609  else
1610  sourceGeom = geom.constGet();
1611  }
1612 
1613  const QgsCurve *curve = dynamic_cast<const QgsCurve *>( sourceGeom );
1614  Q_ASSERT( curve );
1615  writePolyline( *curve, layer, lineStyleName, penColor, width );
1616 
1617  break;
1618  }
1619 
1622  {
1623  if ( !qgsDoubleNear( offset, 0.0 ) )
1624  {
1625  QgsGeos geos( sourceGeom );
1626  tempGeom.reset( geos.offsetCurve( offset, 0, GEOSBUF_JOIN_MITRE, 2.0 ) ); //#spellok
1627  if ( tempGeom )
1628  sourceGeom = tempGeom.get();
1629  else
1630  sourceGeom = geom.constGet();
1631  }
1632 
1633  const QgsGeometryCollection *gc = dynamic_cast<const QgsGeometryCollection *>( sourceGeom );
1634  Q_ASSERT( gc );
1635 
1636  for ( int i = 0; i < gc->numGeometries(); i++ )
1637  {
1638  const QgsCurve *curve = dynamic_cast<const QgsCurve *>( gc->geometryN( i ) );
1639  Q_ASSERT( curve );
1640  writePolyline( *curve, layer, lineStyleName, penColor, width );
1641  }
1642 
1643  break;
1644  }
1645 
1647  case QgsWkbTypes::Polygon:
1648  {
1649  if ( !qgsDoubleNear( offset, 0.0 ) )
1650  {
1651  QgsGeos geos( sourceGeom );
1652  tempGeom.reset( geos.buffer( offset, 0, GEOSBUF_CAP_FLAT, GEOSBUF_JOIN_MITRE, 2.0 ) ); //#spellok
1653  if ( tempGeom )
1654  sourceGeom = tempGeom.get();
1655  else
1656  sourceGeom = geom.constGet();
1657  }
1658 
1659  const QgsCurvePolygon *polygon = dynamic_cast<const QgsCurvePolygon *>( sourceGeom );
1660  Q_ASSERT( polygon );
1661 
1662  writePolyline( *polygon->exteriorRing(), layer, lineStyleName, penColor, width );
1663  for ( int i = 0; i < polygon->numInteriorRings(); i++ )
1664  writePolyline( *polygon->interiorRing( i ), layer, lineStyleName, penColor, width );
1665 
1666  break;
1667  }
1668 
1671  {
1672  if ( !qgsDoubleNear( offset, 0.0 ) )
1673  {
1674  QgsGeos geos( sourceGeom );
1675  tempGeom.reset( geos.buffer( offset, 0, GEOSBUF_CAP_FLAT, GEOSBUF_JOIN_MITRE, 2.0 ) ); //#spellok
1676  if ( tempGeom )
1677  sourceGeom = tempGeom.get();
1678  else
1679  sourceGeom = geom.constGet();
1680  }
1681 
1682  const QgsGeometryCollection *gc = dynamic_cast<const QgsGeometryCollection *>( sourceGeom );
1683  Q_ASSERT( gc );
1684 
1685  for ( int i = 0; i < gc->numGeometries(); i++ )
1686  {
1687  const QgsCurvePolygon *polygon = dynamic_cast<const QgsCurvePolygon *>( gc->geometryN( i ) );
1688  Q_ASSERT( polygon );
1689 
1690  writePolyline( *polygon->exteriorRing(), layer, lineStyleName, penColor, width );
1691  for ( int j = 0; j < polygon->numInteriorRings(); j++ )
1692  writePolyline( *polygon->interiorRing( j ), layer, lineStyleName, penColor, width );
1693  }
1694 
1695  break;
1696  }
1697 
1698  default:
1699  break;
1700  }
1701 
1702  }
1703 
1704  if ( brushStyle != Qt::NoBrush )
1705  {
1706  const QgsAbstractGeometry *sourceGeom = geom.constGet();
1707  std::unique_ptr< QgsAbstractGeometry > tempGeom;
1708 
1709  switch ( QgsWkbTypes::flatType( geometryType ) )
1710  {
1712  case QgsWkbTypes::Polygon:
1713  {
1714  const QgsCurvePolygon *polygon = dynamic_cast<const QgsCurvePolygon *>( sourceGeom );
1715  Q_ASSERT( polygon );
1716  writePolygon( *polygon, layer, QStringLiteral( "SOLID" ), brushColor );
1717  break;
1718  }
1719 
1722  {
1723  const QgsGeometryCollection *gc = dynamic_cast<const QgsGeometryCollection *>( sourceGeom );
1724  Q_ASSERT( gc );
1725 
1726  for ( int i = 0; i < gc->numGeometries(); i++ )
1727  {
1728  const QgsCurvePolygon *polygon = dynamic_cast<const QgsCurvePolygon *>( gc->geometryN( i ) );
1729  Q_ASSERT( polygon );
1730  writePolygon( *polygon, layer, QStringLiteral( "SOLID" ), brushColor );
1731  }
1732  break;
1733  }
1734 
1735  default:
1736  break;
1737 
1738  }
1739  }
1740 }
1741 
1742 QColor QgsDxfExport::colorFromSymbolLayer( const QgsSymbolLayer *symbolLayer, QgsSymbolRenderContext &ctx )
1743 {
1744  if ( !symbolLayer )
1745  return QColor();
1746 
1747  return symbolLayer->dxfColor( ctx );
1748 }
1749 
1750 QString QgsDxfExport::lineStyleFromSymbolLayer( const QgsSymbolLayer *symbolLayer )
1751 {
1752  QString lineStyleName = QStringLiteral( "CONTINUOUS" );
1753  if ( !symbolLayer )
1754  {
1755  return lineStyleName;
1756  }
1757 
1758  QHash< const QgsSymbolLayer *, QString >::const_iterator lineTypeIt = mLineStyles.constFind( symbolLayer );
1759  if ( lineTypeIt != mLineStyles.constEnd() )
1760  {
1761  lineStyleName = lineTypeIt.value();
1762  return lineStyleName;
1763  }
1764  else
1765  {
1766  return lineNameFromPenStyle( symbolLayer->dxfPenStyle() );
1767  }
1768 }
1769 
1771 {
1772  int idx = 0;
1773  int current_distance = std::numeric_limits<int>::max();
1774  for ( int i = 1; i < static_cast< int >( sizeof( sDxfColors ) / sizeof( *sDxfColors ) ); ++i )
1775  {
1776  int dist = color_distance( pixel, i );
1777  if ( dist < current_distance )
1778  {
1779  current_distance = dist;
1780  idx = i;
1781  if ( dist == 0 )
1782  break;
1783  }
1784  }
1785  return idx;
1786 }
1787 
1788 int QgsDxfExport::color_distance( QRgb p1, int index )
1789 {
1790  if ( index > 255 || index < 0 )
1791  {
1792  return 0;
1793  }
1794 
1795  double redDiff = qRed( p1 ) - sDxfColors[index][0];
1796  double greenDiff = qGreen( p1 ) - sDxfColors[index][1];
1797  double blueDiff = qBlue( p1 ) - sDxfColors[index][2];
1798 #if 0
1799  QgsDebugMsg( QStringLiteral( "color_distance( r:%1 g:%2 b:%3 <=> i:%4 r:%5 g:%6 b:%7 ) => %8" )
1800  .arg( qRed( p1 ) ).arg( qGreen( p1 ) ).arg( qBlue( p1 ) )
1801  .arg( index )
1802  .arg( mDxfColors[index][0] )
1803  .arg( mDxfColors[index][1] )
1804  .arg( mDxfColors[index][2] )
1805  .arg( redDiff * redDiff + greenDiff * greenDiff + blueDiff * blueDiff ) );
1806 #endif
1807  return redDiff * redDiff + greenDiff * greenDiff + blueDiff * blueDiff;
1808 }
1809 
1810 QRgb QgsDxfExport::createRgbEntry( qreal r, qreal g, qreal b )
1811 {
1812  return QColor::fromRgbF( r, g, b ).rgb();
1813 }
1814 
1815 QgsRenderContext QgsDxfExport::renderContext() const
1816 {
1817  return mRenderContext;
1818 }
1819 
1820 double QgsDxfExport::mapUnitScaleFactor( double scale, QgsUnitTypes::RenderUnit symbolUnits, QgsUnitTypes::DistanceUnit mapUnits, double mapUnitsPerPixel )
1821 {
1822  if ( symbolUnits == QgsUnitTypes::RenderMapUnits )
1823  {
1824  return 1.0;
1825  }
1826  else if ( symbolUnits == QgsUnitTypes::RenderMillimeters )
1827  {
1829  }
1830  else if ( symbolUnits == QgsUnitTypes::RenderPixels )
1831  {
1832  return mapUnitsPerPixel;
1833  }
1834  return 1.0;
1835 }
1836 
1837 void QgsDxfExport::clipValueToMapUnitScale( double &value, const QgsMapUnitScale &scale, double pixelToMMFactor ) const
1838 {
1839  if ( !scale.minSizeMMEnabled && !scale.maxSizeMMEnabled )
1840  {
1841  return;
1842  }
1843 
1844  double mapUnitsPerPixel = mMapSettings.mapToPixel().mapUnitsPerPixel();
1845 
1846  double minSizeMU = std::numeric_limits<double>::lowest();
1847  if ( scale.minSizeMMEnabled )
1848  {
1849  minSizeMU = scale.minSizeMM * pixelToMMFactor * mapUnitsPerPixel;
1850  }
1851  if ( !qgsDoubleNear( scale.minScale, 0.0 ) )
1852  {
1853  minSizeMU = std::max( minSizeMU, value );
1854  }
1855  value = std::max( value, minSizeMU );
1856 
1857  double maxSizeMU = std::numeric_limits<double>::max();
1858  if ( scale.maxSizeMMEnabled )
1859  {
1860  maxSizeMU = scale.maxSizeMM * pixelToMMFactor * mapUnitsPerPixel;
1861  }
1862  if ( !qgsDoubleNear( scale.maxScale, 0.0 ) )
1863  {
1864  maxSizeMU = std::min( maxSizeMU, value );
1865  }
1866  value = std::min( value, maxSizeMU );
1867 }
1868 
1869 QList< QPair< QgsSymbolLayer *, QgsSymbol * > > QgsDxfExport::symbolLayers( QgsRenderContext &context )
1870 {
1871  QList< QPair< QgsSymbolLayer *, QgsSymbol * > > symbolLayers;
1872 
1873  for ( DxfLayerJob *job : mJobs )
1874  {
1875  const QgsSymbolList symbols = job->renderer->symbols( context );
1876 
1877  for ( QgsSymbol *symbol : symbols )
1878  {
1879  int maxSymbolLayers = symbol->symbolLayerCount();
1880  if ( mSymbologyExport != SymbolLayerSymbology )
1881  {
1882  maxSymbolLayers = 1;
1883  }
1884  for ( int i = 0; i < maxSymbolLayers; ++i )
1885  {
1886  symbolLayers.append( qMakePair( symbol->symbolLayer( i ), symbol ) );
1887  }
1888  }
1889  }
1890 
1891  return symbolLayers;
1892 }
1893 
1894 void QgsDxfExport::writeDefaultLinetypes()
1895 {
1896  // continuous (Qt solid line)
1897  for ( const QString &ltype : { QStringLiteral( "ByLayer" ), QStringLiteral( "ByBlock" ), QStringLiteral( "CONTINUOUS" ) } )
1898  {
1899  writeGroup( 0, QStringLiteral( "LTYPE" ) );
1900  writeHandle();
1901  writeGroup( 100, QStringLiteral( "AcDbSymbolTableRecord" ) );
1902  writeGroup( 100, QStringLiteral( "AcDbLinetypeTableRecord" ) );
1903  writeGroup( 2, ltype );
1904  writeGroup( 70, 64 );
1905  writeGroup( 3, QStringLiteral( "Defaultstyle" ) );
1906  writeGroup( 72, 65 );
1907  writeGroup( 73, 0 );
1908  writeGroup( 40, 0.0 );
1909  }
1910 
1911  double das = dashSize();
1912  double dss = dashSeparatorSize();
1913  double dos = dotSize();
1914 
1915  QVector<qreal> dashVector( 2 );
1916  dashVector[0] = das;
1917  dashVector[1] = dss;
1918  writeLinetype( QStringLiteral( "DASH" ), dashVector, QgsUnitTypes::RenderMapUnits );
1919 
1920  QVector<qreal> dotVector( 2 );
1921  dotVector[0] = dos;
1922  dotVector[1] = dss;
1923  writeLinetype( QStringLiteral( "DOT" ), dotVector, QgsUnitTypes::RenderMapUnits );
1924 
1925  QVector<qreal> dashDotVector( 4 );
1926  dashDotVector[0] = das;
1927  dashDotVector[1] = dss;
1928  dashDotVector[2] = dos;
1929  dashDotVector[3] = dss;
1930  writeLinetype( QStringLiteral( "DASHDOT" ), dashDotVector, QgsUnitTypes::RenderMapUnits );
1931 
1932  QVector<qreal> dashDotDotVector( 6 );
1933  dashDotDotVector[0] = das;
1934  dashDotDotVector[1] = dss;
1935  dashDotDotVector[2] = dos;
1936  dashDotDotVector[3] = dss;
1937  dashDotDotVector[4] = dos;
1938  dashDotDotVector[5] = dss;
1939  writeLinetype( QStringLiteral( "DASHDOTDOT" ), dashDotDotVector, QgsUnitTypes::RenderMapUnits );
1940 }
1941 
1942 void QgsDxfExport::writeSymbolLayerLinetype( const QgsSymbolLayer *symbolLayer )
1943 {
1944  if ( !symbolLayer )
1945  {
1946  return;
1947  }
1948 
1950  QVector<qreal> customLinestyle = symbolLayer->dxfCustomDashPattern( unit );
1951  if ( !customLinestyle.isEmpty() )
1952  {
1953  QString name = QStringLiteral( "symbolLayer%1" ).arg( mSymbolLayerCounter++ );
1954  writeLinetype( name, customLinestyle, unit );
1955  mLineStyles.insert( symbolLayer, name );
1956  }
1957 }
1958 
1959 int QgsDxfExport::nLineTypes( const QList< QPair< QgsSymbolLayer *, QgsSymbol * > > &symbolLayers )
1960 {
1961  int nLineTypes = 0;
1962  for ( const auto &symbolLayer : symbolLayers )
1963  {
1964  const QgsSimpleLineSymbolLayer *simpleLine = dynamic_cast< const QgsSimpleLineSymbolLayer * >( symbolLayer.first );
1965  if ( simpleLine )
1966  {
1967  if ( simpleLine->useCustomDashPattern() )
1968  {
1969  ++nLineTypes;
1970  }
1971  }
1972  }
1973  return nLineTypes;
1974 }
1975 
1976 void QgsDxfExport::writeLinetype( const QString &styleName, const QVector<qreal> &pattern, QgsUnitTypes::RenderUnit u )
1977 {
1978  double length = 0;
1979  for ( qreal size : pattern )
1980  {
1981  length += ( size * mapUnitScaleFactor( mSymbologyScale, u, mMapUnits, mMapSettings.mapToPixel().mapUnitsPerPixel() ) );
1982  }
1983 
1984  writeGroup( 0, QStringLiteral( "LTYPE" ) );
1985  writeHandle();
1986  // 330 5
1987  writeGroup( 100, QStringLiteral( "AcDbSymbolTableRecord" ) );
1988  writeGroup( 100, QStringLiteral( "AcDbLinetypeTableRecord" ) );
1989  writeGroup( 2, styleName );
1990  writeGroup( 70, 64 ); // 0?
1991  writeGroup( 3, QString() );
1992  writeGroup( 72, 65 );
1993  writeGroup( 73, pattern.size() );
1994  writeGroup( 40, length );
1995 
1996  bool isGap = false;
1997  for ( qreal size : pattern )
1998  {
1999  // map units or mm?
2000  double segmentLength = ( isGap ? -size : size );
2001  segmentLength *= mapUnitScaleFactor( mSymbologyScale, u, mMapUnits, mMapSettings.mapToPixel().mapUnitsPerPixel() );
2002  writeGroup( 49, segmentLength );
2003  writeGroup( 74, 0 );
2004  isGap = !isGap;
2005  }
2006 }
2007 
2008 void QgsDxfExport::addGeometryGeneratorSymbolLayer( QgsSymbolRenderContext &ctx, const QgsCoordinateTransform &ct, const QString &layer, QgsSymbolLayer *symbolLayer, bool allSymbolLayers )
2009 {
2010  QgsGeometryGeneratorSymbolLayer *gg = dynamic_cast<QgsGeometryGeneratorSymbolLayer *>( symbolLayer );
2011  if ( !gg )
2012  {
2013  return;
2014  }
2015 
2016  const QgsFeature *fet = ctx.feature();
2017  if ( !fet )
2018  {
2019  return;
2020  }
2021 
2022  QgsFeature f = *fet;
2023 
2024  QgsExpressionContext &expressionContext = ctx.renderContext().expressionContext();
2025  QgsExpression geomExpr( gg->geometryExpression() );
2026  geomExpr.prepare( &expressionContext );
2027  QgsGeometry geom = geomExpr.evaluate( &expressionContext ).value<QgsGeometry>();
2028  f.setGeometry( geom );
2029 
2030  QgsSymbol *symbol = gg->subSymbol();
2031  if ( symbol && symbol->symbolLayerCount() > 0 )
2032  {
2033  QgsExpressionContextScope *symbolExpressionContextScope = symbol->symbolRenderContext()->expressionContextScope();
2034  symbolExpressionContextScope->setFeature( f );
2035 
2036  ctx.setFeature( &f );
2037 
2038  int nSymbolLayers = allSymbolLayers ? symbol->symbolLayerCount() : 1;
2039  for ( int i = 0; i < nSymbolLayers; ++i )
2040  {
2041  addFeature( ctx, ct, layer, symbol->symbolLayer( i ), symbol );
2042  }
2043 
2044  ctx.setFeature( fet );
2045  }
2046 }
2047 
2048 bool QgsDxfExport::hasDataDefinedProperties( const QgsSymbolLayer *sl, const QgsSymbol *symbol )
2049 {
2050  if ( !sl || !symbol )
2051  {
2052  return false;
2053  }
2054 
2055  if ( symbol->renderHints() & QgsSymbol::DynamicRotation )
2056  {
2057  return true;
2058  }
2059 
2060  return sl->hasDataDefinedProperties();
2061 }
2062 
2063 double QgsDxfExport::dashSize() const
2064 {
2065  double size = mSymbologyScale * 0.002;
2066  return sizeToMapUnits( size );
2067 }
2068 
2069 double QgsDxfExport::dotSize() const
2070 {
2071  double size = mSymbologyScale * 0.0006;
2072  return sizeToMapUnits( size );
2073 }
2074 
2075 double QgsDxfExport::dashSeparatorSize() const
2076 {
2077  double size = mSymbologyScale * 0.0006;
2078  return sizeToMapUnits( size );
2079 }
2080 
2081 double QgsDxfExport::sizeToMapUnits( double s ) const
2082 {
2083  double size = s * QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::DistanceMeters, mMapUnits );
2084  return size;
2085 }
2086 
2087 QString QgsDxfExport::lineNameFromPenStyle( Qt::PenStyle style )
2088 {
2089  switch ( style )
2090  {
2091  case Qt::DashLine:
2092  return QStringLiteral( "DASH" );
2093  case Qt::DotLine:
2094  return QStringLiteral( "DOT" );
2095  case Qt::DashDotLine:
2096  return QStringLiteral( "DASHDOT" );
2097  case Qt::DashDotDotLine:
2098  return QStringLiteral( "DASHDOTDOT" );
2099  case Qt::SolidLine:
2100  default:
2101  return QStringLiteral( "CONTINUOUS" );
2102  }
2103 }
2104 
2105 QString QgsDxfExport::dxfLayerName( const QString &name )
2106 {
2107  if ( name.isEmpty() )
2108  return QStringLiteral( "0" );
2109 
2110  // dxf layers can be max 255 characters long
2111  QString layerName = name.left( 255 );
2112 
2113  // replaced restricted characters with underscore
2114  // < > / \ " : ; ? * | = '
2115  // See http://docs.autodesk.com/ACD/2010/ENU/AutoCAD%202010%20User%20Documentation/index.html?url=WS1a9193826455f5ffa23ce210c4a30acaf-7345.htm,topicNumber=d0e41665
2116  layerName.replace( '<', '_' );
2117  layerName.replace( '>', '_' );
2118  layerName.replace( '/', '_' );
2119  layerName.replace( '\\', '_' );
2120  layerName.replace( '\"', '_' );
2121  layerName.replace( ':', '_' );
2122  layerName.replace( ';', '_' );
2123  layerName.replace( '?', '_' );
2124  layerName.replace( '*', '_' );
2125  layerName.replace( '|', '_' );
2126  layerName.replace( '=', '_' );
2127  layerName.replace( '\'', '_' );
2128 
2129  // also remove newline characters (#15067)
2130  layerName.replace( QLatin1String( "\r\n" ), QLatin1String( "_" ) );
2131  layerName.replace( '\r', '_' );
2132  layerName.replace( '\n', '_' );
2133 
2134  return layerName.trimmed();
2135 }
2136 
2137 bool QgsDxfExport::layerIsScaleBasedVisible( const QgsMapLayer *layer ) const
2138 {
2139  if ( !layer )
2140  return false;
2141 
2142  if ( mSymbologyExport == QgsDxfExport::NoSymbology )
2143  return true;
2144 
2145  return layer->isInScaleRange( mSymbologyScale );
2146 }
2147 
2148 QString QgsDxfExport::layerName( const QString &id, const QgsFeature &f ) const
2149 {
2150  // TODO: make this thread safe
2151  const QList< QgsMapLayer * > layers = mMapSettings.layers();
2152  for ( QgsMapLayer *ml : layers )
2153  {
2154  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml );
2155  if ( vl && vl->id() == id )
2156  {
2157  int attrIdx = mLayerNameAttribute.value( vl->id(), -1 );
2158  return dxfLayerName( attrIdx < 0 ? layerName( vl ) : f.attribute( attrIdx ).toString() );
2159  }
2160  }
2161 
2162  return QStringLiteral( "0" );
2163 }
2164 
2165 QString QgsDxfExport::dxfEncoding( const QString &name )
2166 {
2167  const QList< QByteArray > codecs = QTextCodec::availableCodecs();
2168  for ( const QByteArray &codec : codecs )
2169  {
2170  if ( name != codec )
2171  continue;
2172 
2173  int i;
2174  for ( i = 0; i < static_cast< int >( sizeof( DXF_ENCODINGS ) / sizeof( *DXF_ENCODINGS ) ) && name != DXF_ENCODINGS[i][1]; ++i )
2175  ;
2176 
2177  if ( i == static_cast< int >( sizeof( DXF_ENCODINGS ) / sizeof( *DXF_ENCODINGS ) ) )
2178  continue;
2179 
2180  return DXF_ENCODINGS[i][0];
2181  }
2182 
2183  return QString();
2184 }
2185 
2187 {
2188  QStringList encodings;
2189  const QList< QByteArray > codecs = QTextCodec::availableCodecs();
2190  for ( const QByteArray &codec : codecs )
2191  {
2192  int i;
2193  for ( i = 0; i < static_cast< int >( sizeof( DXF_ENCODINGS ) / sizeof( *DXF_ENCODINGS ) ) && strcmp( codec.data(), DXF_ENCODINGS[i][1] ) != 0; ++i )
2194  ;
2195 
2196  if ( i < static_cast< int >( sizeof( DXF_ENCODINGS ) / sizeof( *DXF_ENCODINGS ) ) )
2197  encodings << codec.data();
2198  }
2199  return encodings;
2200 }
2201 
2203 {
2204  Q_ASSERT( vl );
2205  return mLayerTitleAsName && !vl->title().isEmpty() ? vl->title() : vl->name();
2206 }
2207 
2208 void QgsDxfExport::drawLabel( const QString &layerId, QgsRenderContext &context, pal::LabelPosition *label, const QgsPalLayerSettings &settings )
2209 {
2210  Q_UNUSED( context )
2211 
2212  if ( !settings.drawLabels )
2213  return;
2214 
2215  QgsTextLabelFeature *lf = dynamic_cast<QgsTextLabelFeature *>( label->getFeaturePart()->feature() );
2216 
2217  // Copy to temp, editable layer settings
2218  // these settings will be changed by any data defined values, then used for rendering label components
2219  // settings may be adjusted during rendering of components
2220  QgsPalLayerSettings tmpLyr( settings );
2221 
2222  // apply any previously applied data defined settings for the label
2223  const QMap< QgsPalLayerSettings::Property, QVariant > &ddValues = lf->dataDefinedValues();
2224 
2225  //font
2226  QFont dFont = lf->definedFont();
2227  QgsDebugMsgLevel( QStringLiteral( "PAL font tmpLyr: %1, Style: %2" ).arg( tmpLyr.format().font().toString(), tmpLyr.format().font().styleName() ), 4 );
2228  QgsDebugMsgLevel( QStringLiteral( "PAL font definedFont: %1, Style: %2" ).arg( dFont.toString(), dFont.styleName() ), 4 );
2229 
2230  QgsTextFormat format = tmpLyr.format();
2231  format.setFont( dFont );
2232  tmpLyr.setFormat( format );
2233 
2235  {
2236  //calculate font alignment based on label quadrant
2237  switch ( label->getQuadrant() )
2238  {
2243  break;
2248  break;
2253  break;
2254  }
2255  }
2256 
2257  // update tmpLyr with any data defined text style values
2258  QgsPalLabeling::dataDefinedTextStyle( tmpLyr, ddValues );
2259 
2260  // update tmpLyr with any data defined text buffer values
2261  QgsPalLabeling::dataDefinedTextBuffer( tmpLyr, ddValues );
2262 
2263  // update tmpLyr with any data defined text formatting values
2264  QgsPalLabeling::dataDefinedTextFormatting( tmpLyr, ddValues );
2265 
2266  // add to the results
2267  QString txt = label->getFeaturePart()->feature()->labelText();
2268 
2269  QgsFeatureId fid = label->getFeaturePart()->featureId();
2270  QString dxfLayer = mDxfLayerNames[layerId][fid];
2271 
2272  QString wrapchr = tmpLyr.wrapChar.isEmpty() ? QStringLiteral( "\n" ) : tmpLyr.wrapChar;
2273 
2274  //add the direction symbol if needed
2275  if ( !txt.isEmpty() && tmpLyr.placement == QgsPalLayerSettings::Line && tmpLyr.lineSettings().addDirectionSymbol() )
2276  {
2277  bool prependSymb = false;
2278  QString symb = tmpLyr.lineSettings().rightDirectionSymbol();
2279 
2280  if ( label->getReversed() )
2281  {
2282  prependSymb = true;
2283  symb = tmpLyr.lineSettings().leftDirectionSymbol();
2284  }
2285 
2286  if ( tmpLyr.lineSettings().reverseDirectionSymbol() )
2287  {
2288  if ( symb == tmpLyr.lineSettings().rightDirectionSymbol() )
2289  {
2290  prependSymb = true;
2291  symb = tmpLyr.lineSettings().leftDirectionSymbol();
2292  }
2293  else
2294  {
2295  prependSymb = false;
2296  symb = tmpLyr.lineSettings().rightDirectionSymbol();
2297  }
2298  }
2299 
2300  switch ( tmpLyr.lineSettings().directionSymbolPlacement() )
2301  {
2303  prependSymb = true;
2304  symb = symb + wrapchr;
2305  break;
2306 
2308  prependSymb = false;
2309  symb = wrapchr + symb;
2310  break;
2311 
2313  break;
2314  }
2315 
2316  if ( prependSymb )
2317  {
2318  txt.prepend( symb );
2319  }
2320  else
2321  {
2322  txt.append( symb );
2323  }
2324  }
2325 
2326  if ( mFlags & FlagNoMText )
2327  {
2328  txt.replace( QChar( QChar::LineFeed ), ' ' );
2329  txt.replace( QChar( QChar::CarriageReturn ), ' ' );
2330  writeText( dxfLayer, txt, label, tmpLyr, context.expressionContext() );
2331  }
2332  else
2333  {
2334  txt.replace( QString( QChar( QChar::CarriageReturn ) ) + QString( QChar( QChar::LineFeed ) ), QStringLiteral( "\\P" ) );
2335  txt.replace( QChar( QChar::CarriageReturn ), QStringLiteral( "\\P" ) );
2336  txt = txt.replace( wrapchr, QLatin1String( "\\P" ) );
2337  txt.replace( " ", "\\~" );
2338 
2339  if ( tmpLyr.format().font().underline() )
2340  {
2341  txt.prepend( "\\L" ).append( "\\l" );
2342  }
2343 
2344  if ( tmpLyr.format().font().overline() )
2345  {
2346  txt.prepend( "\\O" ).append( "\\o" );
2347  }
2348 
2349  if ( tmpLyr.format().font().strikeOut() )
2350  {
2351  txt.prepend( "\\K" ).append( "\\k" );
2352  }
2353 
2354  txt.prepend( QStringLiteral( "\\f%1|i%2|b%3;\\H%4;" )
2355  .arg( tmpLyr.format().font().family() )
2356  .arg( tmpLyr.format().font().italic() ? 1 : 0 )
2357  .arg( tmpLyr.format().font().bold() ? 1 : 0 )
2358  .arg( label->getHeight() / ( 1 + txt.count( QStringLiteral( "\\P" ) ) ) * 0.75 ) );
2359  writeMText( dxfLayer, txt, QgsPoint( label->getX(), label->getY() ), label->getWidth(), label->getAlpha() * 180.0 / M_PI, tmpLyr.format().color() );
2360  }
2361 }
2362 
2363 
2364 void QgsDxfExport::registerDxfLayer( const QString &layerId, QgsFeatureId fid, const QString &layerName )
2365 {
2366  if ( !mDxfLayerNames.contains( layerId ) )
2367  mDxfLayerNames[ layerId ] = QMap<QgsFeatureId, QString>();
2368 
2369  mDxfLayerNames[layerId][fid] = layerName;
2370 }
2371 
2373 {
2374  mCrs = crs;
2375  mMapUnits = crs.mapUnits();
2376 }
2377 
2379 {
2380  return mCrs;
2381 }
2382 
2384 {
2385  QString splitLayerFieldName;
2386  const QgsFields fields = mLayer->fields();
2387  if ( mLayerOutputAttributeIndex >= 0 && mLayerOutputAttributeIndex < fields.size() )
2388  {
2389  splitLayerFieldName = fields.at( mLayerOutputAttributeIndex ).name();
2390  }
2391 
2392  return splitLayerFieldName;
2393 }
Abstract base class for all geometries.
bool is3D() const SIP_HOLDGIL
Returns true if the geometry is 3D and contains a z-value.
virtual bool hasCurvedSegments() const
Returns true if the geometry contains curved segments.
Circular string geometry type.
QgsPoint pointN(int i) const SIP_HOLDGIL
Returns the point at index i within the circular string.
int numPoints() const override SIP_HOLDGIL
Returns the number of points in the curve.
Compound curve geometry type.
const QgsCurve * curveAt(int i) const SIP_HOLDGIL
Returns the curve at the specified index.
int nCurves() const SIP_HOLDGIL
Returns the number of curves in the geometry.
This class represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
Q_GADGET QgsUnitTypes::DistanceUnit mapUnits
Class for doing transforms between two map coordinate systems.
bool isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...
Curve polygon geometry type.
const QgsCurve * interiorRing(int i) const SIP_HOLDGIL
Retrieves an interior ring from the curve polygon.
const QgsCurve * exteriorRing() const SIP_HOLDGIL
Returns the curve polygon's exterior ring.
int numInteriorRings() const SIP_HOLDGIL
Returns the number of interior rings contained with the curve polygon.
Abstract base class for curved geometry type.
Definition: qgscurve.h:36
QgsCoordinateSequence coordinateSequence() const override
Retrieves the sequence of geometries, rings and nodes.
Definition: qgscurve.cpp:68
virtual int numPoints() const =0
Returns the number of points in the curve.
virtual bool isClosed() const SIP_HOLDGIL
Returns true if the curve is closed.
Definition: qgscurve.cpp:40
void writeFilledCircle(const QString &layer, const QColor &color, const QgsPoint &pt, double radius)
Write filled circle (as hatch)
ExportResult
The result of an export as dxf operation.
Definition: qgsdxfexport.h:122
@ DeviceNotWritableError
Device not writable error.
@ Success
Successful export.
@ EmptyExtentError
Empty extent, no extent given and no extent could be derived from layers.
@ InvalidDeviceError
Invalid device error.
@ SymbolLayerSymbology
Exports one feature per symbol layer (considering symbol levels)
Definition: qgsdxfexport.h:106
@ NoSymbology
Export only data.
Definition: qgsdxfexport.h:104
void writeCircle(const QString &layer, const QColor &color, const QgsPoint &pt, double radius, const QString &lineStyleName, double width)
Write circle (as polyline)
~QgsDxfExport() override
ExportResult writeToFile(QIODevice *d, const QString &codec)
Export to a dxf file in the given encoding.
void writeLine(const QgsPoint &pt1, const QgsPoint &pt2, const QString &layer, const QString &lineStyleName, const QColor &color, double width=-1)
Write line (as a polyline)
static double mapUnitScaleFactor(double scale, QgsUnitTypes::RenderUnit symbolUnits, QgsUnitTypes::DistanceUnit mapUnits, double mapUnitsPerPixel=1.0)
Returns scale factor for conversion to map units.
void writeGroup(int code, int i)
Write a tuple of group code and integer value.
QString layerName(const QString &id, const QgsFeature &f) const
Gets layer name for feature.
void setFlags(QgsDxfExport::Flags flags)
Sets the export flags.
@ FlagNoMText
Export text as TEXT elements. If not set, text will be exported as MTEXT elements.
Definition: qgsdxfexport.h:112
void writeInt(int i)
Write an integer value.
void writeMText(const QString &layer, const QString &text, const QgsPoint &pt, double width, double angle, const QColor &color)
Write mtext (MTEXT)
QgsDxfExport()
Constructor for QgsDxfExport.
QgsUnitTypes::DistanceUnit mapUnits() const
Retrieve map units.
int writeHandle(int code=5, int handle=0)
Write a tuple of group code and a handle.
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination CRS, or an invalid CRS if no reprojection will be done.
HAlign
Horizontal alignments.
Definition: qgsdxfexport.h:143
@ HCenter
Centered (1)
@ Undefined
Undefined.
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
Set destination CRS.
void addLayers(const QList< QgsDxfExport::DxfLayer > &layers)
Add layers to export.
static QString dxfLayerName(const QString &name)
Returns cleaned layer name for use in DXF.
void writeDouble(double d)
Write a floating point value.
void writeText(const QString &layer, const QString &text, const QgsPoint &pt, double size, double angle, const QColor &color, QgsDxfExport::HAlign hali=QgsDxfExport::HAlign::Undefined, QgsDxfExport::VAlign vali=QgsDxfExport::VAlign::Undefined)
Write text (TEXT)
void writeString(const QString &s)
Write a string value.
void writePolygon(const QgsRingSequence &polygon, const QString &layer, const QString &hatchPattern, const QColor &color)
Draw dxf filled polygon (HATCH)
void drawLabel(const QString &layerId, QgsRenderContext &context, pal::LabelPosition *label, const QgsPalLayerSettings &settings) override
Add a label to the dxf output.
static QString dxfEncoding(const QString &name)
Returns DXF encoding for Qt encoding.
static int closestColorMatch(QRgb color)
Gets DXF palette index of nearest entry for given color.
void writePoint(const QString &layer, const QColor &color, const QgsPoint &pt)
Write point.
Q_DECL_DEPRECATED void registerDxfLayer(const QString &layerId, QgsFeatureId fid, const QString &layer)
Register name of layer for feature.
QgsDxfExport::Flags flags() const
Returns the export flags.
VAlign
Vertical alignments.
Definition: qgsdxfexport.h:133
@ Undefined
Undefined.
void clipValueToMapUnitScale(double &value, const QgsMapUnitScale &scale, double pixelToMMFactor) const
Clips value to scale minimum/maximum.
void writePolyline(const QgsPointSequence &line, const QString &layer, const QString &lineStyleName, const QColor &color, double width=-1)
Draw dxf primitives (LWPOLYLINE)
static QStringList encodings()
Returns list of available DXF encodings.
void setMapSettings(const QgsMapSettings &settings)
Set map settings and assign layer name attributes.
void writeGroupCode(int code)
Write a group code.
Single scope for storing variables and functions for use within a QgsExpressionContext.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the scope.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QList< QgsExpressionContextScope * > scopes()
Returns a list of scopes contained within the stack.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
Class for parsing and evaluation of expressions (formerly called "search strings").
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
@ SymbolLevels
Rendering with symbol levels (i.e. implements symbols(), symbolForFeature())
Definition: qgsrenderer.h:254
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
QgsFeatureRequest & setExpressionContext(const QgsExpressionContext &context)
Sets the expression context used to evaluate filter expressions.
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:56
QgsGeometry geometry
Definition: qgsfeature.h:67
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:204
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:287
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Definition: qgsfeature.cpp:144
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
QString name
Definition: qgsfield.h:60
Container of fields for a vector layer.
Definition: qgsfields.h:45
int size() const
Returns number of items.
Definition: qgsfields.cpp:138
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:163
Geometry collection.
int numGeometries() const SIP_HOLDGIL
Returns the number of geometries within the collection.
const QgsAbstractGeometry * geometryN(int n) const
Returns a const reference to a geometry from within the collection.
QString geometryExpression() const
Gets the expression to generate this geometry.
QgsSymbol * subSymbol() override
Returns the symbol's sub symbol, if present.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:124
Does vector analysis using the geos library and handles import, export, exception handling*.
Definition: qgsgeos.h:104
The QgsLabelFeature class describes a feature that should be used within the labeling engine.
QgsPointXY anchorPosition() const
In case of quadrand or aligned positioning, this is set to the anchor point.
QString labelText() const
Text of the label.
bool reverseDirectionSymbol() const
Returns true if direction symbols should be reversed.
DirectionSymbolPlacement directionSymbolPlacement() const
Returns the placement for direction symbols.
QString leftDirectionSymbol() const
Returns the string to use for left direction arrows.
@ SymbolLeftRight
Place direction symbols on left/right of label.
@ SymbolAbove
Place direction symbols on above label.
@ SymbolBelow
Place direction symbols on below label.
QString rightDirectionSymbol() const
Returns the string to use for right direction arrows.
bool addDirectionSymbol() const
Returns true if '<' or '>' (or custom strings set via leftDirectionSymbol and rightDirectionSymbol) w...
virtual void run(QgsRenderContext &context)=0
Runs the labeling job.
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:44
int numPoints() const override SIP_HOLDGIL
Returns the number of points in the curve.
QgsPoint pointN(int i) const
Returns the specified point from inside the line string.
Base class for all map layer types.
Definition: qgsmaplayer.h:85
QString name
Definition: qgsmaplayer.h:88
bool isInScaleRange(double scale) const
Tests whether the layer should be visible at the specified scale.
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
QString title() const
Returns the title of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:301
The QgsMapSettings class contains configuration for rendering of the map.
QgsPointXY layerToMapCoordinates(const QgsMapLayer *layer, QgsPointXY point) const
transform point coordinates from layer's CRS to output CRS
void setLayers(const QList< QgsMapLayer * > &layers)
Sets the list of layers to render in the map.
void setOutputDpi(double dpi)
Sets the dpi (dots per inch) used for conversion between real world units (e.g.
void setExtent(const QgsRectangle &rect, bool magnified=true)
Sets the coordinates of the rectangle which should be rendered.
const QgsMapToPixel & mapToPixel() const
QList< QgsMapLayer * > layers() const
Returns the list of layers which will be rendered in the map.
QMap< QString, QString > layerStyleOverrides() const
Returns the map of map layer style overrides (key: layer ID, value: style name) where a different sty...
void setOutputSize(QSize size)
Sets the size of the resulting map image, in pixels.
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system for the map render.
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
Sets the destination crs (coordinate reference system) for the map render.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context, which stores various information regarding which datum tran...
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:39
double mapUnitsPerPixel() const
Returns current map units per pixel.
Struct for storing maximum and minimum scales for measurements in map units.
bool minSizeMMEnabled
Whether the minimum size in mm should be respected.
double maxScale
The maximum scale, or 0.0 if unset.
double minScale
The minimum scale, or 0.0 if unset.
double maxSizeMM
The maximum size in millimeters, or 0.0 if unset.
bool maxSizeMMEnabled
Whether the maximum size in mm should be respected.
double minSizeMM
The minimum size in millimeters, or 0.0 if unset.
Abstract base class for marker symbol layers.
QgsUnitTypes::RenderUnit sizeUnit() const
Returns the units for the symbol's size.
double size() const
Returns the symbol size.
Contains settings for how a map layer will be labeled.
void setFormat(const QgsTextFormat &format)
Sets the label text formatting settings, e.g., font settings, buffer settings, etc.
QString wrapChar
Wrapping character string.
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the label's property collection, used for data defined overrides.
bool drawLabels
Whether to draw labels for this layer.
QuadrantPosition quadOffset
Sets the quadrant in which to offset labels from feature.
@ Line
Arranges candidates parallel to a generalised line representing the feature or parallel to a polygon'...
MultiLineAlign multilineAlign
Horizontal alignment of multi-line labels.
@ Hali
Horizontal alignment for data defined label position (Left, Center, Right)
@ Vali
Vertical alignment for data defined label position (Bottom, Base, Half, Cap, Top)
const QgsLabelLineSettings & lineSettings() const
Returns the label line settings, which contain settings related to how the label engine places and fo...
const QgsTextFormat & format() const
Returns the label text formatting settings, e.g., font settings, buffer settings, etc.
double y
Definition: qgspointxy.h:48
Q_GADGET double x
Definition: qgspointxy.h:47
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:38
Q_GADGET double x
Definition: qgspoint.h:41
double z
Definition: qgspoint.h:43
double y
Definition: qgspoint.h:42
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:501
A grouped map of multiple QgsProperty objects, each referenced by a integer key value.
QVariant value(int key, const QgsExpressionContext &context, const QVariant &defaultValue=QVariant()) const override
Returns the calculated value of the property with the specified key from within the collection.
bool isActive(int key) const override
Returns true if the collection contains an active property with the specified key.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:172
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:162
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:167
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:177
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
Definition: qgsrectangle.h:209
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Definition: qgsrectangle.h:202
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle.
Definition: qgsrectangle.h:359
bool isEmpty() const
Returns true if the rectangle is empty.
Definition: qgsrectangle.h:437
QgsPointXY center() const SIP_HOLDGIL
Returns the center point of the rectangle.
Definition: qgsrectangle.h:230
Contains information about the context of a rendering operation.
void setScaleFactor(double factor)
Sets the scaling factor for the render to convert painter units to physical sizes.
QgsExpressionContext & expressionContext()
Gets the expression context.
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
void setExtent(const QgsRectangle &extent)
When rendering a map layer, calling this method sets the "clipping" extent for the layer (in the laye...
QgsLabelingEngine * labelingEngine() const
Gets access to new labeling engine (may be nullptr)
void setMapToPixel(const QgsMapToPixel &mtp)
Sets the context's map to pixel transform, which transforms between map coordinates and device coordi...
void setPainter(QPainter *p)
Sets the destination QPainter for the render operation.
void setLabelingEngine(QgsLabelingEngine *engine)
Assign new labeling engine.
void setRendererScale(double scale)
Sets the renderer map scale.
A simple line symbol layer, which renders lines using a line in a variety of styles (e....
bool useCustomDashPattern() const
Returns true if the line uses a custom dash pattern.
virtual double dxfOffset(const QgsDxfExport &e, QgsSymbolRenderContext &context) const
Gets offset.
virtual QColor dxfBrushColor(QgsSymbolRenderContext &context) const
Gets brush/fill color.
virtual QVector< qreal > dxfCustomDashPattern(QgsUnitTypes::RenderUnit &unit) const
Gets dash pattern.
virtual Qt::PenStyle dxfPenStyle() const
Gets pen style.
virtual QColor dxfColor(QgsSymbolRenderContext &context) const
Gets color.
virtual QString layerType() const =0
Returns a string that represents this layer type.
virtual double dxfWidth(const QgsDxfExport &e, QgsSymbolRenderContext &context) const
Gets line width.
virtual double dxfAngle(QgsSymbolRenderContext &context) const
Gets angle.
virtual bool writeDxf(QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift=QPointF(0.0, 0.0)) const
write as DXF
virtual Qt::BrushStyle dxfBrushStyle() const
Gets brush/fill style.
virtual bool hasDataDefinedProperties() const
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties.
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
Definition: qgssymbol.h:794
const QgsFeature * feature() const
Returns the current feature being rendered.
Definition: qgssymbol.h:875
QgsExpressionContextScope * expressionContextScope()
This scope is always available when a symbol of this type is being rendered.
Definition: qgssymbol.cpp:1534
void setFeature(const QgsFeature *f)
Definition: qgssymbol.h:870
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:65
QgsSymbolRenderContext * symbolRenderContext()
Returns the symbol render context.
Definition: qgssymbol.cpp:1417
QgsSymbolLayer * symbolLayer(int layer)
Returns the symbol layer at the specified index.
Definition: qgssymbol.cpp:411
@ DynamicRotation
Rotation of symbol may be changed during rendering and symbol should not be cached.
Definition: qgssymbol.h:107
RenderHints renderHints() const
Returns the rendering hint flags for the symbol.
Definition: qgssymbol.h:500
int symbolLayerCount() const
Returns the total number of symbol layers contained in the symbol.
Definition: qgssymbol.h:199
Container for all settings relating to text rendering.
Definition: qgstextformat.h:41
void setFont(const QFont &font)
Sets the font used for rendering text.
QColor color() const
Returns the color that text will be rendered in.
QFont font() const
Returns the font used for rendering text.
Class that adds extra information to QgsLabelFeature for text labels.
QFont definedFont()
Font to be used for rendering.
const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant > & dataDefinedValues() const
Gets data-defined values.
DistanceUnit
Units of distance.
Definition: qgsunittypes.h:68
@ DistanceMeters
Meters.
Definition: qgsunittypes.h:69
static Q_INVOKABLE double fromUnitToUnitFactor(QgsUnitTypes::DistanceUnit fromUnit, QgsUnitTypes::DistanceUnit toUnit)
Returns the conversion factor between the specified distance units.
RenderUnit
Rendering size units.
Definition: qgsunittypes.h:167
@ RenderPixels
Pixels.
Definition: qgsunittypes.h:170
@ RenderMillimeters
Millimeters.
Definition: qgsunittypes.h:168
@ RenderMapUnits
Map units.
Definition: qgsunittypes.h:169
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) override
Gets an iterator for features matching the specified request.
QgsFields fields() const
Returns the fields that will be available for features that are retrieved from this source.
Represents a vector layer which manages a vector based data sets.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
QgsRectangle extent() const FINAL
Returns the extent of the layer.
QSet< QVariant > uniqueValues(int fieldIndex, int limit=-1) const FINAL
Calculates a list of unique values contained within an attribute in the layer.
QgsFeatureRenderer * renderer()
Returns the feature renderer used for rendering the features in the layer in 2D map views.
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:70
static Type flatType(Type type) SIP_HOLDGIL
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:702
QgsFeatureId featureId() const
Returns the unique ID of the feature.
Definition: feature.cpp:157
QgsLabelFeature * feature()
Returns the parent feature.
Definition: feature.h:118
LabelPosition is a candidate feature label position.
Definition: labelposition.h:56
double getAlpha() const
Returns the angle to rotate text (in rad).
double getHeight() const
bool getReversed() const
Quadrant getQuadrant() const
double getWidth() const
FeaturePart * getFeaturePart() const
Returns the feature corresponding to this labelposition.
double getX(int i=0) const
Returns the down-left x coordinate.
double getY(int i=0) const
Returns the down-left y coordinate.
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
Definition: MathUtils.cpp:786
Contains geos related utilities and functions.
Definition: qgsgeos.h:42
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:798
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:276
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:797
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:316
QVector< QgsRingSequence > QgsCoordinateSequence
QVector< QgsPointSequence > QgsRingSequence
QVector< QgsPoint > QgsPointSequence
#define DXF_HANDMAX
Definition: qgsdxfexport.h:45
#define DXF_HANDPLOTSTYLE
Definition: qgsdxfexport.h:46
#define DXF_TRAILER
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
Definition: qgsfeatureid.h:28
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QList< QgsSymbolLevel > QgsSymbolLevelOrder
Definition: qgsrenderer.h:89
QList< QgsSymbolLevelItem > QgsSymbolLevel
Definition: qgsrenderer.h:85
QList< QgsSymbol * > QgsSymbolList
Definition: qgsrenderer.h:45
QList< QgsSymbolLayer * > QgsSymbolLayerList
Definition: qgssymbol.h:54
const QgsCoordinateReferenceSystem & crs
Holds information about each layer in a DXF job.
QString layerName
std::unique_ptr< QgsFeatureRenderer > renderer
QgsRenderContext renderContext
QgsCoordinateReferenceSystem crs
QgsVectorLayerFeatureSource featureSource
Layers and optional attribute index to split into multiple layers using attribute value as layer name...
Definition: qgsdxfexport.h:73
QString splitLayerAttribute() const
If the split layer attribute is set, the vector layer will be split into several dxf layers,...