QGIS API Documentation  3.21.0-Master (5b68dc587e)
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, Qgis::TransformDirection::Reverse ) );
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, Qgis::JoinStyle::Miter, 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  if ( curve )
1629  {
1630  writePolyline( *curve, layer, lineStyleName, penColor, width );
1631  break;
1632  }
1633 
1634  // Offset with miter might have turned the simple to a multiline string
1635  offset = 0.0;
1636  }
1637  FALLTHROUGH
1638 
1641  {
1642  if ( !qgsDoubleNear( offset, 0.0 ) )
1643  {
1644  QgsGeos geos( sourceGeom );
1645  tempGeom.reset( geos.offsetCurve( offset, 0, Qgis::JoinStyle::Miter, 2.0 ) ); //#spellok
1646  if ( tempGeom )
1647  sourceGeom = tempGeom.get();
1648  else
1649  sourceGeom = geom.constGet();
1650  }
1651 
1652  const QgsGeometryCollection *gc = dynamic_cast<const QgsGeometryCollection *>( sourceGeom );
1653  if ( gc )
1654  {
1655  for ( int i = 0; i < gc->numGeometries(); i++ )
1656  {
1657  const QgsCurve *curve = dynamic_cast<const QgsCurve *>( gc->geometryN( i ) );
1658  Q_ASSERT( curve );
1659  writePolyline( *curve, layer, lineStyleName, penColor, width );
1660  }
1661  }
1662  else
1663  Q_ASSERT( gc );
1664 
1665  break;
1666  }
1667 
1669  case QgsWkbTypes::Polygon:
1670  {
1671  if ( !qgsDoubleNear( offset, 0.0 ) )
1672  {
1673  QgsGeos geos( sourceGeom );
1674  tempGeom.reset( geos.buffer( offset, 0, Qgis::EndCapStyle::Flat, Qgis::JoinStyle::Miter, 2.0 ) ); //#spellok
1675  if ( tempGeom )
1676  sourceGeom = tempGeom.get();
1677  else
1678  sourceGeom = geom.constGet();
1679  }
1680 
1681  const QgsCurvePolygon *polygon = dynamic_cast<const QgsCurvePolygon *>( sourceGeom );
1682  Q_ASSERT( polygon );
1683 
1684  writePolyline( *polygon->exteriorRing(), layer, lineStyleName, penColor, width );
1685  for ( int i = 0; i < polygon->numInteriorRings(); i++ )
1686  writePolyline( *polygon->interiorRing( i ), layer, lineStyleName, penColor, width );
1687 
1688  break;
1689  }
1690 
1693  {
1694  if ( !qgsDoubleNear( offset, 0.0 ) )
1695  {
1696  QgsGeos geos( sourceGeom );
1697  tempGeom.reset( geos.buffer( offset, 0, Qgis::EndCapStyle::Flat, Qgis::JoinStyle::Miter, 2.0 ) ); //#spellok
1698  if ( tempGeom )
1699  sourceGeom = tempGeom.get();
1700  else
1701  sourceGeom = geom.constGet();
1702  }
1703 
1704  const QgsGeometryCollection *gc = dynamic_cast<const QgsGeometryCollection *>( sourceGeom );
1705  Q_ASSERT( gc );
1706 
1707  for ( int i = 0; i < gc->numGeometries(); i++ )
1708  {
1709  const QgsCurvePolygon *polygon = dynamic_cast<const QgsCurvePolygon *>( gc->geometryN( i ) );
1710  Q_ASSERT( polygon );
1711 
1712  writePolyline( *polygon->exteriorRing(), layer, lineStyleName, penColor, width );
1713  for ( int j = 0; j < polygon->numInteriorRings(); j++ )
1714  writePolyline( *polygon->interiorRing( j ), layer, lineStyleName, penColor, width );
1715  }
1716 
1717  break;
1718  }
1719 
1720  default:
1721  break;
1722  }
1723 
1724  }
1725 
1726  if ( brushStyle != Qt::NoBrush )
1727  {
1728  const QgsAbstractGeometry *sourceGeom = geom.constGet();
1729  std::unique_ptr< QgsAbstractGeometry > tempGeom;
1730 
1731  switch ( QgsWkbTypes::flatType( geometryType ) )
1732  {
1734  case QgsWkbTypes::Polygon:
1735  {
1736  const QgsCurvePolygon *polygon = dynamic_cast<const QgsCurvePolygon *>( sourceGeom );
1737  Q_ASSERT( polygon );
1738  writePolygon( *polygon, layer, QStringLiteral( "SOLID" ), brushColor );
1739  break;
1740  }
1741 
1744  {
1745  const QgsGeometryCollection *gc = dynamic_cast<const QgsGeometryCollection *>( sourceGeom );
1746  Q_ASSERT( gc );
1747 
1748  for ( int i = 0; i < gc->numGeometries(); i++ )
1749  {
1750  const QgsCurvePolygon *polygon = dynamic_cast<const QgsCurvePolygon *>( gc->geometryN( i ) );
1751  Q_ASSERT( polygon );
1752  writePolygon( *polygon, layer, QStringLiteral( "SOLID" ), brushColor );
1753  }
1754  break;
1755  }
1756 
1757  default:
1758  break;
1759 
1760  }
1761  }
1762 }
1763 
1764 QColor QgsDxfExport::colorFromSymbolLayer( const QgsSymbolLayer *symbolLayer, QgsSymbolRenderContext &ctx )
1765 {
1766  if ( !symbolLayer )
1767  return QColor();
1768 
1769  return symbolLayer->dxfColor( ctx );
1770 }
1771 
1772 QString QgsDxfExport::lineStyleFromSymbolLayer( const QgsSymbolLayer *symbolLayer )
1773 {
1774  QString lineStyleName = QStringLiteral( "CONTINUOUS" );
1775  if ( !symbolLayer )
1776  {
1777  return lineStyleName;
1778  }
1779 
1780  QHash< const QgsSymbolLayer *, QString >::const_iterator lineTypeIt = mLineStyles.constFind( symbolLayer );
1781  if ( lineTypeIt != mLineStyles.constEnd() )
1782  {
1783  lineStyleName = lineTypeIt.value();
1784  return lineStyleName;
1785  }
1786  else
1787  {
1788  return lineNameFromPenStyle( symbolLayer->dxfPenStyle() );
1789  }
1790 }
1791 
1793 {
1794  int idx = 0;
1795  int current_distance = std::numeric_limits<int>::max();
1796  for ( int i = 1; i < static_cast< int >( sizeof( sDxfColors ) / sizeof( *sDxfColors ) ); ++i )
1797  {
1798  int dist = color_distance( pixel, i );
1799  if ( dist < current_distance )
1800  {
1801  current_distance = dist;
1802  idx = i;
1803  if ( dist == 0 )
1804  break;
1805  }
1806  }
1807  return idx;
1808 }
1809 
1810 int QgsDxfExport::color_distance( QRgb p1, int index )
1811 {
1812  if ( index > 255 || index < 0 )
1813  {
1814  return 0;
1815  }
1816 
1817  double redDiff = qRed( p1 ) - sDxfColors[index][0];
1818  double greenDiff = qGreen( p1 ) - sDxfColors[index][1];
1819  double blueDiff = qBlue( p1 ) - sDxfColors[index][2];
1820 #if 0
1821  QgsDebugMsg( QStringLiteral( "color_distance( r:%1 g:%2 b:%3 <=> i:%4 r:%5 g:%6 b:%7 ) => %8" )
1822  .arg( qRed( p1 ) ).arg( qGreen( p1 ) ).arg( qBlue( p1 ) )
1823  .arg( index )
1824  .arg( mDxfColors[index][0] )
1825  .arg( mDxfColors[index][1] )
1826  .arg( mDxfColors[index][2] )
1827  .arg( redDiff * redDiff + greenDiff * greenDiff + blueDiff * blueDiff ) );
1828 #endif
1829  return redDiff * redDiff + greenDiff * greenDiff + blueDiff * blueDiff;
1830 }
1831 
1832 QRgb QgsDxfExport::createRgbEntry( qreal r, qreal g, qreal b )
1833 {
1834  return QColor::fromRgbF( r, g, b ).rgb();
1835 }
1836 
1837 QgsRenderContext QgsDxfExport::renderContext() const
1838 {
1839  return mRenderContext;
1840 }
1841 
1842 double QgsDxfExport::mapUnitScaleFactor( double scale, QgsUnitTypes::RenderUnit symbolUnits, QgsUnitTypes::DistanceUnit mapUnits, double mapUnitsPerPixel )
1843 {
1844  if ( symbolUnits == QgsUnitTypes::RenderMapUnits )
1845  {
1846  return 1.0;
1847  }
1848  else if ( symbolUnits == QgsUnitTypes::RenderMillimeters )
1849  {
1851  }
1852  else if ( symbolUnits == QgsUnitTypes::RenderPixels )
1853  {
1854  return mapUnitsPerPixel;
1855  }
1856  return 1.0;
1857 }
1858 
1859 void QgsDxfExport::clipValueToMapUnitScale( double &value, const QgsMapUnitScale &scale, double pixelToMMFactor ) const
1860 {
1861  if ( !scale.minSizeMMEnabled && !scale.maxSizeMMEnabled )
1862  {
1863  return;
1864  }
1865 
1866  double mapUnitsPerPixel = mMapSettings.mapToPixel().mapUnitsPerPixel();
1867 
1868  double minSizeMU = std::numeric_limits<double>::lowest();
1869  if ( scale.minSizeMMEnabled )
1870  {
1871  minSizeMU = scale.minSizeMM * pixelToMMFactor * mapUnitsPerPixel;
1872  }
1873  if ( !qgsDoubleNear( scale.minScale, 0.0 ) )
1874  {
1875  minSizeMU = std::max( minSizeMU, value );
1876  }
1877  value = std::max( value, minSizeMU );
1878 
1879  double maxSizeMU = std::numeric_limits<double>::max();
1880  if ( scale.maxSizeMMEnabled )
1881  {
1882  maxSizeMU = scale.maxSizeMM * pixelToMMFactor * mapUnitsPerPixel;
1883  }
1884  if ( !qgsDoubleNear( scale.maxScale, 0.0 ) )
1885  {
1886  maxSizeMU = std::min( maxSizeMU, value );
1887  }
1888  value = std::min( value, maxSizeMU );
1889 }
1890 
1891 QList< QPair< QgsSymbolLayer *, QgsSymbol * > > QgsDxfExport::symbolLayers( QgsRenderContext &context )
1892 {
1893  QList< QPair< QgsSymbolLayer *, QgsSymbol * > > symbolLayers;
1894 
1895  for ( DxfLayerJob *job : std::as_const( mJobs ) )
1896  {
1897  const QgsSymbolList symbols = job->renderer->symbols( context );
1898 
1899  for ( QgsSymbol *symbol : symbols )
1900  {
1901  int maxSymbolLayers = symbol->symbolLayerCount();
1902  if ( mSymbologyExport != SymbolLayerSymbology )
1903  {
1904  maxSymbolLayers = 1;
1905  }
1906  for ( int i = 0; i < maxSymbolLayers; ++i )
1907  {
1908  symbolLayers.append( qMakePair( symbol->symbolLayer( i ), symbol ) );
1909  }
1910  }
1911  }
1912 
1913  return symbolLayers;
1914 }
1915 
1916 void QgsDxfExport::writeDefaultLinetypes()
1917 {
1918  // continuous (Qt solid line)
1919  for ( const QString &ltype : { QStringLiteral( "ByLayer" ), QStringLiteral( "ByBlock" ), QStringLiteral( "CONTINUOUS" ) } )
1920  {
1921  writeGroup( 0, QStringLiteral( "LTYPE" ) );
1922  writeHandle();
1923  writeGroup( 100, QStringLiteral( "AcDbSymbolTableRecord" ) );
1924  writeGroup( 100, QStringLiteral( "AcDbLinetypeTableRecord" ) );
1925  writeGroup( 2, ltype );
1926  writeGroup( 70, 64 );
1927  writeGroup( 3, QStringLiteral( "Defaultstyle" ) );
1928  writeGroup( 72, 65 );
1929  writeGroup( 73, 0 );
1930  writeGroup( 40, 0.0 );
1931  }
1932 
1933  double das = dashSize();
1934  double dss = dashSeparatorSize();
1935  double dos = dotSize();
1936 
1937  QVector<qreal> dashVector( 2 );
1938  dashVector[0] = das;
1939  dashVector[1] = dss;
1940  writeLinetype( QStringLiteral( "DASH" ), dashVector, QgsUnitTypes::RenderMapUnits );
1941 
1942  QVector<qreal> dotVector( 2 );
1943  dotVector[0] = dos;
1944  dotVector[1] = dss;
1945  writeLinetype( QStringLiteral( "DOT" ), dotVector, QgsUnitTypes::RenderMapUnits );
1946 
1947  QVector<qreal> dashDotVector( 4 );
1948  dashDotVector[0] = das;
1949  dashDotVector[1] = dss;
1950  dashDotVector[2] = dos;
1951  dashDotVector[3] = dss;
1952  writeLinetype( QStringLiteral( "DASHDOT" ), dashDotVector, QgsUnitTypes::RenderMapUnits );
1953 
1954  QVector<qreal> dashDotDotVector( 6 );
1955  dashDotDotVector[0] = das;
1956  dashDotDotVector[1] = dss;
1957  dashDotDotVector[2] = dos;
1958  dashDotDotVector[3] = dss;
1959  dashDotDotVector[4] = dos;
1960  dashDotDotVector[5] = dss;
1961  writeLinetype( QStringLiteral( "DASHDOTDOT" ), dashDotDotVector, QgsUnitTypes::RenderMapUnits );
1962 }
1963 
1964 void QgsDxfExport::writeSymbolLayerLinetype( const QgsSymbolLayer *symbolLayer )
1965 {
1966  if ( !symbolLayer )
1967  {
1968  return;
1969  }
1970 
1972  QVector<qreal> customLinestyle = symbolLayer->dxfCustomDashPattern( unit );
1973  if ( !customLinestyle.isEmpty() )
1974  {
1975  QString name = QStringLiteral( "symbolLayer%1" ).arg( mSymbolLayerCounter++ );
1976  writeLinetype( name, customLinestyle, unit );
1977  mLineStyles.insert( symbolLayer, name );
1978  }
1979 }
1980 
1981 int QgsDxfExport::nLineTypes( const QList< QPair< QgsSymbolLayer *, QgsSymbol * > > &symbolLayers )
1982 {
1983  int nLineTypes = 0;
1984  for ( const auto &symbolLayer : symbolLayers )
1985  {
1986  const QgsSimpleLineSymbolLayer *simpleLine = dynamic_cast< const QgsSimpleLineSymbolLayer * >( symbolLayer.first );
1987  if ( simpleLine )
1988  {
1989  if ( simpleLine->useCustomDashPattern() )
1990  {
1991  ++nLineTypes;
1992  }
1993  }
1994  }
1995  return nLineTypes;
1996 }
1997 
1998 void QgsDxfExport::writeLinetype( const QString &styleName, const QVector<qreal> &pattern, QgsUnitTypes::RenderUnit u )
1999 {
2000  double length = 0;
2001  for ( qreal size : pattern )
2002  {
2003  length += ( size * mapUnitScaleFactor( mSymbologyScale, u, mMapUnits, mMapSettings.mapToPixel().mapUnitsPerPixel() ) );
2004  }
2005 
2006  writeGroup( 0, QStringLiteral( "LTYPE" ) );
2007  writeHandle();
2008  // 330 5
2009  writeGroup( 100, QStringLiteral( "AcDbSymbolTableRecord" ) );
2010  writeGroup( 100, QStringLiteral( "AcDbLinetypeTableRecord" ) );
2011  writeGroup( 2, styleName );
2012  writeGroup( 70, 64 ); // 0?
2013  writeGroup( 3, QString() );
2014  writeGroup( 72, 65 );
2015  writeGroup( 73, pattern.size() );
2016  writeGroup( 40, length );
2017 
2018  bool isGap = false;
2019  for ( qreal size : pattern )
2020  {
2021  // map units or mm?
2022  double segmentLength = ( isGap ? -size : size );
2023  segmentLength *= mapUnitScaleFactor( mSymbologyScale, u, mMapUnits, mMapSettings.mapToPixel().mapUnitsPerPixel() );
2024  writeGroup( 49, segmentLength );
2025  writeGroup( 74, 0 );
2026  isGap = !isGap;
2027  }
2028 }
2029 
2030 void QgsDxfExport::addGeometryGeneratorSymbolLayer( QgsSymbolRenderContext &ctx, const QgsCoordinateTransform &ct, const QString &layer, QgsSymbolLayer *symbolLayer, bool allSymbolLayers )
2031 {
2032  QgsGeometryGeneratorSymbolLayer *gg = dynamic_cast<QgsGeometryGeneratorSymbolLayer *>( symbolLayer );
2033  if ( !gg )
2034  {
2035  return;
2036  }
2037 
2038  const QgsFeature *fet = ctx.feature();
2039  if ( !fet )
2040  {
2041  return;
2042  }
2043 
2044  QgsFeature f = *fet;
2045 
2046  QgsExpressionContext &expressionContext = ctx.renderContext().expressionContext();
2047  QgsExpression geomExpr( gg->geometryExpression() );
2048  geomExpr.prepare( &expressionContext );
2049  QgsGeometry geom = geomExpr.evaluate( &expressionContext ).value<QgsGeometry>();
2050  f.setGeometry( geom );
2051 
2052  QgsSymbol *symbol = gg->subSymbol();
2053  if ( symbol && symbol->symbolLayerCount() > 0 )
2054  {
2055  QgsExpressionContextScope *symbolExpressionContextScope = symbol->symbolRenderContext()->expressionContextScope();
2056  symbolExpressionContextScope->setFeature( f );
2057 
2058  ctx.setFeature( &f );
2059 
2060  int nSymbolLayers = allSymbolLayers ? symbol->symbolLayerCount() : 1;
2061  for ( int i = 0; i < nSymbolLayers; ++i )
2062  {
2063  addFeature( ctx, ct, layer, symbol->symbolLayer( i ), symbol );
2064  }
2065 
2066  ctx.setFeature( fet );
2067  }
2068 }
2069 
2070 bool QgsDxfExport::hasDataDefinedProperties( const QgsSymbolLayer *sl, const QgsSymbol *symbol )
2071 {
2072  if ( !sl || !symbol )
2073  {
2074  return false;
2075  }
2076 
2078  {
2079  return true;
2080  }
2081 
2082  return sl->hasDataDefinedProperties();
2083 }
2084 
2085 double QgsDxfExport::dashSize() const
2086 {
2087  double size = mSymbologyScale * 0.002;
2088  return sizeToMapUnits( size );
2089 }
2090 
2091 double QgsDxfExport::dotSize() const
2092 {
2093  double size = mSymbologyScale * 0.0006;
2094  return sizeToMapUnits( size );
2095 }
2096 
2097 double QgsDxfExport::dashSeparatorSize() const
2098 {
2099  double size = mSymbologyScale * 0.0006;
2100  return sizeToMapUnits( size );
2101 }
2102 
2103 double QgsDxfExport::sizeToMapUnits( double s ) const
2104 {
2105  double size = s * QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::DistanceMeters, mMapUnits );
2106  return size;
2107 }
2108 
2109 QString QgsDxfExport::lineNameFromPenStyle( Qt::PenStyle style )
2110 {
2111  switch ( style )
2112  {
2113  case Qt::DashLine:
2114  return QStringLiteral( "DASH" );
2115  case Qt::DotLine:
2116  return QStringLiteral( "DOT" );
2117  case Qt::DashDotLine:
2118  return QStringLiteral( "DASHDOT" );
2119  case Qt::DashDotDotLine:
2120  return QStringLiteral( "DASHDOTDOT" );
2121  case Qt::SolidLine:
2122  default:
2123  return QStringLiteral( "CONTINUOUS" );
2124  }
2125 }
2126 
2127 QString QgsDxfExport::dxfLayerName( const QString &name )
2128 {
2129  if ( name.isEmpty() )
2130  return QStringLiteral( "0" );
2131 
2132  // dxf layers can be max 255 characters long
2133  QString layerName = name.left( 255 );
2134 
2135  // replaced restricted characters with underscore
2136  // < > / \ " : ; ? * | = '
2137  // See http://docs.autodesk.com/ACD/2010/ENU/AutoCAD%202010%20User%20Documentation/index.html?url=WS1a9193826455f5ffa23ce210c4a30acaf-7345.htm,topicNumber=d0e41665
2138  layerName.replace( '<', '_' );
2139  layerName.replace( '>', '_' );
2140  layerName.replace( '/', '_' );
2141  layerName.replace( '\\', '_' );
2142  layerName.replace( '\"', '_' );
2143  layerName.replace( ':', '_' );
2144  layerName.replace( ';', '_' );
2145  layerName.replace( '?', '_' );
2146  layerName.replace( '*', '_' );
2147  layerName.replace( '|', '_' );
2148  layerName.replace( '=', '_' );
2149  layerName.replace( '\'', '_' );
2150 
2151  // also remove newline characters (#15067)
2152  layerName.replace( QLatin1String( "\r\n" ), QLatin1String( "_" ) );
2153  layerName.replace( '\r', '_' );
2154  layerName.replace( '\n', '_' );
2155 
2156  return layerName.trimmed();
2157 }
2158 
2159 bool QgsDxfExport::layerIsScaleBasedVisible( const QgsMapLayer *layer ) const
2160 {
2161  if ( !layer )
2162  return false;
2163 
2164  if ( mSymbologyExport == QgsDxfExport::NoSymbology )
2165  return true;
2166 
2167  return layer->isInScaleRange( mSymbologyScale );
2168 }
2169 
2170 QString QgsDxfExport::layerName( const QString &id, const QgsFeature &f ) const
2171 {
2172  // TODO: make this thread safe
2173  const QList< QgsMapLayer * > layers = mMapSettings.layers();
2174  for ( QgsMapLayer *ml : layers )
2175  {
2176  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml );
2177  if ( vl && vl->id() == id )
2178  {
2179  int attrIdx = mLayerNameAttribute.value( vl->id(), -1 );
2180  return dxfLayerName( attrIdx < 0 ? layerName( vl ) : f.attribute( attrIdx ).toString() );
2181  }
2182  }
2183 
2184  return QStringLiteral( "0" );
2185 }
2186 
2187 QString QgsDxfExport::dxfEncoding( const QString &name )
2188 {
2189  const QList< QByteArray > codecs = QTextCodec::availableCodecs();
2190  for ( const QByteArray &codec : codecs )
2191  {
2192  if ( name != codec )
2193  continue;
2194 
2195  int i;
2196  for ( i = 0; i < static_cast< int >( sizeof( DXF_ENCODINGS ) / sizeof( *DXF_ENCODINGS ) ) && name != DXF_ENCODINGS[i][1]; ++i )
2197  ;
2198 
2199  if ( i == static_cast< int >( sizeof( DXF_ENCODINGS ) / sizeof( *DXF_ENCODINGS ) ) )
2200  continue;
2201 
2202  return DXF_ENCODINGS[i][0];
2203  }
2204 
2205  return QString();
2206 }
2207 
2209 {
2210  QStringList encodings;
2211  const QList< QByteArray > codecs = QTextCodec::availableCodecs();
2212  encodings.reserve( codecs.size() );
2213  for ( const QByteArray &codec : codecs )
2214  {
2215  int i;
2216  for ( i = 0; i < static_cast< int >( sizeof( DXF_ENCODINGS ) / sizeof( *DXF_ENCODINGS ) ) && strcmp( codec.data(), DXF_ENCODINGS[i][1] ) != 0; ++i )
2217  ;
2218 
2219  if ( i < static_cast< int >( sizeof( DXF_ENCODINGS ) / sizeof( *DXF_ENCODINGS ) ) )
2220  encodings << codec.data();
2221  }
2222  return encodings;
2223 }
2224 
2226 {
2227  Q_ASSERT( vl );
2228  return mLayerTitleAsName && !vl->title().isEmpty() ? vl->title() : vl->name();
2229 }
2230 
2231 void QgsDxfExport::drawLabel( const QString &layerId, QgsRenderContext &context, pal::LabelPosition *label, const QgsPalLayerSettings &settings )
2232 {
2233  Q_UNUSED( context )
2234 
2235  if ( !settings.drawLabels )
2236  return;
2237 
2238  QgsTextLabelFeature *lf = dynamic_cast<QgsTextLabelFeature *>( label->getFeaturePart()->feature() );
2239 
2240  // Copy to temp, editable layer settings
2241  // these settings will be changed by any data defined values, then used for rendering label components
2242  // settings may be adjusted during rendering of components
2243  QgsPalLayerSettings tmpLyr( settings );
2244 
2245  // apply any previously applied data defined settings for the label
2246  const QMap< QgsPalLayerSettings::Property, QVariant > &ddValues = lf->dataDefinedValues();
2247 
2248  //font
2249  QFont dFont = lf->definedFont();
2250  QgsDebugMsgLevel( QStringLiteral( "PAL font tmpLyr: %1, Style: %2" ).arg( tmpLyr.format().font().toString(), tmpLyr.format().font().styleName() ), 4 );
2251  QgsDebugMsgLevel( QStringLiteral( "PAL font definedFont: %1, Style: %2" ).arg( dFont.toString(), dFont.styleName() ), 4 );
2252 
2253  QgsTextFormat format = tmpLyr.format();
2254  format.setFont( dFont );
2255  tmpLyr.setFormat( format );
2256 
2258  {
2259  //calculate font alignment based on label quadrant
2260  switch ( label->getQuadrant() )
2261  {
2266  break;
2271  break;
2276  break;
2277  }
2278  }
2279 
2280  // update tmpLyr with any data defined text style values
2281  QgsPalLabeling::dataDefinedTextStyle( tmpLyr, ddValues );
2282 
2283  // update tmpLyr with any data defined text buffer values
2284  QgsPalLabeling::dataDefinedTextBuffer( tmpLyr, ddValues );
2285 
2286  // update tmpLyr with any data defined text formatting values
2287  QgsPalLabeling::dataDefinedTextFormatting( tmpLyr, ddValues );
2288 
2289  // add to the results
2290  QString txt = label->getFeaturePart()->feature()->labelText();
2291 
2292  QgsFeatureId fid = label->getFeaturePart()->featureId();
2293  QString dxfLayer = mDxfLayerNames[layerId][fid];
2294 
2295  QString wrapchr = tmpLyr.wrapChar.isEmpty() ? QStringLiteral( "\n" ) : tmpLyr.wrapChar;
2296 
2297  //add the direction symbol if needed
2298  if ( !txt.isEmpty() && tmpLyr.placement == QgsPalLayerSettings::Line && tmpLyr.lineSettings().addDirectionSymbol() )
2299  {
2300  bool prependSymb = false;
2301  QString symb = tmpLyr.lineSettings().rightDirectionSymbol();
2302 
2303  if ( label->getReversed() )
2304  {
2305  prependSymb = true;
2306  symb = tmpLyr.lineSettings().leftDirectionSymbol();
2307  }
2308 
2309  if ( tmpLyr.lineSettings().reverseDirectionSymbol() )
2310  {
2311  if ( symb == tmpLyr.lineSettings().rightDirectionSymbol() )
2312  {
2313  prependSymb = true;
2314  symb = tmpLyr.lineSettings().leftDirectionSymbol();
2315  }
2316  else
2317  {
2318  prependSymb = false;
2319  symb = tmpLyr.lineSettings().rightDirectionSymbol();
2320  }
2321  }
2322 
2323  switch ( tmpLyr.lineSettings().directionSymbolPlacement() )
2324  {
2326  prependSymb = true;
2327  symb = symb + wrapchr;
2328  break;
2329 
2331  prependSymb = false;
2332  symb = wrapchr + symb;
2333  break;
2334 
2336  break;
2337  }
2338 
2339  if ( prependSymb )
2340  {
2341  txt.prepend( symb );
2342  }
2343  else
2344  {
2345  txt.append( symb );
2346  }
2347  }
2348 
2349  if ( mFlags & FlagNoMText )
2350  {
2351  txt.replace( QChar( QChar::LineFeed ), ' ' );
2352  txt.replace( QChar( QChar::CarriageReturn ), ' ' );
2353  writeText( dxfLayer, txt, label, tmpLyr, context.expressionContext() );
2354  }
2355  else
2356  {
2357  txt.replace( QString( QChar( QChar::CarriageReturn ) ) + QString( QChar( QChar::LineFeed ) ), QStringLiteral( "\\P" ) );
2358  txt.replace( QChar( QChar::CarriageReturn ), QStringLiteral( "\\P" ) );
2359  txt = txt.replace( wrapchr, QLatin1String( "\\P" ) );
2360  txt.replace( QLatin1String( " " ), QLatin1String( "\\~" ) );
2361 
2362  if ( tmpLyr.format().font().underline() )
2363  {
2364  txt.prepend( "\\L" ).append( "\\l" );
2365  }
2366 
2367  if ( tmpLyr.format().font().overline() )
2368  {
2369  txt.prepend( "\\O" ).append( "\\o" );
2370  }
2371 
2372  if ( tmpLyr.format().font().strikeOut() )
2373  {
2374  txt.prepend( "\\K" ).append( "\\k" );
2375  }
2376 
2377  txt.prepend( QStringLiteral( "\\f%1|i%2|b%3;\\H%4;" )
2378  .arg( tmpLyr.format().font().family() )
2379  .arg( tmpLyr.format().font().italic() ? 1 : 0 )
2380  .arg( tmpLyr.format().font().bold() ? 1 : 0 )
2381  .arg( label->getHeight() / ( 1 + txt.count( QStringLiteral( "\\P" ) ) ) * 0.75 ) );
2382  writeMText( dxfLayer, txt, QgsPoint( label->getX(), label->getY() ), label->getWidth(), label->getAlpha() * 180.0 / M_PI, tmpLyr.format().color() );
2383  }
2384 }
2385 
2386 
2387 void QgsDxfExport::registerDxfLayer( const QString &layerId, QgsFeatureId fid, const QString &layerName )
2388 {
2389  if ( !mDxfLayerNames.contains( layerId ) )
2390  mDxfLayerNames[ layerId ] = QMap<QgsFeatureId, QString>();
2391 
2392  mDxfLayerNames[layerId][fid] = layerName;
2393 }
2394 
2396 {
2397  mCrs = crs;
2398  mMapUnits = crs.mapUnits();
2399 }
2400 
2402 {
2403  return mCrs;
2404 }
2405 
2407 {
2408  QString splitLayerFieldName;
2409  const QgsFields fields = mLayer->fields();
2410  if ( mLayerOutputAttributeIndex >= 0 && mLayerOutputAttributeIndex < fields.size() )
2411  {
2412  splitLayerFieldName = fields.at( mLayerOutputAttributeIndex ).name();
2413  }
2414 
2415  return splitLayerFieldName;
2416 }
@ 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.
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:123
@ DeviceNotWritableError
Device not writable error.
@ Success
Successful export.
@ EmptyExtentError
Empty extent, no extent given and no extent could be derived from layers.
@ InvalidDeviceError
Invalid device error.
@ SymbolLayerSymbology
Exports one feature per symbol layer (considering symbol levels)
Definition: qgsdxfexport.h:107
@ NoSymbology
Export only data.
Definition: qgsdxfexport.h:105
void writeCircle(const QString &layer, const QColor &color, const QgsPoint &pt, double radius, const QString &lineStyleName, double width)
Write circle (as polyline)
~QgsDxfExport() override
ExportResult writeToFile(QIODevice *d, const QString &codec)
Export to a dxf file in the given encoding.
void writeLine(const QgsPoint &pt1, const QgsPoint &pt2, const QString &layer, const QString &lineStyleName, const QColor &color, double width=-1)
Write line (as a polyline)
static double mapUnitScaleFactor(double scale, QgsUnitTypes::RenderUnit symbolUnits, QgsUnitTypes::DistanceUnit mapUnits, double mapUnitsPerPixel=1.0)
Returns scale factor for conversion to map units.
void writeGroup(int code, int i)
Write a tuple of group code and integer value.
QString layerName(const QString &id, const QgsFeature &f) const
Gets layer name for feature.
void setFlags(QgsDxfExport::Flags flags)
Sets the export flags.
@ FlagNoMText
Export text as TEXT elements. If not set, text will be exported as MTEXT elements.
Definition: qgsdxfexport.h:113
void writeInt(int i)
Write an integer value.
void writeMText(const QString &layer, const QString &text, const QgsPoint &pt, double width, double angle, const QColor &color)
Write mtext (MTEXT)
QgsDxfExport()
Constructor for QgsDxfExport.
QgsUnitTypes::DistanceUnit mapUnits() const
Retrieve map units.
int writeHandle(int code=5, int handle=0)
Write a tuple of group code and a handle.
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination CRS, or an invalid CRS if no reprojection will be done.
HAlign
Horizontal alignments.
Definition: qgsdxfexport.h:144
@ HCenter
Centered (1)
@ Undefined
Undefined.
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
Set destination CRS.
void addLayers(const QList< QgsDxfExport::DxfLayer > &layers)
Add layers to export.
static QString dxfLayerName(const QString &name)
Returns cleaned layer name for use in DXF.
void writeDouble(double d)
Write a floating point value.
void writeText(const QString &layer, const QString &text, const QgsPoint &pt, double size, double angle, const QColor &color, QgsDxfExport::HAlign hali=QgsDxfExport::HAlign::Undefined, QgsDxfExport::VAlign vali=QgsDxfExport::VAlign::Undefined)
Write text (TEXT)
void writeString(const QString &s)
Write a string value.
void writePolygon(const QgsRingSequence &polygon, const QString &layer, const QString &hatchPattern, const QColor &color)
Draw dxf filled polygon (HATCH)
void drawLabel(const QString &layerId, QgsRenderContext &context, pal::LabelPosition *label, const QgsPalLayerSettings &settings) override
Add a label to the dxf output.
static QString dxfEncoding(const QString &name)
Returns DXF encoding for Qt encoding.
static int closestColorMatch(QRgb color)
Gets DXF palette index of nearest entry for given color.
void writePoint(const QString &layer, const QColor &color, const QgsPoint &pt)
Write point.
Q_DECL_DEPRECATED void registerDxfLayer(const QString &layerId, QgsFeatureId fid, const QString &layer)
Register name of layer for feature.
QgsDxfExport::Flags flags() const
Returns the export flags.
VAlign
Vertical alignments.
Definition: qgsdxfexport.h:134
@ Undefined
Undefined.
void clipValueToMapUnitScale(double &value, const QgsMapUnitScale &scale, double pixelToMMFactor) const
Clips value to scale minimum/maximum.
void writePolyline(const QgsPointSequence &line, const QString &layer, const QString &lineStyleName, const QColor &color, double width=-1)
Draw dxf primitives (LWPOLYLINE)
static QStringList encodings()
Returns list of available DXF encodings.
void setMapSettings(const QgsMapSettings &settings)
Set map settings and assign layer name attributes.
void writeGroupCode(int code)
Write a group code.
Single scope for storing variables and functions for use within a QgsExpressionContext.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the scope.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QList< QgsExpressionContextScope * > scopes()
Returns a list of scopes contained within the stack.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
Class for parsing and evaluation of expressions (formerly called "search strings").
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
@ SymbolLevels
Rendering with symbol levels (i.e. implements symbols(), symbolForFeature())
Definition: qgsrenderer.h:263
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
QgsFeatureRequest & setExpressionContext(const QgsExpressionContext &context)
Sets the expression context used to evaluate filter expressions.
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
QgsGeometry geometry
Definition: qgsfeature.h:67
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp: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:73
QString name
Definition: qgsmaplayer.h:76
bool isInScaleRange(double scale) const
Tests whether the layer should be visible at the specified scale.
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
QString title() const
Returns the title of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:307
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 the current map units per pixel.
Struct for storing maximum and minimum scales for measurements in map units.
bool minSizeMMEnabled
Whether the minimum size in mm should be respected.
double maxScale
The maximum scale, or 0.0 if unset.
double minScale
The minimum scale, or 0.0 if unset.
double maxSizeMM
The maximum size in millimeters, or 0.0 if unset.
bool maxSizeMMEnabled
Whether the maximum size in mm should be respected.
double minSizeMM
The minimum size in millimeters, or 0.0 if unset.
Abstract base class for marker symbol layers.
QgsUnitTypes::RenderUnit sizeUnit() const
Returns the units for the symbol's size.
double size() const
Returns the symbol size.
Contains settings for how a map layer will be labeled.
void setFormat(const QgsTextFormat &format)
Sets the label text formatting settings, e.g., font settings, buffer settings, etc.
QString wrapChar
Wrapping character string.
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the label's property collection, used for data defined overrides.
bool drawLabels
Whether to draw labels for this layer.
QuadrantPosition quadOffset
Sets the quadrant in which to offset labels from feature.
@ Line
Arranges candidates parallel to a generalised line representing the feature or parallel to a polygon'...
MultiLineAlign multilineAlign
Horizontal alignment of multi-line labels.
@ Hali
Horizontal alignment for data defined label position (Left, Center, Right)
@ Vali
Vertical alignment for data defined label position (Bottom, Base, Half, Cap, Top)
const QgsLabelLineSettings & lineSettings() const
Returns the label line settings, which contain settings related to how the label engine places and fo...
const QgsTextFormat & format() const
Returns the label text formatting settings, e.g., font settings, buffer settings, etc.
double y
Definition: qgspointxy.h:63
Q_GADGET double x
Definition: qgspointxy.h:62
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
Q_GADGET double x
Definition: qgspoint.h:52
double z
Definition: qgspoint.h:54
double y
Definition: qgspoint.h:53
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp: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:1444
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 FALLTHROUGH
Definition: qgis.h:1757
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:1730
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:1186
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:1729
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:1234
QVector< QgsRingSequence > QgsCoordinateSequence
QVector< QgsPointSequence > QgsRingSequence
QVector< QgsPoint > QgsPointSequence
#define DXF_HANDMAX
Definition: qgsdxfexport.h:46
#define DXF_HANDPLOTSTYLE
Definition: qgsdxfexport.h:47
#define DXF_TRAILER
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
Definition: qgsfeatureid.h:28
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QList< QgsSymbolLevel > QgsSymbolLevelOrder
Definition: qgsrenderer.h:88
QList< QgsSymbolLevelItem > QgsSymbolLevel
Definition: qgsrenderer.h:84
QList< QgsSymbol * > QgsSymbolList
Definition: qgsrenderer.h:44
QList< QgsSymbolLayer * > QgsSymbolLayerList
Definition: qgssymbol.h:27
const QgsCoordinateReferenceSystem & crs
Holds information about each layer in a DXF job.
QString layerName
std::unique_ptr< QgsFeatureRenderer > renderer
QgsRenderContext renderContext
QgsCoordinateReferenceSystem crs
QgsVectorLayerFeatureSource featureSource
Layers and optional attribute index to split into multiple layers using attribute value as layer name...
Definition: qgsdxfexport.h:74
QString splitLayerAttribute() const
If the split layer attribute is set, the vector layer will be split into several dxf layers,...