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