QGIS API Documentation  3.20.0-Odense (decaadbb31)
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  request.setFilterRect( ct.transformBoundingBox( mExtent, QgsCoordinateTransform::ReverseTransform ) );
667 
668  QgsFeatureIterator featureIt = job->featureSource.getFeatures( request );
669 
670  QgsFeature fet;
671  while ( featureIt.nextFeature( fet ) )
672  {
673  mRenderContext.expressionContext().setFeature( fet );
674  QString lName( dxfLayerName( job->splitLayerAttribute.isNull() ? job->layerTitle : fet.attribute( job->splitLayerAttribute ).toString() ) );
675 
676  sctx.setFeature( &fet );
677 
678  if ( !job->renderer->willRenderFeature( fet, mRenderContext ) )
679  continue;
680 
681  if ( mSymbologyExport == NoSymbology )
682  {
683  addFeature( sctx, ct, lName, nullptr, nullptr ); // no symbology at all
684  }
685  else
686  {
687  const QgsSymbolList symbolList = job->renderer->symbolsForFeature( fet, mRenderContext );
688  bool hasSymbology = symbolList.size() > 0;
689 
690  if ( hasSymbology && mSymbologyExport == QgsDxfExport::SymbolLayerSymbology ) // symbol layer symbology, but layer does not use symbol levels
691  {
692  for ( QgsSymbol *symbol : symbolList )
693  {
694  const QgsSymbolLayerList symbolLayers = symbol->symbolLayers();
695  for ( QgsSymbolLayer *symbolLayer : symbolLayers )
696  {
697  if ( !symbolLayer )
698  continue;
699 
700  bool isGeometryGenerator = ( symbolLayer->layerType() == QLatin1String( "GeometryGenerator" ) );
701  if ( isGeometryGenerator )
702  {
703  addGeometryGeneratorSymbolLayer( sctx, ct, lName, symbolLayer, true );
704  }
705  else
706  {
707  addFeature( sctx, ct, lName, symbolLayer, symbol );
708  }
709  }
710  }
711  }
712  else if ( hasSymbology )
713  {
714  // take first symbollayer from first symbol
715  QgsSymbol *s = symbolList.first();
716  if ( !s || s->symbolLayerCount() < 1 )
717  {
718  continue;
719  }
720 
721  if ( s->symbolLayer( 0 )->layerType() == QLatin1String( "GeometryGenerator" ) )
722  {
723  addGeometryGeneratorSymbolLayer( sctx, ct, lName, s->symbolLayer( 0 ), false );
724  }
725  else
726  {
727  addFeature( sctx, ct, lName, s->symbolLayer( 0 ), s );
728  }
729  }
730 
731  if ( job->labelProvider )
732  {
733  job->labelProvider->registerFeature( fet, mRenderContext );
735  registerDxfLayer( job->featureSource.id(), fet.id(), lName );
737  }
738  else if ( job->ruleBasedLabelProvider )
739  {
740  job->ruleBasedLabelProvider->registerFeature( fet, mRenderContext );
742  registerDxfLayer( job->featureSource.id(), fet.id(), lName );
744  }
745  }
746  }
747  }
748 
749  QImage image( 10, 10, QImage::Format_ARGB32_Premultiplied );
750  image.setDotsPerMeterX( 96 / 25.4 * 1000 );
751  image.setDotsPerMeterY( 96 / 25.4 * 1000 );
752  QPainter painter( &image );
753  mRenderContext.setPainter( &painter );
754 
755  mRenderContext.labelingEngine()->run( mRenderContext );
756 
757  endSection();
758 }
759 
760 void QgsDxfExport::prepareRenderers()
761 {
762  Q_ASSERT( mJobs.empty() ); // If this fails, stopRenderers() was not called after the last job
763 
764  mRenderContext = QgsRenderContext();
765  mRenderContext.setRendererScale( mSymbologyScale );
766  mRenderContext.setExtent( mExtent );
767 
768  mRenderContext.setScaleFactor( 96.0 / 25.4 );
769  mRenderContext.setMapToPixel( QgsMapToPixel( 1.0 / mFactor, mExtent.center().x(), mExtent.center().y(), mExtent.width() * mFactor,
770  mExtent.height() * mFactor, 0 ) );
771 
774 
775  mLabelingEngine = std::make_unique<QgsDefaultLabelingEngine>();
776  mLabelingEngine->setMapSettings( mMapSettings );
777  mRenderContext.setLabelingEngine( mLabelingEngine.get() );
778 
779  const QList< QgsMapLayer * > layers = mMapSettings.layers();
780  for ( QgsMapLayer *ml : layers )
781  {
782  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml );
783  if ( !vl )
784  continue;
785 
786  if ( !vl->renderer() )
787  continue;
788 
789  if ( !layerIsScaleBasedVisible( vl ) )
790  continue;
791 
792  QString splitLayerAttribute;
793  int splitLayerAttributeIndex = mLayerNameAttribute.value( vl->id(), -1 );
794  const QgsFields fields = vl->fields();
795  if ( splitLayerAttributeIndex >= 0 && splitLayerAttributeIndex < fields.size() )
796  splitLayerAttribute = fields.at( splitLayerAttributeIndex ).name();
797  DxfLayerJob *job = new DxfLayerJob( vl, mMapSettings.layerStyleOverrides().value( vl->id() ), mRenderContext, this, splitLayerAttribute );
798  mJobs.append( job );
799  }
800 }
801 
802 void QgsDxfExport::writeEntitiesSymbolLevels( DxfLayerJob *job )
803 {
804  QHash< QgsSymbol *, QList<QgsFeature> > features;
805 
806  QgsRenderContext ctx = renderContext();
807  const QList<QgsExpressionContextScope *> scopes = job->renderContext.expressionContext().scopes();
808  for ( QgsExpressionContextScope *scope : scopes )
810  QgsSymbolRenderContext sctx( ctx, QgsUnitTypes::RenderMillimeters, 1.0, false, Qgis::SymbolRenderHints(), nullptr );
811 
812  // get iterator
813  QgsFeatureRequest req;
814  req.setSubsetOfAttributes( job->renderer->usedAttributes( ctx ), job->featureSource.fields() );
815  QgsCoordinateTransform ct( mMapSettings.destinationCrs(), job->crs, mMapSettings.transformContext() );
816  req.setFilterRect( ct.transform( mExtent ) );
817 
818  QgsFeatureIterator fit = job->featureSource.getFeatures( req );
819 
820  // fetch features
821  QgsFeature fet;
822  QgsSymbol *featureSymbol = nullptr;
823  while ( fit.nextFeature( fet ) )
824  {
825  ctx.expressionContext().setFeature( fet );
826  featureSymbol = job->renderer->symbolForFeature( fet, ctx );
827  if ( !featureSymbol )
828  {
829  continue;
830  }
831 
832  QHash< QgsSymbol *, QList<QgsFeature> >::iterator it = features.find( featureSymbol );
833  if ( it == features.end() )
834  {
835  it = features.insert( featureSymbol, QList<QgsFeature>() );
836  }
837  it.value().append( fet );
838  }
839 
840  // find out order
841  QgsSymbolLevelOrder levels;
842  const QgsSymbolList symbols = job->renderer->symbols( ctx );
843  for ( QgsSymbol *symbol : symbols )
844  {
845  for ( int j = 0; j < symbol->symbolLayerCount(); j++ )
846  {
847  int level = symbol->symbolLayer( j )->renderingPass();
848  if ( level < 0 || level >= 1000 ) // ignore invalid levels
849  continue;
850  QgsSymbolLevelItem item( symbol, j );
851  while ( level >= levels.count() ) // append new empty levels
852  levels.append( QgsSymbolLevel() );
853  levels[level].append( item );
854  }
855  }
856 
857  // export symbol layers and symbology
858  for ( const QgsSymbolLevel &level : std::as_const( levels ) )
859  {
860  for ( const QgsSymbolLevelItem &item : level )
861  {
862  QHash< QgsSymbol *, QList<QgsFeature> >::iterator levelIt = features.find( item.symbol() );
863  if ( levelIt == features.end() )
864  {
865  continue;
866  }
867 
868  int llayer = item.layer();
869  const QList<QgsFeature> &featureList = levelIt.value();
870  for ( const QgsFeature &feature : featureList )
871  {
872  sctx.setFeature( &feature );
873  addFeature( sctx, ct, job->layerName, levelIt.key()->symbolLayer( llayer ), levelIt.key() );
874  }
875  }
876  }
877 }
878 
879 void QgsDxfExport::stopRenderers()
880 {
881  qDeleteAll( mJobs );
882  mJobs.clear();
883 }
884 
885 void QgsDxfExport::writeEndFile()
886 {
887  mTextStream << DXF_TRAILER;
888 
889  writeGroup( 0, QStringLiteral( "EOF" ) );
890 }
891 
892 void QgsDxfExport::startSection()
893 {
894  writeGroup( 0, QStringLiteral( "SECTION" ) );
895 }
896 
897 void QgsDxfExport::endSection()
898 {
899  writeGroup( 0, QStringLiteral( "ENDSEC" ) );
900 }
901 
902 void QgsDxfExport::writePoint( const QgsPoint &pt, const QString &layer, const QColor &color, QgsSymbolRenderContext &ctx, const QgsSymbolLayer *symbolLayer, const QgsSymbol *symbol, double angle )
903 {
904 #if 0
905  // debug: draw rectangle for debugging
906  const QgsMarkerSymbolLayer *msl = dynamic_cast< const QgsMarkerSymbolLayer * >( symbolLayer );
907  if ( msl )
908  {
909  double halfSize = msl->size() * mapUnitScaleFactor( mSymbologyScale,
910  msl->sizeUnit(), mMapUnits ) / 2.0;
911  writeGroup( 0, "SOLID" );
912  writeGroup( 8, layer );
913  writeGroup( 62, 1 );
914  writeGroup( 0, QgsPoint( QgsWkbTypes::PointZ, pt.x() - halfSize, pt.y() - halfSize ) );
915  writeGroup( 1, QgsPoint( QgsWkbTypes::PointZ, pt.x() + halfSize, pt.y() - halfSize ) );
916  writeGroup( 2, QgsPoint( QgsWkbTypes::PointZ, pt.x() - halfSize, pt.y() + halfSize ) );
917  writeGroup( 3, QgsPoint( QgsWkbTypes::PointZ, pt.x() + halfSize, pt.y() + halfSize ) );
918  }
919 #endif // 0
920 
921  // insert block or write point directly?
922  QHash< const QgsSymbolLayer *, QString >::const_iterator blockIt = mPointSymbolBlocks.constFind( symbolLayer );
923  if ( !symbolLayer || blockIt == mPointSymbolBlocks.constEnd() )
924  {
925  // write symbol directly here
926  const QgsMarkerSymbolLayer *msl = dynamic_cast< const QgsMarkerSymbolLayer * >( symbolLayer );
927  if ( msl && symbol )
928  {
929  if ( msl->writeDxf( *this, mapUnitScaleFactor( mSymbologyScale, msl->sizeUnit(), mMapUnits, ctx.renderContext().mapToPixel().mapUnitsPerPixel() ), layer, ctx, QPointF( pt.x(), pt.y() ) ) )
930  {
931  return;
932  }
933  }
934  writePoint( layer, color, pt ); // write default point symbol
935  }
936  else
937  {
938  // insert block reference
939  writeGroup( 0, QStringLiteral( "INSERT" ) );
940  writeHandle();
941  writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
942  writeGroup( 100, QStringLiteral( "AcDbBlockReference" ) );
943  writeGroup( 8, layer );
944  writeGroup( 2, blockIt.value() ); // Block name
945  writeGroup( 50, angle ); // angle
946  writeGroup( 0, pt ); // Insertion point (in OCS)
947  }
948 }
949 
950 void QgsDxfExport::writePolyline( const QgsPointSequence &line, const QString &layer, const QString &lineStyleName, const QColor &color, double width )
951 {
952  int n = line.size();
953  if ( n == 0 )
954  {
955  QgsDebugMsg( QStringLiteral( "writePolyline: empty line layer=%1 lineStyleName=%2" ).arg( layer, lineStyleName ) );
956  return;
957  }
958 
959  if ( n < 2 )
960  {
961  QgsDebugMsg( QStringLiteral( "writePolyline: line too short layer=%1 lineStyleName=%2" ).arg( layer, lineStyleName ) );
962  return;
963  }
964 
965  if ( mForce2d || !line.at( 0 ).is3D() )
966  {
967  bool polygon = line[0] == line[ line.size() - 1 ];
968  if ( polygon )
969  --n;
970 
971  writeGroup( 0, QStringLiteral( "LWPOLYLINE" ) );
972  writeHandle();
973  writeGroup( 8, layer );
974  writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
975  writeGroup( 100, QStringLiteral( "AcDbPolyline" ) );
976  writeGroup( 6, lineStyleName );
977  writeGroup( color );
978 
979  writeGroup( 90, n );
980  writeGroup( 70, polygon ? 1 : 0 );
981  writeGroup( 43, width );
982 
983  for ( int i = 0; i < n; i++ )
984  writeGroup( 0, line[i] );
985  }
986  else
987  {
988  writeGroup( 0, QStringLiteral( "POLYLINE" ) );
989  int plHandle = writeHandle();
990  writeGroup( 330, mBlockHandle );
991  writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
992  writeGroup( 8, layer );
993  writeGroup( 6, lineStyleName );
994  writeGroup( color );
995  writeGroup( 100, QStringLiteral( "AcDb3dPolyline" ) );
996  writeGroup( 0, QgsPoint( QgsWkbTypes::PointZ, 0.0, 0.0, 0.0 ) );
997  writeGroup( 70, 8 );
998 
999  for ( int i = 0; i < n; i++ )
1000  {
1001  writeGroup( 0, QStringLiteral( "VERTEX" ) );
1002  writeHandle();
1003  writeGroup( 330, plHandle );
1004  writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
1005  writeGroup( 8, layer );
1006  writeGroup( color );
1007  writeGroup( 100, QStringLiteral( "AcDbVertex" ) );
1008  writeGroup( 100, QStringLiteral( "AcDb3dPolylineVertex" ) );
1009  writeGroup( 0, line[i] );
1010  writeGroup( 70, 32 );
1011  }
1012 
1013  writeGroup( 0, QStringLiteral( "SEQEND" ) );
1014  writeHandle();
1015  writeGroup( 330, plHandle );
1016  writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
1017  writeGroup( 8, layer );
1018  writeGroup( color );
1019  }
1020 }
1021 
1022 void QgsDxfExport::appendCurve( const QgsCurve &c, QVector<QgsPoint> &points, QVector<double> &bulges )
1023 {
1024  switch ( QgsWkbTypes::flatType( c.wkbType() ) )
1025  {
1027  appendLineString( *dynamic_cast<const QgsLineString *>( &c ), points, bulges );
1028  break;
1029 
1031  appendCircularString( *dynamic_cast<const QgsCircularString *>( &c ), points, bulges );
1032  break;
1033 
1035  appendCompoundCurve( *dynamic_cast<const QgsCompoundCurve *>( &c ), points, bulges );
1036  break;
1037 
1038  default:
1039  QgsDebugMsg( QStringLiteral( "Unexpected curve type %1" ).arg( c.wktTypeStr() ) );
1040  break;
1041  }
1042 }
1043 
1044 void QgsDxfExport::appendLineString( const QgsLineString &ls, QVector<QgsPoint> &points, QVector<double> &bulges )
1045 {
1046  for ( int i = 0; i < ls.numPoints(); i++ )
1047  {
1048  const QgsPoint &p = ls.pointN( i );
1049  if ( !points.isEmpty() && points.last() == p )
1050  continue;
1051 
1052  points << p;
1053  bulges << 0.0;
1054  }
1055 }
1056 
1057 void QgsDxfExport::appendCircularString( const QgsCircularString &cs, QVector<QgsPoint> &points, QVector<double> &bulges )
1058 {
1059  for ( int i = 0; i < cs.numPoints() - 2; i += 2 )
1060  {
1061  const QgsPoint &p1 = cs.pointN( i );
1062  const QgsPoint &p2 = cs.pointN( i + 1 );
1063  const QgsPoint &p3 = cs.pointN( i + 2 );
1064 
1065  if ( points.isEmpty() || points.last() != p1 )
1066  points << p1;
1067  else if ( !bulges.isEmpty() )
1068  bulges.removeLast();
1069 
1070  double a = ( M_PI - ( p1 - p2 ).angle() + ( p3 - p2 ).angle() ) / 2.0;
1071  bulges << sin( a ) / cos( a );
1072 
1073  points << p3;
1074  bulges << 0.0;
1075  }
1076 }
1077 
1078 void QgsDxfExport::appendCompoundCurve( const QgsCompoundCurve &cc, QVector<QgsPoint> &points, QVector<double> &bulges )
1079 {
1080  for ( int i = 0; i < cc.nCurves(); i++ )
1081  {
1082  const QgsCurve *c = cc.curveAt( i );
1083  Q_ASSERT( c );
1084  appendCurve( *c, points, bulges );
1085  }
1086 }
1087 
1088 void QgsDxfExport::writePolyline( const QgsCurve &curve, const QString &layer, const QString &lineStyleName, const QColor &color, double width )
1089 {
1090  int n = curve.numPoints();
1091  if ( n == 0 )
1092  {
1093  QgsDebugMsg( QStringLiteral( "writePolyline: empty line layer=%1 lineStyleName=%2" ).arg( layer, lineStyleName ) );
1094  return;
1095  }
1096 
1097  if ( n < 2 )
1098  {
1099  QgsDebugMsg( QStringLiteral( "writePolyline: line too short layer=%1 lineStyleName=%2" ).arg( layer, lineStyleName ) );
1100  return;
1101  }
1102 
1103  QVector<QgsPoint> points;
1104  QVector<double> bulges;
1105  appendCurve( curve, points, bulges );
1106 
1107  if ( mForce2d || !curve.is3D() )
1108  {
1109  writeGroup( 0, QStringLiteral( "LWPOLYLINE" ) );
1110  writeHandle();
1111  writeGroup( 8, layer );
1112  writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
1113  writeGroup( 100, QStringLiteral( "AcDbPolyline" ) );
1114  writeGroup( 6, lineStyleName );
1115  writeGroup( color );
1116 
1117  writeGroup( 90, points.size() );
1118  QgsDxfExport::DxfPolylineFlags polylineFlags;
1119  if ( curve.isClosed() )
1120  polylineFlags.setFlag( QgsDxfExport::DxfPolylineFlag::Closed );
1121  if ( curve.hasCurvedSegments() )
1122  polylineFlags.setFlag( QgsDxfExport::DxfPolylineFlag::Curve );
1123 
1124  // Might need to conditional once this feature is implemented
1125  // https://github.com/qgis/QGIS/issues/32468
1126  polylineFlags.setFlag( QgsDxfExport::DxfPolylineFlag::ContinuousPattern );
1127 
1128  writeGroup( 70, static_cast<int>( polylineFlags ) );
1129  writeGroup( 43, width );
1130 
1131  for ( int i = 0; i < points.size(); i++ )
1132  {
1133  writeGroup( 0, points[i] );
1134  if ( bulges[i] != 0.0 )
1135  writeGroup( 42, bulges[i] );
1136  }
1137  }
1138  else
1139  {
1140  writeGroup( 0, QStringLiteral( "POLYLINE" ) );
1141  int plHandle = writeHandle();
1142  writeGroup( 330, mBlockHandle );
1143  writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
1144  writeGroup( 8, layer );
1145  writeGroup( 6, lineStyleName );
1146  writeGroup( color );
1147  writeGroup( 100, QStringLiteral( "AcDb3dPolyline" ) );
1148  writeGroup( 0, QgsPoint( QgsWkbTypes::PointZ, 0.0, 0.0, 0.0 ) );
1149  writeGroup( 70, 8 );
1150 
1151  for ( int i = 0; i < points.size(); i++ )
1152  {
1153  writeGroup( 0, QStringLiteral( "VERTEX" ) );
1154  writeHandle();
1155  writeGroup( 330, plHandle );
1156  writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
1157  writeGroup( 8, layer );
1158  writeGroup( color );
1159  writeGroup( 100, QStringLiteral( "AcDbVertex" ) );
1160  writeGroup( 100, QStringLiteral( "AcDb3dPolylineVertex" ) );
1161  writeGroup( 0, points[i] );
1162  if ( bulges[i] != 0.0 )
1163  writeGroup( 42, bulges[i] );
1164  writeGroup( 70, 32 );
1165  }
1166 
1167  writeGroup( 0, QStringLiteral( "SEQEND" ) );
1168  writeHandle();
1169  writeGroup( 330, plHandle );
1170  writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
1171  writeGroup( 8, layer );
1172  writeGroup( color );
1173  }
1174 }
1175 
1176 void QgsDxfExport::writePolygon( const QgsRingSequence &polygon, const QString &layer, const QString &hatchPattern, const QColor &color )
1177 {
1178  writeGroup( 0, QStringLiteral( "HATCH" ) ); // Entity type
1179  writeHandle();
1180  writeGroup( 330, mBlockHandle );
1181  writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
1182  writeGroup( 8, layer ); // Layer name
1183  writeGroup( color ); // Color
1184  writeGroup( 100, QStringLiteral( "AcDbHatch" ) );
1185 
1186  writeGroup( 0, QgsPoint( QgsWkbTypes::PointZ, 0.0, 0.0, 0.0 ) ); // Elevation point (in OCS)
1187  writeGroup( 200, QgsPoint( QgsWkbTypes::PointZ, 0.0, 0.0, 1.0 ) );
1188 
1189  writeGroup( 2, hatchPattern ); // Hatch pattern name
1190  writeGroup( 70, hatchPattern == QLatin1String( "SOLID" ) ); // Solid fill flag (solid fill = 1; pattern fill = 0)
1191  writeGroup( 71, 0 ); // Associativity flag (associative = 1; non-associative = 0)
1192 
1193  writeGroup( 91, polygon.size() ); // Number of boundary paths (loops)
1194  for ( int i = 0; i < polygon.size(); ++i )
1195  {
1196  writeGroup( 92, 2 ); // Boundary path type flag (bit coded): 0 = Default; 1 = External; 2 = Polyline 4 = Derived; 8 = Textbox; 16 = Outermost
1197  writeGroup( 72, 0 ); // Has bulge flag
1198  writeGroup( 73, 1 ); // Is closed flag
1199  writeGroup( 93, polygon[i].size() ); // Number of edges in this boundary path (only if boundary is not a polyline)
1200 
1201  for ( int j = 0; j < polygon[i].size(); ++j )
1202  {
1203  writeGroup( 0, polygon[i][j] ); // Vertex location (in OCS)
1204  }
1205 
1206  writeGroup( 97, 0 ); // Number of source boundary objects
1207  }
1208 
1209  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)
1210  writeGroup( 76, 1 ); // Hatch pattern type: 0 = User-defined; 1 = Predefined; 2 = Custom
1211 
1212  writeGroup( 98, 0 ); // Number of seed points
1213 }
1214 
1215 void QgsDxfExport::writePolygon( const QgsCurvePolygon &polygon, const QString &layer, const QString &hatchPattern, const QColor &color )
1216 {
1217  writeGroup( 0, QStringLiteral( "HATCH" ) ); // Entity type
1218  writeHandle();
1219  writeGroup( 330, mBlockHandle );
1220  writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
1221  writeGroup( 8, layer ); // Layer name
1222  writeGroup( color ); // Color
1223  writeGroup( 100, QStringLiteral( "AcDbHatch" ) );
1224 
1225  writeGroup( 0, QgsPoint( QgsWkbTypes::PointZ, 0.0, 0.0, 0.0 ) ); // Elevation point (in OCS)
1226  writeGroup( 200, QgsPoint( QgsWkbTypes::PointZ, 0.0, 0.0, 1.0 ) );
1227 
1228  writeGroup( 2, hatchPattern ); // Hatch pattern name
1229  writeGroup( 70, hatchPattern == QLatin1String( "SOLID" ) ); // Solid fill flag (solid fill = 1; pattern fill = 0)
1230  writeGroup( 71, 0 ); // Associativity flag (associative = 1; non-associative = 0)
1231 
1232  QVector<QVector<QgsPoint>> points;
1233  QVector<QVector<double>> bulges;
1234 
1235  const int ringCount = polygon.numInteriorRings();
1236  points.reserve( ringCount + 1 );
1237  bulges.reserve( ringCount + 1 );
1238 
1239  points << QVector<QgsPoint>();
1240  bulges << QVector<double>();
1241  appendCurve( *polygon.exteriorRing(), points.last(), bulges.last() );
1242 
1243  for ( int i = 0; i < ringCount; i++ )
1244  {
1245  points << QVector<QgsPoint>();
1246  bulges << QVector<double>();
1247  appendCurve( *polygon.interiorRing( i ), points.last(), bulges.last() );
1248  }
1249 
1250  bool hasBulges = false;
1251  for ( int i = 0; i < points.size() && !hasBulges; ++i )
1252  for ( int j = 0; j < points[i].size() && !hasBulges; ++j )
1253  hasBulges = bulges[i][j] != 0.0;
1254 
1255  writeGroup( 91, points.size() ); // Number of boundary paths (loops)
1256 
1257  for ( int i = 0; i < points.size(); ++i )
1258  {
1259  writeGroup( 92, 2 ); // Boundary path type flag (bit coded): 0 = Default; 1 = External; 2 = Polyline 4 = Derived; 8 = Textbox; 16 = Outermost
1260  writeGroup( 72, hasBulges ? 1 : 0 ); // Has bulge flag
1261  writeGroup( 73, 1 ); // Is closed flag
1262  writeGroup( 93, points[i].size() ); // Number of edges in this boundary path (only if boundary is not a polyline)
1263 
1264  for ( int j = 0; j < points[i].size(); ++j )
1265  {
1266  writeGroup( 0, points[i][j] ); // Vertex location (in OCS)
1267  if ( hasBulges )
1268  writeGroup( 42, bulges[i][j] );
1269  }
1270 
1271  writeGroup( 97, 0 ); // Number of source boundary objects
1272  }
1273 
1274  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)
1275  writeGroup( 76, 1 ); // Hatch pattern type: 0 = User-defined; 1 = Predefined; 2 = Custom
1276 
1277  writeGroup( 98, 0 ); // Number of seed points
1278 }
1279 
1280 void QgsDxfExport::writeLine( const QgsPoint &pt1, const QgsPoint &pt2, const QString &layer, const QString &lineStyleName, const QColor &color, double width )
1281 {
1282  writePolyline( QgsPointSequence() << pt1 << pt2, layer, lineStyleName, color, width );
1283 }
1284 
1285 void QgsDxfExport::writeText( const QString &layer, const QString &text, pal::LabelPosition *label, const QgsPalLayerSettings &layerSettings, const QgsExpressionContext &expressionContext )
1286 {
1287 
1288  double lblX = label->getX();
1289  double lblY = label->getY();
1290 
1291  QgsLabelFeature *labelFeature = label->getFeaturePart()->feature();
1292 
1293  HAlign hali = HAlign::Undefined;
1294  VAlign vali = VAlign::Undefined;
1295 
1296  const QgsPropertyCollection &props = layerSettings.dataDefinedProperties();
1297 
1298  if ( layerSettings.placement == QgsPalLayerSettings::Placement::OverPoint )
1299  {
1300  lblX = labelFeature->anchorPosition().x();
1301  lblY = labelFeature->anchorPosition().y();
1302 
1303  QgsPalLayerSettings::QuadrantPosition offsetQuad = layerSettings.quadOffset;
1304 
1306  {
1307  const QVariant exprVal = props.value( QgsPalLayerSettings::OffsetQuad, expressionContext );
1308  if ( !exprVal.isNull() )
1309  {
1310  offsetQuad = static_cast<QgsPalLayerSettings::QuadrantPosition>( exprVal.toInt() );
1311  }
1312  }
1313 
1314  switch ( offsetQuad )
1315  {
1316  case QgsPalLayerSettings::QuadrantPosition::QuadrantAboveLeft:
1317  hali = HAlign::HRight;
1318  vali = VAlign::VBottom;
1319  break;
1320  case QgsPalLayerSettings::QuadrantPosition::QuadrantAbove:
1321  hali = HAlign::HCenter;
1322  vali = VAlign::VBottom;
1323  break;
1324  case QgsPalLayerSettings::QuadrantPosition::QuadrantAboveRight:
1325  hali = HAlign::HLeft;
1326  vali = VAlign::VBottom;
1327  break;
1328  case QgsPalLayerSettings::QuadrantPosition::QuadrantLeft:
1329  hali = HAlign::HRight;
1330  vali = VAlign::VMiddle;
1331  break;
1332  case QgsPalLayerSettings::QuadrantPosition::QuadrantOver:
1333  hali = HAlign::HCenter;
1334  vali = VAlign::VMiddle;
1335  break;
1336  case QgsPalLayerSettings::QuadrantPosition::QuadrantRight:
1337  hali = HAlign::HLeft;
1338  vali = VAlign::VMiddle;
1339  break;
1340  case QgsPalLayerSettings::QuadrantPosition::QuadrantBelowLeft:
1341  hali = HAlign::HRight;
1342  vali = VAlign::VTop;
1343  break;
1344  case QgsPalLayerSettings::QuadrantPosition::QuadrantBelow:
1345  hali = HAlign::HCenter;
1346  vali = VAlign::VTop;
1347  break;
1348  case QgsPalLayerSettings::QuadrantPosition::QuadrantBelowRight:
1349  hali = HAlign::HLeft;
1350  vali = VAlign::VTop;
1351  break;
1352  }
1353  }
1354 
1355  if ( props.isActive( QgsPalLayerSettings::Hali ) )
1356  {
1357  lblX = labelFeature->anchorPosition().x();
1358  lblY = labelFeature->anchorPosition().y();
1359 
1360  hali = HAlign::HLeft;
1361  QVariant exprVal = props.value( QgsPalLayerSettings::Hali, expressionContext );
1362  if ( !exprVal.isNull() )
1363  {
1364  const QString haliString = exprVal.toString();
1365  if ( haliString.compare( QLatin1String( "Center" ), Qt::CaseInsensitive ) == 0 )
1366  {
1367  hali = HAlign::HCenter;
1368  }
1369  else if ( haliString.compare( QLatin1String( "Right" ), Qt::CaseInsensitive ) == 0 )
1370  {
1371  hali = HAlign::HRight;
1372  }
1373  }
1374  }
1375 
1376  //vertical alignment
1377  if ( props.isActive( QgsPalLayerSettings::Vali ) )
1378  {
1379  vali = VAlign::VBottom;
1380  QVariant exprVal = props.value( QgsPalLayerSettings::Vali, expressionContext );
1381  if ( !exprVal.isNull() )
1382  {
1383  const QString valiString = exprVal.toString();
1384  if ( valiString.compare( QLatin1String( "Bottom" ), Qt::CaseInsensitive ) != 0 )
1385  {
1386  if ( valiString.compare( QLatin1String( "Base" ), Qt::CaseInsensitive ) == 0 )
1387  {
1388  vali = VAlign::VBaseLine;
1389  }
1390  else if ( valiString.compare( QLatin1String( "Half" ), Qt::CaseInsensitive ) == 0 )
1391  {
1392  vali = VAlign::VMiddle;
1393  }
1394  else //'Cap' or 'Top'
1395  {
1396  vali = VAlign::VTop;
1397  }
1398  }
1399  }
1400  }
1401 
1402  writeText( layer, text, QgsPoint( lblX, lblY ), label->getHeight(), label->getAlpha() * 180.0 / M_PI, layerSettings.format().color(), hali, vali );
1403 }
1404 
1405 void QgsDxfExport::writePoint( const QString &layer, const QColor &color, const QgsPoint &pt )
1406 {
1407  writeGroup( 0, QStringLiteral( "POINT" ) );
1408  writeHandle();
1409  writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
1410  writeGroup( 100, QStringLiteral( "AcDbPoint" ) );
1411  writeGroup( 8, layer );
1412  writeGroup( color );
1413  writeGroup( 0, pt );
1414 }
1415 
1416 void QgsDxfExport::writeFilledCircle( const QString &layer, const QColor &color, const QgsPoint &pt, double radius )
1417 {
1418  writeGroup( 0, QStringLiteral( "HATCH" ) ); // Entity type
1419  writeHandle();
1420  writeGroup( 330, mBlockHandle );
1421  writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
1422  writeGroup( 8, layer ); // Layer name
1423  writeGroup( color ); // Color (0 by block, 256 by layer)
1424  writeGroup( 100, QStringLiteral( "AcDbHatch" ) );
1425 
1426  writeGroup( 0, QgsPoint( QgsWkbTypes::PointZ, 0.0, 0.0, 0.0 ) ); // Elevation point (in OCS)
1427  writeGroup( 200, QgsPoint( QgsWkbTypes::PointZ, 0.0, 0.0, 1.0 ) );
1428 
1429  writeGroup( 2, QStringLiteral( "SOLID" ) ); // Hatch pattern name
1430  writeGroup( 70, 1 ); // Solid fill flag (solid fill = 1; pattern fill = 0)
1431  writeGroup( 71, 0 ); // Associativity flag (associative = 1; non-associative = 0)
1432 
1433  writeGroup( 91, 1 ); // Number of boundary paths (loops)
1434 
1435  writeGroup( 92, 3 ); // Boundary path type flag (bit coded): 0 = Default; 1 = External; 2 = Polyline 4 = Derived; 8 = Textbox; 16 = Outermost
1436  writeGroup( 72, 1 );
1437  writeGroup( 73, 1 ); // Is closed flag
1438  writeGroup( 93, 2 ); // Number of polyline vertices
1439 
1440  writeGroup( 0, QgsPoint( QgsWkbTypes::Point, pt.x() - radius, pt.y() ) );
1441  writeGroup( 42, 1.0 );
1442 
1443  writeGroup( 0, QgsPoint( QgsWkbTypes::Point, pt.x() + radius, pt.y() ) );
1444  writeGroup( 42, 1.0 );
1445 
1446  writeGroup( 97, 0 ); // Number of source boundary objects
1447 
1448  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)
1449  writeGroup( 76, 1 ); // Hatch pattern type: 0 = User-defined; 1 = Predefined; 2 = Custom
1450  writeGroup( 98, 0 ); // Number of seed points
1451 }
1452 
1453 void QgsDxfExport::writeCircle( const QString &layer, const QColor &color, const QgsPoint &pt, double radius, const QString &lineStyleName, double width )
1454 {
1455  writeGroup( 0, QStringLiteral( "LWPOLYLINE" ) );
1456  writeHandle();
1457  writeGroup( 330, mBlockHandle );
1458  writeGroup( 8, layer );
1459  writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
1460  writeGroup( 100, QStringLiteral( "AcDbPolyline" ) );
1461  writeGroup( 6, lineStyleName );
1462  writeGroup( color );
1463 
1464  writeGroup( 90, 2 );
1465 
1466  writeGroup( 70, 1 );
1467  writeGroup( 43, width );
1468 
1469  writeGroup( 0, QgsPoint( pt.x() - radius, pt.y() ) );
1470  writeGroup( 42, 1.0 );
1471  writeGroup( 0, QgsPoint( pt.x() + radius, pt.y() ) );
1472  writeGroup( 42, 1.0 );
1473 }
1474 
1475 void QgsDxfExport::writeText( const QString &layer, const QString &text, const QgsPoint &pt, double size, double angle, const QColor &color, HAlign hali, VAlign vali )
1476 {
1477  writeGroup( 0, QStringLiteral( "TEXT" ) );
1478  writeHandle();
1479  writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
1480  // writeGroup( 6, "Continuous" ); // Line style
1481  // writeGroup( 370, 18 ); // Line weight
1482  writeGroup( 100, QStringLiteral( "AcDbText" ) );
1483  writeGroup( 8, layer );
1484  writeGroup( color );
1485  writeGroup( 0, pt );
1486  if ( hali != HAlign::Undefined || vali != VAlign::Undefined )
1487  writeGroup( 1, pt ); // Second alignment point
1488  writeGroup( 40, size );
1489  writeGroup( 1, text );
1490  writeGroup( 50, fmod( angle, 360 ) );
1491  if ( hali != HAlign::Undefined )
1492  writeGroup( 72, static_cast<int>( hali ) );
1493  writeGroup( 7, QStringLiteral( "STANDARD" ) ); // so far only support for standard font
1494  writeGroup( 100, QStringLiteral( "AcDbText" ) );
1495  if ( vali != VAlign::Undefined )
1496  {
1497  writeGroup( 73, static_cast<int>( vali ) );
1498  }
1499 }
1500 
1501 void QgsDxfExport::writeMText( const QString &layer, const QString &text, const QgsPoint &pt, double width, double angle, const QColor &color )
1502 {
1503 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1504  if ( !mTextStream.codec()->canEncode( text ) )
1505  {
1506  // TODO return error
1507  QgsDebugMsg( QStringLiteral( "could not encode:%1" ).arg( text ) );
1508  return;
1509  }
1510 #endif
1511 
1512  writeGroup( 0, QStringLiteral( "MTEXT" ) );
1513  writeHandle();
1514  writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
1515  writeGroup( 100, QStringLiteral( "AcDbMText" ) );
1516  writeGroup( 8, layer );
1517  writeGroup( color );
1518 
1519  writeGroup( 0, pt );
1520 
1521  QString t( text );
1522  while ( t.length() > 250 )
1523  {
1524  writeGroup( 3, t.left( 250 ) );
1525  t = t.mid( 250 );
1526  }
1527  writeGroup( 1, t );
1528 
1529  writeGroup( 50, angle ); // Rotation angle in radians
1530  writeGroup( 41, width * 1.1 ); // Reference rectangle width
1531 
1532  // Attachment point:
1533  // 1 2 3
1534  // 4 5 6
1535  // 7 8 9
1536  writeGroup( 71, 7 );
1537 
1538  writeGroup( 7, QStringLiteral( "STANDARD" ) ); // so far only support for standard font
1539 }
1540 
1541 void QgsDxfExport::addFeature( QgsSymbolRenderContext &ctx, const QgsCoordinateTransform &ct, const QString &layer, const QgsSymbolLayer *symbolLayer, const QgsSymbol *symbol )
1542 {
1543  const QgsFeature *fet = ctx.feature();
1544  if ( !fet )
1545  return;
1546 
1547  if ( !fet->hasGeometry() )
1548  return;
1549 
1550  QgsGeometry geom( fet->geometry() );
1551  if ( ct.isValid() )
1552  {
1553  geom.transform( ct );
1554  }
1555 
1556  QgsWkbTypes::Type geometryType = geom.wkbType();
1557 
1558  QColor penColor;
1559  QColor brushColor;
1560  if ( mSymbologyExport != NoSymbology && symbolLayer )
1561  {
1562  penColor = colorFromSymbolLayer( symbolLayer, ctx );
1563  brushColor = symbolLayer->dxfBrushColor( ctx );
1564  }
1565 
1566  Qt::PenStyle penStyle( Qt::SolidLine );
1567  Qt::BrushStyle brushStyle( Qt::NoBrush );
1568  double width = -1;
1569  double offset = 0.0;
1570  double angle = 0.0;
1571  if ( mSymbologyExport != NoSymbology && symbolLayer )
1572  {
1573  width = symbolLayer->dxfWidth( *this, ctx );
1574  offset = symbolLayer->dxfOffset( *this, ctx );
1575  angle = symbolLayer->dxfAngle( ctx );
1576  penStyle = symbolLayer->dxfPenStyle();
1577  brushStyle = symbolLayer->dxfBrushStyle();
1578 
1579  if ( qgsDoubleNear( offset, 0.0 ) )
1580  offset = 0.0;
1581  }
1582 
1583  QString lineStyleName = QStringLiteral( "CONTINUOUS" );
1584  if ( mSymbologyExport != NoSymbology )
1585  {
1586  lineStyleName = lineStyleFromSymbolLayer( symbolLayer );
1587  }
1588 
1589  // single point
1590  if ( QgsWkbTypes::flatType( geometryType ) == QgsWkbTypes::Point )
1591  {
1592  writePoint( geom.constGet()->coordinateSequence().at( 0 ).at( 0 ).at( 0 ), layer, penColor, ctx, symbolLayer, symbol, angle );
1593  return;
1594  }
1595 
1596  if ( QgsWkbTypes::flatType( geometryType ) == QgsWkbTypes::MultiPoint )
1597  {
1598  const QgsCoordinateSequence &cs = geom.constGet()->coordinateSequence();
1599  for ( int i = 0; i < cs.size(); i++ )
1600  {
1601  writePoint( cs.at( i ).at( 0 ).at( 0 ), layer, penColor, ctx, symbolLayer, symbol, angle );
1602  }
1603  return;
1604  }
1605 
1606  if ( penStyle != Qt::NoPen )
1607  {
1608  const QgsAbstractGeometry *sourceGeom = geom.constGet();
1609  std::unique_ptr< QgsAbstractGeometry > tempGeom;
1610 
1611  switch ( QgsWkbTypes::flatType( geometryType ) )
1612  {
1616  {
1617  if ( !qgsDoubleNear( offset, 0.0 ) )
1618  {
1619  QgsGeos geos( sourceGeom );
1620  tempGeom.reset( geos.offsetCurve( offset, 0, GEOSBUF_JOIN_MITRE, 2.0 ) ); //#spellok
1621  if ( tempGeom )
1622  sourceGeom = tempGeom.get();
1623  else
1624  sourceGeom = geom.constGet();
1625  }
1626 
1627  const QgsCurve *curve = dynamic_cast<const QgsCurve *>( sourceGeom );
1628  Q_ASSERT( curve );
1629  writePolyline( *curve, layer, lineStyleName, penColor, width );
1630 
1631  break;
1632  }
1633 
1636  {
1637  if ( !qgsDoubleNear( offset, 0.0 ) )
1638  {
1639  QgsGeos geos( sourceGeom );
1640  tempGeom.reset( geos.offsetCurve( offset, 0, GEOSBUF_JOIN_MITRE, 2.0 ) ); //#spellok
1641  if ( tempGeom )
1642  sourceGeom = tempGeom.get();
1643  else
1644  sourceGeom = geom.constGet();
1645  }
1646 
1647  const QgsGeometryCollection *gc = dynamic_cast<const QgsGeometryCollection *>( sourceGeom );
1648  Q_ASSERT( gc );
1649 
1650  for ( int i = 0; i < gc->numGeometries(); i++ )
1651  {
1652  const QgsCurve *curve = dynamic_cast<const QgsCurve *>( gc->geometryN( i ) );
1653  Q_ASSERT( curve );
1654  writePolyline( *curve, layer, lineStyleName, penColor, width );
1655  }
1656 
1657  break;
1658  }
1659 
1661  case QgsWkbTypes::Polygon:
1662  {
1663  if ( !qgsDoubleNear( offset, 0.0 ) )
1664  {
1665  QgsGeos geos( sourceGeom );
1666  tempGeom.reset( geos.buffer( offset, 0, GEOSBUF_CAP_FLAT, GEOSBUF_JOIN_MITRE, 2.0 ) ); //#spellok
1667  if ( tempGeom )
1668  sourceGeom = tempGeom.get();
1669  else
1670  sourceGeom = geom.constGet();
1671  }
1672 
1673  const QgsCurvePolygon *polygon = dynamic_cast<const QgsCurvePolygon *>( sourceGeom );
1674  Q_ASSERT( polygon );
1675 
1676  writePolyline( *polygon->exteriorRing(), layer, lineStyleName, penColor, width );
1677  for ( int i = 0; i < polygon->numInteriorRings(); i++ )
1678  writePolyline( *polygon->interiorRing( i ), layer, lineStyleName, penColor, width );
1679 
1680  break;
1681  }
1682 
1685  {
1686  if ( !qgsDoubleNear( offset, 0.0 ) )
1687  {
1688  QgsGeos geos( sourceGeom );
1689  tempGeom.reset( geos.buffer( offset, 0, GEOSBUF_CAP_FLAT, GEOSBUF_JOIN_MITRE, 2.0 ) ); //#spellok
1690  if ( tempGeom )
1691  sourceGeom = tempGeom.get();
1692  else
1693  sourceGeom = geom.constGet();
1694  }
1695 
1696  const QgsGeometryCollection *gc = dynamic_cast<const QgsGeometryCollection *>( sourceGeom );
1697  Q_ASSERT( gc );
1698 
1699  for ( int i = 0; i < gc->numGeometries(); i++ )
1700  {
1701  const QgsCurvePolygon *polygon = dynamic_cast<const QgsCurvePolygon *>( gc->geometryN( i ) );
1702  Q_ASSERT( polygon );
1703 
1704  writePolyline( *polygon->exteriorRing(), layer, lineStyleName, penColor, width );
1705  for ( int j = 0; j < polygon->numInteriorRings(); j++ )
1706  writePolyline( *polygon->interiorRing( j ), layer, lineStyleName, penColor, width );
1707  }
1708 
1709  break;
1710  }
1711 
1712  default:
1713  break;
1714  }
1715 
1716  }
1717 
1718  if ( brushStyle != Qt::NoBrush )
1719  {
1720  const QgsAbstractGeometry *sourceGeom = geom.constGet();
1721  std::unique_ptr< QgsAbstractGeometry > tempGeom;
1722 
1723  switch ( QgsWkbTypes::flatType( geometryType ) )
1724  {
1726  case QgsWkbTypes::Polygon:
1727  {
1728  const QgsCurvePolygon *polygon = dynamic_cast<const QgsCurvePolygon *>( sourceGeom );
1729  Q_ASSERT( polygon );
1730  writePolygon( *polygon, layer, QStringLiteral( "SOLID" ), brushColor );
1731  break;
1732  }
1733 
1736  {
1737  const QgsGeometryCollection *gc = dynamic_cast<const QgsGeometryCollection *>( sourceGeom );
1738  Q_ASSERT( gc );
1739 
1740  for ( int i = 0; i < gc->numGeometries(); i++ )
1741  {
1742  const QgsCurvePolygon *polygon = dynamic_cast<const QgsCurvePolygon *>( gc->geometryN( i ) );
1743  Q_ASSERT( polygon );
1744  writePolygon( *polygon, layer, QStringLiteral( "SOLID" ), brushColor );
1745  }
1746  break;
1747  }
1748 
1749  default:
1750  break;
1751 
1752  }
1753  }
1754 }
1755 
1756 QColor QgsDxfExport::colorFromSymbolLayer( const QgsSymbolLayer *symbolLayer, QgsSymbolRenderContext &ctx )
1757 {
1758  if ( !symbolLayer )
1759  return QColor();
1760 
1761  return symbolLayer->dxfColor( ctx );
1762 }
1763 
1764 QString QgsDxfExport::lineStyleFromSymbolLayer( const QgsSymbolLayer *symbolLayer )
1765 {
1766  QString lineStyleName = QStringLiteral( "CONTINUOUS" );
1767  if ( !symbolLayer )
1768  {
1769  return lineStyleName;
1770  }
1771 
1772  QHash< const QgsSymbolLayer *, QString >::const_iterator lineTypeIt = mLineStyles.constFind( symbolLayer );
1773  if ( lineTypeIt != mLineStyles.constEnd() )
1774  {
1775  lineStyleName = lineTypeIt.value();
1776  return lineStyleName;
1777  }
1778  else
1779  {
1780  return lineNameFromPenStyle( symbolLayer->dxfPenStyle() );
1781  }
1782 }
1783 
1785 {
1786  int idx = 0;
1787  int current_distance = std::numeric_limits<int>::max();
1788  for ( int i = 1; i < static_cast< int >( sizeof( sDxfColors ) / sizeof( *sDxfColors ) ); ++i )
1789  {
1790  int dist = color_distance( pixel, i );
1791  if ( dist < current_distance )
1792  {
1793  current_distance = dist;
1794  idx = i;
1795  if ( dist == 0 )
1796  break;
1797  }
1798  }
1799  return idx;
1800 }
1801 
1802 int QgsDxfExport::color_distance( QRgb p1, int index )
1803 {
1804  if ( index > 255 || index < 0 )
1805  {
1806  return 0;
1807  }
1808 
1809  double redDiff = qRed( p1 ) - sDxfColors[index][0];
1810  double greenDiff = qGreen( p1 ) - sDxfColors[index][1];
1811  double blueDiff = qBlue( p1 ) - sDxfColors[index][2];
1812 #if 0
1813  QgsDebugMsg( QStringLiteral( "color_distance( r:%1 g:%2 b:%3 <=> i:%4 r:%5 g:%6 b:%7 ) => %8" )
1814  .arg( qRed( p1 ) ).arg( qGreen( p1 ) ).arg( qBlue( p1 ) )
1815  .arg( index )
1816  .arg( mDxfColors[index][0] )
1817  .arg( mDxfColors[index][1] )
1818  .arg( mDxfColors[index][2] )
1819  .arg( redDiff * redDiff + greenDiff * greenDiff + blueDiff * blueDiff ) );
1820 #endif
1821  return redDiff * redDiff + greenDiff * greenDiff + blueDiff * blueDiff;
1822 }
1823 
1824 QRgb QgsDxfExport::createRgbEntry( qreal r, qreal g, qreal b )
1825 {
1826  return QColor::fromRgbF( r, g, b ).rgb();
1827 }
1828 
1829 QgsRenderContext QgsDxfExport::renderContext() const
1830 {
1831  return mRenderContext;
1832 }
1833 
1834 double QgsDxfExport::mapUnitScaleFactor( double scale, QgsUnitTypes::RenderUnit symbolUnits, QgsUnitTypes::DistanceUnit mapUnits, double mapUnitsPerPixel )
1835 {
1836  if ( symbolUnits == QgsUnitTypes::RenderMapUnits )
1837  {
1838  return 1.0;
1839  }
1840  else if ( symbolUnits == QgsUnitTypes::RenderMillimeters )
1841  {
1843  }
1844  else if ( symbolUnits == QgsUnitTypes::RenderPixels )
1845  {
1846  return mapUnitsPerPixel;
1847  }
1848  return 1.0;
1849 }
1850 
1851 void QgsDxfExport::clipValueToMapUnitScale( double &value, const QgsMapUnitScale &scale, double pixelToMMFactor ) const
1852 {
1853  if ( !scale.minSizeMMEnabled && !scale.maxSizeMMEnabled )
1854  {
1855  return;
1856  }
1857 
1858  double mapUnitsPerPixel = mMapSettings.mapToPixel().mapUnitsPerPixel();
1859 
1860  double minSizeMU = std::numeric_limits<double>::lowest();
1861  if ( scale.minSizeMMEnabled )
1862  {
1863  minSizeMU = scale.minSizeMM * pixelToMMFactor * mapUnitsPerPixel;
1864  }
1865  if ( !qgsDoubleNear( scale.minScale, 0.0 ) )
1866  {
1867  minSizeMU = std::max( minSizeMU, value );
1868  }
1869  value = std::max( value, minSizeMU );
1870 
1871  double maxSizeMU = std::numeric_limits<double>::max();
1872  if ( scale.maxSizeMMEnabled )
1873  {
1874  maxSizeMU = scale.maxSizeMM * pixelToMMFactor * mapUnitsPerPixel;
1875  }
1876  if ( !qgsDoubleNear( scale.maxScale, 0.0 ) )
1877  {
1878  maxSizeMU = std::min( maxSizeMU, value );
1879  }
1880  value = std::min( value, maxSizeMU );
1881 }
1882 
1883 QList< QPair< QgsSymbolLayer *, QgsSymbol * > > QgsDxfExport::symbolLayers( QgsRenderContext &context )
1884 {
1885  QList< QPair< QgsSymbolLayer *, QgsSymbol * > > symbolLayers;
1886 
1887  for ( DxfLayerJob *job : std::as_const( mJobs ) )
1888  {
1889  const QgsSymbolList symbols = job->renderer->symbols( context );
1890 
1891  for ( QgsSymbol *symbol : symbols )
1892  {
1893  int maxSymbolLayers = symbol->symbolLayerCount();
1894  if ( mSymbologyExport != SymbolLayerSymbology )
1895  {
1896  maxSymbolLayers = 1;
1897  }
1898  for ( int i = 0; i < maxSymbolLayers; ++i )
1899  {
1900  symbolLayers.append( qMakePair( symbol->symbolLayer( i ), symbol ) );
1901  }
1902  }
1903  }
1904 
1905  return symbolLayers;
1906 }
1907 
1908 void QgsDxfExport::writeDefaultLinetypes()
1909 {
1910  // continuous (Qt solid line)
1911  for ( const QString &ltype : { QStringLiteral( "ByLayer" ), QStringLiteral( "ByBlock" ), QStringLiteral( "CONTINUOUS" ) } )
1912  {
1913  writeGroup( 0, QStringLiteral( "LTYPE" ) );
1914  writeHandle();
1915  writeGroup( 100, QStringLiteral( "AcDbSymbolTableRecord" ) );
1916  writeGroup( 100, QStringLiteral( "AcDbLinetypeTableRecord" ) );
1917  writeGroup( 2, ltype );
1918  writeGroup( 70, 64 );
1919  writeGroup( 3, QStringLiteral( "Defaultstyle" ) );
1920  writeGroup( 72, 65 );
1921  writeGroup( 73, 0 );
1922  writeGroup( 40, 0.0 );
1923  }
1924 
1925  double das = dashSize();
1926  double dss = dashSeparatorSize();
1927  double dos = dotSize();
1928 
1929  QVector<qreal> dashVector( 2 );
1930  dashVector[0] = das;
1931  dashVector[1] = dss;
1932  writeLinetype( QStringLiteral( "DASH" ), dashVector, QgsUnitTypes::RenderMapUnits );
1933 
1934  QVector<qreal> dotVector( 2 );
1935  dotVector[0] = dos;
1936  dotVector[1] = dss;
1937  writeLinetype( QStringLiteral( "DOT" ), dotVector, QgsUnitTypes::RenderMapUnits );
1938 
1939  QVector<qreal> dashDotVector( 4 );
1940  dashDotVector[0] = das;
1941  dashDotVector[1] = dss;
1942  dashDotVector[2] = dos;
1943  dashDotVector[3] = dss;
1944  writeLinetype( QStringLiteral( "DASHDOT" ), dashDotVector, QgsUnitTypes::RenderMapUnits );
1945 
1946  QVector<qreal> dashDotDotVector( 6 );
1947  dashDotDotVector[0] = das;
1948  dashDotDotVector[1] = dss;
1949  dashDotDotVector[2] = dos;
1950  dashDotDotVector[3] = dss;
1951  dashDotDotVector[4] = dos;
1952  dashDotDotVector[5] = dss;
1953  writeLinetype( QStringLiteral( "DASHDOTDOT" ), dashDotDotVector, QgsUnitTypes::RenderMapUnits );
1954 }
1955 
1956 void QgsDxfExport::writeSymbolLayerLinetype( const QgsSymbolLayer *symbolLayer )
1957 {
1958  if ( !symbolLayer )
1959  {
1960  return;
1961  }
1962 
1964  QVector<qreal> customLinestyle = symbolLayer->dxfCustomDashPattern( unit );
1965  if ( !customLinestyle.isEmpty() )
1966  {
1967  QString name = QStringLiteral( "symbolLayer%1" ).arg( mSymbolLayerCounter++ );
1968  writeLinetype( name, customLinestyle, unit );
1969  mLineStyles.insert( symbolLayer, name );
1970  }
1971 }
1972 
1973 int QgsDxfExport::nLineTypes( const QList< QPair< QgsSymbolLayer *, QgsSymbol * > > &symbolLayers )
1974 {
1975  int nLineTypes = 0;
1976  for ( const auto &symbolLayer : symbolLayers )
1977  {
1978  const QgsSimpleLineSymbolLayer *simpleLine = dynamic_cast< const QgsSimpleLineSymbolLayer * >( symbolLayer.first );
1979  if ( simpleLine )
1980  {
1981  if ( simpleLine->useCustomDashPattern() )
1982  {
1983  ++nLineTypes;
1984  }
1985  }
1986  }
1987  return nLineTypes;
1988 }
1989 
1990 void QgsDxfExport::writeLinetype( const QString &styleName, const QVector<qreal> &pattern, QgsUnitTypes::RenderUnit u )
1991 {
1992  double length = 0;
1993  for ( qreal size : pattern )
1994  {
1995  length += ( size * mapUnitScaleFactor( mSymbologyScale, u, mMapUnits, mMapSettings.mapToPixel().mapUnitsPerPixel() ) );
1996  }
1997 
1998  writeGroup( 0, QStringLiteral( "LTYPE" ) );
1999  writeHandle();
2000  // 330 5
2001  writeGroup( 100, QStringLiteral( "AcDbSymbolTableRecord" ) );
2002  writeGroup( 100, QStringLiteral( "AcDbLinetypeTableRecord" ) );
2003  writeGroup( 2, styleName );
2004  writeGroup( 70, 64 ); // 0?
2005  writeGroup( 3, QString() );
2006  writeGroup( 72, 65 );
2007  writeGroup( 73, pattern.size() );
2008  writeGroup( 40, length );
2009 
2010  bool isGap = false;
2011  for ( qreal size : pattern )
2012  {
2013  // map units or mm?
2014  double segmentLength = ( isGap ? -size : size );
2015  segmentLength *= mapUnitScaleFactor( mSymbologyScale, u, mMapUnits, mMapSettings.mapToPixel().mapUnitsPerPixel() );
2016  writeGroup( 49, segmentLength );
2017  writeGroup( 74, 0 );
2018  isGap = !isGap;
2019  }
2020 }
2021 
2022 void QgsDxfExport::addGeometryGeneratorSymbolLayer( QgsSymbolRenderContext &ctx, const QgsCoordinateTransform &ct, const QString &layer, QgsSymbolLayer *symbolLayer, bool allSymbolLayers )
2023 {
2024  QgsGeometryGeneratorSymbolLayer *gg = dynamic_cast<QgsGeometryGeneratorSymbolLayer *>( symbolLayer );
2025  if ( !gg )
2026  {
2027  return;
2028  }
2029 
2030  const QgsFeature *fet = ctx.feature();
2031  if ( !fet )
2032  {
2033  return;
2034  }
2035 
2036  QgsFeature f = *fet;
2037 
2038  QgsExpressionContext &expressionContext = ctx.renderContext().expressionContext();
2039  QgsExpression geomExpr( gg->geometryExpression() );
2040  geomExpr.prepare( &expressionContext );
2041  QgsGeometry geom = geomExpr.evaluate( &expressionContext ).value<QgsGeometry>();
2042  f.setGeometry( geom );
2043 
2044  QgsSymbol *symbol = gg->subSymbol();
2045  if ( symbol && symbol->symbolLayerCount() > 0 )
2046  {
2047  QgsExpressionContextScope *symbolExpressionContextScope = symbol->symbolRenderContext()->expressionContextScope();
2048  symbolExpressionContextScope->setFeature( f );
2049 
2050  ctx.setFeature( &f );
2051 
2052  int nSymbolLayers = allSymbolLayers ? symbol->symbolLayerCount() : 1;
2053  for ( int i = 0; i < nSymbolLayers; ++i )
2054  {
2055  addFeature( ctx, ct, layer, symbol->symbolLayer( i ), symbol );
2056  }
2057 
2058  ctx.setFeature( fet );
2059  }
2060 }
2061 
2062 bool QgsDxfExport::hasDataDefinedProperties( const QgsSymbolLayer *sl, const QgsSymbol *symbol )
2063 {
2064  if ( !sl || !symbol )
2065  {
2066  return false;
2067  }
2068 
2070  {
2071  return true;
2072  }
2073 
2074  return sl->hasDataDefinedProperties();
2075 }
2076 
2077 double QgsDxfExport::dashSize() const
2078 {
2079  double size = mSymbologyScale * 0.002;
2080  return sizeToMapUnits( size );
2081 }
2082 
2083 double QgsDxfExport::dotSize() const
2084 {
2085  double size = mSymbologyScale * 0.0006;
2086  return sizeToMapUnits( size );
2087 }
2088 
2089 double QgsDxfExport::dashSeparatorSize() const
2090 {
2091  double size = mSymbologyScale * 0.0006;
2092  return sizeToMapUnits( size );
2093 }
2094 
2095 double QgsDxfExport::sizeToMapUnits( double s ) const
2096 {
2097  double size = s * QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::DistanceMeters, mMapUnits );
2098  return size;
2099 }
2100 
2101 QString QgsDxfExport::lineNameFromPenStyle( Qt::PenStyle style )
2102 {
2103  switch ( style )
2104  {
2105  case Qt::DashLine:
2106  return QStringLiteral( "DASH" );
2107  case Qt::DotLine:
2108  return QStringLiteral( "DOT" );
2109  case Qt::DashDotLine:
2110  return QStringLiteral( "DASHDOT" );
2111  case Qt::DashDotDotLine:
2112  return QStringLiteral( "DASHDOTDOT" );
2113  case Qt::SolidLine:
2114  default:
2115  return QStringLiteral( "CONTINUOUS" );
2116  }
2117 }
2118 
2119 QString QgsDxfExport::dxfLayerName( const QString &name )
2120 {
2121  if ( name.isEmpty() )
2122  return QStringLiteral( "0" );
2123 
2124  // dxf layers can be max 255 characters long
2125  QString layerName = name.left( 255 );
2126 
2127  // replaced restricted characters with underscore
2128  // < > / \ " : ; ? * | = '
2129  // See http://docs.autodesk.com/ACD/2010/ENU/AutoCAD%202010%20User%20Documentation/index.html?url=WS1a9193826455f5ffa23ce210c4a30acaf-7345.htm,topicNumber=d0e41665
2130  layerName.replace( '<', '_' );
2131  layerName.replace( '>', '_' );
2132  layerName.replace( '/', '_' );
2133  layerName.replace( '\\', '_' );
2134  layerName.replace( '\"', '_' );
2135  layerName.replace( ':', '_' );
2136  layerName.replace( ';', '_' );
2137  layerName.replace( '?', '_' );
2138  layerName.replace( '*', '_' );
2139  layerName.replace( '|', '_' );
2140  layerName.replace( '=', '_' );
2141  layerName.replace( '\'', '_' );
2142 
2143  // also remove newline characters (#15067)
2144  layerName.replace( QLatin1String( "\r\n" ), QLatin1String( "_" ) );
2145  layerName.replace( '\r', '_' );
2146  layerName.replace( '\n', '_' );
2147 
2148  return layerName.trimmed();
2149 }
2150 
2151 bool QgsDxfExport::layerIsScaleBasedVisible( const QgsMapLayer *layer ) const
2152 {
2153  if ( !layer )
2154  return false;
2155 
2156  if ( mSymbologyExport == QgsDxfExport::NoSymbology )
2157  return true;
2158 
2159  return layer->isInScaleRange( mSymbologyScale );
2160 }
2161 
2162 QString QgsDxfExport::layerName( const QString &id, const QgsFeature &f ) const
2163 {
2164  // TODO: make this thread safe
2165  const QList< QgsMapLayer * > layers = mMapSettings.layers();
2166  for ( QgsMapLayer *ml : layers )
2167  {
2168  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml );
2169  if ( vl && vl->id() == id )
2170  {
2171  int attrIdx = mLayerNameAttribute.value( vl->id(), -1 );
2172  return dxfLayerName( attrIdx < 0 ? layerName( vl ) : f.attribute( attrIdx ).toString() );
2173  }
2174  }
2175 
2176  return QStringLiteral( "0" );
2177 }
2178 
2179 QString QgsDxfExport::dxfEncoding( const QString &name )
2180 {
2181  const QList< QByteArray > codecs = QTextCodec::availableCodecs();
2182  for ( const QByteArray &codec : codecs )
2183  {
2184  if ( name != codec )
2185  continue;
2186 
2187  int i;
2188  for ( i = 0; i < static_cast< int >( sizeof( DXF_ENCODINGS ) / sizeof( *DXF_ENCODINGS ) ) && name != DXF_ENCODINGS[i][1]; ++i )
2189  ;
2190 
2191  if ( i == static_cast< int >( sizeof( DXF_ENCODINGS ) / sizeof( *DXF_ENCODINGS ) ) )
2192  continue;
2193 
2194  return DXF_ENCODINGS[i][0];
2195  }
2196 
2197  return QString();
2198 }
2199 
2201 {
2202  QStringList encodings;
2203  const QList< QByteArray > codecs = QTextCodec::availableCodecs();
2204  encodings.reserve( codecs.size() );
2205  for ( const QByteArray &codec : codecs )
2206  {
2207  int i;
2208  for ( i = 0; i < static_cast< int >( sizeof( DXF_ENCODINGS ) / sizeof( *DXF_ENCODINGS ) ) && strcmp( codec.data(), DXF_ENCODINGS[i][1] ) != 0; ++i )
2209  ;
2210 
2211  if ( i < static_cast< int >( sizeof( DXF_ENCODINGS ) / sizeof( *DXF_ENCODINGS ) ) )
2212  encodings << codec.data();
2213  }
2214  return encodings;
2215 }
2216 
2218 {
2219  Q_ASSERT( vl );
2220  return mLayerTitleAsName && !vl->title().isEmpty() ? vl->title() : vl->name();
2221 }
2222 
2223 void QgsDxfExport::drawLabel( const QString &layerId, QgsRenderContext &context, pal::LabelPosition *label, const QgsPalLayerSettings &settings )
2224 {
2225  Q_UNUSED( context )
2226 
2227  if ( !settings.drawLabels )
2228  return;
2229 
2230  QgsTextLabelFeature *lf = dynamic_cast<QgsTextLabelFeature *>( label->getFeaturePart()->feature() );
2231 
2232  // Copy to temp, editable layer settings
2233  // these settings will be changed by any data defined values, then used for rendering label components
2234  // settings may be adjusted during rendering of components
2235  QgsPalLayerSettings tmpLyr( settings );
2236 
2237  // apply any previously applied data defined settings for the label
2238  const QMap< QgsPalLayerSettings::Property, QVariant > &ddValues = lf->dataDefinedValues();
2239 
2240  //font
2241  QFont dFont = lf->definedFont();
2242  QgsDebugMsgLevel( QStringLiteral( "PAL font tmpLyr: %1, Style: %2" ).arg( tmpLyr.format().font().toString(), tmpLyr.format().font().styleName() ), 4 );
2243  QgsDebugMsgLevel( QStringLiteral( "PAL font definedFont: %1, Style: %2" ).arg( dFont.toString(), dFont.styleName() ), 4 );
2244 
2245  QgsTextFormat format = tmpLyr.format();
2246  format.setFont( dFont );
2247  tmpLyr.setFormat( format );
2248 
2250  {
2251  //calculate font alignment based on label quadrant
2252  switch ( label->getQuadrant() )
2253  {
2258  break;
2263  break;
2268  break;
2269  }
2270  }
2271 
2272  // update tmpLyr with any data defined text style values
2273  QgsPalLabeling::dataDefinedTextStyle( tmpLyr, ddValues );
2274 
2275  // update tmpLyr with any data defined text buffer values
2276  QgsPalLabeling::dataDefinedTextBuffer( tmpLyr, ddValues );
2277 
2278  // update tmpLyr with any data defined text formatting values
2279  QgsPalLabeling::dataDefinedTextFormatting( tmpLyr, ddValues );
2280 
2281  // add to the results
2282  QString txt = label->getFeaturePart()->feature()->labelText();
2283 
2284  QgsFeatureId fid = label->getFeaturePart()->featureId();
2285  QString dxfLayer = mDxfLayerNames[layerId][fid];
2286 
2287  QString wrapchr = tmpLyr.wrapChar.isEmpty() ? QStringLiteral( "\n" ) : tmpLyr.wrapChar;
2288 
2289  //add the direction symbol if needed
2290  if ( !txt.isEmpty() && tmpLyr.placement == QgsPalLayerSettings::Line && tmpLyr.lineSettings().addDirectionSymbol() )
2291  {
2292  bool prependSymb = false;
2293  QString symb = tmpLyr.lineSettings().rightDirectionSymbol();
2294 
2295  if ( label->getReversed() )
2296  {
2297  prependSymb = true;
2298  symb = tmpLyr.lineSettings().leftDirectionSymbol();
2299  }
2300 
2301  if ( tmpLyr.lineSettings().reverseDirectionSymbol() )
2302  {
2303  if ( symb == tmpLyr.lineSettings().rightDirectionSymbol() )
2304  {
2305  prependSymb = true;
2306  symb = tmpLyr.lineSettings().leftDirectionSymbol();
2307  }
2308  else
2309  {
2310  prependSymb = false;
2311  symb = tmpLyr.lineSettings().rightDirectionSymbol();
2312  }
2313  }
2314 
2315  switch ( tmpLyr.lineSettings().directionSymbolPlacement() )
2316  {
2318  prependSymb = true;
2319  symb = symb + wrapchr;
2320  break;
2321 
2323  prependSymb = false;
2324  symb = wrapchr + symb;
2325  break;
2326 
2328  break;
2329  }
2330 
2331  if ( prependSymb )
2332  {
2333  txt.prepend( symb );
2334  }
2335  else
2336  {
2337  txt.append( symb );
2338  }
2339  }
2340 
2341  if ( mFlags & FlagNoMText )
2342  {
2343  txt.replace( QChar( QChar::LineFeed ), ' ' );
2344  txt.replace( QChar( QChar::CarriageReturn ), ' ' );
2345  writeText( dxfLayer, txt, label, tmpLyr, context.expressionContext() );
2346  }
2347  else
2348  {
2349  txt.replace( QString( QChar( QChar::CarriageReturn ) ) + QString( QChar( QChar::LineFeed ) ), QStringLiteral( "\\P" ) );
2350  txt.replace( QChar( QChar::CarriageReturn ), QStringLiteral( "\\P" ) );
2351  txt = txt.replace( wrapchr, QLatin1String( "\\P" ) );
2352  txt.replace( QLatin1String( " " ), QLatin1String( "\\~" ) );
2353 
2354  if ( tmpLyr.format().font().underline() )
2355  {
2356  txt.prepend( "\\L" ).append( "\\l" );
2357  }
2358 
2359  if ( tmpLyr.format().font().overline() )
2360  {
2361  txt.prepend( "\\O" ).append( "\\o" );
2362  }
2363 
2364  if ( tmpLyr.format().font().strikeOut() )
2365  {
2366  txt.prepend( "\\K" ).append( "\\k" );
2367  }
2368 
2369  txt.prepend( QStringLiteral( "\\f%1|i%2|b%3;\\H%4;" )
2370  .arg( tmpLyr.format().font().family() )
2371  .arg( tmpLyr.format().font().italic() ? 1 : 0 )
2372  .arg( tmpLyr.format().font().bold() ? 1 : 0 )
2373  .arg( label->getHeight() / ( 1 + txt.count( QStringLiteral( "\\P" ) ) ) * 0.75 ) );
2374  writeMText( dxfLayer, txt, QgsPoint( label->getX(), label->getY() ), label->getWidth(), label->getAlpha() * 180.0 / M_PI, tmpLyr.format().color() );
2375  }
2376 }
2377 
2378 
2379 void QgsDxfExport::registerDxfLayer( const QString &layerId, QgsFeatureId fid, const QString &layerName )
2380 {
2381  if ( !mDxfLayerNames.contains( layerId ) )
2382  mDxfLayerNames[ layerId ] = QMap<QgsFeatureId, QString>();
2383 
2384  mDxfLayerNames[layerId][fid] = layerName;
2385 }
2386 
2388 {
2389  mCrs = crs;
2390  mMapUnits = crs.mapUnits();
2391 }
2392 
2394 {
2395  return mCrs;
2396 }
2397 
2399 {
2400  QString splitLayerFieldName;
2401  const QgsFields fields = mLayer->fields();
2402  if ( mLayerOutputAttributeIndex >= 0 && mLayerOutputAttributeIndex < fields.size() )
2403  {
2404  splitLayerFieldName = fields.at( mLayerOutputAttributeIndex ).name();
2405  }
2406 
2407  return splitLayerFieldName;
2408 }
@ 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.
@ ReverseTransform
Transform from destination to source CRS.
bool isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...
Curve polygon geometry type.
const QgsCurve * interiorRing(int i) const SIP_HOLDGIL
Retrieves an interior ring from the curve polygon.
const QgsCurve * exteriorRing() const SIP_HOLDGIL
Returns the curve polygon's exterior ring.
int numInteriorRings() const SIP_HOLDGIL
Returns the number of interior rings contained with the curve polygon.
Abstract base class for curved geometry type.
Definition: qgscurve.h:36
QgsCoordinateSequence coordinateSequence() const override
Retrieves the sequence of geometries, rings and nodes.
Definition: qgscurve.cpp:76
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:52
void writeFilledCircle(const QString &layer, const QColor &color, const QgsPoint &pt, double radius)
Write filled circle (as hatch)
ExportResult
The result of an export as dxf operation.
Definition: qgsdxfexport.h:122
@ DeviceNotWritableError
Device not writable error.
@ Success
Successful export.
@ EmptyExtentError
Empty extent, no extent given and no extent could be derived from layers.
@ InvalidDeviceError
Invalid device error.
@ SymbolLayerSymbology
Exports one feature per symbol layer (considering symbol levels)
Definition: qgsdxfexport.h:106
@ NoSymbology
Export only data.
Definition: qgsdxfexport.h:104
void writeCircle(const QString &layer, const QColor &color, const QgsPoint &pt, double radius, const QString &lineStyleName, double width)
Write circle (as polyline)
~QgsDxfExport() override
ExportResult writeToFile(QIODevice *d, const QString &codec)
Export to a dxf file in the given encoding.
void writeLine(const QgsPoint &pt1, const QgsPoint &pt2, const QString &layer, const QString &lineStyleName, const QColor &color, double width=-1)
Write line (as a polyline)
static double mapUnitScaleFactor(double scale, QgsUnitTypes::RenderUnit symbolUnits, QgsUnitTypes::DistanceUnit mapUnits, double mapUnitsPerPixel=1.0)
Returns scale factor for conversion to map units.
void writeGroup(int code, int i)
Write a tuple of group code and integer value.
QString layerName(const QString &id, const QgsFeature &f) const
Gets layer name for feature.
void setFlags(QgsDxfExport::Flags flags)
Sets the export flags.
@ FlagNoMText
Export text as TEXT elements. If not set, text will be exported as MTEXT elements.
Definition: qgsdxfexport.h:112
void writeInt(int i)
Write an integer value.
void writeMText(const QString &layer, const QString &text, const QgsPoint &pt, double width, double angle, const QColor &color)
Write mtext (MTEXT)
QgsDxfExport()
Constructor for QgsDxfExport.
QgsUnitTypes::DistanceUnit mapUnits() const
Retrieve map units.
int writeHandle(int code=5, int handle=0)
Write a tuple of group code and a handle.
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination CRS, or an invalid CRS if no reprojection will be done.
HAlign
Horizontal alignments.
Definition: qgsdxfexport.h:143
@ HCenter
Centered (1)
@ Undefined
Undefined.
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
Set destination CRS.
void addLayers(const QList< QgsDxfExport::DxfLayer > &layers)
Add layers to export.
static QString dxfLayerName(const QString &name)
Returns cleaned layer name for use in DXF.
void writeDouble(double d)
Write a floating point value.
void writeText(const QString &layer, const QString &text, const QgsPoint &pt, double size, double angle, const QColor &color, QgsDxfExport::HAlign hali=QgsDxfExport::HAlign::Undefined, QgsDxfExport::VAlign vali=QgsDxfExport::VAlign::Undefined)
Write text (TEXT)
void writeString(const QString &s)
Write a string value.
void writePolygon(const QgsRingSequence &polygon, const QString &layer, const QString &hatchPattern, const QColor &color)
Draw dxf filled polygon (HATCH)
void drawLabel(const QString &layerId, QgsRenderContext &context, pal::LabelPosition *label, const QgsPalLayerSettings &settings) override
Add a label to the dxf output.
static QString dxfEncoding(const QString &name)
Returns DXF encoding for Qt encoding.
static int closestColorMatch(QRgb color)
Gets DXF palette index of nearest entry for given color.
void writePoint(const QString &layer, const QColor &color, const QgsPoint &pt)
Write point.
Q_DECL_DEPRECATED void registerDxfLayer(const QString &layerId, QgsFeatureId fid, const QString &layer)
Register name of layer for feature.
QgsDxfExport::Flags flags() const
Returns the export flags.
VAlign
Vertical alignments.
Definition: qgsdxfexport.h:133
@ Undefined
Undefined.
void clipValueToMapUnitScale(double &value, const QgsMapUnitScale &scale, double pixelToMMFactor) const
Clips value to scale minimum/maximum.
void writePolyline(const QgsPointSequence &line, const QString &layer, const QString &lineStyleName, const QColor &color, double width=-1)
Draw dxf primitives (LWPOLYLINE)
static QStringList encodings()
Returns list of available DXF encodings.
void setMapSettings(const QgsMapSettings &settings)
Set map settings and assign layer name attributes.
void writeGroupCode(int code)
Write a group code.
Single scope for storing variables and functions for use within a QgsExpressionContext.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the scope.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QList< QgsExpressionContextScope * > scopes()
Returns a list of scopes contained within the stack.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
Class for parsing and evaluation of expressions (formerly called "search strings").
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
@ SymbolLevels
Rendering with symbol levels (i.e. implements symbols(), symbolForFeature())
Definition: qgsrenderer.h:262
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:205
QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
Definition: qgsfeature.cpp:302
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Definition: qgsfeature.cpp:145
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:124
Does vector analysis using the geos library and handles import, export, exception handling*.
Definition: qgsgeos.h:104
The QgsLabelFeature class describes a feature that should be used within the labeling engine.
QgsPointXY anchorPosition() const
In case of quadrand or aligned positioning, this is set to the anchor point.
QString labelText() const
Text of the label.
bool reverseDirectionSymbol() const
Returns true if direction symbols should be reversed.
DirectionSymbolPlacement directionSymbolPlacement() const
Returns the placement for direction symbols.
QString leftDirectionSymbol() const
Returns the string to use for left direction arrows.
@ SymbolLeftRight
Place direction symbols on left/right of label.
@ SymbolAbove
Place direction symbols on above label.
@ SymbolBelow
Place direction symbols on below label.
QString rightDirectionSymbol() const
Returns the string to use for right direction arrows.
bool addDirectionSymbol() const
Returns true if '<' or '>' (or custom strings set via leftDirectionSymbol and rightDirectionSymbol) w...
virtual void run(QgsRenderContext &context)=0
Runs the labeling job.
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:44
int numPoints() const override SIP_HOLDGIL
Returns the number of points in the curve.
QgsPoint pointN(int i) const
Returns the specified point from inside the line string.
Base class for all map layer types.
Definition: qgsmaplayer.h:70
QString name
Definition: qgsmaplayer.h:73
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:287
The QgsMapSettings class contains configuration for rendering of the map.
QgsPointXY layerToMapCoordinates(const QgsMapLayer *layer, QgsPointXY point) const
transform point coordinates from layer's CRS to output CRS
void setLayers(const QList< QgsMapLayer * > &layers)
Sets the list of layers to render in the map.
void setOutputDpi(double dpi)
Sets the dpi (dots per inch) used for conversion between real world units (e.g.
void setExtent(const QgsRectangle &rect, bool magnified=true)
Sets the coordinates of the rectangle which should be rendered.
const QgsMapToPixel & mapToPixel() const
QList< QgsMapLayer * > layers() const
Returns the list of layers which will be rendered in the map.
QMap< QString, QString > layerStyleOverrides() const
Returns the map of map layer style overrides (key: layer ID, value: style name) where a different sty...
void setOutputSize(QSize size)
Sets the size of the resulting map image, in pixels.
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system for the map render.
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
Sets the destination crs (coordinate reference system) for the map render.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context, which stores various information regarding which datum tran...
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:39
double mapUnitsPerPixel() const
Returns current map units per pixel.
Struct for storing maximum and minimum scales for measurements in map units.
bool minSizeMMEnabled
Whether the minimum size in mm should be respected.
double maxScale
The maximum scale, or 0.0 if unset.
double minScale
The minimum scale, or 0.0 if unset.
double maxSizeMM
The maximum size in millimeters, or 0.0 if unset.
bool maxSizeMMEnabled
Whether the maximum size in mm should be respected.
double minSizeMM
The minimum size in millimeters, or 0.0 if unset.
Abstract base class for marker symbol layers.
QgsUnitTypes::RenderUnit sizeUnit() const
Returns the units for the symbol's size.
double size() const
Returns the symbol size.
Contains settings for how a map layer will be labeled.
void setFormat(const QgsTextFormat &format)
Sets the label text formatting settings, e.g., font settings, buffer settings, etc.
QString wrapChar
Wrapping character string.
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the label's property collection, used for data defined overrides.
bool drawLabels
Whether to draw labels for this layer.
QuadrantPosition quadOffset
Sets the quadrant in which to offset labels from feature.
@ Line
Arranges candidates parallel to a generalised line representing the feature or parallel to a polygon'...
MultiLineAlign multilineAlign
Horizontal alignment of multi-line labels.
@ Hali
Horizontal alignment for data defined label position (Left, Center, Right)
@ Vali
Vertical alignment for data defined label position (Bottom, Base, Half, Cap, Top)
const QgsLabelLineSettings & lineSettings() const
Returns the label line settings, which contain settings related to how the label engine places and fo...
const QgsTextFormat & format() const
Returns the label text formatting settings, e.g., font settings, buffer settings, etc.
double y
Definition: qgspointxy.h: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:467
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)
Assign new labeling engine.
void setRendererScale(double scale)
Sets the renderer map scale.
A simple line symbol layer, which renders lines using a line in a variety of styles (e....
bool useCustomDashPattern() const
Returns true if the line uses a custom dash pattern.
virtual double dxfOffset(const QgsDxfExport &e, QgsSymbolRenderContext &context) const
Gets offset.
virtual QColor dxfBrushColor(QgsSymbolRenderContext &context) const
Gets brush/fill color.
virtual QVector< qreal > dxfCustomDashPattern(QgsUnitTypes::RenderUnit &unit) const
Gets dash pattern.
virtual Qt::PenStyle dxfPenStyle() const
Gets pen style.
virtual QColor dxfColor(QgsSymbolRenderContext &context) const
Gets color.
virtual QString layerType() const =0
Returns a string that represents this layer type.
virtual double dxfWidth(const QgsDxfExport &e, QgsSymbolRenderContext &context) const
Gets line width.
virtual double dxfAngle(QgsSymbolRenderContext &context) const
Gets angle.
virtual bool writeDxf(QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift=QPointF(0.0, 0.0)) const
write as DXF
virtual Qt::BrushStyle dxfBrushStyle() const
Gets brush/fill style.
virtual bool hasDataDefinedProperties() const
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties.
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
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:1433
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:702
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:1080
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:550
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:1079
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:598
QVector< QgsRingSequence > QgsCoordinateSequence
QVector< QgsPointSequence > QgsRingSequence
QVector< QgsPoint > QgsPointSequence
#define DXF_HANDMAX
Definition: qgsdxfexport.h:45
#define DXF_HANDPLOTSTYLE
Definition: qgsdxfexport.h:46
#define DXF_TRAILER
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
Definition: qgsfeatureid.h:28
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QList< QgsSymbolLevel > QgsSymbolLevelOrder
Definition: qgsrenderer.h:87
QList< QgsSymbolLevelItem > QgsSymbolLevel
Definition: qgsrenderer.h:83
QList< QgsSymbol * > QgsSymbolList
Definition: qgsrenderer.h:43
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:73
QString splitLayerAttribute() const
If the split layer attribute is set, the vector layer will be split into several dxf layers,...