LCOV - code coverage report
Current view: top level - core/dxf - qgsdxfexport.cpp (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 0 1476 0.0 %
Date: 2021-03-26 12:19:53 Functions: 0 0 -
Branches: 0 0 -

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

Generated by: LCOV version 1.14