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