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