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