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