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