QGIS/src/core/dxf/qgsdxfexport.cpp
2017-09-25 17:12:03 +10:00

4457 lines
86 KiB
C++

/***************************************************************************
qgsdxfexport.cpp
----------------
begin : September 2013
copyright : (C) 2013 by Marco Hugentobler
email : marco at sourcepole dot ch
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
// Specs:
// AutoCAD 2000: http://www.autodesk.com/techpubs/autocad/acad2000/dxf/
// AutoCAD 2002: http://www.autodesk.com/techpubs/autocad/dxf/dxf2002.pdf
// AutoCAD 2004: http://atrey.karlin.mff.cuni.cz/projekty/vrr/doc/dxf14.pdf
// AutoCAD 2006: http://images.autodesk.com/adsk/files/dxf_format.pdf
// AutoCAD 2008: http://images.autodesk.com/adsk/files/acad_dxf0.pdf
// AutoCAD 2009: http://images.autodesk.com/adsk/files/acad_dxf.pdf
// AutoCAD 2011: http://images.autodesk.com/adsk/files/acad_dxf2.pdf
// AutoCAD 2012: http://images.autodesk.com/adsk/files/autocad_2012_pdf_dxf-reference_enu.pdf
// AutoCAD 2014: http://images.autodesk.com/adsk/files/autocad_2014_pdf_dxf_reference_enu.pdf
#include "qgsdxfexport.h"
#include "qgsdxfpallabeling.h"
#include "qgsvectordataprovider.h"
#include "qgspointxy.h"
#include "qgsproject.h"
#include "qgsrenderer.h"
#include "qgssymbollayer.h"
#include "qgsfillsymbollayer.h"
#include "qgsfeatureiterator.h"
#include "qgslinesymbollayer.h"
#include "qgsvectorlayer.h"
#include "qgsunittypes.h"
#include "qgstextlabelfeature.h"
#include "qgscrscache.h"
#include "qgslogger.h"
#include "qgsmaplayerstylemanager.h"
#include "qgswkbtypes.h"
#include "qgspoint.h"
#include "qgsgeos.h"
#include "pal/feature.h"
#include "pal/pointset.h"
#include "pal/labelposition.h"
#include <QIODevice>
#define DXF_HANDSEED 100
#define DXF_HANDMAX 9999999
#define DXF_HANDPLOTSTYLE 0xf
// dxf color palette
int QgsDxfExport::sDxfColors[][3] =
{
{ 255, 255, 255 },
{ 255, 0, 0 },
{ 255, 255, 0 },
{ 0, 255, 0 },
{ 0, 255, 255 },
{ 0, 0, 255 },
{ 255, 0, 255 },
{ 0, 0, 0 },
{ 128, 128, 128 },
{ 192, 192, 192 },
{ 255, 0, 0 },
{ 255, 127, 127 },
{ 204, 0, 0 },
{ 204, 102, 102 },
{ 153, 0, 0 },
{ 153, 76, 76 },
{ 127, 0, 0 },
{ 127, 63, 63 },
{ 76, 0, 0 },
{ 76, 38, 38 },
{ 255, 63, 0 },
{ 255, 159, 127 },
{ 204, 51, 0 },
{ 204, 127, 102 },
{ 153, 38, 0 },
{ 153, 95, 76 },
{ 127, 31, 0 },
{ 127, 79, 63 },
{ 76, 19, 0 },
{ 76, 47, 38 },
{ 255, 127, 0 },
{ 255, 191, 127 },
{ 204, 102, 0 },
{ 204, 153, 102 },
{ 153, 76, 0 },
{ 153, 114, 76 },
{ 127, 63, 0 },
{ 127, 95, 63 },
{ 76, 38, 0 },
{ 76, 57, 38 },
{ 255, 191, 0 },
{ 255, 223, 127 },
{ 204, 153, 0 },
{ 204, 178, 102 },
{ 153, 114, 0 },
{ 153, 133, 76 },
{ 127, 95, 0 },
{ 127, 111, 63 },
{ 76, 57, 0 },
{ 76, 66, 38 },
{ 255, 255, 0 },
{ 255, 255, 127 },
{ 204, 204, 0 },
{ 204, 204, 102 },
{ 153, 153, 0 },
{ 153, 153, 76 },
{ 127, 127, 0 },
{ 127, 127, 63 },
{ 76, 76, 0 },
{ 76, 76, 38 },
{ 191, 255, 0 },
{ 223, 255, 127 },
{ 153, 204, 0 },
{ 178, 204, 102 },
{ 114, 153, 0 },
{ 133, 153, 76 },
{ 95, 127, 0 },
{ 111, 127, 63 },
{ 57, 76, 0 },
{ 66, 76, 38 },
{ 127, 255, 0 },
{ 191, 255, 127 },
{ 102, 204, 0 },
{ 153, 204, 102 },
{ 76, 153, 0 },
{ 114, 153, 76 },
{ 63, 127, 0 },
{ 95, 127, 63 },
{ 38, 76, 0 },
{ 57, 76, 38 },
{ 63, 255, 0 },
{ 159, 255, 127 },
{ 51, 204, 0 },
{ 127, 204, 102 },
{ 38, 153, 0 },
{ 95, 153, 76 },
{ 31, 127, 0 },
{ 79, 127, 63 },
{ 19, 76, 0 },
{ 47, 76, 38 },
{ 0, 255, 0 },
{ 127, 255, 127 },
{ 0, 204, 0 },
{ 102, 204, 102 },
{ 0, 153, 0 },
{ 76, 153, 76 },
{ 0, 127, 0 },
{ 63, 127, 63 },
{ 0, 76, 0 },
{ 38, 76, 38 },
{ 0, 255, 63 },
{ 127, 255, 159 },
{ 0, 204, 51 },
{ 102, 204, 127 },
{ 0, 153, 38 },
{ 76, 153, 95 },
{ 0, 127, 31 },
{ 63, 127, 79 },
{ 0, 76, 19 },
{ 38, 76, 47 },
{ 0, 255, 127 },
{ 127, 255, 191 },
{ 0, 204, 102 },
{ 102, 204, 153 },
{ 0, 153, 76 },
{ 76, 153, 114 },
{ 0, 127, 63 },
{ 63, 127, 95 },
{ 0, 76, 38 },
{ 38, 76, 57 },
{ 0, 255, 191 },
{ 127, 255, 223 },
{ 0, 204, 153 },
{ 102, 204, 178 },
{ 0, 153, 114 },
{ 76, 153, 133 },
{ 0, 127, 95 },
{ 63, 127, 111 },
{ 0, 76, 57 },
{ 38, 76, 66 },
{ 0, 255, 255 },
{ 127, 255, 255 },
{ 0, 204, 204 },
{ 102, 204, 204 },
{ 0, 153, 153 },
{ 76, 153, 153 },
{ 0, 127, 127 },
{ 63, 127, 127 },
{ 0, 76, 76 },
{ 38, 76, 76 },
{ 0, 191, 255 },
{ 127, 223, 255 },
{ 0, 153, 204 },
{ 102, 178, 204 },
{ 0, 114, 153 },
{ 76, 133, 153 },
{ 0, 95, 127 },
{ 63, 111, 127 },
{ 0, 57, 76 },
{ 38, 66, 76 },
{ 0, 127, 255 },
{ 127, 191, 255 },
{ 0, 102, 204 },
{ 102, 153, 204 },
{ 0, 76, 153 },
{ 76, 114, 153 },
{ 0, 63, 127 },
{ 63, 95, 127 },
{ 0, 38, 76 },
{ 38, 57, 76 },
{ 0, 63, 255 },
{ 127, 159, 255 },
{ 0, 51, 204 },
{ 102, 127, 204 },
{ 0, 38, 153 },
{ 76, 95, 153 },
{ 0, 31, 127 },
{ 63, 79, 127 },
{ 0, 19, 76 },
{ 38, 47, 76 },
{ 0, 0, 255 },
{ 127, 127, 255 },
{ 0, 0, 204 },
{ 102, 102, 204 },
{ 0, 0, 153 },
{ 76, 76, 153 },
{ 0, 0, 127 },
{ 63, 63, 127 },
{ 0, 0, 76 },
{ 38, 38, 76 },
{ 63, 0, 255 },
{ 159, 127, 255 },
{ 51, 0, 204 },
{ 127, 102, 204 },
{ 38, 0, 153 },
{ 95, 76, 153 },
{ 31, 0, 127 },
{ 79, 63, 127 },
{ 19, 0, 76 },
{ 47, 38, 76 },
{ 127, 0, 255 },
{ 191, 127, 255 },
{ 102, 0, 204 },
{ 153, 102, 204 },
{ 76, 0, 153 },
{ 114, 76, 153 },
{ 63, 0, 127 },
{ 95, 63, 127 },
{ 38, 0, 76 },
{ 57, 38, 76 },
{ 191, 0, 255 },
{ 223, 127, 255 },
{ 153, 0, 204 },
{ 178, 102, 204 },
{ 114, 0, 153 },
{ 133, 76, 153 },
{ 95, 0, 127 },
{ 111, 63, 127 },
{ 57, 0, 76 },
{ 66, 38, 76 },
{ 255, 0, 255 },
{ 255, 127, 255 },
{ 204, 0, 204 },
{ 204, 102, 204 },
{ 153, 0, 153 },
{ 153, 76, 153 },
{ 127, 0, 127 },
{ 127, 63, 127 },
{ 76, 0, 76 },
{ 76, 38, 76 },
{ 255, 0, 191 },
{ 255, 127, 223 },
{ 204, 0, 153 },
{ 204, 102, 178 },
{ 153, 0, 114 },
{ 153, 76, 133 },
{ 127, 0, 95 },
{ 127, 63, 111 },
{ 76, 0, 57 },
{ 76, 38, 66 },
{ 255, 0, 127 },
{ 255, 127, 191 },
{ 204, 0, 102 },
{ 204, 102, 153 },
{ 153, 0, 76 },
{ 153, 76, 114 },
{ 127, 0, 63 },
{ 127, 63, 95 },
{ 76, 0, 38 },
{ 76, 38, 57 },
{ 255, 0, 63 },
{ 255, 127, 159 },
{ 204, 0, 51 },
{ 204, 102, 127 },
{ 153, 0, 38 },
{ 153, 76, 95 },
{ 127, 0, 31 },
{ 127, 63, 79 },
{ 76, 0, 19 },
{ 76, 38, 47 },
{ 51, 51, 51 },
{ 91, 91, 91 },
{ 132, 132, 132 },
{ 173, 173, 173 },
{ 214, 214, 214 },
{ 255, 255, 255 },
};
const char *QgsDxfExport::DXF_ENCODINGS[][2] =
{
{ "ASCII", "" },
{ "8859_1", "ISO-8859-1" },
{ "8859_2", "ISO-8859-2" },
{ "8859_3", "ISO-8859-3" },
{ "8859_4", "ISO-8859-4" },
{ "8859_5", "ISO-8859-5" },
{ "8859_6", "ISO-8859-6" },
{ "8859_7", "ISO-8859-7" },
{ "8859_8", "ISO-8859-8" },
{ "8859_9", "ISO-8859-9" },
// { "DOS437", "" },
{ "DOS850", "CP850" },
// { "DOS852", "" },
// { "DOS855", "" },
// { "DOS857", "" },
// { "DOS860", "" },
// { "DOS861", "" },
// { "DOS863", "" },
// { "DOS864", "" },
// { "DOS865", "" },
// { "DOS869", "" },
// { "DOS932", "" },
{ "MACINTOSH", "MacRoman" },
{ "BIG5", "Big5" },
{ "KSC5601", "ksc5601.1987-0" },
// { "JOHAB", "" },
{ "DOS866", "CP866" },
{ "ANSI_1250", "CP1250" },
{ "ANSI_1251", "CP1251" },
{ "ANSI_1252", "CP1252" },
{ "GB2312", "GB2312" },
{ "ANSI_1253", "CP1253" },
{ "ANSI_1254", "CP1254" },
{ "ANSI_1255", "CP1255" },
{ "ANSI_1256", "CP1256" },
{ "ANSI_1257", "CP1257" },
{ "ANSI_874", "CP874" },
{ "ANSI_932", "Shift_JIS" },
{ "ANSI_936", "CP936" },
{ "ANSI_949", "cp949" },
{ "ANSI_950", "CP950" },
// { "ANSI_1361", "" },
// { "ANSI_1200", "" },
{ "ANSI_1258", "CP1258" },
};
QgsDxfExport::QgsDxfExport()
: mSymbologyScale( 1.0 )
, mSymbologyExport( NoSymbology )
, mMapUnits( QgsUnitTypes::DistanceMeters )
, mLayerTitleAsName( false )
, mSymbolLayerCounter( 0 )
, mNextHandleId( DXF_HANDSEED )
, mBlockCounter( 0 )
{
}
QgsDxfExport::QgsDxfExport( const QgsDxfExport &dxfExport )
{
*this = dxfExport;
}
QgsDxfExport &QgsDxfExport::operator=( const QgsDxfExport &dxfExport )
{
mMapSettings = dxfExport.mMapSettings;
mLayerNameAttribute = dxfExport.mLayerNameAttribute;
mSymbologyScale = dxfExport.mSymbologyScale;
mSymbologyExport = dxfExport.mSymbologyExport;
mMapUnits = dxfExport.mMapUnits;
mLayerTitleAsName = dxfExport.mLayerTitleAsName;
mSymbolLayerCounter = 0; // internal counter
mNextHandleId = 0;
mBlockCounter = 0;
mCrs = QgsCoordinateReferenceSystem();
mFactor = dxfExport.mFactor;
mForce2d = dxfExport.mForce2d;
return *this;
}
void QgsDxfExport::setMapSettings( const QgsMapSettings &settings )
{
mMapSettings = settings;
}
void QgsDxfExport::addLayers( const QList< QPair< QgsVectorLayer *, int > > &layers )
{
QList<QgsMapLayer *> layerList;
mLayerNameAttribute.clear();
QList< QPair< QgsVectorLayer *, int > >::const_iterator layerIt = layers.constBegin();
for ( ; layerIt != layers.constEnd(); ++layerIt )
{
layerList << layerIt->first;
if ( layerIt->second >= 0 )
mLayerNameAttribute.insert( layerIt->first->id(), layerIt->second );
}
mMapSettings.setLayers( layerList );
}
void QgsDxfExport::writeGroup( int code, int i )
{
writeGroupCode( code );
writeInt( i );
}
void QgsDxfExport::writeGroup( int code, double d )
{
writeGroupCode( code );
writeDouble( d );
}
void QgsDxfExport::writeGroup( int code, const QString &s )
{
writeGroupCode( code );
writeString( s );
}
void QgsDxfExport::writeGroup( int code, const QgsPoint &p )
{
writeGroup( code + 10, p.x() );
writeGroup( code + 20, p.y() );
if ( !mForce2d && p.is3D() && std::isfinite( p.z() ) )
writeGroup( code + 30, p.z() );
}
void QgsDxfExport::writeGroup( const QColor &color, int exactMatchCode, int rgbCode, int transparencyCode )
{
int minDistAt = -1;
int minDist = INT_MAX;
for ( int i = 1; i < static_cast< int >( sizeof( sDxfColors ) / sizeof( *sDxfColors ) ) && minDist > 0; ++i )
{
int dist = color_distance( color.rgba(), i );
if ( dist >= minDist )
continue;
minDistAt = i;
minDist = dist;
}
if ( minDist == 0 && minDistAt != 7 )
{
// exact full opaque match, not black/white
writeGroup( exactMatchCode, minDistAt );
if ( color.alpha() == 255 )
return;
}
int c = ( color.red() & 0xff ) * 0x10000 + ( color.green() & 0xff ) * 0x100 + ( color.blue() & 0xff );
writeGroup( rgbCode, c );
if ( transparencyCode != -1 && color.alpha() < 255 )
writeGroup( transparencyCode, 0x2000000 | color.alpha() );
}
void QgsDxfExport::writeGroupCode( int code )
{
mTextStream << QStringLiteral( "%1\n" ).arg( code, 3, 10, QChar( ' ' ) );
}
void QgsDxfExport::writeInt( int i )
{
mTextStream << QStringLiteral( "%1\n" ).arg( i, 6, 10, QChar( ' ' ) );
}
void QgsDxfExport::writeDouble( double d )
{
QString s( qgsDoubleToString( d ) );
if ( !s.contains( '.' ) )
s += QLatin1String( ".0" );
mTextStream << s << '\n';
}
void QgsDxfExport::writeString( const QString &s )
{
mTextStream << s << '\n';
}
int QgsDxfExport::writeToFile( QIODevice *d, const QString &encoding )
{
if ( !d )
{
return 1;
}
if ( !d->isOpen() && !d->open( QIODevice::WriteOnly ) )
{
return 2;
}
mTextStream.setDevice( d );
mTextStream.setCodec( encoding.toLocal8Bit() );
if ( mExtent.isEmpty() )
{
const QList< QgsMapLayer * > layers = mMapSettings.layers();
for ( QgsMapLayer *ml : layers )
{
QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml );
if ( !vl )
continue;
QgsRectangle layerExtent = vl->extent();
layerExtent = mMapSettings.layerToMapCoordinates( vl, layerExtent );
if ( mExtent.isEmpty() )
{
mExtent = layerExtent;
}
else
{
mExtent.combineExtentWith( layerExtent );
}
}
}
QgsUnitTypes::DistanceUnit mapUnits = mCrs.mapUnits();
mMapSettings.setExtent( mExtent );
int dpi = 96;
mFactor = 1000 * dpi / mSymbologyScale / 25.4 * QgsUnitTypes::fromUnitToUnitFactor( mapUnits, QgsUnitTypes::DistanceMeters );
mMapSettings.setOutputSize( QSize( mExtent.width() * mFactor, mExtent.height() * mFactor ) );
mMapSettings.setOutputDpi( dpi );
if ( mCrs.isValid() )
mMapSettings.setDestinationCrs( mCrs );
writeHeader( dxfEncoding( encoding ) );
writeTables();
writeBlocks();
writeEntities();
writeEndFile();
return 0;
}
QgsUnitTypes::DistanceUnit QgsDxfExport::mapUnits() const
{
return mMapUnits;
}
void QgsDxfExport::writeHeader( const QString &codepage )
{
writeGroup( 999, QStringLiteral( "DXF created from QGIS" ) );
startSection();
writeGroup( 2, QStringLiteral( "HEADER" ) );
// ACADVER
writeGroup( 9, QStringLiteral( "$ACADVER" ) );
writeGroup( 1, QStringLiteral( "AC1015" ) );
// EXTMIN
writeGroup( 9, QStringLiteral( "$EXTMIN" ) );
writeGroup( 0, QgsPoint( QgsWkbTypes::PointZ, mExtent.xMinimum(), mExtent.yMinimum() ) );
// EXTMAX
writeGroup( 9, QStringLiteral( "$EXTMAX" ) );
writeGroup( 0, QgsPoint( QgsWkbTypes::PointZ, mExtent.xMaximum(), mExtent.yMaximum() ) );
// Global linetype scale
writeGroup( 9, QStringLiteral( "$LTSCALE" ) );
writeGroup( 40, 1.0 );
// Point display mode (33 = circle)
writeGroup( 9, QStringLiteral( "$PDMODE" ) );
writeGroup( 70, 33 );
// Point display size
writeGroup( 9, QStringLiteral( "$PDSIZE" ) );
writeGroup( 40, 1 );
// Controls paper space linetype scaling (1 = No special linetype scaling, 0 = Viewport scaling governs linetype scaling)
writeGroup( 9, QStringLiteral( "$PSLTSCALE" ) );
writeGroup( 70, 0 );
writeGroup( 9, QStringLiteral( "$HANDSEED" ) );
writeGroup( 5, DXF_HANDMAX );
writeGroup( 9, QStringLiteral( "$DWGCODEPAGE" ) );
writeGroup( 3, codepage );
endSection();
}
int QgsDxfExport::writeHandle( int code, int handle )
{
if ( handle == 0 )
handle = mNextHandleId++;
Q_ASSERT_X( handle < DXF_HANDMAX, "QgsDxfExport::writeHandle(int, int)", "DXF handle too large" );
writeGroup( code, QStringLiteral( "%1" ).arg( handle, 0, 16 ) );
return handle;
}
void QgsDxfExport::writeTables()
{
startSection();
writeGroup( 2, QStringLiteral( "TABLES" ) );
// Iterate through all layers and get symbol layer pointers
QgsRenderContext context = renderContext();
QList< QPair< QgsSymbolLayer *, QgsSymbol * > > slList;
if ( mSymbologyExport != NoSymbology )
{
slList = symbolLayers( context );
}
// Line types
mLineStyles.clear();
writeGroup( 0, QStringLiteral( "TABLE" ) );
writeGroup( 2, QStringLiteral( "LTYPE" ) );
writeHandle();
writeGroup( 100, QStringLiteral( "AcDbSymbolTable" ) );
writeGroup( 70, nLineTypes( slList ) + 5 );
writeDefaultLinetypes();
// Add custom linestyles
QList< QPair< QgsSymbolLayer *, QgsSymbol *> >::const_iterator slIt = slList.constBegin();
for ( ; slIt != slList.constEnd(); ++slIt )
{
writeSymbolLayerLinetype( slIt->first );
}
writeGroup( 0, QStringLiteral( "ENDTAB" ) );
// BLOCK_RECORD
writeGroup( 0, QStringLiteral( "TABLE" ) );
writeGroup( 2, QStringLiteral( "BLOCK_RECORD" ) );
writeHandle();
writeGroup( 100, QStringLiteral( "AcDbSymbolTable" ) );
writeGroup( 70, 0 );
const QStringList blockStrings = QStringList() << QStringLiteral( "*Model_Space" ) << QStringLiteral( "*Paper_Space" ) << QStringLiteral( "*Paper_Space0" );
for ( const QString &block : blockStrings )
{
writeGroup( 0, QStringLiteral( "BLOCK_RECORD" ) );
mBlockHandles.insert( block, writeHandle() );
writeGroup( 100, QStringLiteral( "AcDbSymbolTableRecord" ) );
writeGroup( 100, QStringLiteral( "AcDbBlockTableRecord" ) );
writeGroup( 2, block );
}
int i = 0;
slIt = slList.constBegin();
for ( ; slIt != slList.constEnd(); ++slIt )
{
QgsMarkerSymbolLayer *ml = dynamic_cast< QgsMarkerSymbolLayer *>( slIt->first );
if ( !ml )
continue;
if ( hasDataDefinedProperties( ml, slIt->second ) )
continue;
QString name = QStringLiteral( "symbolLayer%1" ).arg( i++ );
writeGroup( 0, QStringLiteral( "BLOCK_RECORD" ) );
mBlockHandles.insert( name, writeHandle() );
writeGroup( 100, QStringLiteral( "AcDbSymbolTableRecord" ) );
writeGroup( 100, QStringLiteral( "AcDbBlockTableRecord" ) );
writeGroup( 2, name );
}
writeGroup( 0, QStringLiteral( "ENDTAB" ) );
// APPID
writeGroup( 0, QStringLiteral( "TABLE" ) );
writeGroup( 2, QStringLiteral( "APPID" ) );
writeHandle();
writeGroup( 100, QStringLiteral( "AcDbSymbolTable" ) );
writeGroup( 70, 1 );
writeGroup( 0, QStringLiteral( "APPID" ) );
writeHandle();
writeGroup( 100, QStringLiteral( "AcDbSymbolTableRecord" ) );
writeGroup( 100, QStringLiteral( "AcDbRegAppTableRecord" ) );
writeGroup( 2, QStringLiteral( "ACAD" ) );
writeGroup( 70, 0 );
writeGroup( 0, QStringLiteral( "ENDTAB" ) );
// VIEW
writeGroup( 0, QStringLiteral( "TABLE" ) );
writeGroup( 2, QStringLiteral( "VIEW" ) );
writeHandle();
writeGroup( 100, QStringLiteral( "AcDbSymbolTable" ) );
writeGroup( 70, 0 );
writeGroup( 0, QStringLiteral( "ENDTAB" ) );
// UCS
writeGroup( 0, QStringLiteral( "TABLE" ) );
writeGroup( 2, QStringLiteral( "UCS" ) );
writeHandle();
writeGroup( 100, QStringLiteral( "AcDbSymbolTable" ) );
writeGroup( 70, 0 );
writeGroup( 0, QStringLiteral( "ENDTAB" ) );
// VPORT
writeGroup( 0, QStringLiteral( "TABLE" ) );
writeGroup( 2, QStringLiteral( "VPORT" ) );
writeHandle();
writeGroup( 100, QStringLiteral( "AcDbSymbolTable" ) );
writeGroup( 0, QStringLiteral( "VPORT" ) );
writeHandle();
writeGroup( 100, QStringLiteral( "AcDbSymbolTableRecord" ) );
writeGroup( 100, QStringLiteral( "AcDbViewportTableRecord" ) );
writeGroup( 2, QStringLiteral( "*ACTIVE" ) );
writeGroup( 70, 0 ); // flags
writeGroup( 0, QgsPoint( 0.0, 0.0 ) ); // lower left
writeGroup( 1, QgsPoint( 1.0, 1.0 ) ); // upper right
writeGroup( 2, QgsPoint( 0.0, 0.0 ) ); // view center point
writeGroup( 3, QgsPoint( 0.0, 0.0 ) ); // snap base point
writeGroup( 4, QgsPoint( 1.0, 1.0 ) ); // snap spacing
writeGroup( 5, QgsPoint( 1.0, 1.0 ) ); // grid spacing
writeGroup( 6, QgsPoint( QgsWkbTypes::PointZ, 0.0, 0.0, 1.0 ) ); // view direction from target point
writeGroup( 7, QgsPoint( mExtent.center() ) ); // view target point
writeGroup( 40, mExtent.height() ); // view height
writeGroup( 41, mExtent.width() / mExtent.height() ); // view aspect ratio
writeGroup( 42, 50.0 ); // lens length
writeGroup( 43, 0.0 ); // front clipping plane
writeGroup( 44, 0.0 ); // back clipping plane
writeGroup( 50, 0.0 ); // snap rotation
writeGroup( 51, 0.0 ); // view twist angle
writeGroup( 71, 0 ); // view mode (0 = deactivates)
writeGroup( 72, 100 ); // circle zoom percent
writeGroup( 73, 1 ); // fast zoom setting
writeGroup( 74, 1 ); // UCSICON setting
writeGroup( 75, 0 ); // snapping off
writeGroup( 76, 0 ); // grid off
writeGroup( 77, 0 ); // snap style
writeGroup( 78, 0 ); // snap isopair
writeGroup( 281, 0 ); // render mode (0 = 2D optimized)
writeGroup( 65, 1 ); // value of UCSVP for this viewport
writeGroup( 100, QgsPoint( QgsWkbTypes::PointZ ) ); // UCS origin
writeGroup( 101, QgsPoint( QgsWkbTypes::PointZ, 1.0 ) ); // UCS x axis
writeGroup( 102, QgsPoint( QgsWkbTypes::PointZ, 0.0, 1.0 ) ); // UCS y axis
writeGroup( 79, 0 ); // Orthographic type of UCS (0 = UCS is not orthographic)
writeGroup( 146, 0.0 ); // Elevation
writeGroup( 70, 0 );
writeGroup( 0, QStringLiteral( "ENDTAB" ) );
// DIMSTYLE
writeGroup( 0, QStringLiteral( "TABLE" ) );
writeGroup( 2, QStringLiteral( "DIMSTYLE" ) );
writeHandle();
writeGroup( 100, QStringLiteral( "AcDbSymbolTable" ) );
writeGroup( 100, QStringLiteral( "AcDbDimStyleTable" ) );
writeGroup( 70, 0 );
writeGroup( 0, QStringLiteral( "ENDTAB" ) );
QSet<QString> layerNames;
const QList< QgsMapLayer * > layers = mMapSettings.layers();
for ( QgsMapLayer *ml : layers )
{
if ( !layerIsScaleBasedVisible( ml ) )
continue;
QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml );
if ( !vl )
continue;
int attrIdx = mLayerNameAttribute.value( vl->id(), -1 );
if ( attrIdx < 0 )
{
layerNames << dxfLayerName( layerName( vl ) );
}
else
{
const QSet<QVariant> values = vl->uniqueValues( attrIdx );
for ( const QVariant &v : values )
{
layerNames << dxfLayerName( v.toString() );
}
}
}
// Layers
// TODO: iterate features of all layer to produce a data-defined layer list
writeGroup( 0, QStringLiteral( "TABLE" ) );
writeGroup( 2, QStringLiteral( "LAYER" ) );
writeHandle();
writeGroup( 100, QStringLiteral( "AcDbSymbolTable" ) );
writeGroup( 70, layerNames.size() + 1 );
writeGroup( 0, QStringLiteral( "LAYER" ) );
writeHandle();
writeGroup( 100, QStringLiteral( "AcDbSymbolTableRecord" ) );
writeGroup( 100, QStringLiteral( "AcDbLayerTableRecord" ) );
writeGroup( 2, QStringLiteral( "0" ) );
writeGroup( 70, 64 );
writeGroup( 62, 1 );
writeGroup( 6, QStringLiteral( "CONTINUOUS" ) );
writeHandle( 390, DXF_HANDPLOTSTYLE );
for ( const QString &layerName : qgsAsConst( layerNames ) )
{
writeGroup( 0, QStringLiteral( "LAYER" ) );
writeHandle();
writeGroup( 100, QStringLiteral( "AcDbSymbolTableRecord" ) );
writeGroup( 100, QStringLiteral( "AcDbLayerTableRecord" ) );
writeGroup( 2, layerName );
writeGroup( 70, 64 );
writeGroup( 62, 1 );
writeGroup( 6, QStringLiteral( "CONTINUOUS" ) );
writeHandle( 390, DXF_HANDPLOTSTYLE );
}
writeGroup( 0, QStringLiteral( "ENDTAB" ) );
// Text styles
writeGroup( 0, QStringLiteral( "TABLE" ) );
writeGroup( 2, QStringLiteral( "STYLE" ) );
writeHandle();
writeGroup( 100, QStringLiteral( "AcDbSymbolTable" ) );
writeGroup( 70, 1 );
// Provide only standard font for the moment
writeGroup( 0, QStringLiteral( "STYLE" ) );
writeHandle();
writeGroup( 100, QStringLiteral( "AcDbSymbolTableRecord" ) );
writeGroup( 100, QStringLiteral( "AcDbTextStyleTableRecord" ) );
writeGroup( 2, QStringLiteral( "STANDARD" ) );
writeGroup( 70, 64 );
writeGroup( 40, 0.0 );
writeGroup( 41, 1.0 );
writeGroup( 50, 0.0 );
writeGroup( 71, 0 );
writeGroup( 42, 5.0 );
writeGroup( 3, QStringLiteral( "romans.shx" ) );
writeGroup( 4, QLatin1String( "" ) );
writeGroup( 0, QStringLiteral( "ENDTAB" ) );
endSection();
}
void QgsDxfExport::writeBlocks()
{
startSection();
writeGroup( 2, QStringLiteral( "BLOCKS" ) );
const QStringList blockStrings = QStringList() << QStringLiteral( "*Model_Space" ) << QStringLiteral( "*Paper_Space" ) << QStringLiteral( "*Paper_Space0" );
for ( const QString &block : blockStrings )
{
writeGroup( 0, QStringLiteral( "BLOCK" ) );
writeHandle();
writeGroup( 330, QStringLiteral( "%1" ).arg( mBlockHandles[ block ], 0, 16 ) );
writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
writeGroup( 8, QStringLiteral( "0" ) );
writeGroup( 100, QStringLiteral( "AcDbBlockBegin" ) );
writeGroup( 2, block );
writeGroup( 70, 0 );
writeGroup( 0, QgsPoint( QgsWkbTypes::PointZ ) );
writeGroup( 3, block );
writeGroup( 1, QLatin1String( "" ) );
writeGroup( 0, QStringLiteral( "ENDBLK" ) );
writeHandle();
writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
writeGroup( 8, QStringLiteral( "0" ) );
writeGroup( 100, QStringLiteral( "AcDbBlockEnd" ) );
}
QgsRenderContext ct = renderContext();
// Iterate through all layers and get symbol layer pointers
QList< QPair< QgsSymbolLayer *, QgsSymbol * > > slList;
if ( mSymbologyExport != NoSymbology )
{
slList = symbolLayers( ct );
}
QList< QPair< QgsSymbolLayer *, QgsSymbol * > >::const_iterator slIt = slList.constBegin();
for ( ; slIt != slList.constEnd(); ++slIt )
{
QgsMarkerSymbolLayer *ml = dynamic_cast< QgsMarkerSymbolLayer *>( slIt->first );
if ( !ml )
continue;
// if point symbol layer and no data defined properties: write block
QgsSymbolRenderContext ctx( ct, QgsUnitTypes::RenderMapUnits, slIt->second->opacity(), false, slIt->second->renderHints(), nullptr );
ml->startRender( ctx );
// markers with data defined properties are inserted inline
if ( hasDataDefinedProperties( ml, slIt->second ) )
{
continue;
// ml->stopRender( ctx );
}
QString block( QStringLiteral( "symbolLayer%1" ).arg( mBlockCounter++ ) );
mBlockHandle = QStringLiteral( "%1" ).arg( mBlockHandles[ block ], 0, 16 );
writeGroup( 0, QStringLiteral( "BLOCK" ) );
writeHandle();
writeGroup( 330, mBlockHandle );
writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
writeGroup( 8, QStringLiteral( "0" ) );
writeGroup( 100, QStringLiteral( "AcDbBlockBegin" ) );
writeGroup( 2, block );
writeGroup( 70, 0 );
// x/y/z coordinates of reference point
// todo: consider anchor point
// double size = ml->size();
// size *= mapUnitScaleFactor( mSymbologyScale, ml->sizeUnit(), mMapUnits );
writeGroup( 0, QgsPoint( QgsWkbTypes::PointZ ) );
writeGroup( 3, block );
writeGroup( 1, QLatin1String( "" ) );
// maplayer 0 -> block receives layer from INSERT statement
ml->writeDxf( *this, mapUnitScaleFactor( mSymbologyScale, ml->sizeUnit(), mMapUnits ), QStringLiteral( "0" ), ctx );
writeGroup( 0, QStringLiteral( "ENDBLK" ) );
writeHandle();
writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
writeGroup( 8, QStringLiteral( "0" ) );
writeGroup( 100, QStringLiteral( "AcDbBlockEnd" ) );
mPointSymbolBlocks.insert( ml, block );
ml->stopRender( ctx );
}
endSection();
}
void QgsDxfExport::writeEntities()
{
startSection();
writeGroup( 2, QStringLiteral( "ENTITIES" ) );
mBlockHandle = QStringLiteral( "%1" ).arg( mBlockHandles[ QStringLiteral( "*Model_Space" )], 0, 16 );
QImage image( 10, 10, QImage::Format_ARGB32_Premultiplied );
image.setDotsPerMeterX( 96 / 25.4 * 1000 );
image.setDotsPerMeterY( 96 / 25.4 * 1000 );
QPainter painter( &image );
QgsRenderContext ctx;
ctx.setPainter( &painter );
ctx.setRendererScale( mSymbologyScale );
ctx.setExtent( mExtent );
ctx.setScaleFactor( 96.0 / 25.4 );
ctx.setMapToPixel( QgsMapToPixel( 1.0 / mFactor, mExtent.center().x(), mExtent.center().y(), mExtent.width() * mFactor,
mExtent.height() * mFactor, 0 ) );
// label engine
QgsLabelingEngine engine;
engine.setMapSettings( mMapSettings );
// iterate through the maplayers
const QList< QgsMapLayer *> layers = mMapSettings.layers();
for ( QgsMapLayer *ml : layers )
{
QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml );
if ( !vl || !layerIsScaleBasedVisible( vl ) )
{
continue;
}
bool hasStyleOverride = mMapSettings.layerStyleOverrides().contains( vl->id() );
if ( hasStyleOverride )
{
QgsDebugMsg( QString( "%1: apply override style" ).arg( vl->id() ) );
vl->styleManager()->setOverrideStyle( mMapSettings.layerStyleOverrides().value( vl->id() ) );
}
else
{
QgsDebugMsg( QString( "%1: not override style" ).arg( vl->id() ) );
}
QgsSymbolRenderContext sctx( ctx, QgsUnitTypes::RenderMillimeters, 1.0, false, 0, nullptr );
QgsFeatureRenderer *renderer = vl->renderer();
if ( !renderer )
{
if ( hasStyleOverride )
vl->styleManager()->restoreOverrideStyle();
continue;
}
renderer->startRender( ctx, vl->fields() );
QSet<QString> attributes = renderer->usedAttributes( ctx );
int attrIdx = mLayerNameAttribute.value( vl->id(), -1 );
if ( vl->fields().exists( attrIdx ) )
{
QString layerAttr = vl->fields().at( attrIdx ).name();
attributes << layerAttr;
}
const QgsAbstractVectorLayerLabeling *labeling = vl->labeling();
QgsDxfLabelProvider *lp = nullptr;
QgsDxfRuleBasedLabelProvider *rblp = nullptr;
if ( const QgsRuleBasedLabeling *rbl = dynamic_cast<const QgsRuleBasedLabeling *>( labeling ) )
{
rblp = new QgsDxfRuleBasedLabelProvider( *rbl, vl, this );
rblp->reinit( vl );
engine.addProvider( rblp );
if ( !rblp->prepare( ctx, attributes ) )
{
engine.removeProvider( rblp );
rblp = nullptr;
}
}
else
{
lp = new QgsDxfLabelProvider( vl, QString(), this, nullptr );
engine.addProvider( lp );
if ( !lp->prepare( ctx, attributes ) )
{
engine.removeProvider( lp );
lp = nullptr;
}
}
if ( mSymbologyExport == QgsDxfExport::SymbolLayerSymbology &&
( renderer->capabilities() & QgsFeatureRenderer::SymbolLevels ) &&
renderer->usingSymbolLevels() )
{
writeEntitiesSymbolLevels( vl );
renderer->stopRender( ctx );
if ( hasStyleOverride )
vl->styleManager()->restoreOverrideStyle();
continue;
}
QgsFeatureRequest freq = QgsFeatureRequest().setSubsetOfAttributes( attributes, vl->fields() ).setExpressionContext( ctx.expressionContext() );
freq.setFilterRect( mMapSettings.mapToLayerCoordinates( vl, mExtent ) );
QgsFeatureIterator featureIt = vl->getFeatures( freq );
QgsCoordinateTransform ct = mMapSettings.layerTransform( vl );
QgsFeature fet;
while ( featureIt.nextFeature( fet ) )
{
ctx.expressionContext().setFeature( fet );
QString lName( dxfLayerName( attrIdx < 0 ? layerName( vl ) : fet.attribute( attrIdx ).toString() ) );
sctx.setFeature( &fet );
if ( mSymbologyExport == NoSymbology )
{
addFeature( sctx, ct, lName, nullptr, nullptr ); // no symbology at all
}
else
{
QgsSymbolList symbolList = renderer->symbolsForFeature( fet, ctx );
if ( symbolList.empty() )
{
continue;
}
if ( mSymbologyExport == QgsDxfExport::SymbolLayerSymbology ) // symbol layer symbology, but layer does not use symbol levels
{
QgsSymbolList::iterator symbolIt = symbolList.begin();
for ( ; symbolIt != symbolList.end(); ++symbolIt )
{
int nSymbolLayers = ( *symbolIt )->symbolLayerCount();
for ( int i = 0; i < nSymbolLayers; ++i )
{
addFeature( sctx, ct, lName, ( *symbolIt )->symbolLayer( i ), *symbolIt );
}
}
}
else
{
// take first symbollayer from first symbol
QgsSymbol *s = symbolList.first();
if ( !s || s->symbolLayerCount() < 1 )
{
continue;
}
addFeature( sctx, ct, lName, s->symbolLayer( 0 ), s );
}
if ( lp )
{
lp->registerDxfFeature( fet, ctx, lName );
}
else if ( rblp )
{
rblp->registerDxfFeature( fet, ctx, lName );
}
}
}
renderer->stopRender( ctx );
if ( hasStyleOverride )
vl->styleManager()->restoreOverrideStyle();
}
engine.run( ctx );
endSection();
}
void QgsDxfExport::writeEntitiesSymbolLevels( QgsVectorLayer *layer )
{
if ( !layer )
{
return;
}
QgsFeatureRenderer *renderer = layer->renderer();
if ( !renderer )
{
// TODO return error
return;
}
QHash< QgsSymbol *, QList<QgsFeature> > features;
QgsRenderContext ctx = renderContext();
ctx.expressionContext().appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
QgsSymbolRenderContext sctx( ctx, QgsUnitTypes::RenderMillimeters, 1.0, false, 0, nullptr );
renderer->startRender( ctx, layer->fields() );
// get iterator
QgsFeatureRequest req;
if ( layer->wkbType() == QgsWkbTypes::NoGeometry )
{
req.setFlags( QgsFeatureRequest::NoGeometry );
}
req.setSubsetOfAttributes( renderer->usedAttributes( ctx ), layer->fields() );
req.setFilterRect( mMapSettings.mapToLayerCoordinates( layer, mExtent ) );
QgsFeatureIterator fit = layer->getFeatures( req );
// fetch features
QgsFeature fet;
QgsSymbol *featureSymbol = nullptr;
while ( fit.nextFeature( fet ) )
{
ctx.expressionContext().setFeature( fet );
featureSymbol = renderer->symbolForFeature( fet, ctx );
if ( !featureSymbol )
{
continue;
}
QHash< QgsSymbol *, QList<QgsFeature> >::iterator it = features.find( featureSymbol );
if ( it == features.end() )
{
it = features.insert( featureSymbol, QList<QgsFeature>() );
}
it.value().append( fet );
}
// find out order
QgsSymbolLevelOrder levels;
QgsSymbolList symbols = renderer->symbols( ctx );
for ( int i = 0; i < symbols.count(); i++ )
{
QgsSymbol *sym = symbols[i];
for ( int j = 0; j < sym->symbolLayerCount(); j++ )
{
int level = sym->symbolLayer( j )->renderingPass();
if ( level < 0 || level >= 1000 ) // ignore invalid levels
continue;
QgsSymbolLevelItem item( sym, j );
while ( level >= levels.count() ) // append new empty levels
levels.append( QgsSymbolLevel() );
levels[level].append( item );
}
}
QgsCoordinateTransform ct = mMapSettings.layerTransform( layer );
// export symbol layers and symbology
for ( int l = 0; l < levels.count(); l++ )
{
QgsSymbolLevel &level = levels[l];
for ( int i = 0; i < level.count(); i++ )
{
QgsSymbolLevelItem &item = level[i];
QHash< QgsSymbol *, QList<QgsFeature> >::iterator levelIt = features.find( item.symbol() );
if ( levelIt == features.end() )
{
QgsDebugMsg( QString( "No feature found for symbol on %1 %2.%3" ).arg( layer->id() ).arg( l ).arg( i ) );
continue;
}
int llayer = item.layer();
QList<QgsFeature> &featureList = levelIt.value();
QList<QgsFeature>::iterator featureIt = featureList.begin();
for ( ; featureIt != featureList.end(); ++featureIt )
{
sctx.setFeature( &*featureIt );
addFeature( sctx, ct, layer->name(), levelIt.key()->symbolLayer( llayer ), levelIt.key() );
}
}
}
renderer->stopRender( ctx );
}
void QgsDxfExport::writeEndFile()
{
// From GDAL trailer.dxf
mTextStream << "\
0\n\
SECTION\n\
2\n\
OBJECTS\n\
0\n\
DICTIONARY\n\
5\n\
C\n\
330\n\
0\n\
100\n\
AcDbDictionary\n\
281\n\
1\n\
3\n\
ACAD_GROUP\n\
350\n\
D\n\
3\n\
ACAD_LAYOUT\n\
350\n\
1A\n\
3\n\
ACAD_MLEADERSTYLE\n\
350\n\
43\n\
3\n\
ACAD_MLINESTYLE\n\
350\n\
17\n\
3\n\
ACAD_PLOTSETTINGS\n\
350\n\
19\n\
3\n\
ACAD_PLOTSTYLENAME\n\
350\n\
E\n\
3\n\
ACAD_TABLESTYLE\n\
350\n\
42\n\
3\n\
ACAD_VISUALSTYLE\n\
350\n\
2A\n\
0\n\
DICTIONARY\n\
5\n\
D\n\
102\n\
{ACAD_REACTORS\n\
330\n\
C\n\
102\n\
}\n\
330\n\
C\n\
100\n\
AcDbDictionary\n\
281\n\
1\n\
0\n\
DICTIONARY\n\
5\n\
1A\n\
102\n\
{ACAD_REACTORS\n\
330\n\
C\n\
102\n\
}\n\
330\n\
C\n\
100\n\
AcDbDictionary\n\
281\n\
1\n\
3\n\
Layout1\n\
350\n\
1E\n\
3\n\
Layout2\n\
350\n\
26\n\
3\n\
Model\n\
350\n\
22\n\
0\n\
DICTIONARY\n\
5\n\
43\n\
102\n\
{ACAD_REACTORS\n\
330\n\
C\n\
102\n\
}\n\
330\n\
C\n\
100\n\
AcDbDictionary\n\
281\n\
1\n\
0\n\
DICTIONARY\n\
5\n\
17\n\
102\n\
{ACAD_REACTORS\n\
330\n\
C\n\
102\n\
}\n\
330\n\
C\n\
100\n\
AcDbDictionary\n\
281\n\
1\n\
3\n\
Standard\n\
350\n\
18\n\
0\n\
DICTIONARY\n\
5\n\
19\n\
102\n\
{ACAD_REACTORS\n\
330\n\
C\n\
102\n\
}\n\
330\n\
C\n\
100\n\
AcDbDictionary\n\
281\n\
1\n\
0\n\
ACDBDICTIONARYWDFLT\n\
5\n\
E\n\
102\n\
{ACAD_REACTORS\n\
330\n\
C\n\
102\n\
}\n\
330\n\
C\n\
100\n\
AcDbDictionary\n\
281\n\
1\n\
3\n\
Normal\n\
350\n\
F\n\
100\n\
AcDbDictionaryWithDefault\n\
340\n\
F\n\
0\n\
DICTIONARY\n\
5\n\
42\n\
102\n\
{ACAD_REACTORS\n\
330\n\
C\n\
102\n\
}\n\
330\n\
C\n\
100\n\
AcDbDictionary\n\
281\n\
1\n\
0\n\
DICTIONARY\n\
5\n\
2A\n\
102\n\
{ACAD_REACTORS\n\
330\n\
C\n\
102\n\
}\n\
330\n\
C\n\
100\n\
AcDbDictionary\n\
281\n\
1\n\
3\n\
2dWireframe\n\
350\n\
2F\n\
3\n\
3D Hidden\n\
350\n\
31\n\
3\n\
3dWireframe\n\
350\n\
30\n\
3\n\
Basic\n\
350\n\
32\n\
3\n\
Brighten\n\
350\n\
36\n\
3\n\
ColorChange\n\
350\n\
3A\n\
3\n\
Conceptual\n\
350\n\
34\n\
3\n\
Dim\n\
350\n\
35\n\
3\n\
Facepattern\n\
350\n\
39\n\
3\n\
Flat\n\
350\n\
2B\n\
3\n\
FlatWithEdges\n\
350\n\
2C\n\
3\n\
Gouraud\n\
350\n\
2D\n\
3\n\
GouraudWithEdges\n\
350\n\
2E\n\
3\n\
Linepattern\n\
350\n\
38\n\
3\n\
Realistic\n\
350\n\
33\n\
3\n\
Thicken\n\
350\n\
37\n\
0\n\
LAYOUT\n\
5\n\
1E\n\
102\n\
{ACAD_REACTORS\n\
330\n\
1A\n\
102\n\
}\n\
330\n\
1A\n\
100\n\
AcDbPlotSettings\n\
1\n\
\n\
2\n\
none_device\n\
4\n\
\n\
6\n\
\n\
40\n\
0.0\n\
41\n\
0.0\n\
42\n\
0.0\n\
43\n\
0.0\n\
44\n\
0.0\n\
45\n\
0.0\n\
46\n\
0.0\n\
47\n\
0.0\n\
48\n\
0.0\n\
49\n\
0.0\n\
140\n\
0.0\n\
141\n\
0.0\n\
142\n\
1.0\n\
143\n\
1.0\n\
70\n\
688\n\
72\n\
0\n\
73\n\
0\n\
74\n\
5\n\
7\n\
\n\
75\n\
16\n\
76\n\
0\n\
77\n\
2\n\
78\n\
300\n\
147\n\
1.0\n\
148\n\
0.0\n\
149\n\
0.0\n\
100\n\
AcDbLayout\n\
1\n\
Layout1\n\
70\n\
1\n\
71\n\
1\n\
10\n\
0.0\n\
20\n\
0.0\n\
11\n\
12.0\n\
21\n\
9.0\n\
12\n\
0.0\n\
22\n\
0.0\n\
32\n\
0.0\n\
14\n\
1.000000000000000E+20\n\
24\n\
1.000000000000000E+20\n\
34\n\
1.000000000000000E+20\n\
15\n\
-1.000000000000000E+20\n\
25\n\
-1.000000000000000E+20\n\
35\n\
-1.000000000000000E+20\n\
146\n\
0.0\n\
13\n\
0.0\n\
23\n\
0.0\n\
33\n\
0.0\n\
16\n\
1.0\n\
26\n\
0.0\n\
36\n\
0.0\n\
17\n\
0.0\n\
27\n\
1.0\n\
37\n\
0.0\n\
76\n\
0\n\
330\n\
1B\n\
0\n\
LAYOUT\n\
5\n\
26\n\
102\n\
{ACAD_REACTORS\n\
330\n\
1A\n\
102\n\
}\n\
330\n\
1A\n\
100\n\
AcDbPlotSettings\n\
1\n\
\n\
2\n\
none_device\n\
4\n\
\n\
6\n\
\n\
40\n\
0.0\n\
41\n\
0.0\n\
42\n\
0.0\n\
43\n\
0.0\n\
44\n\
0.0\n\
45\n\
0.0\n\
46\n\
0.0\n\
47\n\
0.0\n\
48\n\
0.0\n\
49\n\
0.0\n\
140\n\
0.0\n\
141\n\
0.0\n\
142\n\
1.0\n\
143\n\
1.0\n\
70\n\
688\n\
72\n\
0\n\
73\n\
0\n\
74\n\
5\n\
7\n\
\n\
75\n\
16\n\
76\n\
0\n\
77\n\
2\n\
78\n\
300\n\
147\n\
1.0\n\
148\n\
0.0\n\
149\n\
0.0\n\
100\n\
AcDbLayout\n\
1\n\
Layout2\n\
70\n\
1\n\
71\n\
2\n\
10\n\
0.0\n\
20\n\
0.0\n\
11\n\
0.0\n\
21\n\
0.0\n\
12\n\
0.0\n\
22\n\
0.0\n\
32\n\
0.0\n\
14\n\
0.0\n\
24\n\
0.0\n\
34\n\
0.0\n\
15\n\
0.0\n\
25\n\
0.0\n\
35\n\
0.0\n\
146\n\
0.0\n\
13\n\
0.0\n\
23\n\
0.0\n\
33\n\
0.0\n\
16\n\
1.0\n\
26\n\
0.0\n\
36\n\
0.0\n\
17\n\
0.0\n\
27\n\
1.0\n\
37\n\
0.0\n\
76\n\
0\n\
330\n\
23\n\
0\n\
LAYOUT\n\
5\n\
22\n\
102\n\
{ACAD_REACTORS\n\
330\n\
1A\n\
102\n\
}\n\
330\n\
1A\n\
100\n\
AcDbPlotSettings\n\
1\n\
\n\
2\n\
none_device\n\
4\n\
\n\
6\n\
\n\
40\n\
0.0\n\
41\n\
0.0\n\
42\n\
0.0\n\
43\n\
0.0\n\
44\n\
0.0\n\
45\n\
0.0\n\
46\n\
0.0\n\
47\n\
0.0\n\
48\n\
0.0\n\
49\n\
0.0\n\
140\n\
0.0\n\
141\n\
0.0\n\
142\n\
1.0\n\
143\n\
1.0\n\
70\n\
1712\n\
72\n\
0\n\
73\n\
0\n\
74\n\
0\n\
7\n\
\n\
75\n\
0\n\
76\n\
0\n\
77\n\
2\n\
78\n\
300\n\
147\n\
1.0\n\
148\n\
0.0\n\
149\n\
0.0\n\
100\n\
AcDbLayout\n\
1\n\
Model\n\
70\n\
1\n\
71\n\
0\n\
10\n\
0.0\n\
20\n\
0.0\n\
11\n\
12.0\n\
21\n\
9.0\n\
12\n\
0.0\n\
22\n\
0.0\n\
32\n\
0.0\n\
14\n\
30.0\n\
24\n\
49.75\n\
34\n\
0.0\n\
15\n\
130.5\n\
25\n\
163.1318914119703\n\
35\n\
0.0\n\
146\n\
0.0\n\
13\n\
0.0\n\
23\n\
0.0\n\
33\n\
0.0\n\
16\n\
1.0\n\
26\n\
0.0\n\
36\n\
0.0\n\
17\n\
0.0\n\
27\n\
1.0\n\
37\n\
0.0\n\
76\n\
0\n\
330\n\
1F\n\
331\n\
29\n\
0\n\
MLINESTYLE\n\
5\n\
18\n\
102\n\
{ACAD_REACTORS\n\
330\n\
17\n\
102\n\
}\n\
330\n\
17\n\
100\n\
AcDbMlineStyle\n\
2\n\
Standard\n\
70\n\
0\n\
3\n\
\n\
62\n\
256\n\
51\n\
90.0\n\
52\n\
90.0\n\
71\n\
2\n\
49\n\
0.5\n\
62\n\
256\n\
6\n\
BYLAYER\n\
49\n\
-0.5\n\
62\n\
256\n\
6\n\
BYLAYER\n\
0\n\
ACDBPLACEHOLDER\n\
5\n\
F\n\
102\n\
{ACAD_REACTORS\n\
330\n\
E\n\
102\n\
}\n\
330\n\
E\n\
0\n\
VISUALSTYLE\n\
5\n\
2F\n\
102\n\
{ACAD_REACTORS\n\
330\n\
2A\n\
102\n\
}\n\
330\n\
2A\n\
100\n\
AcDbVisualStyle\n\
2\n\
2dWireframe\n\
70\n\
4\n\
71\n\
0\n\
72\n\
2\n\
73\n\
0\n\
90\n\
0\n\
40\n\
-0.6\n\
41\n\
-30.0\n\
62\n\
5\n\
63\n\
7\n\
421\n\
16777215\n\
74\n\
1\n\
91\n\
4\n\
64\n\
7\n\
65\n\
257\n\
75\n\
1\n\
175\n\
1\n\
42\n\
1.0\n\
92\n\
0\n\
66\n\
257\n\
43\n\
1.0\n\
76\n\
1\n\
77\n\
6\n\
78\n\
2\n\
67\n\
7\n\
79\n\
5\n\
170\n\
0\n\
171\n\
0\n\
290\n\
0\n\
174\n\
0\n\
93\n\
1\n\
44\n\
0.0\n\
173\n\
0\n\
291\n\
0\n\
45\n\
0.0\n\
1001\n\
ACAD\n\
1000\n\
AcDbSavedByObjectVersion\n\
1070\n\
0\n\
0\n\
VISUALSTYLE\n\
5\n\
31\n\
102\n\
{ACAD_REACTORS\n\
330\n\
2A\n\
102\n\
}\n\
330\n\
2A\n\
100\n\
AcDbVisualStyle\n\
2\n\
3D Hidden\n\
70\n\
6\n\
71\n\
1\n\
72\n\
2\n\
73\n\
2\n\
90\n\
0\n\
40\n\
-0.6\n\
41\n\
-30.0\n\
62\n\
5\n\
63\n\
7\n\
421\n\
16777215\n\
74\n\
2\n\
91\n\
2\n\
64\n\
7\n\
65\n\
257\n\
75\n\
2\n\
175\n\
1\n\
42\n\
40.0\n\
92\n\
0\n\
66\n\
257\n\
43\n\
1.0\n\
76\n\
1\n\
77\n\
6\n\
78\n\
2\n\
67\n\
7\n\
79\n\
3\n\
170\n\
0\n\
171\n\
0\n\
290\n\
0\n\
174\n\
0\n\
93\n\
1\n\
44\n\
0.0\n\
173\n\
0\n\
291\n\
0\n\
45\n\
0.0\n\
1001\n\
ACAD\n\
1000\n\
AcDbSavedByObjectVersion\n\
1070\n\
0\n\
0\n\
VISUALSTYLE\n\
5\n\
30\n\
102\n\
{ACAD_REACTORS\n\
330\n\
2A\n\
102\n\
}\n\
330\n\
2A\n\
100\n\
AcDbVisualStyle\n\
2\n\
3dWireframe\n\
70\n\
5\n\
71\n\
0\n\
72\n\
2\n\
73\n\
0\n\
90\n\
0\n\
40\n\
-0.6\n\
41\n\
-30.0\n\
62\n\
5\n\
63\n\
7\n\
421\n\
16777215\n\
74\n\
1\n\
91\n\
4\n\
64\n\
7\n\
65\n\
257\n\
75\n\
1\n\
175\n\
1\n\
42\n\
1.0\n\
92\n\
0\n\
66\n\
257\n\
43\n\
1.0\n\
76\n\
1\n\
77\n\
6\n\
78\n\
2\n\
67\n\
7\n\
79\n\
5\n\
170\n\
0\n\
171\n\
0\n\
290\n\
0\n\
174\n\
0\n\
93\n\
1\n\
44\n\
0.0\n\
173\n\
0\n\
291\n\
0\n\
45\n\
0.0\n\
1001\n\
ACAD\n\
1000\n\
AcDbSavedByObjectVersion\n\
1070\n\
0\n\
0\n\
VISUALSTYLE\n\
5\n\
32\n\
102\n\
{ACAD_REACTORS\n\
330\n\
2A\n\
102\n\
}\n\
330\n\
2A\n\
100\n\
AcDbVisualStyle\n\
2\n\
Basic\n\
70\n\
7\n\
71\n\
1\n\
72\n\
0\n\
73\n\
1\n\
90\n\
0\n\
40\n\
-0.6\n\
41\n\
-30.0\n\
62\n\
5\n\
63\n\
7\n\
421\n\
16777215\n\
74\n\
0\n\
91\n\
4\n\
64\n\
7\n\
65\n\
257\n\
75\n\
1\n\
175\n\
1\n\
42\n\
1.0\n\
92\n\
8\n\
66\n\
7\n\
43\n\
1.0\n\
76\n\
1\n\
77\n\
6\n\
78\n\
2\n\
67\n\
7\n\
79\n\
5\n\
170\n\
0\n\
171\n\
0\n\
290\n\
0\n\
174\n\
0\n\
93\n\
1\n\
44\n\
0.0\n\
173\n\
0\n\
291\n\
1\n\
45\n\
0.0\n\
1001\n\
ACAD\n\
1000\n\
AcDbSavedByObjectVersion\n\
1070\n\
0\n\
0\n\
VISUALSTYLE\n\
5\n\
36\n\
102\n\
{ACAD_REACTORS\n\
330\n\
2A\n\
102\n\
}\n\
330\n\
2A\n\
100\n\
AcDbVisualStyle\n\
2\n\
Brighten\n\
70\n\
12\n\
71\n\
2\n\
72\n\
2\n\
73\n\
0\n\
90\n\
0\n\
40\n\
-0.6\n\
41\n\
-30.0\n\
62\n\
5\n\
63\n\
7\n\
421\n\
16777215\n\
74\n\
1\n\
91\n\
4\n\
64\n\
7\n\
65\n\
257\n\
75\n\
1\n\
175\n\
1\n\
42\n\
1.0\n\
92\n\
8\n\
66\n\
7\n\
43\n\
1.0\n\
76\n\
1\n\
77\n\
6\n\
78\n\
2\n\
67\n\
7\n\
79\n\
5\n\
170\n\
0\n\
171\n\
0\n\
290\n\
0\n\
174\n\
0\n\
93\n\
1\n\
44\n\
50.0\n\
173\n\
0\n\
291\n\
1\n\
45\n\
0.0\n\
1001\n\
ACAD\n\
1000\n\
AcDbSavedByObjectVersion\n\
1070\n\
0\n\
0\n\
VISUALSTYLE\n\
5\n\
3A\n\
102\n\
{ACAD_REACTORS\n\
330\n\
2A\n\
102\n\
}\n\
330\n\
2A\n\
100\n\
AcDbVisualStyle\n\
2\n\
ColorChange\n\
70\n\
16\n\
71\n\
2\n\
72\n\
2\n\
73\n\
3\n\
90\n\
0\n\
40\n\
-0.6\n\
41\n\
-30.0\n\
62\n\
5\n\
63\n\
8\n\
421\n\
8421504\n\
74\n\
1\n\
91\n\
4\n\
64\n\
7\n\
65\n\
257\n\
75\n\
1\n\
175\n\
1\n\
42\n\
1.0\n\
92\n\
8\n\
66\n\
8\n\
424\n\
8421504\n\
43\n\
1.0\n\
76\n\
1\n\
77\n\
6\n\
78\n\
2\n\
67\n\
7\n\
79\n\
5\n\
170\n\
0\n\
171\n\
0\n\
290\n\
0\n\
174\n\
0\n\
93\n\
1\n\
44\n\
0.0\n\
173\n\
0\n\
291\n\
1\n\
45\n\
0.0\n\
1001\n\
ACAD\n\
1000\n\
AcDbSavedByObjectVersion\n\
1070\n\
0\n\
0\n\
VISUALSTYLE\n\
5\n\
34\n\
102\n\
{ACAD_REACTORS\n\
330\n\
2A\n\
102\n\
}\n\
330\n\
2A\n\
100\n\
AcDbVisualStyle\n\
2\n\
Conceptual\n\
70\n\
9\n\
71\n\
3\n\
72\n\
2\n\
73\n\
0\n\
90\n\
0\n\
40\n\
-0.6\n\
41\n\
-30.0\n\
62\n\
5\n\
63\n\
7\n\
421\n\
16777215\n\
74\n\
2\n\
91\n\
2\n\
64\n\
7\n\
65\n\
257\n\
75\n\
1\n\
175\n\
1\n\
42\n\
40.0\n\
92\n\
8\n\
66\n\
7\n\
43\n\
1.0\n\
76\n\
1\n\
77\n\
6\n\
78\n\
2\n\
67\n\
7\n\
79\n\
3\n\
170\n\
0\n\
171\n\
0\n\
290\n\
0\n\
174\n\
0\n\
93\n\
1\n\
44\n\
0.0\n\
173\n\
0\n\
291\n\
0\n\
45\n\
0.0\n\
1001\n\
ACAD\n\
1000\n\
AcDbSavedByObjectVersion\n\
1070\n\
0\n\
0\n\
VISUALSTYLE\n\
5\n\
35\n\
102\n\
{ACAD_REACTORS\n\
330\n\
2A\n\
102\n\
}\n\
330\n\
2A\n\
100\n\
AcDbVisualStyle\n\
2\n\
Dim\n\
70\n\
11\n\
71\n\
2\n\
72\n\
2\n\
73\n\
0\n\
90\n\
0\n\
40\n\
-0.6\n\
41\n\
-30.0\n\
62\n\
5\n\
63\n\
7\n\
421\n\
16777215\n\
74\n\
1\n\
91\n\
4\n\
64\n\
7\n\
65\n\
257\n\
75\n\
1\n\
175\n\
1\n\
42\n\
1.0\n\
92\n\
8\n\
66\n\
7\n\
43\n\
1.0\n\
76\n\
1\n\
77\n\
6\n\
78\n\
2\n\
67\n\
7\n\
79\n\
5\n\
170\n\
0\n\
171\n\
0\n\
290\n\
0\n\
174\n\
0\n\
93\n\
1\n\
44\n\
-50.0\n\
173\n\
0\n\
291\n\
1\n\
45\n\
0.0\n\
1001\n\
ACAD\n\
1000\n\
AcDbSavedByObjectVersion\n\
1070\n\
0\n\
0\n\
VISUALSTYLE\n\
5\n\
39\n\
102\n\
{ACAD_REACTORS\n\
330\n\
2A\n\
102\n\
}\n\
330\n\
2A\n\
100\n\
AcDbVisualStyle\n\
2\n\
Facepattern\n\
70\n\
15\n\
71\n\
2\n\
72\n\
2\n\
73\n\
0\n\
90\n\
0\n\
40\n\
-0.6\n\
41\n\
-30.0\n\
62\n\
5\n\
63\n\
7\n\
421\n\
16777215\n\
74\n\
1\n\
91\n\
4\n\
64\n\
7\n\
65\n\
257\n\
75\n\
1\n\
175\n\
1\n\
42\n\
1.0\n\
92\n\
8\n\
66\n\
7\n\
43\n\
1.0\n\
76\n\
1\n\
77\n\
6\n\
78\n\
2\n\
67\n\
7\n\
79\n\
5\n\
170\n\
0\n\
171\n\
0\n\
290\n\
0\n\
174\n\
0\n\
93\n\
1\n\
44\n\
0.0\n\
173\n\
0\n\
291\n\
1\n\
45\n\
0.0\n\
1001\n\
ACAD\n\
1000\n\
AcDbSavedByObjectVersion\n\
1070\n\
0\n\
0\n\
VISUALSTYLE\n\
5\n\
2B\n\
102\n\
{ACAD_REACTORS\n\
330\n\
2A\n\
102\n\
}\n\
330\n\
2A\n\
100\n\
AcDbVisualStyle\n\
2\n\
Flat\n\
70\n\
0\n\
71\n\
2\n\
72\n\
1\n\
73\n\
1\n\
90\n\
2\n\
40\n\
-0.6\n\
41\n\
30.0\n\
62\n\
5\n\
63\n\
7\n\
421\n\
16777215\n\
74\n\
0\n\
91\n\
4\n\
64\n\
7\n\
65\n\
257\n\
75\n\
1\n\
175\n\
1\n\
42\n\
1.0\n\
92\n\
8\n\
66\n\
7\n\
43\n\
1.0\n\
76\n\
1\n\
77\n\
6\n\
78\n\
2\n\
67\n\
7\n\
79\n\
5\n\
170\n\
0\n\
171\n\
0\n\
290\n\
0\n\
174\n\
0\n\
93\n\
13\n\
44\n\
0.0\n\
173\n\
0\n\
291\n\
1\n\
45\n\
0.0\n\
1001\n\
ACAD\n\
1000\n\
AcDbSavedByObjectVersion\n\
1070\n\
0\n\
0\n\
VISUALSTYLE\n\
5\n\
2C\n\
102\n\
{ACAD_REACTORS\n\
330\n\
2A\n\
102\n\
}\n\
330\n\
2A\n\
100\n\
AcDbVisualStyle\n\
2\n\
FlatWithEdges\n\
70\n\
1\n\
71\n\
2\n\
72\n\
1\n\
73\n\
1\n\
90\n\
2\n\
40\n\
-0.6\n\
41\n\
30.0\n\
62\n\
5\n\
63\n\
7\n\
421\n\
16777215\n\
74\n\
1\n\
91\n\
4\n\
64\n\
7\n\
65\n\
257\n\
75\n\
1\n\
175\n\
1\n\
42\n\
1.0\n\
92\n\
0\n\
66\n\
257\n\
43\n\
1.0\n\
76\n\
1\n\
77\n\
6\n\
78\n\
2\n\
67\n\
7\n\
79\n\
5\n\
170\n\
0\n\
171\n\
0\n\
290\n\
0\n\
174\n\
0\n\
93\n\
13\n\
44\n\
0.0\n\
173\n\
0\n\
291\n\
1\n\
45\n\
0.0\n\
1001\n\
ACAD\n\
1000\n\
AcDbSavedByObjectVersion\n\
1070\n\
0\n\
0\n\
VISUALSTYLE\n\
5\n\
2D\n\
102\n\
{ACAD_REACTORS\n\
330\n\
2A\n\
102\n\
}\n\
330\n\
2A\n\
100\n\
AcDbVisualStyle\n\
2\n\
Gouraud\n\
70\n\
2\n\
71\n\
2\n\
72\n\
2\n\
73\n\
1\n\
90\n\
2\n\
40\n\
-0.6\n\
41\n\
30.0\n\
62\n\
5\n\
63\n\
7\n\
421\n\
16777215\n\
74\n\
0\n\
91\n\
4\n\
64\n\
7\n\
65\n\
257\n\
75\n\
1\n\
175\n\
1\n\
42\n\
1.0\n\
92\n\
0\n\
66\n\
7\n\
43\n\
1.0\n\
76\n\
1\n\
77\n\
6\n\
78\n\
2\n\
67\n\
7\n\
79\n\
5\n\
170\n\
0\n\
171\n\
0\n\
290\n\
0\n\
174\n\
0\n\
93\n\
13\n\
44\n\
0.0\n\
173\n\
0\n\
291\n\
1\n\
45\n\
0.0\n\
1001\n\
ACAD\n\
1000\n\
AcDbSavedByObjectVersion\n\
1070\n\
0\n\
0\n\
VISUALSTYLE\n\
5\n\
2E\n\
102\n\
{ACAD_REACTORS\n\
330\n\
2A\n\
102\n\
}\n\
330\n\
2A\n\
100\n\
AcDbVisualStyle\n\
2\n\
GouraudWithEdges\n\
70\n\
3\n\
71\n\
2\n\
72\n\
2\n\
73\n\
1\n\
90\n\
2\n\
40\n\
-0.6\n\
41\n\
30.0\n\
62\n\
5\n\
63\n\
7\n\
421\n\
16777215\n\
74\n\
1\n\
91\n\
4\n\
64\n\
7\n\
65\n\
257\n\
75\n\
1\n\
175\n\
1\n\
42\n\
1.0\n\
92\n\
0\n\
66\n\
257\n\
43\n\
1.0\n\
76\n\
1\n\
77\n\
6\n\
78\n\
2\n\
67\n\
7\n\
79\n\
5\n\
170\n\
0\n\
171\n\
0\n\
290\n\
0\n\
174\n\
0\n\
93\n\
13\n\
44\n\
0.0\n\
173\n\
0\n\
291\n\
1\n\
45\n\
0.0\n\
1001\n\
ACAD\n\
1000\n\
AcDbSavedByObjectVersion\n\
1070\n\
0\n\
0\n\
VISUALSTYLE\n\
5\n\
38\n\
102\n\
{ACAD_REACTORS\n\
330\n\
2A\n\
102\n\
}\n\
330\n\
2A\n\
100\n\
AcDbVisualStyle\n\
2\n\
Linepattern\n\
70\n\
14\n\
71\n\
2\n\
72\n\
2\n\
73\n\
0\n\
90\n\
0\n\
40\n\
-0.6\n\
41\n\
-30.0\n\
62\n\
5\n\
63\n\
7\n\
421\n\
16777215\n\
74\n\
1\n\
91\n\
4\n\
64\n\
7\n\
65\n\
257\n\
75\n\
7\n\
175\n\
7\n\
42\n\
1.0\n\
92\n\
8\n\
66\n\
7\n\
43\n\
1.0\n\
76\n\
1\n\
77\n\
6\n\
78\n\
2\n\
67\n\
7\n\
79\n\
5\n\
170\n\
0\n\
171\n\
0\n\
290\n\
0\n\
174\n\
0\n\
93\n\
1\n\
44\n\
0.0\n\
173\n\
0\n\
291\n\
1\n\
45\n\
0.0\n\
1001\n\
ACAD\n\
1000\n\
AcDbSavedByObjectVersion\n\
1070\n\
0\n\
0\n\
VISUALSTYLE\n\
5\n\
33\n\
102\n\
{ACAD_REACTORS\n\
330\n\
2A\n\
102\n\
}\n\
330\n\
2A\n\
100\n\
AcDbVisualStyle\n\
2\n\
Realistic\n\
70\n\
8\n\
71\n\
2\n\
72\n\
2\n\
73\n\
0\n\
90\n\
0\n\
40\n\
-0.6\n\
41\n\
-30.0\n\
62\n\
5\n\
63\n\
7\n\
421\n\
16777215\n\
74\n\
1\n\
91\n\
0\n\
64\n\
7\n\
65\n\
257\n\
75\n\
1\n\
175\n\
1\n\
42\n\
1.0\n\
92\n\
8\n\
66\n\
8\n\
424\n\
7895160\n\
43\n\
1.0\n\
76\n\
1\n\
77\n\
6\n\
78\n\
2\n\
67\n\
7\n\
79\n\
5\n\
170\n\
0\n\
171\n\
0\n\
290\n\
0\n\
174\n\
0\n\
93\n\
13\n\
44\n\
0.0\n\
173\n\
0\n\
291\n\
0\n\
45\n\
0.0\n\
1001\n\
ACAD\n\
1000\n\
AcDbSavedByObjectVersion\n\
1070\n\
0\n\
0\n\
VISUALSTYLE\n\
5\n\
37\n\
102\n\
{ACAD_REACTORS\n\
330\n\
2A\n\
102\n\
}\n\
330\n\
2A\n\
100\n\
AcDbVisualStyle\n\
2\n\
Thicken\n\
70\n\
13\n\
71\n\
2\n\
72\n\
2\n\
73\n\
0\n\
90\n\
0\n\
40\n\
-0.6\n\
41\n\
-30.0\n\
62\n\
5\n\
63\n\
7\n\
421\n\
16777215\n\
74\n\
1\n\
91\n\
4\n\
64\n\
7\n\
65\n\
257\n\
75\n\
1\n\
175\n\
1\n\
42\n\
1.0\n\
92\n\
12\n\
66\n\
7\n\
43\n\
1.0\n\
76\n\
1\n\
77\n\
6\n\
78\n\
2\n\
67\n\
7\n\
79\n\
5\n\
170\n\
0\n\
171\n\
0\n\
290\n\
0\n\
174\n\
0\n\
93\n\
1\n\
44\n\
0.0\n\
173\n\
0\n\
291\n\
1\n\
45\n\
0.0\n\
1001\n\
ACAD\n\
1000\n\
AcDbSavedByObjectVersion\n\
1070\n\
0\n\
0\n\
ENDSEC\n\
";
writeGroup( 0, QStringLiteral( "EOF" ) );
}
void QgsDxfExport::startSection()
{
writeGroup( 0, QStringLiteral( "SECTION" ) );
}
void QgsDxfExport::endSection()
{
writeGroup( 0, QStringLiteral( "ENDSEC" ) );
}
void QgsDxfExport::writePoint( const QgsPoint &pt, const QString &layer, const QColor &color, QgsSymbolRenderContext &ctx, const QgsSymbolLayer *symbolLayer, const QgsSymbol *symbol, double angle )
{
#if 0
// debug: draw rectangle for debugging
const QgsMarkerSymbolLayer *msl = dynamic_cast< const QgsMarkerSymbolLayer * >( symbolLayer );
if ( msl )
{
double halfSize = msl->size() * mapUnitScaleFactor( mSymbologyScale,
msl->sizeUnit(), mMapUnits ) / 2.0;
writeGroup( 0, "SOLID" );
writeGroup( 8, layer );
writeGroup( 62, 1 );
writeGroup( 0, QgsPoint( QgsWkbTypes::PointZ, pt.x() - halfSize, pt.y() - halfSize ) );
writeGroup( 1, QgsPoint( QgsWkbTypes::PointZ, pt.x() + halfSize, pt.y() - halfSize ) );
writeGroup( 2, QgsPoint( QgsWkbTypes::PointZ, pt.x() - halfSize, pt.y() + halfSize ) );
writeGroup( 3, QgsPoint( QgsWkbTypes::PointZ, pt.x() + halfSize, pt.y() + halfSize ) );
}
#endif // 0
// insert block or write point directly?
QHash< const QgsSymbolLayer *, QString >::const_iterator blockIt = mPointSymbolBlocks.constFind( symbolLayer );
if ( !symbolLayer || blockIt == mPointSymbolBlocks.constEnd() )
{
// write symbol directly here
const QgsMarkerSymbolLayer *msl = dynamic_cast< const QgsMarkerSymbolLayer * >( symbolLayer );
if ( msl && symbol )
{
if ( symbolLayer->writeDxf( *this, mapUnitScaleFactor( mSymbologyScale, msl->sizeUnit(), mMapUnits ), layer, ctx, QPointF( pt.x(), pt.y() ) ) )
{
return;
}
}
writePoint( layer, color, pt ); // write default point symbol
}
else
{
// insert block reference
writeGroup( 0, QStringLiteral( "INSERT" ) );
writeHandle();
writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
writeGroup( 100, QStringLiteral( "AcDbBlockReference" ) );
writeGroup( 8, layer );
writeGroup( 2, blockIt.value() ); // Block name
writeGroup( 50, angle ); // angle
writeGroup( 0, pt ); // Insertion point (in OCS)
}
}
void QgsDxfExport::writePolyline( const QgsPointSequence &line, const QString &layer, const QString &lineStyleName, const QColor &color, double width )
{
int n = line.size();
if ( n == 0 )
{
QgsDebugMsg( QString( "writePolyline: empty line layer=%1 lineStyleName=%2" ).arg( layer, lineStyleName ) );
return;
}
bool polygon = line[0] == line[ line.size() - 1 ];
if ( polygon )
--n;
if ( n < 2 )
{
QgsDebugMsg( QString( "writePolyline: line too short layer=%1 lineStyleName=%2" ).arg( layer, lineStyleName ) );
return;
}
if ( mForce2d || !line.at( 0 ).is3D() )
{
writeGroup( 0, QStringLiteral( "LWPOLYLINE" ) );
writeHandle();
writeGroup( 8, layer );
writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
writeGroup( 100, QStringLiteral( "AcDbPolyline" ) );
writeGroup( 6, lineStyleName );
writeGroup( color );
writeGroup( 90, n );
writeGroup( 70, polygon ? 1 : 0 );
writeGroup( 43, width );
for ( int i = 0; i < n; i++ )
writeGroup( 0, line[i] );
}
else
{
writeGroup( 0, QStringLiteral( "POLYLINE" ) );
int plHandle = writeHandle();
writeGroup( 330, mBlockHandle );
writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
writeGroup( 8, layer );
writeGroup( 6, lineStyleName );
writeGroup( color );
writeGroup( 100, QStringLiteral( "AcDb3dPolyline" ) );
writeGroup( 0, QgsPoint( QgsWkbTypes::PointZ ) );
writeGroup( 70, 8 );
for ( int i = 0; i < n; i++ )
{
writeGroup( 0, QStringLiteral( "VERTEX" ) );
writeHandle();
writeGroup( 330, plHandle );
writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
writeGroup( 8, layer );
writeGroup( color );
writeGroup( 100, QStringLiteral( "AcDbVertex" ) );
writeGroup( 100, QStringLiteral( "AcDb3dPolylineVertex" ) );
writeGroup( 0, line[i] );
writeGroup( 70, 32 );
}
writeGroup( 0, QStringLiteral( "SEQEND" ) );
writeHandle();
writeGroup( 330, plHandle );
writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
writeGroup( 8, layer );
writeGroup( color );
}
}
void QgsDxfExport::writePolygon( const QgsRingSequence &polygon, const QString &layer, const QString &hatchPattern, const QColor &color )
{
writeGroup( 0, QStringLiteral( "HATCH" ) ); // Entity type
writeHandle();
writeGroup( 330, mBlockHandle );
writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
writeGroup( 8, layer ); // Layer name
writeGroup( color ); // Color
writeGroup( 100, QStringLiteral( "AcDbHatch" ) );
writeGroup( 0, QgsPoint( QgsWkbTypes::PointZ ) ); // Elevation point (in OCS)
writeGroup( 200, QgsPoint( QgsWkbTypes::PointZ, 0.0, 0.0, 1.0 ) );
writeGroup( 2, hatchPattern ); // Hatch pattern name
writeGroup( 70, hatchPattern == QLatin1String( "SOLID" ) ); // Solid fill flag (solid fill = 1; pattern fill = 0)
writeGroup( 71, 0 ); // Associativity flag (associative = 1; non-associative = 0)
writeGroup( 91, polygon.size() ); // Number of boundary paths (loops)
for ( int i = 0; i < polygon.size(); ++i )
{
writeGroup( 92, 2 ); // Boundary path type flag (bit coded): 0 = Default; 1 = External; 2 = Polyline 4 = Derived; 8 = Textbox; 16 = Outermost
writeGroup( 72, 0 ); // Has bulge flag
writeGroup( 73, 1 ); // Is closed flag
writeGroup( 93, polygon[i].size() ); // Number of edges in this boundary path (only if boundary is not a polyline
for ( int j = 0; j < polygon[i].size(); ++j )
{
writeGroup( 0, polygon[i][j] ); // Vertex location (in OCS)
}
writeGroup( 97, 0 ); // Number of source boundary objects
}
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)
writeGroup( 76, 1 ); // Hatch pattern type: 0 = User-defined; 1 = Predefined; 2 = Custom
writeGroup( 98, 0 ); // Number of seed points
}
void QgsDxfExport::writeLine( const QgsPoint &pt1, const QgsPoint &pt2, const QString &layer, const QString &lineStyleName, const QColor &color, double width )
{
writePolyline( QgsPointSequence() << pt1 << pt2, layer, lineStyleName, color, width );
}
void QgsDxfExport::writePoint( const QString &layer, const QColor &color, const QgsPoint &pt )
{
writeGroup( 0, QStringLiteral( "POINT" ) );
writeHandle();
writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
writeGroup( 100, QStringLiteral( "AcDbPoint" ) );
writeGroup( 8, layer );
writeGroup( color );
writeGroup( 0, pt );
}
void QgsDxfExport::writeFilledCircle( const QString &layer, const QColor &color, const QgsPoint &pt, double radius )
{
writeGroup( 0, QStringLiteral( "HATCH" ) ); // Entity type
writeHandle();
writeGroup( 330, mBlockHandle );
writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
writeGroup( 8, layer ); // Layer name
writeGroup( color ); // Color (0 by block, 256 by layer)
writeGroup( 100, QStringLiteral( "AcDbHatch" ) );
writeGroup( 0, QgsPoint( QgsWkbTypes::PointZ ) ); // Elevation point (in OCS)
writeGroup( 200, QgsPoint( QgsWkbTypes::PointZ, 0.0, 0.0, 1.0 ) );
writeGroup( 2, QStringLiteral( "SOLID" ) ); // Hatch pattern name
writeGroup( 70, 1 ); // Solid fill flag (solid fill = 1; pattern fill = 0)
writeGroup( 71, 0 ); // Associativity flag (associative = 1; non-associative = 0)
writeGroup( 91, 1 ); // Number of boundary paths (loops)
writeGroup( 92, 3 ); // Boundary path type flag (bit coded): 0 = Default; 1 = External; 2 = Polyline 4 = Derived; 8 = Textbox; 16 = Outermost
writeGroup( 72, 1 );
writeGroup( 73, 1 ); // Is closed flag
writeGroup( 93, 2 ); // Number of polyline vertices
writeGroup( 0, QgsPoint( QgsWkbTypes::Point, pt.x() - radius, pt.y() ) );
writeGroup( 42, 1.0 );
writeGroup( 0, QgsPoint( QgsWkbTypes::Point, pt.x() + radius, pt.y() ) );
writeGroup( 42, 1.0 );
writeGroup( 97, 0 ); // Number of source boundary objects
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)
writeGroup( 76, 1 ); // Hatch pattern type: 0 = User-defined; 1 = Predefined; 2 = Custom
writeGroup( 98, 0 ); // Number of seed points
}
void QgsDxfExport::writeCircle( const QString &layer, const QColor &color, const QgsPoint &pt, double radius, const QString &lineStyleName, double width )
{
writeGroup( 0, QStringLiteral( "LWPOLYLINE" ) );
writeHandle();
writeGroup( 330, mBlockHandle );
writeGroup( 8, layer );
writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
writeGroup( 100, QStringLiteral( "AcDbPolyline" ) );
writeGroup( 6, lineStyleName );
writeGroup( color );
writeGroup( 90, 2 );
writeGroup( 70, 1 );
writeGroup( 43, width );
writeGroup( 0, QgsPoint( pt.x() - radius, pt.y() ) );
writeGroup( 42, 1.0 );
writeGroup( 0, QgsPoint( pt.x() + radius, pt.y() ) );
writeGroup( 42, 1.0 );
}
void QgsDxfExport::writeText( const QString &layer, const QString &text, const QgsPoint &pt, double size, double angle, const QColor &color )
{
writeGroup( 0, QStringLiteral( "TEXT" ) );
writeHandle();
writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
writeGroup( 100, QStringLiteral( "AcDbText" ) );
writeGroup( 8, layer );
writeGroup( color );
writeGroup( 0, pt );
writeGroup( 40, size );
writeGroup( 1, text );
writeGroup( 50, angle );
writeGroup( 7, QStringLiteral( "STANDARD" ) ); // so far only support for standard font
}
void QgsDxfExport::writeMText( const QString &layer, const QString &text, const QgsPoint &pt, double width, double angle, const QColor &color )
{
if ( !mTextStream.codec()->canEncode( text ) )
{
// TODO return error
QgsDebugMsg( QString( "could not encode:%1" ).arg( text ) );
return;
}
writeGroup( 0, QStringLiteral( "MTEXT" ) );
writeHandle();
writeGroup( 100, QStringLiteral( "AcDbEntity" ) );
writeGroup( 100, QStringLiteral( "AcDbMText" ) );
writeGroup( 8, layer );
writeGroup( color );
writeGroup( 0, pt );
QString t( text );
while ( t.length() > 250 )
{
writeGroup( 3, t.left( 250 ) );
t = t.mid( 250 );
}
writeGroup( 1, text );
writeGroup( 50, angle ); // Rotation angle in radians
writeGroup( 41, width * 1.1 ); // Reference rectangle width
// Attachment point:
// 1 2 3
// 4 5 6
// 7 8 9
writeGroup( 71, 7 );
writeGroup( 7, QStringLiteral( "STANDARD" ) ); // so far only support for standard font
}
void QgsDxfExport::addFeature( QgsSymbolRenderContext &ctx, const QgsCoordinateTransform &ct, const QString &layer, const QgsSymbolLayer *symbolLayer, const QgsSymbol *symbol )
{
const QgsFeature *fet = ctx.feature();
if ( !fet )
return;
if ( !fet->hasGeometry() )
return;
std::unique_ptr<QgsAbstractGeometry> geom( fet->geometry().geometry()->clone() );
if ( ct.isValid() )
{
geom->transform( ct );
}
QgsWkbTypes::Type geometryType = geom->wkbType();
QColor penColor;
QColor brushColor;
if ( mSymbologyExport != NoSymbology && symbolLayer )
{
penColor = colorFromSymbolLayer( symbolLayer, ctx );
brushColor = symbolLayer->dxfBrushColor( ctx );
}
Qt::PenStyle penStyle( Qt::SolidLine );
Qt::BrushStyle brushStyle( Qt::NoBrush );
double width = -1;
double offset = 0.0;
double angle = 0.0;
if ( mSymbologyExport != NoSymbology && symbolLayer )
{
width = symbolLayer->dxfWidth( *this, ctx );
offset = symbolLayer->dxfOffset( *this, ctx );
angle = symbolLayer->dxfAngle( ctx );
penStyle = symbolLayer->dxfPenStyle();
brushStyle = symbolLayer->dxfBrushStyle();
if ( qgsDoubleNear( offset, 0.0 ) )
offset = 0.0;
}
QString lineStyleName = QStringLiteral( "CONTINUOUS" );
if ( mSymbologyExport != NoSymbology )
{
lineStyleName = lineStyleFromSymbolLayer( symbolLayer );
}
// single point
if ( QgsWkbTypes::flatType( geometryType ) == QgsWkbTypes::Point )
{
writePoint( geom->coordinateSequence().at( 0 ).at( 0 ).at( 0 ), layer, penColor, ctx, symbolLayer, symbol, angle );
return;
}
if ( QgsWkbTypes::flatType( geometryType ) == QgsWkbTypes::MultiPoint )
{
const QgsCoordinateSequence &cs = geom->coordinateSequence();
for ( int i = 0; i < cs.size(); i++ )
{
writePoint( cs.at( i ).at( 0 ).at( 0 ), layer, penColor, ctx, symbolLayer, symbol, angle );
}
return;
}
if ( penStyle != Qt::NoPen )
{
const QgsAbstractGeometry *tempGeom = geom.get();
switch ( QgsWkbTypes::flatType( geometryType ) )
{
case QgsWkbTypes::CircularString:
case QgsWkbTypes::CompoundCurve:
tempGeom = geom->segmentize();
if ( !tempGeom )
break;
FALLTHROUGH;
case QgsWkbTypes::LineString:
if ( !qgsDoubleNear( offset, 0.0 ) )
{
QgsGeos geos( tempGeom );
if ( tempGeom != geom.get() )
delete tempGeom;
tempGeom = geos.offsetCurve( offset, 0, GEOSBUF_JOIN_MITRE, 2.0 ); //#spellok
if ( !tempGeom )
tempGeom = geom.get();
}
writePolyline( tempGeom->coordinateSequence().at( 0 ).at( 0 ), layer, lineStyleName, penColor, width );
break;
case QgsWkbTypes::MultiCurve:
tempGeom = geom->segmentize();
if ( !tempGeom )
break;
FALLTHROUGH;
case QgsWkbTypes::MultiLineString:
{
if ( !qgsDoubleNear( offset, 0.0 ) )
{
QgsGeos geos( tempGeom );
if ( tempGeom != geom.get() )
delete tempGeom;
tempGeom = geos.offsetCurve( offset, 0, GEOSBUF_JOIN_MITRE, 2.0 ); //#spellok
if ( !tempGeom )
tempGeom = geom.get();
}
const QgsCoordinateSequence &cs = tempGeom->coordinateSequence();
for ( int i = 0; i < cs.size(); i++ )
{
writePolyline( cs.at( i ).at( 0 ), layer, lineStyleName, penColor, width );
}
break;
}
case QgsWkbTypes::CurvePolygon:
tempGeom = geom->segmentize();
if ( !tempGeom )
break;
FALLTHROUGH;
case QgsWkbTypes::Polygon:
{
if ( !qgsDoubleNear( offset, 0.0 ) )
{
QgsGeos geos( tempGeom );
if ( tempGeom != geom.get() )
delete tempGeom;
tempGeom = geos.buffer( offset, 0, GEOSBUF_CAP_FLAT, GEOSBUF_JOIN_MITRE, 2.0 ); //#spellok
if ( !tempGeom )
tempGeom = geom.get();
}
const QgsCoordinateSequence &cs = tempGeom->coordinateSequence();
for ( int i = 0; i < cs.at( 0 ).size(); i++ )
{
writePolyline( cs.at( 0 ).at( i ), layer, lineStyleName, penColor, width );
}
break;
}
case QgsWkbTypes::MultiPolygon:
{
if ( !qgsDoubleNear( offset, 0.0 ) )
{
QgsGeos geos( tempGeom );
if ( tempGeom != geom.get() )
delete tempGeom;
tempGeom = geos.buffer( offset, 0, GEOSBUF_CAP_FLAT, GEOSBUF_JOIN_MITRE, 2.0 ); //#spellok
if ( !tempGeom )
tempGeom = geom.get();
}
const QgsCoordinateSequence &cs = tempGeom->coordinateSequence();
for ( int i = 0; i < cs.size(); i++ )
for ( int j = 0; j < cs.at( i ).size(); j++ )
writePolyline( cs.at( i ).at( j ), layer, lineStyleName, penColor, width );
break;
}
default:
break;
}
if ( tempGeom != geom.get() )
delete tempGeom;
}
if ( brushStyle != Qt::NoBrush )
{
const QgsAbstractGeometry *tempGeom = geom.get();
switch ( QgsWkbTypes::flatType( geometryType ) )
{
case QgsWkbTypes::CurvePolygon:
tempGeom = tempGeom->segmentize();
if ( !tempGeom )
break;
FALLTHROUGH;
case QgsWkbTypes::Polygon:
writePolygon( tempGeom->coordinateSequence().at( 0 ), layer, QStringLiteral( "SOLID" ), brushColor );
break;
case QgsWkbTypes::MultiPolygon:
{
const QgsCoordinateSequence &cs = geom->coordinateSequence();
for ( int i = 0; i < cs.size(); i++ )
{
writePolygon( cs.at( i ), layer, QStringLiteral( "SOLID" ), brushColor );
}
break;
}
default:
break;
}
if ( tempGeom != geom.get() )
delete tempGeom;
}
}
QColor QgsDxfExport::colorFromSymbolLayer( const QgsSymbolLayer *symbolLayer, QgsSymbolRenderContext &ctx )
{
if ( !symbolLayer )
return QColor();
return symbolLayer->dxfColor( ctx );
}
QString QgsDxfExport::lineStyleFromSymbolLayer( const QgsSymbolLayer *symbolLayer )
{
QString lineStyleName = QStringLiteral( "CONTINUOUS" );
if ( !symbolLayer )
{
return lineStyleName;
}
QHash< const QgsSymbolLayer *, QString >::const_iterator lineTypeIt = mLineStyles.constFind( symbolLayer );
if ( lineTypeIt != mLineStyles.constEnd() )
{
lineStyleName = lineTypeIt.value();
return lineStyleName;
}
else
{
return lineNameFromPenStyle( symbolLayer->dxfPenStyle() );
}
}
int QgsDxfExport::closestColorMatch( QRgb pixel )
{
int idx = 0;
int current_distance = INT_MAX;
for ( int i = 1; i < static_cast< int >( sizeof( sDxfColors ) / sizeof( *sDxfColors ) ); ++i )
{
int dist = color_distance( pixel, i );
if ( dist < current_distance )
{
current_distance = dist;
idx = i;
if ( dist == 0 )
break;
}
}
return idx;
}
int QgsDxfExport::color_distance( QRgb p1, int index )
{
if ( index > 255 || index < 0 )
{
return 0;
}
double redDiff = qRed( p1 ) - sDxfColors[index][0];
double greenDiff = qGreen( p1 ) - sDxfColors[index][1];
double blueDiff = qBlue( p1 ) - sDxfColors[index][2];
#if 0
QgsDebugMsg( QString( "color_distance( r:%1 g:%2 b:%3 <=> i:%4 r:%5 g:%6 b:%7 ) => %8" )
.arg( qRed( p1 ) ).arg( qGreen( p1 ) ).arg( qBlue( p1 ) )
.arg( index )
.arg( mDxfColors[index][0] )
.arg( mDxfColors[index][1] )
.arg( mDxfColors[index][2] )
.arg( redDiff * redDiff + greenDiff * greenDiff + blueDiff * blueDiff ) );
#endif
return redDiff * redDiff + greenDiff * greenDiff + blueDiff * blueDiff;
}
QRgb QgsDxfExport::createRgbEntry( qreal r, qreal g, qreal b )
{
return QColor::fromRgbF( r, g, b ).rgb();
}
QgsRenderContext QgsDxfExport::renderContext() const
{
QgsRenderContext context;
context.setRendererScale( mSymbologyScale );
return context;
}
double QgsDxfExport::mapUnitScaleFactor( double scale, QgsUnitTypes::RenderUnit symbolUnits, QgsUnitTypes::DistanceUnit mapUnits )
{
if ( symbolUnits == QgsUnitTypes::RenderMapUnits )
{
return 1.0;
}
// MM symbol unit
return scale * QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::DistanceMeters, mapUnits ) / 1000.0;
}
QList< QPair< QgsSymbolLayer *, QgsSymbol * > > QgsDxfExport::symbolLayers( QgsRenderContext &context )
{
QList< QPair< QgsSymbolLayer *, QgsSymbol * > > symbolLayers;
const QList< QgsMapLayer * > layers = mMapSettings.layers();
for ( QgsMapLayer *ml : layers )
{
QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml );
if ( !vl )
{
continue;
}
// get renderer
QgsFeatureRenderer *r = vl->renderer();
if ( !r )
{
continue;
}
// get all symbols
QgsSymbolList symbols = r->symbols( context );
QgsSymbolList::iterator symbolIt = symbols.begin();
for ( ; symbolIt != symbols.end(); ++symbolIt )
{
int maxSymbolLayers = ( *symbolIt )->symbolLayerCount();
if ( mSymbologyExport != SymbolLayerSymbology )
{
maxSymbolLayers = 1;
}
for ( int i = 0; i < maxSymbolLayers; ++i )
{
symbolLayers.append( qMakePair( ( *symbolIt )->symbolLayer( i ), *symbolIt ) );
}
}
}
return symbolLayers;
}
void QgsDxfExport::writeDefaultLinetypes()
{
// continuous (Qt solid line)
const QStringList blockStrings = QStringList() << QStringLiteral( "ByLayer" ) << QStringLiteral( "ByBlock" ) << QStringLiteral( "CONTINUOUS" );
for ( const QString &ltype : blockStrings )
{
writeGroup( 0, QStringLiteral( "LTYPE" ) );
writeHandle();
writeGroup( 100, QStringLiteral( "AcDbSymbolTableRecord" ) );
writeGroup( 100, QStringLiteral( "AcDbLinetypeTableRecord" ) );
writeGroup( 2, ltype );
writeGroup( 70, 64 );
writeGroup( 3, QStringLiteral( "Defaultstyle" ) );
writeGroup( 72, 65 );
writeGroup( 73, 0 );
writeGroup( 40, 0.0 );
}
double das = dashSize();
double dss = dashSeparatorSize();
double dos = dotSize();
QVector<qreal> dashVector( 2 );
dashVector[0] = das;
dashVector[1] = dss;
writeLinetype( QStringLiteral( "DASH" ), dashVector, QgsUnitTypes::RenderMapUnits );
QVector<qreal> dotVector( 2 );
dotVector[0] = dos;
dotVector[1] = dss;
writeLinetype( QStringLiteral( "DOT" ), dotVector, QgsUnitTypes::RenderMapUnits );
QVector<qreal> dashDotVector( 4 );
dashDotVector[0] = das;
dashDotVector[1] = dss;
dashDotVector[2] = dos;
dashDotVector[3] = dss;
writeLinetype( QStringLiteral( "DASHDOT" ), dashDotVector, QgsUnitTypes::RenderMapUnits );
QVector<qreal> dashDotDotVector( 6 );
dashDotDotVector[0] = das;
dashDotDotVector[1] = dss;
dashDotDotVector[2] = dos;
dashDotDotVector[3] = dss;
dashDotDotVector[4] = dos;
dashDotDotVector[5] = dss;
writeLinetype( QStringLiteral( "DASHDOTDOT" ), dashDotDotVector, QgsUnitTypes::RenderMapUnits );
}
void QgsDxfExport::writeSymbolLayerLinetype( const QgsSymbolLayer *symbolLayer )
{
if ( !symbolLayer )
{
return;
}
QgsUnitTypes::RenderUnit unit;
QVector<qreal> customLinestyle = symbolLayer->dxfCustomDashPattern( unit );
if ( !customLinestyle.isEmpty() )
{
QString name = QStringLiteral( "symbolLayer%1" ).arg( mSymbolLayerCounter++ );
writeLinetype( name, customLinestyle, unit );
mLineStyles.insert( symbolLayer, name );
}
}
int QgsDxfExport::nLineTypes( const QList< QPair< QgsSymbolLayer *, QgsSymbol * > > &symbolLayers )
{
int nLineTypes = 0;
QList< QPair< QgsSymbolLayer *, QgsSymbol *> >::const_iterator slIt = symbolLayers.constBegin();
for ( ; slIt != symbolLayers.constEnd(); ++slIt )
{
const QgsSimpleLineSymbolLayer *simpleLine = dynamic_cast< const QgsSimpleLineSymbolLayer * >( slIt->first );
if ( simpleLine )
{
if ( simpleLine->useCustomDashPattern() )
{
++nLineTypes;
}
}
}
return nLineTypes;
}
void QgsDxfExport::writeLinetype( const QString &styleName, const QVector<qreal> &pattern, QgsUnitTypes::RenderUnit u )
{
double length = 0;
QVector<qreal>::const_iterator dashIt = pattern.constBegin();
for ( ; dashIt != pattern.constEnd(); ++dashIt )
{
length += ( *dashIt * mapUnitScaleFactor( mSymbologyScale, u, mMapUnits ) );
}
writeGroup( 0, QStringLiteral( "LTYPE" ) );
writeHandle();
// 330 5
writeGroup( 100, QStringLiteral( "AcDbSymbolTableRecord" ) );
writeGroup( 100, QStringLiteral( "AcDbLinetypeTableRecord" ) );
writeGroup( 2, styleName );
writeGroup( 70, 64 ); // 0?
writeGroup( 3, QLatin1String( "" ) );
writeGroup( 72, 65 );
writeGroup( 73, pattern.size() );
writeGroup( 40, length );
dashIt = pattern.constBegin();
bool isGap = false;
for ( ; dashIt != pattern.constEnd(); ++dashIt )
{
// map units or mm?
double segmentLength = ( isGap ? -*dashIt : *dashIt );
segmentLength *= mapUnitScaleFactor( mSymbologyScale, u, mMapUnits );
writeGroup( 49, segmentLength );
writeGroup( 74, 0 );
isGap = !isGap;
}
}
bool QgsDxfExport::hasDataDefinedProperties( const QgsSymbolLayer *sl, const QgsSymbol *symbol )
{
if ( !sl || !symbol )
{
return false;
}
if ( symbol->renderHints() & QgsSymbol::DynamicRotation )
{
return true;
}
return sl->dataDefinedProperties().hasActiveProperties();
}
double QgsDxfExport::dashSize() const
{
double size = mSymbologyScale * 0.002;
return sizeToMapUnits( size );
}
double QgsDxfExport::dotSize() const
{
double size = mSymbologyScale * 0.0006;
return sizeToMapUnits( size );
}
double QgsDxfExport::dashSeparatorSize() const
{
double size = mSymbologyScale * 0.0006;
return sizeToMapUnits( size );
}
double QgsDxfExport::sizeToMapUnits( double s ) const
{
double size = s * QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::DistanceMeters, mMapUnits );
return size;
}
QString QgsDxfExport::lineNameFromPenStyle( Qt::PenStyle style )
{
switch ( style )
{
case Qt::DashLine:
return QStringLiteral( "DASH" );
case Qt::DotLine:
return QStringLiteral( "DOT" );
case Qt::DashDotLine:
return QStringLiteral( "DASHDOT" );
case Qt::DashDotDotLine:
return QStringLiteral( "DASHDOTDOT" );
case Qt::SolidLine:
default:
return QStringLiteral( "CONTINUOUS" );
}
}
QString QgsDxfExport::dxfLayerName( const QString &name )
{
if ( name.isEmpty() )
return QStringLiteral( "0" );
// dxf layers can be max 255 characters long
QString layerName = name.left( 255 );
// replaced restricted characters with underscore
// < > / \ " : ; ? * | = '
// See http://docs.autodesk.com/ACD/2010/ENU/AutoCAD%202010%20User%20Documentation/index.html?url=WS1a9193826455f5ffa23ce210c4a30acaf-7345.htm,topicNumber=d0e41665
layerName.replace( '<', '_' );
layerName.replace( '>', '_' );
layerName.replace( '/', '_' );
layerName.replace( '\\', '_' );
layerName.replace( '\"', '_' );
layerName.replace( ':', '_' );
layerName.replace( ';', '_' );
layerName.replace( '?', '_' );
layerName.replace( '*', '_' );
layerName.replace( '|', '_' );
layerName.replace( '=', '_' );
layerName.replace( '\'', '_' );
// also remove newline characters (#15067)
layerName.replace( QLatin1String( "\r\n" ), QLatin1String( "_" ) );
layerName.replace( '\r', '_' );
layerName.replace( '\n', '_' );
return layerName.trimmed();
}
bool QgsDxfExport::layerIsScaleBasedVisible( const QgsMapLayer *layer ) const
{
if ( !layer )
return false;
if ( mSymbologyExport == QgsDxfExport::NoSymbology )
return true;
return layer->isInScaleRange( mSymbologyScale );
}
QString QgsDxfExport::layerName( const QString &id, const QgsFeature &f ) const
{
const QList< QgsMapLayer * > layers = mMapSettings.layers();
for ( QgsMapLayer *ml : layers )
{
QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml );
if ( vl && vl->id() == id )
{
int attrIdx = mLayerNameAttribute.value( vl->id(), -1 );
return dxfLayerName( attrIdx < 0 ? layerName( vl ) : f.attribute( attrIdx ).toString() );
}
}
return QStringLiteral( "0" );
}
QString QgsDxfExport::dxfEncoding( const QString &name )
{
const QList< QByteArray > codecs = QTextCodec::availableCodecs();
for ( const QByteArray &codec : codecs )
{
if ( name != codec )
continue;
int i;
for ( i = 0; i < static_cast< int >( sizeof( DXF_ENCODINGS ) / sizeof( *DXF_ENCODINGS ) ) && name != DXF_ENCODINGS[i][1]; ++i )
;
if ( i == static_cast< int >( sizeof( DXF_ENCODINGS ) / sizeof( *DXF_ENCODINGS ) ) )
continue;
return DXF_ENCODINGS[i][0];
}
return QString();
}
QStringList QgsDxfExport::encodings()
{
QStringList encodings;
const QList< QByteArray > codecs = QTextCodec::availableCodecs();
for ( const QByteArray &codec : codecs )
{
int i;
for ( i = 0; i < static_cast< int >( sizeof( DXF_ENCODINGS ) / sizeof( *DXF_ENCODINGS ) ) && strcmp( codec.data(), DXF_ENCODINGS[i][1] ) != 0; ++i )
;
if ( i < static_cast< int >( sizeof( DXF_ENCODINGS ) / sizeof( *DXF_ENCODINGS ) ) )
encodings << codec.data();
}
return encodings;
}
QString QgsDxfExport::layerName( QgsVectorLayer *vl ) const
{
Q_ASSERT( vl );
return mLayerTitleAsName && !vl->title().isEmpty() ? vl->title() : vl->name();
}
void QgsDxfExport::drawLabel( const QString &layerId, QgsRenderContext &context, pal::LabelPosition *label, const QgsPalLayerSettings &settings )
{
Q_UNUSED( context );
if ( !settings.drawLabels )
return;
QgsTextLabelFeature *lf = dynamic_cast<QgsTextLabelFeature *>( label->getFeaturePart()->feature() );
// Copy to temp, editable layer settings
// these settings will be changed by any data defined values, then used for rendering label components
// settings may be adjusted during rendering of components
QgsPalLayerSettings tmpLyr( settings );
// apply any previously applied data defined settings for the label
const QMap< QgsPalLayerSettings::Property, QVariant > &ddValues = lf->dataDefinedValues();
//font
QFont dFont = lf->definedFont();
QgsDebugMsgLevel( QString( "PAL font tmpLyr: %1, Style: %2" ).arg( tmpLyr.format().font().toString(), tmpLyr.format().font().styleName() ), 4 );
QgsDebugMsgLevel( QString( "PAL font definedFont: %1, Style: %2" ).arg( dFont.toString(), dFont.styleName() ), 4 );
QgsTextFormat format = tmpLyr.format();
format.setFont( dFont );
tmpLyr.setFormat( format );
if ( tmpLyr.multilineAlign == QgsPalLayerSettings::MultiFollowPlacement )
{
//calculate font alignment based on label quadrant
switch ( label->getQuadrant() )
{
case pal::LabelPosition::QuadrantAboveLeft:
case pal::LabelPosition::QuadrantLeft:
case pal::LabelPosition::QuadrantBelowLeft:
tmpLyr.multilineAlign = QgsPalLayerSettings::MultiRight;
break;
case pal::LabelPosition::QuadrantAbove:
case pal::LabelPosition::QuadrantOver:
case pal::LabelPosition::QuadrantBelow:
tmpLyr.multilineAlign = QgsPalLayerSettings::MultiCenter;
break;
case pal::LabelPosition::QuadrantAboveRight:
case pal::LabelPosition::QuadrantRight:
case pal::LabelPosition::QuadrantBelowRight:
tmpLyr.multilineAlign = QgsPalLayerSettings::MultiLeft;
break;
}
}
// update tmpLyr with any data defined text style values
QgsPalLabeling::dataDefinedTextStyle( tmpLyr, ddValues );
// update tmpLyr with any data defined text buffer values
QgsPalLabeling::dataDefinedTextBuffer( tmpLyr, ddValues );
// update tmpLyr with any data defined text formatting values
QgsPalLabeling::dataDefinedTextFormatting( tmpLyr, ddValues );
// add to the results
QString txt = label->getFeaturePart()->feature()->labelText();
QgsFeatureId fid = label->getFeaturePart()->featureId();
QString dxfLayer = mDxfLayerNames[layerId][fid];
QString wrapchr = tmpLyr.wrapChar.isEmpty() ? QStringLiteral( "\n" ) : tmpLyr.wrapChar;
//add the direction symbol if needed
if ( !txt.isEmpty() && tmpLyr.placement == QgsPalLayerSettings::Line && tmpLyr.addDirectionSymbol )
{
bool prependSymb = false;
QString symb = tmpLyr.rightDirectionSymbol;
if ( label->getReversed() )
{
prependSymb = true;
symb = tmpLyr.leftDirectionSymbol;
}
if ( tmpLyr.reverseDirectionSymbol )
{
if ( symb == tmpLyr.rightDirectionSymbol )
{
prependSymb = true;
symb = tmpLyr.leftDirectionSymbol;
}
else
{
prependSymb = false;
symb = tmpLyr.rightDirectionSymbol;
}
}
if ( tmpLyr.placeDirectionSymbol == QgsPalLayerSettings::SymbolAbove )
{
prependSymb = true;
symb = symb + wrapchr;
}
else if ( tmpLyr.placeDirectionSymbol == QgsPalLayerSettings::SymbolBelow )
{
prependSymb = false;
symb = wrapchr + symb;
}
if ( prependSymb )
{
txt.prepend( symb );
}
else
{
txt.append( symb );
}
}
txt = txt.replace( wrapchr, QLatin1String( "\\P" ) );
if ( tmpLyr.format().font().underline() )
{
txt.prepend( "\\L" ).append( "\\l" );
}
if ( tmpLyr.format().font().overline() )
{
txt.prepend( "\\O" ).append( "\\o" );
}
if ( tmpLyr.format().font().strikeOut() )
{
txt.prepend( "\\K" ).append( "\\k" );
}
txt.prepend( QStringLiteral( "\\f%1|i%2|b%3;\\H%4;" )
.arg( tmpLyr.format().font().family() )
.arg( tmpLyr.format().font().italic() ? 1 : 0 )
.arg( tmpLyr.format().font().bold() ? 1 : 0 )
.arg( label->getHeight() / ( 1 + txt.count( QStringLiteral( "\\P" ) ) ) * 0.75 ) );
writeMText( dxfLayer, txt, QgsPoint( label->getX(), label->getY() ), label->getWidth(), label->getAlpha() * 180.0 / M_PI, tmpLyr.format().color() );
}
void QgsDxfExport::registerDxfLayer( const QString &layerId, QgsFeatureId fid, const QString &layerName )
{
if ( !mDxfLayerNames.contains( layerId ) )
mDxfLayerNames[ layerId ] = QMap<QgsFeatureId, QString>();
mDxfLayerNames[layerId][fid] = layerName;
}
void QgsDxfExport::setDestinationCrs( const QgsCoordinateReferenceSystem &crs )
{
mCrs = crs;
mMapUnits = crs.mapUnits();
}
QgsCoordinateReferenceSystem QgsDxfExport::destinationCrs() const
{
return mCrs;
}