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