/****************************************************************************
** Copyright (C) 2001-2013 RibbonSoft, GmbH. All rights reserved.
**
** This file is part of the dxflib project.
**
** This file 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.
**
** Licensees holding valid dxflib Professional Edition licenses may use
** this file in accordance with the dxflib Commercial License
** Agreement provided with the Software.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
** See http://www.ribbonsoft.com for further details.
**
** Contact info@ribbonsoft.com if any conditions of this licensing are
** not clear to you.
**
**********************************************************************/

#include "dl_dxf.h"

#include <algorithm>
#include <string>
#include <cstdio>
#include <cassert>
#include <cmath>

#include "dl_attributes.h"
#include "dl_codes.h"
#include "dl_creationadapter.h"
#include "dl_writer_ascii.h"

#include "iostream"

/**
 * Default constructor.
 */
DL_Dxf::DL_Dxf()
{
    version = DL_VERSION_2000;

    vertices = NULL;
    maxVertices = 0;
    vertexIndex = 0;

    knots = NULL;
    maxKnots = 0;
    knotIndex = 0;

    weights = NULL;
    weightIndex = 0;

    controlPoints = NULL;
    maxControlPoints = 0;
    controlPointIndex = 0;

    fitPoints = NULL;
    maxFitPoints = 0;
    fitPointIndex = 0;

    leaderVertices = NULL;
    maxLeaderVertices = 0;
    leaderVertexIndex = 0;

    // Aux members, initialized here to avoid warnings in analysers
    firstHatchLoop = true;
    xRecordValues = false;
    groupCode = 0;
    currentObjectType = 0;
    settingValue[0] = 0;
    firstCall = true;
    libVersion = 0;
    appDictionaryHandle = 0;
    styleHandleStd = 0;
}


/**
 * Destructor.
 */
DL_Dxf::~DL_Dxf()
{
    if( vertices!=NULL )
    {
        delete[] vertices;
    }

    if( knots!=NULL )
    {
        delete[] knots;
    }

    if( controlPoints!=NULL )
    {
        delete[] controlPoints;
    }

    if( fitPoints!=NULL )
    {
        delete[] fitPoints;
    }

    if( weights!=NULL )
    {
        delete[] weights;
    }

    if( leaderVertices!=NULL )
    {
        delete[] leaderVertices;
    }
}


/**
 * @brief Reads the given file and calls the appropriate functions in
 * the given creation interface for every entity found in the file.
 *
 * @param file Input the file pointer to read
 * @param creationInterface
 *      Pointer to the class which takes care of the entities in the file.
 *
 * @retval true if fp is valid (i.e. not NULL), false otherwise.
 */
bool DL_Dxf::in( FILE* fp, DL_CreationInterface* creationInterface )
{
    firstCall = true;
    currentObjectType = DL_UNKNOWN;

    if( fp )
    {
        std::locale oldLocale = std::locale::global( std::locale( "C" ) );    // use dot in numbers

        while( readDxfGroups( fp, creationInterface ) )
        {
        }

        std::locale::global( oldLocale );
        fclose( fp );
        return true;
    }

    return false;
}

/**
 * @brief Reads the given file and calls the appropriate functions in
 * the given creation interface for every entity found in the file.
 *
 * @param file Input
 *      Path and name of file to read
 * Note: file is not very well utf8 compatible, depending on the platform.
 * @param creationInterface
 *      Pointer to the class which takes care of the entities in the file.
 *
 * @retval true If \p file could be opened.
 * @retval false If \p file could not be opened.
 */
bool DL_Dxf::in( const std::string& file, DL_CreationInterface* creationInterface )
{
    FILE* fp;

    firstCall = true;
    currentObjectType = DL_UNKNOWN;

    fp = fopen( file.c_str(), "rt" );

    if( fp )
    {
        std::locale oldLocale = std::locale::global( std::locale( "C" ) );    // use dot in numbers

        while( readDxfGroups( fp, creationInterface ) )
        {
        }

        std::locale::global( oldLocale );
        fclose( fp );
        return true;
    }

    return false;
}


/**
 * Reads a DXF file from an existing stream.
 *
 * @param stream The input stream.
 * @param creationInterface
 *      Pointer to the class which takes care of the entities in the file.
 *
 * @retval true If \p file could be opened.
 * @retval false If \p file could not be opened.
 */
bool DL_Dxf::in( std::istream& stream,
        DL_CreationInterface* creationInterface )
{
    if( stream.good() )
    {
        firstCall = true;
        currentObjectType = DL_UNKNOWN;

        while( readDxfGroups( stream, creationInterface ) )
        {
        }

        return true;
    }

    return false;
}


/**
 * @brief Reads a group couplet from a DXF file.  Calls another function
 * to process it.
 *
 * A group couplet consists of two lines that represent a single
 * piece of data.  An integer constant on the first line indicates
 * the type of data.  The value is on the next line.\n
 *
 * This function reads a couplet, determines the type of data, and
 * passes the value to the the appropriate handler function of
 * \p creationInterface.\n
 *
 * \p fp is advanced so that the next call to \p readDXFGroups() reads
 * the next couplet in the file.
 *
 * @param fp Handle of input file
 * @param creationInterface Handle of class which processes entities
 *      in the file
 *
 * @retval true If EOF not reached.
 * @retval false If EOF reached.
 */
bool DL_Dxf::readDxfGroups( FILE* fp, DL_CreationInterface* creationInterface )
{
    static int line = 1;

    // Read one group of the DXF file and strip the lines:
    if( DL_Dxf::getStrippedLine( groupCodeTmp, DL_DXF_MAXLINE, fp )
        && DL_Dxf::getStrippedLine( groupValue, DL_DXF_MAXLINE, fp, false ) )
    {
        groupCode = (unsigned int) toInt( groupCodeTmp );

        creationInterface->processCodeValuePair( groupCode, groupValue );
        line += 2;
        processDXFGroup( creationInterface, groupCode, groupValue );
    }

    return !feof( fp );
}


/**
 * Same as above but for input streams.
 */
bool DL_Dxf::readDxfGroups( std::istream& stream,
        DL_CreationInterface* creationInterface )
{
    static int line = 1;

    // Read one group of the DXF file and chop the lines:
    if( DL_Dxf::getStrippedLine( groupCodeTmp, DL_DXF_MAXLINE, stream )
        && DL_Dxf::getStrippedLine( groupValue, DL_DXF_MAXLINE, stream, false ) )
    {
        groupCode = (unsigned int) toInt( groupCodeTmp );

        line += 2;
        processDXFGroup( creationInterface, groupCode, groupValue );
    }

    return !stream.eof();
}


/**
 * @brief Reads line from file & strips whitespace at start and newline
 * at end.
 *
 * @param s Output\n
 *      Pointer to character array that chopped line will be returned in.
 * @param size Size of \p s.  (Including space for NULL.)
 * @param fp Input\n
 *      Handle of input file.
 *
 * @retval true if line could be read
 * @retval false if \p fp is already at end of file
 *
 * @todo Change function to use safer FreeBSD strl* functions
 * @todo Is it a problem if line is blank (i.e., newline only)?
 *      Then, when function returns, (s==NULL).
 */
bool DL_Dxf::getStrippedLine( std::string& s, unsigned int size, FILE* fp, bool stripSpace )
{
    if( !feof( fp ) )
    {
        // The whole line in the file.  Includes space for NULL.
        char* wholeLine = new char[size];
        // Only the useful part of the line
        char* line;

        line = fgets( wholeLine, size, fp );

        if( line!=NULL && line[0] != '\0' )    // Evaluates to fgets() retval
        {
            // line == wholeLine at this point.
            // Both guaranteed to be NULL terminated.

            // Strip leading whitespace and trailing CR/LF.
            stripWhiteSpace( &line, stripSpace );

            s = line;
            assert( size > s.length() );
        }

        delete[] wholeLine;    // Done with wholeLine

        return true;
    }
    else
    {
        s = "";
        return false;
    }
}


/**
 * Same as above but for input streams.
 */
bool DL_Dxf::getStrippedLine( std::string& s, unsigned int size,
        std::istream& stream, bool stripSpace )
{
    if( !stream.eof() )
    {
        // Only the useful part of the line
        char* line = new char[size + 1];
        char* oriLine = line;
        stream.getline( line, size );
        stripWhiteSpace( &line, stripSpace );
        s = line;
        assert( size > s.length() );
        delete[] oriLine;
        return true;
    }
    else
    {
        s[0] = '\0';
        return false;
    }
}


/**
 * @brief Strips leading whitespace and trailing Carriage Return (CR)
 * and Line Feed (LF) from NULL terminated string.
 *
 * @param s Input and output.
 *      NULL terminates string.
 *
 * @retval true if \p s is non-NULL
 * @retval false if \p s is NULL
 */
bool DL_Dxf::stripWhiteSpace( char** s, bool stripSpace )
{
    // last non-NULL char:
    int lastChar = strlen( *s ) - 1;

    // Is last character CR or LF?
    while( (lastChar >= 0)
           && ( ( (*s)[lastChar] == 10 ) || ( (*s)[lastChar] == 13 )
                || ( stripSpace && ( (*s)[lastChar] == ' ' || ( (*s)[lastChar] == '\t' ) ) ) ) )
    {
        (*s)[lastChar] = '\0';
        lastChar--;
    }

    // Skip whitespace, excluding \n, at beginning of line
    if( stripSpace )
    {
        while( (*s)[0]==' ' || (*s)[0]=='\t' )
        {
            ++(*s);
        }
    }

    return (*s) ? true : false;
}


/**
 * Processes a group (pair of group code and value).
 *
 * @param creationInterface Handle to class that creates entities and
 * other CAD data from DXF group codes
 *
 * @param agroupCode Constant indicating the data type of the group.
 * @param agroupValue The data value.
 *
 * @retval true if done processing current entity and new entity begun
 * @retval false if not done processing current entity
 */
bool DL_Dxf::processDXFGroup( DL_CreationInterface* creationInterface,
        int agroupCode, const std::string& agroupValue )
{
    // printf("%d\n", agroupCode);
    // printf("%s\n", agroupValue.c_str());

    // Init values on first call
    if( firstCall )
    {
        settingValue[0] = '\0';
        firstCall = false;
    }

    // Indicates comment or dxflib version:
    if( agroupCode==999 )
    {
        if( !agroupValue.empty() )
        {
            if( agroupValue.substr( 0, 6 )=="dxflib" )
            {
                libVersion = getLibVersion( agroupValue.substr( 7 ) );
            }

            addComment( creationInterface, agroupValue );
        }
    }
    // Indicates start of new entity or variable:
    else if( agroupCode==0 || agroupCode==9 )
    {
        // If new entity is encountered, the last one is complete.
        // Prepare default attributes for next entity:
        std::string layer = getStringValue( 8, "0" );

        int width;

        // Compatibility with qcad1:
        if( hasValue( 39 ) && !hasValue( 370 ) )
        {
            width = getIntValue( 39, -1 );
        }
        // since autocad 2002:
        else if( hasValue( 370 ) )
        {
            width = getIntValue( 370, -1 );
        }
        // default to BYLAYER:
        else
        {
            width = -1;
        }

        int color;
        color = getIntValue( 62, 256 );
        int color24;
        color24 = getIntValue( 420, -1 );
        int handle;
        handle = getInt16Value( 5, -1 );

        std::string linetype = getStringValue( 6, "BYLAYER" );

        attrib = DL_Attributes( layer,                  // layer
                color,                                  // color
                color24,                                // 24 bit color
                width,                                  // width
                linetype,                               // linetype
                handle );                               // handle
        attrib.setInPaperSpace( (bool) getIntValue( 67, 0 ) );
        attrib.setLinetypeScale( getRealValue( 48, 1.0 ) );
        creationInterface->setAttributes( attrib );

        int elevationGroupCode = 30;

        if( currentObjectType==DL_ENTITY_LWPOLYLINE )
        {
            // see lwpolyline group codes reference
            elevationGroupCode = 38;
        }
        else
        {
            // see polyline group codes reference
            elevationGroupCode = 30;
        }

        creationInterface->setExtrusion( getRealValue( 210, 0.0 ),
                getRealValue( 220, 0.0 ),
                getRealValue( 230, 1.0 ),
                getRealValue( elevationGroupCode, 0.0 ) );

        // Add the previously parsed entity via creationInterface
        switch( currentObjectType )
        {
        case DL_SETTING:
            addSetting( creationInterface );
            break;

        case DL_LAYER:
            addLayer( creationInterface );
            break;

        case DL_LINETYPE:
            addLinetype( creationInterface );
            break;

        case DL_BLOCK:
            addBlock( creationInterface );
            break;

        case DL_ENDBLK:
            endBlock( creationInterface );
            break;

        case DL_STYLE:
            addTextStyle( creationInterface );
            break;

        case DL_ENTITY_POINT:
            addPoint( creationInterface );
            break;

        case DL_ENTITY_LINE:
            addLine( creationInterface );
            break;

        case DL_ENTITY_XLINE:
            addXLine( creationInterface );
            break;

        case DL_ENTITY_RAY:
            addRay( creationInterface );
            break;

        case DL_ENTITY_POLYLINE:
        case DL_ENTITY_LWPOLYLINE:
            addPolyline( creationInterface );
            break;

        case DL_ENTITY_VERTEX:
            addVertex( creationInterface );
            break;

        case DL_ENTITY_SPLINE:
            addSpline( creationInterface );
            break;

        case DL_ENTITY_ARC:
            addArc( creationInterface );
            break;

        case DL_ENTITY_CIRCLE:
            addCircle( creationInterface );
            break;

        case DL_ENTITY_ELLIPSE:
            addEllipse( creationInterface );
            break;

        case DL_ENTITY_INSERT:
            addInsert( creationInterface );
            break;

        case DL_ENTITY_MTEXT:
            addMText( creationInterface );
            break;

        case DL_ENTITY_TEXT:
            addText( creationInterface );
            break;

        case DL_ENTITY_ARCALIGNEDTEXT:
            addArcAlignedText( creationInterface );
            break;

        case DL_ENTITY_ATTRIB:
            addAttribute( creationInterface );
            break;

        case DL_ENTITY_DIMENSION:
        {
            int type = (getIntValue( 70, 0 ) & 0x07);

            switch( type )
            {
            case 0:
                addDimLinear( creationInterface );
                break;

            case 1:
                addDimAligned( creationInterface );
                break;

            case 2:
                addDimAngular( creationInterface );
                break;

            case 3:
                addDimDiametric( creationInterface );
                break;

            case 4:
                addDimRadial( creationInterface );
                break;

            case 5:
                addDimAngular3P( creationInterface );
                break;

            case 6:
                addDimOrdinate( creationInterface );
                break;

            default:
                break;
            }
        }
        break;

        case DL_ENTITY_LEADER:
            addLeader( creationInterface );
            break;

        case DL_ENTITY_HATCH:
            addHatch(creationInterface);
            handleHatchData( creationInterface );
            break;

        case DL_ENTITY_IMAGE:
            addImage( creationInterface );
            break;

        case DL_ENTITY_IMAGEDEF:
            addImageDef( creationInterface );
            break;

        case DL_ENTITY_TRACE:
            addTrace( creationInterface );
            break;

        case DL_ENTITY_3DFACE:
            add3dFace( creationInterface );
            break;

        case DL_ENTITY_SOLID:
            addSolid( creationInterface );
            break;

        case DL_ENTITY_SEQEND:
            endSequence( creationInterface );
            break;

        default:
            break;
        }

        creationInterface->endSection();

        // reset all values (they are not persistent and only this
        // way we can set defaults for omitted values)
// for (int i=0; i<DL_DXF_MAXGROUPCODE; ++i) {
// values[i][0] = '\0';
// }
        values.clear();
        settingValue[0] = '\0';
        settingKey = "";
        firstHatchLoop = true;
        // firstHatchEdge = true;
        hatchEdge = DL_HatchEdgeData();
        // xRecordHandle = "";
        xRecordValues = false;

        // Last DXF entity or setting has been handled
        // Now determine what the next entity or setting type is

        int prevEntity = currentObjectType;

        // Read DXF variable:
        if( agroupValue[0]=='$' )
        {
            currentObjectType = DL_SETTING;
            settingKey = agroupValue;
        }
        // Read Layers:
        else if( agroupValue=="LAYER" )
        {
            currentObjectType = DL_LAYER;
        }
        // Read Linetypes:
        else if( agroupValue=="LTYPE" )
        {
            currentObjectType = DL_LINETYPE;
        }
        // Read Blocks:
        else if( agroupValue=="BLOCK" )
        {
            currentObjectType = DL_BLOCK;
        }
        else if( agroupValue=="ENDBLK" )
        {
            currentObjectType = DL_ENDBLK;
        }
        // Read text styles:
        else if( agroupValue=="STYLE" )
        {
            currentObjectType = DL_STYLE;
        }
        // Read entities:
        else if( agroupValue=="POINT" )
        {
            currentObjectType = DL_ENTITY_POINT;
        }
        else if( agroupValue=="LINE" )
        {
            currentObjectType = DL_ENTITY_LINE;
        }
        else if( agroupValue=="XLINE" )
        {
            currentObjectType = DL_ENTITY_XLINE;
        }
        else if( agroupValue=="RAY" )
        {
            currentObjectType = DL_ENTITY_RAY;
        }
        else if( agroupValue=="POLYLINE" )
        {
            currentObjectType = DL_ENTITY_POLYLINE;
        }
        else if( agroupValue=="LWPOLYLINE" )
        {
            currentObjectType = DL_ENTITY_LWPOLYLINE;
        }
        else if( agroupValue=="VERTEX" )
        {
            currentObjectType = DL_ENTITY_VERTEX;
        }
        else if( agroupValue=="SPLINE" )
        {
            currentObjectType = DL_ENTITY_SPLINE;
        }
        else if( agroupValue=="ARC" )
        {
            currentObjectType = DL_ENTITY_ARC;
        }
        else if( agroupValue=="ELLIPSE" )
        {
            currentObjectType = DL_ENTITY_ELLIPSE;
        }
        else if( agroupValue=="CIRCLE" )
        {
            currentObjectType = DL_ENTITY_CIRCLE;
        }
        else if( agroupValue=="INSERT" )
        {
            currentObjectType = DL_ENTITY_INSERT;
        }
        else if( agroupValue=="TEXT" )
        {
            currentObjectType = DL_ENTITY_TEXT;
        }
        else if( agroupValue=="MTEXT" )
        {
            currentObjectType = DL_ENTITY_MTEXT;
        }
        else if( agroupValue=="ARCALIGNEDTEXT" )
        {
            currentObjectType = DL_ENTITY_ARCALIGNEDTEXT;
        }
        else if( agroupValue=="ATTRIB" )
        {
            currentObjectType = DL_ENTITY_ATTRIB;
        }
        else if( agroupValue=="DIMENSION" )
        {
            currentObjectType = DL_ENTITY_DIMENSION;
        }
        else if( agroupValue=="LEADER" )
        {
            currentObjectType = DL_ENTITY_LEADER;
        }
        else if( agroupValue=="HATCH" )
        {
            currentObjectType = DL_ENTITY_HATCH;
        }
        else if( agroupValue=="IMAGE" )
        {
            currentObjectType = DL_ENTITY_IMAGE;
        }
        else if( agroupValue=="IMAGEDEF" )
        {
            currentObjectType = DL_ENTITY_IMAGEDEF;
        }
        else if( agroupValue=="TRACE" )
        {
            currentObjectType = DL_ENTITY_TRACE;
        }
        else if( agroupValue=="SOLID" )
        {
            currentObjectType = DL_ENTITY_SOLID;
        }
        else if( agroupValue=="3DFACE" )
        {
            currentObjectType = DL_ENTITY_3DFACE;
        }
        else if( agroupValue=="SEQEND" )
        {
            currentObjectType = DL_ENTITY_SEQEND;
        }
        else if( agroupValue=="XRECORD" )
        {
            currentObjectType = DL_XRECORD;
        }
        else if( agroupValue=="DICTIONARY" )
        {
            currentObjectType = DL_DICTIONARY;
        }
        else
        {
            currentObjectType = DL_UNKNOWN;
        }

        // end of old style POLYLINE entity
        if( prevEntity==DL_ENTITY_VERTEX && currentObjectType!=DL_ENTITY_VERTEX )
        {
            endEntity( creationInterface );
        }

        // TODO: end of SPLINE entity
        // if (prevEntity==DL_ENTITY_CONTROLPOINT && currentEntity!=DL_ENTITY_CONTROLPOINT) {
        // endEntity(creationInterface);
        // }

        return true;
    }
    else
    {
        // Group code does not indicate start of new entity or setting,
        // so this group must be continuation of data for the current
        // one.
        if( agroupCode<DL_DXF_MAXGROUPCODE )
        {
            bool handled = false;

            switch( currentObjectType )
            {
            case DL_ENTITY_MTEXT:
                handled = handleMTextData( creationInterface );
                break;

            case DL_ENTITY_LWPOLYLINE:
                handled = handleLWPolylineData( creationInterface );
                break;

            case DL_ENTITY_SPLINE:
                handled = handleSplineData( creationInterface );
                break;

            case DL_ENTITY_LEADER:
                handled = handleLeaderData( creationInterface );
                break;

            case DL_ENTITY_HATCH:
                handled = handleHatchData( creationInterface );
                break;

            case DL_XRECORD:
                handled = handleXRecordData( creationInterface );
                break;

            case DL_DICTIONARY:
                handled = handleDictionaryData( creationInterface );
                break;

            case DL_LINETYPE:
                handled = handleLinetypeData( creationInterface );
                break;

            default:
                break;
            }

            // Always try to handle XData, unless we're in an XData record:
            if( currentObjectType!=DL_XRECORD )
            {
                handled = handleXData( creationInterface );
            }

            if( !handled )
            {
                // Normal group / value pair:
                values[agroupCode] = agroupValue;
            }
        }

        return false;
    }

    return false;
}


/**
 * Adds a comment from the DXF file.
 */
void DL_Dxf::addComment( DL_CreationInterface* creationInterface, const std::string& comment )
{
    creationInterface->addComment( comment );
}


void DL_Dxf::addDictionary( DL_CreationInterface* creationInterface )
{
    creationInterface->addDictionary( DL_DictionaryData( getStringValue( 5, "" ) ) );
}


void DL_Dxf::addDictionaryEntry( DL_CreationInterface* creationInterface )
{
    creationInterface->addDictionaryEntry( DL_DictionaryEntryData( getStringValue( 3, "" ),
                    getStringValue( 350, "" ) ) );
}


/**
 * Adds a variable from the DXF file.
 */
void DL_Dxf::addSetting( DL_CreationInterface* creationInterface )
{
    int c = -1;
    std::map<int, std::string>::iterator it = values.begin();

    if( it!=values.end() )
    {
        c = it->first;
    }

// for (int i=0; i<=380; ++i) {
// if (values[i][0]!='\0') {
// c = i;
// break;
// }
// }

    // string
    if( c>=0 && c<=9 )
    {
        creationInterface->setVariableString( settingKey, values[c], c );
 #ifdef DL_COMPAT
        // backwards compatibility:
        creationInterface->setVariableString( settingKey.c_str(), values[c].c_str(), c );
 #endif
    }
    // vector
    else if( c>=10 && c<=39 )
    {
        if( c==10 )
        {
            creationInterface->setVariableVector(
                    settingKey,
                    getRealValue( c, 0.0 ),
                    getRealValue( c + 10, 0.0 ),
                    getRealValue( c + 20, 0.0 ),
                    c );
        }
    }
    // double
    else if( c>=40 && c<=59 )
    {
        creationInterface->setVariableDouble( settingKey, getRealValue( c, 0.0 ), c );
    }
    // int
    else if( c>=60 && c<=99 )
    {
        creationInterface->setVariableInt( settingKey, getIntValue( c, 0 ), c );
    }
    // misc
    else if( c>=0 )
    {
        creationInterface->setVariableString( settingKey, getStringValue( c, "" ), c );
    }
}


/**
 * Adds a layer that was read from the file via the creation interface.
 */
void DL_Dxf::addLayer( DL_CreationInterface* creationInterface )
{
    // correct some invalid attributes for layers:
    attrib = creationInterface->getAttributes();

    if( attrib.getColor()==256 || attrib.getColor()==0 )
    {
        attrib.setColor( 7 );
    }

    if( attrib.getWidth()<0 )
    {
        attrib.setWidth( 1 );
    }

    std::string linetype = attrib.getLinetype();
    std::transform( linetype.begin(), linetype.end(), linetype.begin(), ::toupper );

    if( linetype=="BYLAYER" || linetype=="BYBLOCK" )
    {
        attrib.setLinetype( "CONTINUOUS" );
    }

    // add layer
    std::string name = getStringValue( 2, "" );

    if( name.length()==0 )
    {
        return;
    }

    creationInterface->addLayer( DL_LayerData( name, getIntValue( 70, 0 ) ) );
}


/**
 * Adds a linetype that was read from the file via the creation interface.
 */
void DL_Dxf::addLinetype( DL_CreationInterface* creationInterface )
{
    std::string name = getStringValue( 2, "" );

    if( name.length()==0 )
    {
        return;
    }

    int numDashes = getIntValue( 73, 0 );
    // double dashes[numDashes];

    DL_LinetypeData d(
            // name:
            name,
            // description:
            getStringValue( 3, "" ),
            // flags
            getIntValue( 70, 0 ),
            // number of dashes:
            numDashes,
            // pattern length:
            getRealValue( 40, 0.0 )
            // pattern:
            // dashes
            );

    if( name != "By Layer" && name != "By Block" && name != "BYLAYER" && name != "BYBLOCK" )
    {
        creationInterface->addLinetype( d );
    }
}


/**
 * Handles all dashes in linetype pattern.
 */
bool DL_Dxf::handleLinetypeData( DL_CreationInterface* creationInterface )
{
    if( groupCode == 49 )
    {
        creationInterface->addLinetypeDash( toReal( groupValue ) );
        return true;
    }

    return false;
}


/**
 * Adds a block that was read from the file via the creation interface.
 */
void DL_Dxf::addBlock( DL_CreationInterface* creationInterface )
{
    std::string name = getStringValue( 2, "" );

    if( name.length()==0 )
    {
        return;
    }

    DL_BlockData d(
            // Name:
            name,
            // flags:
            getIntValue( 70, 0 ),
            // base point:
            getRealValue( 10, 0.0 ),
            getRealValue( 20, 0.0 ),
            getRealValue( 30, 0.0 ) );

    creationInterface->addBlock( d );
}


/**
 * Ends a block that was read from the file via the creation interface.
 */
void DL_Dxf::endBlock( DL_CreationInterface* creationInterface )
{
    creationInterface->endBlock();
}


void DL_Dxf::addTextStyle( DL_CreationInterface* creationInterface )
{
    std::string name = getStringValue( 2, "" );

    if( name.length()==0 )
    {
        return;
    }

    DL_StyleData d(
            // name:
            name,
            // flags
            getIntValue( 70, 0 ),
            // fixed text heigth:
            getRealValue( 40, 0.0 ),
            // width factor:
            getRealValue( 41, 0.0 ),
            // oblique angle:
            getRealValue( 50, 0.0 ),
            // text generation flags:
            getIntValue( 71, 0 ),
            // last height used:
            getRealValue( 42, 0.0 ),
            // primart font file:
            getStringValue( 3, "" ),
            // big font file:
            getStringValue( 4, "" )
            );
    creationInterface->addTextStyle( d );
}


/**
 * Adds a point entity that was read from the file via the creation interface.
 */
void DL_Dxf::addPoint( DL_CreationInterface* creationInterface )
{
    DL_PointData d( getRealValue( 10, 0.0 ),
            getRealValue( 20, 0.0 ),
            getRealValue( 30, 0.0 ),
            getRealValue( 39, 0.0 ) );

    creationInterface->addPoint( d );
}


/**
 * Adds a line entity that was read from the file via the creation interface.
 */
void DL_Dxf::addLine( DL_CreationInterface* creationInterface )
{
    DL_LineData d( getRealValue( 10, 0.0 ),
            getRealValue( 20, 0.0 ),
            getRealValue( 30, 0.0 ),
            getRealValue( 11, 0.0 ),
            getRealValue( 21, 0.0 ),
            getRealValue( 31, 0.0 ) );

    creationInterface->addLine( d );
}


/**
 * Adds an xline entity that was read from the file via the creation interface.
 */
void DL_Dxf::addXLine( DL_CreationInterface* creationInterface )
{
    DL_XLineData d( getRealValue( 10, 0.0 ),
            getRealValue( 20, 0.0 ),
            getRealValue( 30, 0.0 ),
            getRealValue( 11, 0.0 ),
            getRealValue( 21, 0.0 ),
            getRealValue( 31, 0.0 ) );

    creationInterface->addXLine( d );
}


/**
 * Adds a ray entity that was read from the file via the creation interface.
 */
void DL_Dxf::addRay( DL_CreationInterface* creationInterface )
{
    DL_RayData d( getRealValue( 10, 0.0 ),
            getRealValue( 20, 0.0 ),
            getRealValue( 30, 0.0 ),
            getRealValue( 11, 0.0 ),
            getRealValue( 21, 0.0 ),
            getRealValue( 31, 0.0 ) );

    creationInterface->addRay( d );
}


/**
 * Adds a polyline entity that was read from the file via the creation interface.
 */
void DL_Dxf::addPolyline( DL_CreationInterface* creationInterface )
{
    DL_PolylineData pd( maxVertices, getIntValue( 71, 0 ), getIntValue( 72, 0 ), getIntValue( 70,
                    0 ), getRealValue( 38, 0 ) );

    creationInterface->addPolyline( pd );

    maxVertices = std::min( maxVertices, vertexIndex + 1 );

    if( currentObjectType==DL_ENTITY_LWPOLYLINE )
    {
        for( int i = 0; i<maxVertices; i++ )
        {
            DL_VertexData d( vertices[i * 4],
                    vertices[i * 4 + 1],
                    vertices[i * 4 + 2],
                    vertices[i * 4 + 3] );

            creationInterface->addVertex( d );
        }

        creationInterface->endEntity();
    }
}


/**
 * Adds a polyline vertex entity that was read from the file
 * via the creation interface.
 */
void DL_Dxf::addVertex( DL_CreationInterface* creationInterface )
{
    // vertex defines a face of the mesh if its vertex flags group has the
    // 128 bit set but not the 64 bit. 10, 20, 30 are irrelevant and set to
    // 0 in this case
    if( (getIntValue( 70, 0 ) & 128) && !(getIntValue( 70, 0 ) & 64) )
    {
        return;
    }

    DL_VertexData d( getRealValue( 10, 0.0 ),
            getRealValue( 20, 0.0 ),
            getRealValue( 30, 0.0 ),
            getRealValue( 42, 0.0 ),
            getRealValue( 40, 0.0 ),
            getRealValue( 41, 0.0 ) );

    creationInterface->addVertex( d );
}


/**
 * Adds a spline entity that was read from the file via the creation interface.
 */
void DL_Dxf::addSpline( DL_CreationInterface* creationInterface )
{
    DL_SplineData sd( getIntValue( 71, 3 ),
            maxKnots,
            maxControlPoints,
            maxFitPoints,
            getIntValue( 70, 4 ) );

    sd.tangentStartX = getRealValue( 12, 0.0 );
    sd.tangentStartY = getRealValue( 22, 0.0 );
    sd.tangentStartZ    = getRealValue( 32, 0.0 );
    sd.tangentEndX  = getRealValue( 13, 0.0 );
    sd.tangentEndY  = getRealValue( 23, 0.0 );
    sd.tangentEndZ  = getRealValue( 33, 0.0 );

    creationInterface->addSpline( sd );

    int i;

    for( i = 0; i<maxControlPoints; i++ )
    {
        DL_ControlPointData d( controlPoints[i * 3],
                controlPoints[i * 3 + 1],
                controlPoints[i * 3 + 2],
                weights[i] );

        creationInterface->addControlPoint( d );
    }

    for( i = 0; i<maxFitPoints; i++ )
    {
        DL_FitPointData d( fitPoints[i * 3],
                fitPoints[i * 3 + 1],
                fitPoints[i * 3 + 2] );

        creationInterface->addFitPoint( d );
    }

    for( i = 0; i<maxKnots; i++ )
    {
        DL_KnotData k( knots[i] );

        creationInterface->addKnot( k );
    }

    creationInterface->endEntity();
}


/**
 * Adds an arc entity that was read from the file via the creation interface.
 */
void DL_Dxf::addArc( DL_CreationInterface* creationInterface )
{
    DL_ArcData d( getRealValue( 10, 0.0 ),
            getRealValue( 20, 0.0 ),
            getRealValue( 30, 0.0 ),
            getRealValue( 40, 0.0 ),
            getRealValue( 50, 0.0 ),
            getRealValue( 51, 0.0 ) );

    creationInterface->addArc( d );
}


/**
 * Adds a circle entity that was read from the file via the creation interface.
 */
void DL_Dxf::addCircle( DL_CreationInterface* creationInterface )
{
    DL_CircleData d( getRealValue( 10, 0.0 ),
            getRealValue( 20, 0.0 ),
            getRealValue( 30, 0.0 ),
            getRealValue( 40, 0.0 ) );

    creationInterface->addCircle( d );
}


/**
 * Adds an ellipse entity that was read from the file via the creation interface.
 */
void DL_Dxf::addEllipse( DL_CreationInterface* creationInterface )
{
    DL_EllipseData d( getRealValue( 10, 0.0 ),
            getRealValue( 20, 0.0 ),
            getRealValue( 30, 0.0 ),
            getRealValue( 11, 0.0 ),
            getRealValue( 21, 0.0 ),
            getRealValue( 31, 0.0 ),
            getRealValue( 40, 1.0 ),
            getRealValue( 41, 0.0 ),
            getRealValue( 42, 2 * M_PI ) );

    creationInterface->addEllipse( d );
}


/**
 * Adds an insert entity that was read from the file via the creation interface.
 */
void DL_Dxf::addInsert( DL_CreationInterface* creationInterface )
{
    // printf("addInsert\n");
    // printf("code 50: %s\n", values[50]);
    // printf("code 50 length: %d\n", strlen(values[50]));
    // printf("code 50:\n");
    // getRealValue(50, 0.0);

    std::string name = getStringValue( 2, "" );

    if( name.length()==0 )
    {
        return;
    }

    DL_InsertData d( name,
            // insertion point
            getRealValue( 10, 0.0 ),
            getRealValue( 20, 0.0 ),
            getRealValue( 30, 0.0 ),
            // scale:
            getRealValue( 41, 1.0 ),
            getRealValue( 42, 1.0 ),
            getRealValue( 43, 1.0 ),
            // angle (deg):
            getRealValue( 50, 0.0 ),
            // cols / rows:
            getIntValue( 70, 1 ),
            getIntValue( 71, 1 ),
            // spacing:
            getRealValue( 44, 0.0 ),
            getRealValue( 45, 0.0 ) );

    creationInterface->addInsert( d );
}


/**
 * Adds a trace entity (4 edge closed polyline) that was read from the file via the creation interface.
 *
 * @author AHM
 */
void DL_Dxf::addTrace( DL_CreationInterface* creationInterface )
{
    DL_TraceData td;

    for( int k = 0; k < 4; k++ )
    {
        td.x[k] = getRealValue( 10 + k, 0.0 );
        td.y[k] = getRealValue( 20 + k, 0.0 );
        td.z[k] = getRealValue( 30 + k, 0.0 );
    }

    creationInterface->addTrace( td );
}


/**
 * Adds a 3dface entity that was read from the file via the creation interface.
 */
void DL_Dxf::add3dFace( DL_CreationInterface* creationInterface )
{
    DL_3dFaceData td;

    for( int k = 0; k < 4; k++ )
    {
        td.x[k] = getRealValue( 10 + k, 0.0 );
        td.y[k] = getRealValue( 20 + k, 0.0 );
        td.z[k] = getRealValue( 30 + k, 0.0 );
    }

    creationInterface->add3dFace( td );
}


/**
 * Adds a solid entity (filled trace) that was read from the file via the creation interface.
 *
 * @author AHM
 */
void DL_Dxf::addSolid( DL_CreationInterface* creationInterface )
{
    DL_SolidData sd;

    for( int k = 0; k < 4; k++ )
    {
        sd.x[k] = getRealValue( 10 + k, 0.0 );
        sd.y[k] = getRealValue( 20 + k, 0.0 );
        sd.z[k] = getRealValue( 30 + k, 0.0 );
    }

    creationInterface->addSolid( sd );
}


/**
 * Adds an MText entity that was read from the file via the creation interface.
 */
void DL_Dxf::addMText( DL_CreationInterface* creationInterface )
{
    double angle = 0.0;

    if( hasValue( 50 ) )
    {
        if( libVersion<=0x02000200 )
        {
            // wrong but compatible with dxflib <=2.0.2.0 (angle stored in rad):
            angle = getRealValue( 50, 0.0 );
        }
        else
        {
            angle = (getRealValue( 50, 0.0 ) * 2 * M_PI) / 360.0;
        }
    }
    else if( hasValue( 11 ) && hasValue( 21 ) )
    {
        double x = getRealValue( 11, 0.0 );
        double y = getRealValue( 21, 0.0 );

        if( fabs( x )<1.0e-6 )
        {
            if( y>0.0 )
            {
                angle = M_PI / 2.0;
            }
            else
            {
                angle = M_PI / 2.0 * 3.0;
            }
        }
        else
        {
            angle = atan( y / x );
        }
    }

    DL_MTextData d(
            // insertion point
            getRealValue( 10, 0.0 ),
            getRealValue( 20, 0.0 ),
            getRealValue( 30, 0.0 ),
            // X direction vector
            getRealValue( 11, 0.0 ),
            getRealValue( 21, 0.0 ),
            getRealValue( 31, 0.0 ),
            // height
            getRealValue( 40, 2.5 ),
            // width
            getRealValue( 41, 0.0 ),
            // attachment point
            getIntValue( 71, 1 ),
            // drawing direction
            getIntValue( 72, 1 ),
            // line spacing style
            getIntValue( 73, 1 ),
            // line spacing factor
            getRealValue( 44, 1.0 ),
            // text
            getStringValue( 1, "" ),
            // style
            getStringValue( 7, "" ),
            // angle
            angle );
    creationInterface->addMText( d );
}


/**
 * Handles all XRecord data.
 */
bool DL_Dxf::handleXRecordData( DL_CreationInterface* creationInterface )
{
    if( groupCode==105 )
    {
        return false;
    }

    if( groupCode==5 )
    {
        creationInterface->addXRecord( groupValue );
        return true;
    }

    if( groupCode==280 )
    {
        xRecordValues = true;
        return true;
    }

    if( !xRecordValues )
    {
        return false;
    }

    // string:
    if( groupCode<=9
        || groupCode==100 || groupCode==102 || groupCode==105
        || (groupCode>=300 && groupCode<=369)
        || (groupCode>=1000 && groupCode<=1009) )
    {
        creationInterface->addXRecordString( groupCode, groupValue );
        return true;
    }
    // int:
    else if( (groupCode>=60 && groupCode<=99) || (groupCode>=160 && groupCode<=179)
             || (groupCode>=270 && groupCode<=289) )
    {
        creationInterface->addXRecordInt( groupCode, toInt( groupValue ) );
        return true;
    }
    // bool:
    else if( groupCode>=290 && groupCode<=299 )
    {
        creationInterface->addXRecordBool( groupCode, toBool( groupValue ) );
        return true;
    }
    // double:
    else if( (groupCode>=10 && groupCode<=59) || (groupCode>=110 && groupCode<=149)
             || (groupCode>=210 && groupCode<=239) )
    {
        creationInterface->addXRecordReal( groupCode, toReal( groupValue ) );
        return true;
    }

    return false;
}


/**
 * Handles all dictionary data.
 */
bool DL_Dxf::handleDictionaryData( DL_CreationInterface* creationInterface )
{
    if( groupCode==3 )
    {
        return true;
    }

    if( groupCode==5 )
    {
        creationInterface->addDictionary( DL_DictionaryData( groupValue ) );
        return true;
    }

    if( groupCode==350 )
    {
        creationInterface->addDictionaryEntry( DL_DictionaryEntryData( getStringValue( 3, "" ),
                        groupValue ) );
        return true;
    }

    return false;
}


/**
 * Handles XData for all object types.
 */
bool DL_Dxf::handleXData( DL_CreationInterface* creationInterface )
{
    if( groupCode==1001 )
    {
        creationInterface->addXDataApp( groupValue );
        return true;
    }
    else if( groupCode>=1000 && groupCode<=1009 )
    {
        creationInterface->addXDataString( groupCode, groupValue );
        return true;
    }
    else if( groupCode>=1010 && groupCode<=1059 )
    {
        creationInterface->addXDataReal( groupCode, toReal( groupValue ) );
        return true;
    }
    else if( groupCode>=1060 && groupCode<=1070 )
    {
        creationInterface->addXDataInt( groupCode, toInt( groupValue ) );
        return true;
    }
    else if( groupCode==1071 )
    {
        creationInterface->addXDataInt( groupCode, toInt( groupValue ) );
        return true;
    }

    return false;
}


/**
 * Handles additional MText data.
 */
bool DL_Dxf::handleMTextData( DL_CreationInterface* creationInterface )
{
    // Special handling of text chunks for MTEXT entities:
    if( groupCode==3 )
    {
        creationInterface->addMTextChunk( groupValue );
        return true;
    }

    return false;
}


/**
 * Handles additional polyline data.
 */
bool DL_Dxf::handleLWPolylineData( DL_CreationInterface*    /*creationInterface*/ )
{
    // Allocate LWPolyline vertices (group code 90):
    if( groupCode==90 )
    {
        maxVertices = toInt( groupValue );

        if( maxVertices>0 )
        {
            if( vertices!=NULL )
            {
                delete[] vertices;
            }

            vertices = new double[4 * maxVertices];

            for( int i = 0; i<maxVertices; ++i )
            {
                vertices[i * 4] = 0.0;
                vertices[i * 4 + 1] = 0.0;
                vertices[i * 4 + 2] = 0.0;
                vertices[i * 4 + 3] = 0.0;
            }
        }

        vertexIndex = -1;
        return true;
    }
    // Process LWPolylines vertices (group codes 10/20/30/42):
    else if( groupCode==10 || groupCode==20
             || groupCode==30 || groupCode==42 )
    {
        if( vertexIndex<maxVertices - 1 && groupCode==10 )
        {
            vertexIndex++;
        }

        if( groupCode<=30 )
        {
            if( vertexIndex>=0 && vertexIndex<maxVertices )
            {
                vertices[4 * vertexIndex + (groupCode / 10 - 1)] = toReal( groupValue );
            }
        }
        else if( groupCode == 42 && vertexIndex < maxVertices && vertexIndex >= 0 )
        {
            vertices[4 * vertexIndex + 3] = toReal( groupValue );
        }

        return true;
    }

    return false;
}


/**
 * Handles additional spline data.
 */
bool DL_Dxf::handleSplineData( DL_CreationInterface*    /*creationInterface*/ )
{
    // Allocate Spline knots (group code 72):
    if( groupCode==72 )
    {
        maxKnots = toInt( groupValue );

        if( maxKnots>0 )
        {
            if( knots!=NULL )
            {
                delete[] knots;
            }

            knots = new double[maxKnots];

            for( int i = 0; i<maxKnots; ++i )
            {
                knots[i] = 0.0;
            }
        }

        knotIndex = -1;
        return true;
    }
    // Allocate Spline control points / weights (group code 73):
    else if( groupCode==73 )
    {
        maxControlPoints = toInt( groupValue );

        if( maxControlPoints>0 )
        {
            if( controlPoints!=NULL )
            {
                delete[] controlPoints;
            }

            if( weights!=NULL )
            {
                delete[] weights;
            }

            controlPoints = new double[3 * maxControlPoints];
            weights = new double[maxControlPoints];

            for( int i = 0; i<maxControlPoints; ++i )
            {
                controlPoints[i * 3] = 0.0;
                controlPoints[i * 3 + 1] = 0.0;
                controlPoints[i * 3 + 2] = 0.0;
                weights[i] = 1.0;
            }
        }

        controlPointIndex = -1;
        weightIndex = -1;
        return true;
    }
    // Allocate Spline fit points (group code 74):
    else if( groupCode==74 )
    {
        maxFitPoints = toInt( groupValue );

        if( maxFitPoints>0 )
        {
            if( fitPoints!=NULL )
            {
                delete[] fitPoints;
            }

            fitPoints = new double[3 * maxFitPoints];

            for( int i = 0; i<maxFitPoints; ++i )
            {
                fitPoints[i * 3] = 0.0;
                fitPoints[i * 3 + 1] = 0.0;
                fitPoints[i * 3 + 2] = 0.0;
            }
        }

        fitPointIndex = -1;
        return true;
    }
    // Process spline knot vertices (group code 40):
    else if( groupCode==40 )
    {
        if( knotIndex<maxKnots - 1 )
        {
            knotIndex++;
            knots[knotIndex] = toReal( groupValue );
        }

        return true;
    }
    // Process spline control points (group codes 10/20/30):
    else if( groupCode==10 || groupCode==20
             || groupCode==30 )
    {
        if( controlPointIndex<maxControlPoints - 1 && groupCode==10 )
        {
            controlPointIndex++;
        }

        if( controlPointIndex>=0 && controlPointIndex<maxControlPoints )
        {
            controlPoints[3 * controlPointIndex + (groupCode / 10 - 1)] = toReal( groupValue );
        }

        return true;
    }
    // Process spline fit points (group codes 11/21/31):
    else if( groupCode==11 || groupCode==21 || groupCode==31 )
    {
        if( fitPointIndex<maxFitPoints - 1 && groupCode==11 )
        {
            fitPointIndex++;
        }

        if( fitPointIndex>=0 && fitPointIndex<maxFitPoints )
        {
            fitPoints[3 * fitPointIndex + ( (groupCode - 1) / 10 - 1 )] = toReal( groupValue );
        }

        return true;
    }
    // Process spline weights (group code 41)
    else if( groupCode==41 )
    {
        if( weightIndex<maxControlPoints - 1 )
        {
            weightIndex++;
        }

        if( weightIndex>=0 && weightIndex<maxControlPoints )
        {
            weights[weightIndex] = toReal( groupValue );
        }

        return true;
    }

    return false;
}


/**
 * Handles additional leader data.
 */
bool DL_Dxf::handleLeaderData( DL_CreationInterface*    /*creationInterface*/ )
{
    // Allocate Leader vertices (group code 76):
    if( groupCode==76 )
    {
        maxLeaderVertices = toInt( groupValue );

        if( maxLeaderVertices>0 )
        {
            if( leaderVertices!=NULL )
            {
                delete[] leaderVertices;
            }

            leaderVertices = new double[3 * maxLeaderVertices];

            for( int i = 0; i<maxLeaderVertices; ++i )
            {
                leaderVertices[i * 3] = 0.0;
                leaderVertices[i * 3 + 1] = 0.0;
                leaderVertices[i * 3 + 2] = 0.0;
            }
        }

        leaderVertexIndex = -1;
        return true;
    }
    // Process Leader vertices (group codes 10/20/30):
    else if( groupCode==10 || groupCode==20 || groupCode==30 )
    {
        if( leaderVertexIndex<maxLeaderVertices - 1 && groupCode==10 )
        {
            leaderVertexIndex++;
        }

        if( groupCode<=30 )
        {
            if( leaderVertexIndex>=0
                && leaderVertexIndex<maxLeaderVertices )
            {
                leaderVertices[3 * leaderVertexIndex + (groupCode / 10 - 1)]
                    = toReal( groupValue );
            }
        }

        return true;
    }

    return false;
}


/**
 * Adds an text entity that was read from the file via the creation interface.
 */
void DL_Dxf::addText( DL_CreationInterface* creationInterface )
{
    DL_TextData d(
            // insertion point
            getRealValue( 10, 0.0 ),
            getRealValue( 20, 0.0 ),
            getRealValue( 30, 0.0 ),
            // alignment point
            getRealValue( 11, DL_NANDOUBLE ),
            getRealValue( 21, DL_NANDOUBLE ),
            getRealValue( 31, DL_NANDOUBLE ),
            // height
            getRealValue( 40, 2.5 ),
            // x scale
            getRealValue( 41, 1.0 ),
            // generation flags
            getIntValue( 71, 0 ),
            // h just
            getIntValue( 72, 0 ),
            // v just
            getIntValue( 73, 0 ),
            // text
            getStringValue( 1, "" ),
            // style
            getStringValue( 7, "" ),
            // angle
            (getRealValue( 50, 0.0 ) * 2 * M_PI) / 360.0 );

    creationInterface->addText( d );
}


/**
 * Adds an arc aligned text entity that was read from the file via the creation interface.
 */
void DL_Dxf::addArcAlignedText( DL_CreationInterface* creationInterface )
{
    DL_ArcAlignedTextData d;

    d.text      = getStringValue( 1, "" );
    d.font      = getStringValue( 2, "" );
    d.style     = getStringValue( 7, "" );
    d.cx        = getRealValue( 10, 0.0 );
    d.cy        = getRealValue( 20, 0.0 );
    d.cz        = getRealValue( 30, 0.0 );
    d.radius    = getRealValue( 40, 0.0 );
    d.xScaleFactor = getRealValue( 41, 0.0 );
    d.height = getRealValue( 42, 0.0 );
    d.spacing = getRealValue( 43, 0.0 );
    d.offset = getRealValue( 44, 0.0 );
    d.rightOffset = getRealValue( 45, 0.0 );
    d.leftOffset = getRealValue( 46, 0.0 );
    d.startAngle = getRealValue( 50, 0.0 );
    d.endAngle = getRealValue( 51, 0.0 );
    d.reversedCharacterOrder = getIntValue( 70, 0 );
    d.direction = getIntValue( 71, 0 );
    d.alignment = getIntValue( 72, 0 );
    d.side  = getIntValue( 73, 0 );
    d.bold  = getIntValue( 74, 0 );
    d.italic = getIntValue( 75, 0 );
    d.underline = getIntValue( 76, 0 );
    d.characerSet = getIntValue( 77, 0 );
    d.pitch = getIntValue( 78, 0 );
    d.shxFont = getIntValue( 79, 0 );
    d.wizard = getIntValue( 280, 0 );
    d.arcHandle = getIntValue( 330, 0 );

    creationInterface->addArcAlignedText( d );
}


/**
 * Adds an attrib entity that was read from the file via the creation interface.
 * @todo add attrib instead of normal text
 */
void DL_Dxf::addAttribute( DL_CreationInterface* creationInterface )
{
    DL_AttributeData d(
            // insertion point
            getRealValue( 10, 0.0 ),
            getRealValue( 20, 0.0 ),
            getRealValue( 30, 0.0 ),
            // alignment point
            getRealValue( 11, 0.0 ),
            getRealValue( 21, 0.0 ),
            getRealValue( 31, 0.0 ),
            // height
            getRealValue( 40, 2.5 ),
            // x scale
            getRealValue( 41, 1.0 ),
            // generation flags
            getIntValue( 71, 0 ),
            // h just
            getIntValue( 72, 0 ),
            // v just
            getIntValue( 74, 0 ),
            // tag
            getStringValue( 2, "" ),
            // text
            getStringValue( 1, "" ),
            // style
            getStringValue( 7, "" ),
            // angle
            (getRealValue( 50, 0.0 ) * 2 * M_PI) / 360.0 );

    creationInterface->addAttribute( d );
}


/**
 * @return dimension data from current values.
 */
DL_DimensionData DL_Dxf::getDimData()
{
    // generic dimension data:
    DL_DimensionData ret(
            // def point
            getRealValue( 10, 0.0 ),
            getRealValue( 20, 0.0 ),
            getRealValue( 30, 0.0 ),
            // text middle point
            getRealValue( 11, 0.0 ),
            getRealValue( 21, 0.0 ),
            getRealValue( 31, 0.0 ),
            // type
            getIntValue( 70, 0 ),
            // attachment point
            getIntValue( 71, 5 ),
            // line sp. style
            getIntValue( 72, 1 ),
            // line sp. factor
            getRealValue( 41, 1.0 ),
            // text
            getStringValue( 1, "" ),
            // style
            getStringValue( 3, "" ),
            // angle
            getRealValue( 53, 0.0 ) );

    ret.arrow1Flipped = getIntValue( 74, 0 ) == 1;
    ret.arrow2Flipped = getIntValue( 75, 0 ) == 1;
    return ret;
}


/**
 * Adds a linear dimension entity that was read from the file via the creation interface.
 */
void DL_Dxf::addDimLinear( DL_CreationInterface* creationInterface )
{
    DL_DimensionData d = getDimData();

    // horizontal / vertical / rotated dimension:
    DL_DimLinearData dl(
            // definition point 1
            getRealValue( 13, 0.0 ),
            getRealValue( 23, 0.0 ),
            getRealValue( 33, 0.0 ),
            // definition point 2
            getRealValue( 14, 0.0 ),
            getRealValue( 24, 0.0 ),
            getRealValue( 34, 0.0 ),
            // angle
            getRealValue( 50, 0.0 ),
            // oblique
            getRealValue( 52, 0.0 ) );

    creationInterface->addDimLinear( d, dl );
}


/**
 * Adds an aligned dimension entity that was read from the file via the creation interface.
 */
void DL_Dxf::addDimAligned( DL_CreationInterface* creationInterface )
{
    DL_DimensionData d = getDimData();

    // aligned dimension:
    DL_DimAlignedData da(
            // extension point 1
            getRealValue( 13, 0.0 ),
            getRealValue( 23, 0.0 ),
            getRealValue( 33, 0.0 ),
            // extension point 2
            getRealValue( 14, 0.0 ),
            getRealValue( 24, 0.0 ),
            getRealValue( 34, 0.0 ) );

    creationInterface->addDimAlign( d, da );
}


/**
 * Adds a radial dimension entity that was read from the file via the creation interface.
 */
void DL_Dxf::addDimRadial( DL_CreationInterface* creationInterface )
{
    DL_DimensionData d = getDimData();

    DL_DimRadialData dr(
            // definition point
            getRealValue( 15, 0.0 ),
            getRealValue( 25, 0.0 ),
            getRealValue( 35, 0.0 ),
            // leader length:
            getRealValue( 40, 0.0 ) );

    creationInterface->addDimRadial( d, dr );
}


/**
 * Adds a diametric dimension entity that was read from the file via the creation interface.
 */
void DL_Dxf::addDimDiametric( DL_CreationInterface* creationInterface )
{
    DL_DimensionData d = getDimData();

    // diametric dimension:
    DL_DimDiametricData dr(
            // definition point
            getRealValue( 15, 0.0 ),
            getRealValue( 25, 0.0 ),
            getRealValue( 35, 0.0 ),
            // leader length:
            getRealValue( 40, 0.0 ) );

    creationInterface->addDimDiametric( d, dr );
}


/**
 * Adds an angular dimension entity that was read from the file via the creation interface.
 */
void DL_Dxf::addDimAngular( DL_CreationInterface* creationInterface )
{
    DL_DimensionData d = getDimData();

    // angular dimension:
    DL_DimAngular2LData da(
            // definition point 1
            getRealValue( 13, 0.0 ),
            getRealValue( 23, 0.0 ),
            getRealValue( 33, 0.0 ),
            // definition point 2
            getRealValue( 14, 0.0 ),
            getRealValue( 24, 0.0 ),
            getRealValue( 34, 0.0 ),
            // definition point 3
            getRealValue( 15, 0.0 ),
            getRealValue( 25, 0.0 ),
            getRealValue( 35, 0.0 ),
            // definition point 4
            getRealValue( 16, 0.0 ),
            getRealValue( 26, 0.0 ),
            getRealValue( 36, 0.0 ) );

    creationInterface->addDimAngular( d, da );
}


/**
 * Adds an angular dimension entity that was read from the file via the creation interface.
 */
void DL_Dxf::addDimAngular3P( DL_CreationInterface* creationInterface )
{
    DL_DimensionData d = getDimData();

    // angular dimension (3P):
    DL_DimAngular3PData da(
            // definition point 1
            getRealValue( 13, 0.0 ),
            getRealValue( 23, 0.0 ),
            getRealValue( 33, 0.0 ),
            // definition point 2
            getRealValue( 14, 0.0 ),
            getRealValue( 24, 0.0 ),
            getRealValue( 34, 0.0 ),
            // definition point 3
            getRealValue( 15, 0.0 ),
            getRealValue( 25, 0.0 ),
            getRealValue( 35, 0.0 ) );

    creationInterface->addDimAngular3P( d, da );
}


/**
 * Adds an ordinate dimension entity that was read from the file via the creation interface.
 */
void DL_Dxf::addDimOrdinate( DL_CreationInterface* creationInterface )
{
    DL_DimensionData d = getDimData();

    // ordinate dimension:
    DL_DimOrdinateData dl(
            // definition point 1
            getRealValue( 13, 0.0 ),
            getRealValue( 23, 0.0 ),
            getRealValue( 33, 0.0 ),
            // definition point 2
            getRealValue( 14, 0.0 ),
            getRealValue( 24, 0.0 ),
            getRealValue( 34, 0.0 ),
            (getIntValue( 70, 0 ) & 64)==64 // true: X-type, false: Y-type
            );

    creationInterface->addDimOrdinate( d, dl );
}


/**
 * Adds a leader entity that was read from the file via the creation interface.
 */
void DL_Dxf::addLeader( DL_CreationInterface* creationInterface )
{
    // leader (arrow)
    DL_LeaderData le(
            // arrow head flag
            getIntValue( 71, 1 ),
            // leader path type
            getIntValue( 72, 0 ),
            // Leader creation flag
            getIntValue( 73, 3 ),
            // Hookline direction flag
            getIntValue( 74, 1 ),
            // Hookline flag
            getIntValue( 75, 0 ),
            // Text annotation height
            getRealValue( 40, 1.0 ),
            // Text annotation width
            getRealValue( 41, 1.0 ),
            // Number of vertices in leader
            getIntValue( 76, 0 )
            );

    creationInterface->addLeader( le );

    for( int i = 0; i<maxLeaderVertices; i++ )
    {
        DL_LeaderVertexData d( leaderVertices[i * 3],
                leaderVertices[i * 3 + 1],
                leaderVertices[i * 3 + 2] );

        creationInterface->addLeaderVertex( d );
    }

    creationInterface->endEntity();
}


/**
 * Adds a hatch entity that was read from the file via the creation interface.
 */
void DL_Dxf::addHatch( DL_CreationInterface* creationInterface )
{
    DL_HatchData hd( getIntValue( 91, 1 ),
            getIntValue( 70, 0 ),
            getRealValue( 41, 1.0 ),
            getRealValue( 52, 0.0 ),
            getStringValue( 2, "" ) );

    creationInterface->addHatch( hd );

    for( unsigned int i = 0; i<hatchEdges.size(); i++ )
    {
        creationInterface->addHatchLoop( DL_HatchLoopData( static_cast<int>( hatchEdges[i].size() ) ) );

        for( unsigned int k = 0; k<hatchEdges[i].size(); k++ )
        {
            creationInterface->addHatchEdge( DL_HatchEdgeData( hatchEdges[i][k] ) );
        }
    }

    creationInterface->endEntity();
}


void DL_Dxf::addHatchLoop()
{
    addHatchEdge();
    hatchEdges.emplace_back( );
}


void DL_Dxf::addHatchEdge()
{
    if( hatchEdge.defined )
    {
        if( hatchEdges.size()>0 )
        {
            hatchEdges.back().push_back( hatchEdge );
        }

        hatchEdge = DL_HatchEdgeData();
    }
}


/**
 * Handles all hatch data.
 */
bool DL_Dxf::handleHatchData( DL_CreationInterface* creationInterface )
{
    // New polyline loop, group code 92
    // or new loop with individual edges, group code 93
    if( groupCode==92 || groupCode==93 )
    {
        if( firstHatchLoop )
        {
            hatchEdges.clear();
            firstHatchLoop = false;
        }

        if( groupCode==92 && (toInt( groupValue ) & 2)==2 )
        {
            addHatchLoop();
        }

        if( groupCode==93 )
        {
            addHatchLoop();
        }

        return true;
    }

    // New hatch edge or new section / entity: add last hatch edge:
    if( groupCode==72 || groupCode==0 || groupCode==78 || groupCode==98 )
    {
        // polyline boundaries use code 72 for bulge flag:
        if( groupCode!=72 || (getIntValue( 92, 0 ) & 2)==0 )
        {
            addHatchEdge();
        }

        if( groupCode==0    /*|| groupCode==78*/ )
        {
            addHatch( creationInterface );
        }
        else
        {
            hatchEdge.type = toInt( groupValue );
        }

        return true;
    }

    // polyline boundary:
    if( (getIntValue( 92, 0 ) & 2)==2 )
    {
        switch( groupCode )
        {
        case 10:
            hatchEdge.type = 0;
            hatchEdge.vertices.emplace_back( );
            hatchEdge.vertices.back().push_back( toReal( groupValue ) );
            return true;

        case 20:

            if( !hatchEdge.vertices.empty() )
            {
                hatchEdge.vertices.back().push_back( toReal( groupValue ) );
                hatchEdge.defined = true;
            }

            return true;

        case 42:

            if( !hatchEdge.vertices.empty() )
            {
                hatchEdge.vertices.back().push_back( toReal( groupValue ) );
                hatchEdge.defined = true;
            }

            return true;
        }
    }
    else
    {
        // Line edge:
        if( hatchEdge.type==1 )
        {
            switch( groupCode )
            {
            case 10:
                hatchEdge.x1 = toReal( groupValue );
                return true;

            case 20:
                hatchEdge.y1 = toReal( groupValue );
                return true;

            case 11:
                hatchEdge.x2 = toReal( groupValue );
                return true;

            case 21:
                hatchEdge.y2 = toReal( groupValue );
                hatchEdge.defined = true;
                return true;
            }
        }

        // Arc edge:
        if( hatchEdge.type==2 )
        {
            switch( groupCode )
            {
            case 10:
                hatchEdge.cx = toReal( groupValue );
                return true;

            case 20:
                hatchEdge.cy = toReal( groupValue );
                return true;

            case 40:
                hatchEdge.radius = toReal( groupValue );
                return true;

            case 50:
                hatchEdge.angle1 = toReal( groupValue ) / 360.0 * 2 * M_PI;
                return true;

            case 51:
                hatchEdge.angle2 = toReal( groupValue ) / 360.0 * 2 * M_PI;
                return true;

            case 73:
                hatchEdge.ccw = (bool) toInt( groupValue );
                hatchEdge.defined = true;
                return true;
            }
        }

        // Ellipse arc edge:
        if( hatchEdge.type==3 )
        {
            switch( groupCode )
            {
            case 10:
                hatchEdge.cx = toReal( groupValue );
                return true;

            case 20:
                hatchEdge.cy = toReal( groupValue );
                return true;

            case 11:
                hatchEdge.mx = toReal( groupValue );
                return true;

            case 21:
                hatchEdge.my = toReal( groupValue );
                return true;

            case 40:
                hatchEdge.ratio = toReal( groupValue );
                return true;

            case 50:
                hatchEdge.angle1 = toReal( groupValue ) / 360.0 * 2 * M_PI;
                return true;

            case 51:
                hatchEdge.angle2 = toReal( groupValue ) / 360.0 * 2 * M_PI;
                return true;

            case 73:
                hatchEdge.ccw = (bool) toInt( groupValue );
                hatchEdge.defined = true;
                return true;
            }
        }

        // Spline edge:
        if( hatchEdge.type==4 )
        {
            switch( groupCode )
            {
            case 94:
                hatchEdge.degree = toInt( groupValue );
                return true;

            case 73:
                hatchEdge.rational = toBool( groupValue );
                return true;

            case 74:
                hatchEdge.periodic = toBool( groupValue );
                return true;

            case 95:
                hatchEdge.nKnots = toInt( groupValue );
                return true;

            case 96:
                hatchEdge.nControl = toInt( groupValue );
                return true;

            case 97:
                hatchEdge.nFit = toInt( groupValue );
                return true;

            case 40:

                if( hatchEdge.knots.size() < hatchEdge.nKnots )
                {
                    hatchEdge.knots.push_back( toReal( groupValue ) );
                }

                return true;

            case 10:

                if( hatchEdge.controlPoints.size() < hatchEdge.nControl )
                {
                    std::vector<double> v;
                    v.push_back( toReal( groupValue ) );
                    hatchEdge.controlPoints.push_back( v );
                }

                return true;

            case 20:

                if( !hatchEdge.controlPoints.empty() && hatchEdge.controlPoints.back().size()==1 )
                {
                    hatchEdge.controlPoints.back().push_back( toReal( groupValue ) );
                }

                hatchEdge.defined = true;
                return true;

            case 42:

                if( hatchEdge.weights.size() < hatchEdge.nControl )
                {
                    hatchEdge.weights.push_back( toReal( groupValue ) );
                }

                return true;

            case 11:

                if( hatchEdge.fitPoints.size() < hatchEdge.nFit )
                {
                    std::vector<double> v;
                    v.push_back( toReal( groupValue ) );
                    hatchEdge.fitPoints.push_back( v );
                }

                return true;

            case 21:

                if( !hatchEdge.fitPoints.empty() && hatchEdge.fitPoints.back().size()==1 )
                {
                    hatchEdge.fitPoints.back().push_back( toReal( groupValue ) );
                }

                hatchEdge.defined = true;
                return true;

            case 12:
                hatchEdge.startTangentX = toReal( groupValue );
                return true;

            case 22:
                hatchEdge.startTangentY = toReal( groupValue );
                return true;

            case 13:
                hatchEdge.endTangentX = toReal( groupValue );
                return true;

            case 23:
                hatchEdge.endTangentY = toReal( groupValue );
                return true;
            }
        }
    }

    return false;
}


/**
 * Adds an image entity that was read from the file via the creation interface.
 */
void DL_Dxf::addImage( DL_CreationInterface* creationInterface )
{
    DL_ImageData id( // pass ref insead of name we don't have yet
            getStringValue( 340, "" ),
            // ins point:
            getRealValue( 10, 0.0 ),
            getRealValue( 20, 0.0 ),
            getRealValue( 30, 0.0 ),
            // u vector:
            getRealValue( 11, 1.0 ),
            getRealValue( 21, 0.0 ),
            getRealValue( 31, 0.0 ),
            // v vector:
            getRealValue( 12, 0.0 ),
            getRealValue( 22, 1.0 ),
            getRealValue( 32, 0.0 ),
            // image size (pixel):
            getIntValue( 13, 1 ),
            getIntValue( 23, 1 ),
            // brightness, contrast, fade
            getIntValue( 281, 50 ),
            getIntValue( 282, 50 ),
            getIntValue( 283, 0 ) );

    creationInterface->addImage( id );
    creationInterface->endEntity();
    currentObjectType = DL_UNKNOWN;
}


/**
 * Adds an image definition that was read from the file via the creation interface.
 */
void DL_Dxf::addImageDef( DL_CreationInterface* creationInterface )
{
    DL_ImageDefData id( // handle
            getStringValue( 5, "" ),
            getStringValue( 1, "" ) );

    creationInterface->linkImage( id );
    creationInterface->endEntity();
    currentObjectType = DL_UNKNOWN;
}


/**
 * Ends some special entities like hatches or old style polylines.
 */
void DL_Dxf::endEntity( DL_CreationInterface* creationInterface )
{
    creationInterface->endEntity();
}


/**
 * Ends a sequence and notifies the creation interface.
 */
void DL_Dxf::endSequence( DL_CreationInterface* creationInterface )
{
    creationInterface->endSequence();
}


/**
 * Converts the given string into an int.
 * ok is set to false if there was an error.
 */
// int DL_Dxf::stringToInt(const char* s, bool* ok) {
// if (ok!=NULL) {
//// check string:
// *ok = true;
// int i=0;
// bool dot = false;
// do {
// if (s[i]=='\0') {
// break;
// } else if (s[i]=='.') {
// if (dot==true) {
////std::cerr << "two dots\n";
// *ok = false;
// } else {
// dot = true;
// }
// } else if (s[i]<'0' || s[i]>'9') {
////std::cerr << "NaN: '" << s[i] << "'\n";
// *ok = false;
// }
// i++;
// } while(s[i]!='\0' && *ok==true);
// }

// return atoi(s);
// }


/**
 * @brief Opens the given file for writing and returns a pointer
 * to the dxf writer. This pointer needs to be passed on to other
 * writing functions.
 *
 * @param file Full path of the file to open.
 *
 * @return Pointer to an ascii dxf writer object.
 */
DL_WriterA* DL_Dxf::out( const char* file, DL_Codes::version aVersion )
{
    char* f = new char[strlen( file ) + 1];

    strcpy( f, file );
    this->version = aVersion;

    DL_WriterA* dw = new DL_WriterA( f, aVersion );

    if( dw->openFailed() )
    {
        delete dw;
        delete[] f;
        return NULL;
    }
    else
    {
        delete[] f;
        return dw;
    }
}


/**
 * @brief Writes a DXF header to the file currently opened
 * by the given DXF writer object.
 */
void DL_Dxf::writeHeader( DL_WriterA& dw )
{
    dw.comment( "dxflib " DL_VERSION );
    dw.sectionHeader();

    dw.dxfString( 9, "$ACADVER" );

    switch( version )
    {
    case DL_Codes::AC1009:
        dw.dxfString( 1, "AC1009" );
        break;

    case DL_Codes::AC1012:
        dw.dxfString( 1, "AC1012" );
        break;

    case DL_Codes::AC1014:
        dw.dxfString( 1, "AC1014" );
        break;

    case DL_Codes::AC1015:
        dw.dxfString( 1, "AC1015" );
        break;

    case DL_Codes::AC1009_MIN:
        // minimalistic DXF version is unidentified in file:
        break;
    }

    // Newer version require that (otherwise a*cad crashes..)
    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 9, "$HANDSEED" );
        dw.dxfHex( 5, 0xFFFF );
    }

    // commented out: more variables can be added after that by caller:
    // dw.sectionEnd();
}


/**
 * Writes a point entity to the file.
 *
 * @param dw DXF writer
 * @param data Entity data from the file
 * @param aAttrib Attributes
 */
void DL_Dxf::writePoint( DL_WriterA& dw,
        const DL_PointData& data,
        const DL_Attributes& aAttrib )
{
    dw.entity( "POINT" );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbEntity" );
    }

    dw.entityAttributes( aAttrib );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbPoint" );
    }

    dw.coord( DL_POINT_COORD_CODE, data.x, data.y, data.z );
}


/**
 * Writes a line entity to the file.
 *
 * @param dw DXF writer
 * @param data Entity data from the file
 * @param aAttrib Attributes
 */
void DL_Dxf::writeLine( DL_WriterA& dw,
        const DL_LineData& data,
        const DL_Attributes& aAttrib )
{
    dw.entity( "LINE" );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbEntity" );
    }

    dw.entityAttributes( aAttrib );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbLine" );
    }

    dw.coord( DL_LINE_START_CODE, data.x1, data.y1, data.z1 );
    dw.coord( DL_LINE_END_CODE, data.x2, data.y2, data.z2 );
}


/**
 * Writes an x line entity to the file.
 *
 * @param dw DXF writer
 * @param data Entity data from the file
 * @param aAttrib Attributes
 */
void DL_Dxf::writeXLine( DL_WriterA& dw,
        const DL_XLineData& data,
        const DL_Attributes& aAttrib )
{
    dw.entity( "XLINE" );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbEntity" );
    }

    dw.entityAttributes( aAttrib );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbLine" );
    }

    dw.coord( DL_LINE_START_CODE, data.bx, data.by, data.bz );
    dw.coord( DL_LINE_END_CODE, data.dx, data.dy, data.dz );
}


/**
 * Writes a ray entity to the file.
 *
 * @param dw DXF writer
 * @param data Entity data from the file
 * @param aAttrib Attributes
 */
void DL_Dxf::writeRay( DL_WriterA& dw,
        const DL_RayData& data,
        const DL_Attributes& aAttrib )
{
    dw.entity( "RAY" );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbEntity" );
    }

    dw.entityAttributes( aAttrib );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbLine" );
    }

    dw.coord( DL_LINE_START_CODE, data.bx, data.by, data.bz );
    dw.coord( DL_LINE_END_CODE, data.dx, data.dy, data.dz );
}


/**
 * Writes a polyline entity to the file.
 *
 * @param dw DXF writer
 * @param data Entity data from the file
 * @param aAttrib Attributes
 * @see writeVertex
 */
void DL_Dxf::writePolyline( DL_WriterA& dw,
        const DL_PolylineData& data,
        const DL_Attributes& aAttrib )
{
    if( version==DL_VERSION_2000 )
    {
        dw.entity( "LWPOLYLINE" );
        dw.dxfString( 100, "AcDbEntity" );
        dw.entityAttributes( attrib );
        dw.dxfString( 100, "AcDbPolyline" );
        dw.dxfInt( 90, (int) data.number );
        dw.dxfInt( 70, data.flags );
    }
    else
    {
        dw.entity( "POLYLINE" );
        dw.entityAttributes( attrib );
        polylineLayer = attrib.getLayer();
        dw.dxfInt( 66, 1 );
        dw.dxfInt( 70, data.flags );
        dw.coord( DL_VERTEX_COORD_CODE, 0.0, 0.0, 0.0 );
    }
}


/**
 * Writes a single vertex of a polyline to the file.
 *
 * @param dw DXF writer
 * @param data Entity data from the file
 * @param aAttrib Attributes
 */
void DL_Dxf::writeVertex( DL_WriterA& dw,
        const DL_VertexData& data )
{
    if( version==DL_VERSION_2000 )
    {
        dw.dxfReal( 10, data.x );
        dw.dxfReal( 20, data.y );

        if( fabs( data.bulge )>1.0e-10 )
        {
            dw.dxfReal( 42, data.bulge );
        }
    }
    else
    {
        dw.entity( "VERTEX" );
        // dw.entityAttributes(aAttrib);
        dw.dxfString( 8, polylineLayer );
        dw.coord( DL_VERTEX_COORD_CODE, data.x, data.y, data.z );

        if( fabs( data.bulge )>1.0e-10 )
        {
            dw.dxfReal( 42, data.bulge );
        }
    }
}


/**
 * Writes the polyline end. Only needed for DXF R12.
 */
void DL_Dxf::writePolylineEnd( DL_WriterA& dw )
{
    if( version==DL_VERSION_2000 )
    {
    }
    else
    {
        dw.entity( "SEQEND" );
    }
}


/**
 * Writes a spline entity to the file.
 *
 * @param dw DXF writer
 * @param data Entity data from the file
 * @param aAttrib Attributes
 * @see writeControlPoint
 */
void DL_Dxf::writeSpline( DL_WriterA& dw,
        const DL_SplineData& data,
        const DL_Attributes& aAttrib )
{
    dw.entity( "SPLINE" );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbEntity" );
    }

    dw.entityAttributes( aAttrib );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbSpline" );
    }

    dw.dxfInt( 70, data.flags );
    dw.dxfInt( 71, data.degree );
    dw.dxfInt( 72, data.nKnots );               // number of knots
    dw.dxfInt( 73, data.nControl );             // number of control points
    dw.dxfInt( 74, data.nFit );                 // number of fit points
}


/**
 * Writes a single control point of a spline to the file.
 *
 * @param dw DXF writer
 * @param data Entity data from the file
 * @param aAttrib Attributes
 */
void DL_Dxf::writeControlPoint( DL_WriterA& dw,
        const DL_ControlPointData& data )
{
    dw.dxfReal( 10, data.x );
    dw.dxfReal( 20, data.y );
    dw.dxfReal( 30, data.z );
}


/**
 * Writes a single fit point of a spline to the file.
 *
 * @param dw DXF writer
 * @param data Entity data from the file
 */
void DL_Dxf::writeFitPoint( DL_WriterA& dw,
        const DL_FitPointData& data )
{
    dw.dxfReal( 11, data.x );
    dw.dxfReal( 21, data.y );
    dw.dxfReal( 31, data.z );
}


/**
 * Writes a single knot of a spline to the file.
 *
 * @param dw DXF writer
 * @param data Entity data from the file
 */
void DL_Dxf::writeKnot( DL_WriterA& dw,
        const DL_KnotData& data )
{
    dw.dxfReal( 40, data.k );
}


/**
 * Writes a circle entity to the file.
 *
 * @param dw DXF writer
 * @param data Entity data from the file
 * @param aAttrib Attributes
 */
void DL_Dxf::writeCircle( DL_WriterA& dw,
        const DL_CircleData& data,
        const DL_Attributes& aAttrib )
{
    dw.entity( "CIRCLE" );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbEntity" );
    }

    dw.entityAttributes( aAttrib );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbCircle" );
    }

    dw.coord( 10, data.cx, data.cy, data.cz );
    dw.dxfReal( 40, data.radius );
}


/**
 * Writes an arc entity to the file.
 *
 * @param dw DXF writer
 * @param data Entity data from the file
 * @param aAttrib Attributes
 */
void DL_Dxf::writeArc( DL_WriterA& dw,
        const DL_ArcData& data,
        const DL_Attributes& aAttrib )
{
    dw.entity( "ARC" );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbEntity" );
    }

    dw.entityAttributes( aAttrib );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbCircle" );
    }

    dw.coord( 10, data.cx, data.cy, data.cz );
    dw.dxfReal( 40, data.radius );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbArc" );
    }

    dw.dxfReal( 50, data.angle1 );
    dw.dxfReal( 51, data.angle2 );
}


/**
 * Writes an ellipse entity to the file.
 *
 * @param dw DXF writer
 * @param data Entity data from the file
 * @param aAttrib Attributes
 */
void DL_Dxf::writeEllipse( DL_WriterA& dw,
        const DL_EllipseData& data,
        const DL_Attributes& aAttrib )
{
    if( version>DL_VERSION_R12 )
    {
        dw.entity( "ELLIPSE" );

        if( version==DL_VERSION_2000 )
        {
            dw.dxfString( 100, "AcDbEntity" );
        }

        dw.entityAttributes( aAttrib );

        if( version==DL_VERSION_2000 )
        {
            dw.dxfString( 100, "AcDbEllipse" );
        }

        dw.coord( 10, data.cx, data.cy, data.cz );
        dw.coord( 11, data.mx, data.my, data.mz );
        dw.dxfReal( 40, data.ratio );
        dw.dxfReal( 41, data.angle1 );
        dw.dxfReal( 42, data.angle2 );
    }
}


/**
 * Writes a solid entity to the file.
 *
 * @param dw DXF writer
 * @param data Entity data from the file
 * @param aAttrib Attributes
 */
void DL_Dxf::writeSolid( DL_WriterA& dw,
        const DL_SolidData& data,
        const DL_Attributes& aAttrib )
{
    dw.entity( "SOLID" );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbEntity" );
    }

    dw.entityAttributes( aAttrib );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbTrace" );
    }

    dw.coord( 10, data.x[0], data.y[0], data.z[0] );
    dw.coord( 11, data.x[1], data.y[1], data.z[1] );
    dw.coord( 12, data.x[2], data.y[2], data.z[2] );
    dw.coord( 13, data.x[3], data.y[3], data.z[3] );
    dw.dxfReal( 39, data.thickness );
}


/**
 * Writes a trace entity to the file.
 *
 * @param dw DXF writer
 * @param data Entity data from the file
 * @param aAttrib Attributes
 */
void DL_Dxf::writeTrace( DL_WriterA& dw,
        const DL_TraceData& data,
        const DL_Attributes& aAttrib )
{
    dw.entity( "TRACE" );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbEntity" );
    }

    dw.entityAttributes( aAttrib );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbTrace" );
    }

    dw.coord( 10, data.x[0], data.y[0], data.z[0] );
    dw.coord( 11, data.x[1], data.y[1], data.z[1] );
    dw.coord( 12, data.x[2], data.y[2], data.z[2] );
    dw.coord( 13, data.x[3], data.y[3], data.z[3] );
    dw.dxfReal( 39, data.thickness );
}


/**
 * Writes a 3d face entity to the file.
 *
 * @param dw DXF writer
 * @param data Entity data from the file
 * @param aAttrib Attributes
 */
void DL_Dxf::write3dFace( DL_WriterA& dw,
        const DL_3dFaceData& data,
        const DL_Attributes& aAttrib )
{
    dw.entity( "3DFACE" );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbEntity" );
    }

    dw.entityAttributes( aAttrib );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbFace" );
    }

    dw.coord( 10, data.x[0], data.y[0], data.z[0] );
    dw.coord( 11, data.x[1], data.y[1], data.z[1] );
    dw.coord( 12, data.x[2], data.y[2], data.z[2] );
    dw.coord( 13, data.x[3], data.y[3], data.z[3] );
}


/**
 * Writes an insert to the file.
 *
 * @param dw DXF writer
 * @param data Entity data from the file
 * @param aAttrib Attributes
 */
void DL_Dxf::writeInsert( DL_WriterA& dw,
        const DL_InsertData& data,
        const DL_Attributes& aAttrib )
{
    if( data.name.empty() )
    {
        std::cerr   << "DL_Dxf::writeInsert: "
                    << "Block name must not be empty\n";
        return;
    }

    dw.entity( "INSERT" );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbEntity" );
    }

    dw.entityAttributes( aAttrib );

    if( version==DL_VERSION_2000 )
    {
        if( data.cols!=1 || data.rows!=1 )
        {
            dw.dxfString( 100, "AcDbMInsertBlock" );
        }
        else
        {
            dw.dxfString( 100, "AcDbBlockReference" );
        }
    }

    dw.dxfString( 2, data.name );
    dw.dxfReal( 10, data.ipx );
    dw.dxfReal( 20, data.ipy );
    dw.dxfReal( 30, data.ipz );

    if( data.sx!=1.0 || data.sy!=1.0 )
    {
        dw.dxfReal( 41, data.sx );
        dw.dxfReal( 42, data.sy );
        dw.dxfReal( 43, 1.0 );
    }

    if( data.angle!=0.0 )
    {
        dw.dxfReal( 50, data.angle );
    }

    if( data.cols!=1 || data.rows!=1 )
    {
        dw.dxfInt( 70, data.cols );
        dw.dxfInt( 71, data.rows );
    }

    if( data.colSp!=0.0 || data.rowSp!=0.0 )
    {
        dw.dxfReal( 44, data.colSp );
        dw.dxfReal( 45, data.rowSp );
    }
}


/**
 * Writes a multi text entity to the file.
 *
 * @param dw DXF writer
 * @param data Entity data from the file
 * @param aAttrib Attributes
 */
void DL_Dxf::writeMText( DL_WriterA& dw,
        const DL_MTextData& data,
        const DL_Attributes& aAttrib )
{
    dw.entity( "MTEXT" );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbEntity" );
    }

    dw.entityAttributes( aAttrib );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbMText" );
    }

    dw.dxfReal( 10, data.ipx );
    dw.dxfReal( 20, data.ipy );
    dw.dxfReal( 30, data.ipz );
    dw.dxfReal( 40, data.height );
    dw.dxfReal( 41, data.width );

    dw.dxfInt( 71, data.attachmentPoint );
    dw.dxfInt( 72, data.drawingDirection );

    // Creare text chunks of 250 characters each:
    #define TEXT_CHUNK_SIZE 250

    std::size_t length = data.text.length();
    char chunk[TEXT_CHUNK_SIZE+1];
    std::size_t i = 0;

    for( i = TEXT_CHUNK_SIZE; i < length; i += TEXT_CHUNK_SIZE )
    {
        strncpy( chunk, &data.text.c_str()[i - TEXT_CHUNK_SIZE], TEXT_CHUNK_SIZE );
        chunk[TEXT_CHUNK_SIZE] = '\0';
        dw.dxfString( 3, chunk );
    }

    strncpy( chunk, &data.text.c_str()[i - TEXT_CHUNK_SIZE], TEXT_CHUNK_SIZE );
    chunk[TEXT_CHUNK_SIZE] = '\0';
    dw.dxfString( 1, chunk );

    dw.dxfString( 7, data.style );

    // since dxflib 2.0.2.1: degrees not rad (error in autodesk dxf doc)
    dw.dxfReal( 50, data.angle / (2.0 * M_PI) * 360.0 );

    dw.dxfInt( 73, data.lineSpacingStyle );
    dw.dxfReal( 44, data.lineSpacingFactor );
}


/**
 * Writes a text entity to the file.
 *
 * @param dw DXF writer
 * @param data Entity data from the file
 * @param aAttrib Attributes
 */
void DL_Dxf::writeText( DL_WriterA& dw,
        const DL_TextData& data,
        const DL_Attributes& aAttrib )
{
    dw.entity( "TEXT" );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbEntity" );
    }

    dw.entityAttributes( aAttrib );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbText" );
    }

    dw.dxfReal( 10, data.ipx );
    dw.dxfReal( 20, data.ipy );
    dw.dxfReal( 30, data.ipz );
    dw.dxfReal( 40, data.height );
    dw.dxfString( 1, data.text );
    dw.dxfReal( 50, data.angle / (2 * M_PI) * 360.0 );
    dw.dxfReal( 41, data.xScaleFactor );
    dw.dxfString( 7, data.style );

    dw.dxfInt( 71, data.textGenerationFlags );
    dw.dxfInt( 72, data.hJustification );

    dw.dxfReal( 11, data.apx );
    dw.dxfReal( 21, data.apy );
    dw.dxfReal( 31, data.apz );

    if( version==DL_VERSION_2000 )
    {
        // required twice for some reason:
        dw.dxfString( 100, "AcDbText" );
    }

    dw.dxfInt( 73, data.vJustification );
}


void DL_Dxf::writeAttribute( DL_WriterA& dw,
        const DL_AttributeData& data,
        const DL_Attributes& aAttrib )
{
    dw.entity( "ATTRIB" );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbEntity" );
    }

    dw.entityAttributes( aAttrib );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbText" );
    }

    dw.dxfReal( 10, data.ipx );
    dw.dxfReal( 20, data.ipy );
    dw.dxfReal( 30, data.ipz );
    dw.dxfReal( 40, data.height );
    dw.dxfString( 1, data.text );
    dw.dxfReal( 50, data.angle / (2 * M_PI) * 360.0 );
    dw.dxfReal( 41, data.xScaleFactor );
    dw.dxfString( 7, data.style );

    dw.dxfInt( 71, data.textGenerationFlags );
    dw.dxfInt( 72, data.hJustification );

    dw.dxfReal( 11, data.apx );
    dw.dxfReal( 21, data.apy );
    dw.dxfReal( 31, data.apz );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbAttribute" );
    }

    dw.dxfString( 2, data.tag );
    dw.dxfInt( 74, data.vJustification );
}


void DL_Dxf::writeDimStyleOverrides( DL_WriterA& dw,
        const DL_DimensionData& data )
{
    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 1001, "ACAD" );
        dw.dxfString( 1000, "DSTYLE" );
        dw.dxfString( 1002, "{" );
        if( data.type & 128 )
        {
            // custom text position:
            dw.dxfInt( 1070, 279 );
            dw.dxfInt( 1070, 2 );
        }
        dw.dxfInt( 1070, 144 );
        dw.dxfReal( 1040, data.linearFactor );
        dw.dxfInt( 1070, 40 );
        dw.dxfReal( 1040, data.dimScale );
        dw.dxfString( 1002, "}" );
    }
}


/**
 * Writes an aligned dimension entity to the file.
 *
 * @param dw DXF writer
 * @param data Generic dimension data for from the file
 * @param data Specific aligned dimension data from the file
 * @param aAttrib Attributes
 */
void DL_Dxf::writeDimAligned( DL_WriterA& dw,
        const DL_DimensionData& data,
        const DL_DimAlignedData& edata,
        const DL_Attributes& aAttrib )
{
    dw.entity( "DIMENSION" );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbEntity" );
    }

    dw.entityAttributes( aAttrib );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbDimension" );
    }

    dw.dxfReal( 10, data.dpx );
    dw.dxfReal( 20, data.dpy );
    dw.dxfReal( 30, data.dpz );

    dw.dxfReal( 11, data.mpx );
    dw.dxfReal( 21, data.mpy );
    dw.dxfReal( 31, 0.0 );

    dw.dxfInt( 70, data.type );

    if( version>DL_VERSION_R12 )
    {
        dw.dxfInt( 71, data.attachmentPoint );
        dw.dxfInt( 72, data.lineSpacingStyle );     // opt
        dw.dxfInt( 74, data.arrow1Flipped );
        dw.dxfInt( 75, data.arrow2Flipped );
        dw.dxfReal( 41, data.lineSpacingFactor );   // opt
    }

    dw.dxfReal( 42, data.angle );

    dw.dxfString( 1, data.text );    // opt
    // dw.dxfString(3, data.style);
    dw.dxfString( 3, "Standard" );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbAlignedDimension" );
    }

    dw.dxfReal( 13, edata.epx1 );
    dw.dxfReal( 23, edata.epy1 );
    dw.dxfReal( 33, 0.0 );

    dw.dxfReal( 14, edata.epx2 );
    dw.dxfReal( 24, edata.epy2 );
    dw.dxfReal( 34, 0.0 );

    writeDimStyleOverrides( dw, data );
}


/**
 * Writes a linear dimension entity to the file.
 *
 * @param dw DXF writer
 * @param data Generic dimension data for from the file
 * @param data Specific linear dimension data from the file
 * @param aAttrib Attributes
 */
void DL_Dxf::writeDimLinear( DL_WriterA& dw,
        const DL_DimensionData& data,
        const DL_DimLinearData& edata,
        const DL_Attributes& aAttrib )
{
    dw.entity( "DIMENSION" );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbEntity" );
    }

    dw.entityAttributes( aAttrib );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbDimension" );
    }

    dw.dxfReal( 10, data.dpx );
    dw.dxfReal( 20, data.dpy );
    dw.dxfReal( 30, data.dpz );

    dw.dxfReal( 11, data.mpx );
    dw.dxfReal( 21, data.mpy );
    dw.dxfReal( 31, 0.0 );

    dw.dxfInt( 70, data.type );

    if( version>DL_VERSION_R12 )
    {
        dw.dxfInt( 71, data.attachmentPoint );
        dw.dxfInt( 72, data.lineSpacingStyle );     // opt
        dw.dxfInt( 74, data.arrow1Flipped );
        dw.dxfInt( 75, data.arrow2Flipped );
        dw.dxfReal( 41, data.lineSpacingFactor );   // opt
    }

    dw.dxfReal( 42, data.angle );

    dw.dxfString( 1, data.text );    // opt
    // dw.dxfString(3, data.style);
    dw.dxfString( 3, "Standard" );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbAlignedDimension" );
    }

    dw.dxfReal( 13, edata.dpx1 );
    dw.dxfReal( 23, edata.dpy1 );
    dw.dxfReal( 33, 0.0 );

    dw.dxfReal( 14, edata.dpx2 );
    dw.dxfReal( 24, edata.dpy2 );
    dw.dxfReal( 34, 0.0 );

    dw.dxfReal( 50, edata.angle / (2.0 * M_PI) * 360.0 );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbRotatedDimension" );
    }

    writeDimStyleOverrides( dw, data );
}


/**
 * Writes a radial dimension entity to the file.
 *
 * @param dw DXF writer
 * @param data Generic dimension data for from the file
 * @param data Specific radial dimension data from the file
 * @param aAttrib Attributes
 */
void DL_Dxf::writeDimRadial( DL_WriterA& dw,
        const DL_DimensionData& data,
        const DL_DimRadialData& edata,
        const DL_Attributes& aAttrib )
{
    dw.entity( "DIMENSION" );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbEntity" );
    }

    dw.entityAttributes( aAttrib );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbDimension" );
    }

    dw.dxfReal( 10, data.dpx );
    dw.dxfReal( 20, data.dpy );
    dw.dxfReal( 30, data.dpz );

    dw.dxfReal( 11, data.mpx );
    dw.dxfReal( 21, data.mpy );
    dw.dxfReal( 31, 0.0 );

    dw.dxfInt( 70, data.type );

    if( version>DL_VERSION_R12 )
    {
        dw.dxfInt( 71, data.attachmentPoint );
        dw.dxfInt( 72, data.lineSpacingStyle );     // opt
        dw.dxfInt( 74, data.arrow1Flipped );
        //dw.dxfInt(75, data.arrow2Flipped);
        dw.dxfReal( 41, data.lineSpacingFactor );   // opt
    }

    dw.dxfReal( 42, data.angle );

    dw.dxfString( 1, data.text );    // opt
    // dw.dxfString(3, data.style);
    dw.dxfString( 3, "Standard" );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbRadialDimension" );
    }

    dw.dxfReal( 15, edata.dpx );
    dw.dxfReal( 25, edata.dpy );
    dw.dxfReal( 35, 0.0 );

    dw.dxfReal( 40, edata.leader );

    writeDimStyleOverrides( dw, data );
}


/**
 * Writes a diametric dimension entity to the file.
 *
 * @param dw DXF writer
 * @param data Generic dimension data for from the file
 * @param data Specific diametric dimension data from the file
 * @param aAttrib Attributes
 */
void DL_Dxf::writeDimDiametric( DL_WriterA& dw,
        const DL_DimensionData& data,
        const DL_DimDiametricData& edata,
        const DL_Attributes& aAttrib )
{
    dw.entity( "DIMENSION" );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbEntity" );
    }

    dw.entityAttributes( aAttrib );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbDimension" );
    }

    dw.dxfReal( 10, data.dpx );
    dw.dxfReal( 20, data.dpy );
    dw.dxfReal( 30, data.dpz );

    dw.dxfReal( 11, data.mpx );
    dw.dxfReal( 21, data.mpy );
    dw.dxfReal( 31, 0.0 );

    dw.dxfInt( 70, data.type );

    if( version>DL_VERSION_R12 )
    {
        dw.dxfInt( 71, data.attachmentPoint );
        dw.dxfInt( 72, data.lineSpacingStyle );     // opt
        dw.dxfInt( 74, data.arrow1Flipped );
        dw.dxfInt( 75, data.arrow2Flipped );
        dw.dxfReal( 41, data.lineSpacingFactor );   // opt
    }

    dw.dxfReal( 42, data.angle );

    dw.dxfString( 1, data.text );    // opt
    // dw.dxfString(3, data.style);
    dw.dxfString( 3, "Standard" );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbDiametricDimension" );
    }

    dw.dxfReal( 15, edata.dpx );
    dw.dxfReal( 25, edata.dpy );
    dw.dxfReal( 35, 0.0 );

    dw.dxfReal( 40, edata.leader );

    writeDimStyleOverrides( dw, data );
}


/**
 * Writes an angular dimension entity to the file.
 *
 * @param dw DXF writer
 * @param data Generic dimension data for from the file
 * @param data Specific angular dimension data from the file
 * @param aAttrib Attributes
 */
void DL_Dxf::writeDimAngular2L( DL_WriterA& dw,
        const DL_DimensionData& data,
        const DL_DimAngular2LData& edata,
        const DL_Attributes& aAttrib )
{
    dw.entity( "DIMENSION" );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbEntity" );
    }

    dw.entityAttributes( aAttrib );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbDimension" );
    }

    dw.dxfReal( 10, data.dpx );
    dw.dxfReal( 20, data.dpy );
    dw.dxfReal( 30, data.dpz );

    dw.dxfReal( 11, data.mpx );
    dw.dxfReal( 21, data.mpy );
    dw.dxfReal( 31, 0.0 );

    dw.dxfInt( 70, data.type );

    if( version>DL_VERSION_R12 )
    {
        dw.dxfInt( 71, data.attachmentPoint );
        dw.dxfInt( 72, data.lineSpacingStyle );     // opt
        dw.dxfInt( 74, data.arrow1Flipped );
        dw.dxfInt( 75, data.arrow2Flipped );
        dw.dxfReal( 41, data.lineSpacingFactor );   // opt
    }

    dw.dxfReal( 42, data.angle );

    dw.dxfString( 1, data.text );    // opt
    // dw.dxfString(3, data.style);
    dw.dxfString( 3, "Standard" );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDb2LineAngularDimension" );
    }

    dw.dxfReal( 13, edata.dpx1 );
    dw.dxfReal( 23, edata.dpy1 );
    dw.dxfReal( 33, 0.0 );

    dw.dxfReal( 14, edata.dpx2 );
    dw.dxfReal( 24, edata.dpy2 );
    dw.dxfReal( 34, 0.0 );

    dw.dxfReal( 15, edata.dpx3 );
    dw.dxfReal( 25, edata.dpy3 );
    dw.dxfReal( 35, 0.0 );

    dw.dxfReal( 16, edata.dpx4 );
    dw.dxfReal( 26, edata.dpy4 );
    dw.dxfReal( 36, 0.0 );
}


/**
 * Writes an angular dimension entity (3 points version) to the file.
 *
 * @param dw DXF writer
 * @param data Generic dimension data for from the file
 * @param data Specific angular dimension data from the file
 * @param aAttrib Attributes
 */
void DL_Dxf::writeDimAngular3P( DL_WriterA& dw,
        const DL_DimensionData& data,
        const DL_DimAngular3PData& edata,
        const DL_Attributes& aAttrib )
{
    dw.entity( "DIMENSION" );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbEntity" );
    }

    dw.entityAttributes( aAttrib );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbDimension" );
    }

    dw.dxfReal( 10, data.dpx );
    dw.dxfReal( 20, data.dpy );
    dw.dxfReal( 30, data.dpz );

    dw.dxfReal( 11, data.mpx );
    dw.dxfReal( 21, data.mpy );
    dw.dxfReal( 31, 0.0 );

    dw.dxfInt( 70, data.type );

    if( version>DL_VERSION_R12 )
    {
        dw.dxfInt( 71, data.attachmentPoint );
        dw.dxfInt( 72, data.lineSpacingStyle );     // opt
        dw.dxfInt( 74, data.arrow1Flipped );
        dw.dxfInt( 75, data.arrow2Flipped );
        dw.dxfReal( 41, data.lineSpacingFactor );   // opt
    }

    dw.dxfReal( 42, data.angle );

    dw.dxfString( 1, data.text );    // opt
    // dw.dxfString(3, data.style);
    dw.dxfString( 3, "Standard" );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDb3PointAngularDimension" );
    }

    dw.dxfReal( 13, edata.dpx1 );
    dw.dxfReal( 23, edata.dpy1 );
    dw.dxfReal( 33, 0.0 );

    dw.dxfReal( 14, edata.dpx2 );
    dw.dxfReal( 24, edata.dpy2 );
    dw.dxfReal( 34, 0.0 );

    dw.dxfReal( 15, edata.dpx3 );
    dw.dxfReal( 25, edata.dpy3 );
    dw.dxfReal( 35, 0.0 );
}


/**
 * Writes an ordinate dimension entity to the file.
 *
 * @param dw DXF writer
 * @param data Generic dimension data for from the file
 * @param data Specific ordinate dimension data from the file
 * @param aAttrib Attributes
 */
void DL_Dxf::writeDimOrdinate( DL_WriterA& dw,
        const DL_DimensionData& data,
        const DL_DimOrdinateData& edata,
        const DL_Attributes& aAttrib )
{
    dw.entity( "DIMENSION" );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbEntity" );
    }

    dw.entityAttributes( aAttrib );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbDimension" );
    }

    dw.dxfReal( 10, data.dpx );
    dw.dxfReal( 20, data.dpy );
    dw.dxfReal( 30, data.dpz );

    dw.dxfReal( 11, data.mpx );
    dw.dxfReal( 21, data.mpy );
    dw.dxfReal( 31, 0.0 );

    int type = data.type;

    if( edata.xtype )
    {
        type |= 0x40;
    }

    dw.dxfInt( 70, type );

    if( version>DL_VERSION_R12 )
    {
        dw.dxfInt( 71, data.attachmentPoint );
        dw.dxfInt( 72, data.lineSpacingStyle );     // opt
        //dw.dxfInt(74, data.arrow1Flipped);
        //dw.dxfInt(75, data.arrow2Flipped);
        dw.dxfReal( 41, data.lineSpacingFactor );   // opt
    }

    dw.dxfString( 1, data.text );    // opt
    // dw.dxfString(3, data.style);
    dw.dxfString( 3, "Standard" );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbOrdinateDimension" );
    }

    dw.dxfReal( 13, edata.dpx1 );
    dw.dxfReal( 23, edata.dpy1 );
    dw.dxfReal( 33, 0.0 );

    dw.dxfReal( 14, edata.dpx2 );
    dw.dxfReal( 24, edata.dpy2 );
    dw.dxfReal( 34, 0.0 );
}


/**
 * Writes a leader entity to the file.
 *
 * @param dw DXF writer
 * @param data Entity data from the file
 * @param aAttrib Attributes
 * @see writeVertex
 */
void DL_Dxf::writeLeader( DL_WriterA& dw,
        const DL_LeaderData& data,
        const DL_Attributes& aAttrib )
{
    if( version>DL_VERSION_R12 )
    {
        dw.entity( "LEADER" );

        if( version==DL_VERSION_2000 )
        {
            dw.dxfString( 100, "AcDbEntity" );
        }

        dw.entityAttributes( aAttrib );

        if( version==DL_VERSION_2000 )
        {
            dw.dxfString( 100, "AcDbLeader" );
        }

        dw.dxfString( 3, "Standard" );
        dw.dxfInt( 71, data.arrowHeadFlag );
        dw.dxfInt( 72, data.leaderPathType );
        dw.dxfInt( 73, data.leaderCreationFlag );
        dw.dxfInt( 74, data.hooklineDirectionFlag );
        dw.dxfInt( 75, data.hooklineFlag );
        dw.dxfReal( 40, data.textAnnotationHeight );
        dw.dxfReal( 41, data.textAnnotationWidth );
        dw.dxfInt( 76, data.number );
    }
}


/**
 * Writes a single vertex of a leader to the file.
 *
 * @param dw DXF writer
 * @param data Entity data
 */
void DL_Dxf::writeLeaderVertex( DL_WriterA& dw,
        const DL_LeaderVertexData& data )
{
    if( version>DL_VERSION_R12 )
    {
        dw.dxfReal( 10, data.x );
        dw.dxfReal( 20, data.y );
    }
}

void DL_Dxf::writeLeaderEnd( DL_WriterA& dw, const DL_LeaderData& data )
{
    if( version == DL_VERSION_2000 )
    {
        dw.dxfString( 1001, "ACAD" );
        dw.dxfString( 1000, "DSTYLE" );
        dw.dxfString( 1002, "{" );
        dw.dxfInt( 1070, 40 );
        dw.dxfReal( 1040, data.dimScale );
        dw.dxfString( 1002, "}" );
    }
}

/**
 * Writes the beginning of a hatch entity to the file.
 * This must be followed by one or more writeHatchLoop()
 * calls and a writeHatch2() call.
 *
 * @param dw DXF writer
 * @param data Entity data.
 * @param aAttrib Attributes
 */
void DL_Dxf::writeHatch1( DL_WriterA& dw,
        const DL_HatchData& data,
        const DL_Attributes& aAttrib )
{
    dw.entity( "HATCH" );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbEntity" );
    }

    dw.entityAttributes( aAttrib );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbHatch" );
    }

    dw.dxfReal( 10, 0.0 );             // elevation
    dw.dxfReal( 20, 0.0 );
    dw.dxfReal( 30, 0.0 );
    dw.dxfReal( 210, 0.0 );             // extrusion dir.
    dw.dxfReal( 220, 0.0 );
    dw.dxfReal( 230, 1.0 );

    if( data.solid==false )
    {
        dw.dxfString( 2, data.pattern );
    }
    else
    {
        dw.dxfString( 2, "SOLID" );
    }

    dw.dxfInt( 70, (int) data.solid );
    dw.dxfInt( 71, 0 );                // non-associative
    dw.dxfInt( 91, data.numLoops );
}


/**
 * Writes the end of a hatch entity to the file.
 *
 * @param dw DXF writer
 * @param data Entity data.
 * @param aAttrib Attributes
 */
void DL_Dxf::writeHatch2( DL_WriterA& dw,
        const DL_HatchData& data,
        const DL_Attributes&    /*aAttrib*/ )
{
    dw.dxfInt( 75, 0 );                 // odd parity
    dw.dxfInt( 76, 1 );                 // pattern type

    if( data.solid==false )
    {
        dw.dxfReal( 52, data.angle );
        dw.dxfReal( 41, data.scale );
        dw.dxfInt( 77, 0 );            // not double
        // dw.dxfInt(78, 0);
        dw.dxfInt( 78, 1 );
        dw.dxfReal( 53, 45.0 );
        dw.dxfReal( 43, 0.0 );
        dw.dxfReal( 44, 0.0 );
        dw.dxfReal( 45, -0.0883883476483184 );
        dw.dxfReal( 46, 0.0883883476483185 );
        dw.dxfInt( 79, 0 );
    }

    dw.dxfInt( 98, 0 );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 1001, "ACAD" );
        dw.dxfReal( 1010, data.originX );
        dw.dxfReal( 1020, data.originY );
        dw.dxfInt( 1030, 0 );
    }
}


/**
 * Writes the beginning of a hatch loop to the file. This
 * must happen after writing the beginning of a hatch entity.
 *
 * @param dw DXF writer
 * @param data Entity data.
 */
void DL_Dxf::writeHatchLoop1( DL_WriterA& dw,
        const DL_HatchLoopData& data )
{
    dw.dxfInt( 92, 1 );
    dw.dxfInt( 93, data.numEdges );
    // dw.dxfInt(97, 0);
}


/**
 * Writes the end of a hatch loop to the file.
 *
 * @param dw DXF writer
 * @param data Entity data.
 */
void DL_Dxf::writeHatchLoop2( DL_WriterA& dw,
        const DL_HatchLoopData&    /*data*/ )
{
    dw.dxfInt( 97, 0 );
}


/**
 * Writes the beginning of a hatch entity to the file.
 *
 * @param dw DXF writer
 * @param data Entity data.
 */
void DL_Dxf::writeHatchEdge( DL_WriterA& dw,
        const DL_HatchEdgeData& data )
{
    if( data.type<1 || data.type>4 )
    {
        printf( "WARNING: unsupported hatch edge type: %d", data.type );
    }

    dw.dxfInt( 72, data.type );

    switch( data.type )
    {
    // line:
    case 1:
        dw.dxfReal( 10, data.x1 );
        dw.dxfReal( 20, data.y1 );
        dw.dxfReal( 11, data.x2 );
        dw.dxfReal( 21, data.y2 );
        break;

    // arc:
    case 2:
        dw.dxfReal( 10, data.cx );
        dw.dxfReal( 20, data.cy );
        dw.dxfReal( 40, data.radius );
        dw.dxfReal( 50, data.angle1 / (2 * M_PI) * 360.0 );
        dw.dxfReal( 51, data.angle2 / (2 * M_PI) * 360.0 );
        dw.dxfInt( 73, (int) (data.ccw) );
        break;

    // ellipse arc:
    case 3:
        dw.dxfReal( 10, data.cx );
        dw.dxfReal( 20, data.cy );
        dw.dxfReal( 11, data.mx );
        dw.dxfReal( 21, data.my );
        dw.dxfReal( 40, data.ratio );
        dw.dxfReal( 50, data.angle1 / (2 * M_PI) * 360.0 );
        dw.dxfReal( 51, data.angle2 / (2 * M_PI) * 360.0 );
        dw.dxfInt( 73, (int) (data.ccw) );
        break;

    // spline:
    case 4:
        dw.dxfInt( 94, data.degree );
        dw.dxfBool( 73, data.rational );
        dw.dxfBool( 74, data.periodic );
        dw.dxfInt( 95, data.nKnots );
        dw.dxfInt( 96, data.nControl );

        for( unsigned int i = 0; i<data.knots.size(); i++ )
        {
            dw.dxfReal( 40, data.knots[i] );
        }

        for( unsigned int i = 0; i<data.controlPoints.size(); i++ )
        {
            dw.dxfReal( 10, data.controlPoints[i][0] );
            dw.dxfReal( 20, data.controlPoints[i][1] );
        }

        for( unsigned int i = 0; i<data.weights.size(); i++ )
        {
            dw.dxfReal( 42, data.weights[i] );
        }

        if( data.nFit>0 )
        {
            dw.dxfInt( 97, data.nFit );

            for( unsigned int i = 0; i<data.fitPoints.size(); i++ )
            {
                dw.dxfReal( 11, data.fitPoints[i][0] );
                dw.dxfReal( 21, data.fitPoints[i][1] );
            }
        }

        if( fabs( data.startTangentX )>1.0e-4 || fabs( data.startTangentY )>1.0e-4 )
        {
            dw.dxfReal( 12, data.startTangentX );
            dw.dxfReal( 22, data.startTangentY );
        }

        if( fabs( data.endTangentX )>1.0e-4 || fabs( data.endTangentY )>1.0e-4 )
        {
            dw.dxfReal( 13, data.endTangentX );
            dw.dxfReal( 23, data.endTangentY );
        }

        break;

    default:
        break;
    }
}


/**
 * Writes an image entity.
 *
 * @return IMAGEDEF handle. Needed for the IMAGEDEF counterpart.
 */
unsigned int DL_Dxf::writeImage( DL_WriterA& dw,
        const DL_ImageData& data,
        const DL_Attributes& aAttrib )
{
    /*if (data.file.empty()) {
     *   std::cerr << "DL_Dxf::writeImage: "
     *   << "Image file must not be empty\n";
     *   return;
     *  }*/

    dw.entity( "IMAGE" );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbEntity" );
    }

    dw.entityAttributes( aAttrib );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbRasterImage" );
        dw.dxfInt( 90, 0 );
    }

    // insertion point
    dw.dxfReal( 10, data.ipx );
    dw.dxfReal( 20, data.ipy );
    dw.dxfReal( 30, data.ipz );

    // vector along bottom side (1 pixel long)
    dw.dxfReal( 11, data.ux );
    dw.dxfReal( 21, data.uy );
    dw.dxfReal( 31, data.uz );

    // vector along left side (1 pixel long)
    dw.dxfReal( 12, data.vx );
    dw.dxfReal( 22, data.vy );
    dw.dxfReal( 32, data.vz );

    // image size in pixel
    dw.dxfReal( 13, data.width );
    dw.dxfReal( 23, data.height );

    // handle of IMAGEDEF object
    unsigned int handle = dw.handle(340);

    // flags
    dw.dxfInt( 70, 15 );

    // clipping:
    dw.dxfInt( 280, 0 );

    // brightness, contrast, fade
    dw.dxfInt( 281, data.brightness );
    dw.dxfInt( 282, data.contrast );
    dw.dxfInt( 283, data.fade );

    return handle;
}


/**
 * Writes an image definiition entity.
 */
void DL_Dxf::writeImageDef( DL_WriterA& dw,
        int handle,
        const DL_ImageData& data )
{
    /*if (data.file.empty()) {
     *   std::cerr << "DL_Dxf::writeImage: "
     *   << "Image file must not be empty\n";
     *   return;
     *  }*/

    dw.dxfString( 0, "IMAGEDEF" );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfHex( 5, handle );
    }

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbRasterImageDef" );
        dw.dxfInt( 90, 0 );
    }

    // file name:
    dw.dxfString( 1, data.ref );

    // image size in pixel
    dw.dxfReal( 10, data.width );
    dw.dxfReal( 20, data.height );

    dw.dxfReal( 11, 1.0 );
    dw.dxfReal( 21, 1.0 );

    // loaded:
    dw.dxfInt( 280, 1 );
    // units:
    dw.dxfInt( 281, 0 );
}


/**
 * Writes a layer to the file. Layers are stored in the
 * tables section of a DXF file.
 *
 * @param dw DXF writer
 * @param data Entity data from the file
 * @param aAttrib Attributes
 */
void DL_Dxf::writeLayer( DL_WriterA& dw,
        const DL_LayerData& data,
        const DL_Attributes& aAttrib )
{
    if( data.name.empty() )
    {
        std::cerr   << "DL_Dxf::writeLayer: "
                    << "Layer name must not be empty\n";
        return;
    }

    int color = aAttrib.getColor();

    if( color>=256 )
    {
        std::cerr << "Layer color cannot be " << color << ". Changed to 7.\n";
        color = 7;
    }

    if( data.off )
    {
        // negative color value means layer is off:
        color = -color;
    }

    if( data.name == "0" )
    {
        dw.tableLayerEntry( 0x10 );
    }
    else
    {
        dw.tableLayerEntry();
    }

    dw.dxfString( 2, data.name );
    dw.dxfInt( 70, data.flags );
    dw.dxfInt( 62, color );

    if( version>=DL_VERSION_2000 && aAttrib.getColor24()!=-1 )
    {
        dw.dxfInt( 420, attrib.getColor24() );
    }

    dw.dxfString( 6, ( attrib.getLinetype().length()==0 ?
                       std::string( "CONTINUOUS" ) : attrib.getLinetype() ) );

    if( version>=DL_VERSION_2000 )
    {
        // layer defpoints cannot be plotted
        std::string lstr = data.name;
        std::transform( lstr.begin(), lstr.end(), lstr.begin(), tolower );

        if( lstr=="defpoints" )
        {
            dw.dxfInt( 290, 0 );
        }
    }

    if( version>=DL_VERSION_2000 && attrib.getWidth()!=-1 )
    {
        dw.dxfInt( 370, attrib.getWidth() );
    }

    if( version>=DL_VERSION_2000 )
    {
        dw.dxfHex( 390, 0xF );
    }
}


/**
 * Writes a line type to the file. Line types are stored in the
 * tables section of a DXF file.
 */
void DL_Dxf::writeLinetype( DL_WriterA& dw,
        const DL_LinetypeData& data )
{
    std::string nameUpper = data.name;
    std::transform( nameUpper.begin(), nameUpper.end(), nameUpper.begin(), ::toupper );

    if( data.name.empty() )
    {
        std::cerr   << "DL_Dxf::writeLinetype: "
                    << "Line type name must not be empty\n";
        return;
    }

    // ignore BYLAYER, BYBLOCK for R12
    if( version<DL_VERSION_2000 )
    {
        if( nameUpper=="BYBLOCK" || nameUpper=="BYLAYER" )
        {
            return;
        }
    }

    // write id (not for R12)
    if( nameUpper=="BYBLOCK" )
    {
        dw.tableLinetypeEntry( 0x14 );
    }
    else if( nameUpper=="BYLAYER" )
    {
        dw.tableLinetypeEntry( 0x15 );
    }
    else if( nameUpper=="CONTINUOUS" )
    {
        dw.tableLinetypeEntry( 0x16 );
    }
    else
    {
        dw.tableLinetypeEntry();
    }

    dw.dxfString( 2, data.name );
    dw.dxfInt( 70, data.flags );

    if( nameUpper=="BYBLOCK" )
    {
        dw.dxfString( 3, "" );
        dw.dxfInt( 72, 65 );
        dw.dxfInt( 73, 0 );
        dw.dxfReal( 40, 0.0 );
    }
    else if( nameUpper=="BYLAYER" )
    {
        dw.dxfString( 3, "" );
        dw.dxfInt( 72, 65 );
        dw.dxfInt( 73, 0 );
        dw.dxfReal( 40, 0.0 );
    }
    else if( nameUpper=="CONTINUOUS" )
    {
        dw.dxfString( 3, "Solid line" );
        dw.dxfInt( 72, 65 );
        dw.dxfInt( 73, 0 );
        dw.dxfReal( 40, 0.0 );
    }
    else
    {
        dw.dxfString( 3, data.description );
        dw.dxfInt( 72, 65 );
        dw.dxfInt( 73, data.numberOfDashes );
        dw.dxfReal( 40, data.patternLength );

        for( int i = 0; i < data.numberOfDashes; i++ )
        {
            dw.dxfReal( 49, data.pattern[i] );

            if( version>=DL_VERSION_R13 )
            {
                dw.dxfInt( 74, 0 );
            }
        }
    }
}


/**
 * Writes the APPID section to the DXF file.
 *
 * @param name Application name
 */
void DL_Dxf::writeAppid( DL_WriterA& dw, const std::string& name )
{
    if( name.empty() )
    {
        std::cerr   << "DL_Dxf::writeAppid: "
                    << "Application  name must not be empty\n";
        return;
    }

    std::string n = name;
    std::transform( n.begin(), n.end(), n.begin(), ::toupper );

    if( n=="ACAD" )
    {
        dw.tableAppidEntry( 0x12 );
    }
    else
    {
        dw.tableAppidEntry();
    }

    dw.dxfString( 2, name );
    dw.dxfInt( 70, 0 );
}


/**
 * Writes a block's definition (no entities) to the DXF file.
 */
void DL_Dxf::writeBlock( DL_WriterA& dw, const DL_BlockData& data )
{
    if( data.name.empty() )
    {
        std::cerr   << "DL_Dxf::writeBlock: "
                    << "Block name must not be empty\n";
        return;
    }

    std::string n = data.name;
    std::transform( n.begin(), n.end(), n.begin(), ::toupper );

    if( n=="*PAPER_SPACE" )
    {
        dw.sectionBlockEntry( 0x1C );
    }
    else if( n=="*MODEL_SPACE" )
    {
        dw.sectionBlockEntry( 0x20 );
    }
    else if( n=="*PAPER_SPACE0" )
    {
        dw.sectionBlockEntry( 0x24 );
    }
    else
    {
        dw.sectionBlockEntry();
    }

    dw.dxfString( 2, data.name );
    dw.dxfInt( 70, 0 );
    dw.coord( 10, data.bpx, data.bpy, data.bpz );
    dw.dxfString( 3, data.name );
    dw.dxfString( 1, "" );
}


/**
 * Writes a block end.
 *
 * @param name Block name
 */
void DL_Dxf::writeEndBlock( DL_WriterA& dw, const std::string& name )
{
    std::string n = name;
    std::transform( n.begin(), n.end(), n.begin(), ::toupper );

    if( n=="*PAPER_SPACE" )
    {
        dw.sectionBlockEntryEnd( 0x1D );
    }
    else if( n=="*MODEL_SPACE" )
    {
        dw.sectionBlockEntryEnd( 0x21 );
    }
    else if( n=="*PAPER_SPACE0" )
    {
        dw.sectionBlockEntryEnd( 0x25 );
    }
    else
    {
        dw.sectionBlockEntryEnd();
    }
}


/**
 * Writes a viewport section. This section is needed in DL_VERSION_R13.
 * Note that this method currently only writes a faked VPORT section
 * to make the file readable by Aut*cad.
 */
void DL_Dxf::writeVPort( DL_WriterA& dw )
{
    dw.dxfString( 0, "TABLE" );
    dw.dxfString( 2, "VPORT" );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfHex( 5, 0x8 );
    }

    // dw.dxfHex(330, 0);
    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbSymbolTable" );
    }

    dw.dxfInt( 70, 1 );
    dw.dxfString( 0, "VPORT" );

    // dw.dxfHex(5, 0x2F);
    if( version==DL_VERSION_2000 )
    {
        dw.handle();
    }

    // dw.dxfHex(330, 8);
    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbSymbolTableRecord" );
        dw.dxfString( 100, "AcDbViewportTableRecord" );
    }

    dw.dxfString(  2, "*Active" );
    dw.dxfInt( 70, 0 );
    dw.dxfReal( 10, 0.0 );
    dw.dxfReal( 20, 0.0 );
    dw.dxfReal( 11, 1.0 );
    dw.dxfReal( 21, 1.0 );
    dw.dxfReal( 12, 286.3055555555555 );
    dw.dxfReal( 22, 148.5 );
    dw.dxfReal( 13, 0.0 );
    dw.dxfReal( 23, 0.0 );
    dw.dxfReal( 14, 10.0 );
    dw.dxfReal( 24, 10.0 );
    dw.dxfReal( 15, 10.0 );
    dw.dxfReal( 25, 10.0 );
    dw.dxfReal( 16, 0.0 );
    dw.dxfReal( 26, 0.0 );
    dw.dxfReal( 36, 1.0 );
    dw.dxfReal( 17, 0.0 );
    dw.dxfReal( 27, 0.0 );
    dw.dxfReal( 37, 0.0 );
    dw.dxfReal( 40, 297.0 );
    dw.dxfReal( 41, 1.92798353909465 );
    dw.dxfReal( 42, 50.0 );
    dw.dxfReal( 43, 0.0 );
    dw.dxfReal( 44, 0.0 );
    dw.dxfReal( 50, 0.0 );
    dw.dxfReal( 51, 0.0 );
    dw.dxfInt( 71, 0 );
    dw.dxfInt( 72, 100 );
    dw.dxfInt( 73, 1 );
    dw.dxfInt( 74, 3 );
    dw.dxfInt( 75, 1 );
    dw.dxfInt( 76, 1 );
    dw.dxfInt( 77, 0 );
    dw.dxfInt( 78, 0 );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfInt( 281, 0 );
        dw.dxfInt( 65, 1 );
        dw.dxfReal( 110, 0.0 );
        dw.dxfReal( 120, 0.0 );
        dw.dxfReal( 130, 0.0 );
        dw.dxfReal( 111, 1.0 );
        dw.dxfReal( 121, 0.0 );
        dw.dxfReal( 131, 0.0 );
        dw.dxfReal( 112, 0.0 );
        dw.dxfReal( 122, 1.0 );
        dw.dxfReal( 132, 0.0 );
        dw.dxfInt( 79, 0 );
        dw.dxfReal( 146, 0.0 );
    }

    dw.dxfString(  0, "ENDTAB" );
}


/**
 * Writes a style section. This section is needed in DL_VERSION_R13.
 */
void DL_Dxf::writeStyle( DL_WriterA& dw, const DL_StyleData& style )
{
// dw.dxfString(  0, "TABLE");
// dw.dxfString(  2, "STYLE");
// if (version==DL_VERSION_2000) {
// dw.dxfHex(5, 3);
// }
    // dw.dxfHex(330, 0);
// if (version==DL_VERSION_2000) {
// dw.dxfString(100, "AcDbSymbolTable");
// }
// dw.dxfInt( 70, 1);
    dw.dxfString(  0, "STYLE" );

    if( version==DL_VERSION_2000 )
    {
        if( style.name=="Standard" )
        {
            // dw.dxfHex(5, 0x11);
            styleHandleStd = dw.handle();
        }
        else
        {
            dw.handle();
        }
    }

    // dw.dxfHex(330, 3);
    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbSymbolTableRecord" );
        dw.dxfString( 100, "AcDbTextStyleTableRecord" );
    }

    dw.dxfString(  2, style.name );
    dw.dxfInt( 70, style.flags );
    dw.dxfReal( 40, style.fixedTextHeight );
    dw.dxfReal( 41, style.widthFactor );
    dw.dxfReal( 50, style.obliqueAngle );
    dw.dxfInt( 71, style.textGenerationFlags );
    dw.dxfReal( 42, style.lastHeightUsed );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString(  3, "" );
        dw.dxfString(  4, "" );
        dw.dxfString( 1001, "ACAD" );
        // dw.dxfString(1000, style.name);
        dw.dxfString( 1000, style.primaryFontFile );
        int xFlags = 0;

        if( style.bold )
        {
            xFlags = xFlags | 0x2000000;
        }

        if( style.italic )
        {
            xFlags = xFlags | 0x1000000;
        }

        dw.dxfInt( 1071, xFlags );
    }
    else
    {
        dw.dxfString(  3, style.primaryFontFile );
        dw.dxfString(  4, style.bigFontFile );
    }

    // dw.dxfString(  0, "ENDTAB");
}


/**
 * Writes a view section. This section is needed in DL_VERSION_R13.
 * Note that this method currently only writes a faked VIEW section
 * to make the file readable by Aut*cad.
 */
void DL_Dxf::writeView( DL_WriterA& dw )
{
    dw.dxfString(  0, "TABLE" );
    dw.dxfString(  2, "VIEW" );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfHex( 5, 6 );
    }

    // dw.dxfHex(330, 0);
    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbSymbolTable" );
    }

    dw.dxfInt( 70, 0 );
    dw.dxfString(  0, "ENDTAB" );
}


/**
 * Writes a ucs section. This section is needed in DL_VERSION_R13.
 * Note that this method currently only writes a faked UCS section
 * to make the file readable by Aut*cad.
 */
void DL_Dxf::writeUcs( DL_WriterA& dw )
{
    dw.dxfString(  0, "TABLE" );
    dw.dxfString(  2, "UCS" );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfHex( 5, 7 );
    }

    // dw.dxfHex(330, 0);
    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbSymbolTable" );
    }

    dw.dxfInt( 70, 0 );
    dw.dxfString(  0, "ENDTAB" );
}


/**
 * Writes a dimstyle section. This section is needed in DL_VERSION_R13.
 * Note that this method currently only writes a faked DIMSTYLE section
 * to make the file readable by Aut*cad.
 */
void DL_Dxf::writeDimStyle( DL_WriterA& dw,
        double dimasz, double dimexe, double dimexo,
        double dimgap, double dimtxt, int dimtad, bool dimtih )
{
    dw.dxfString(  0, "TABLE" );
    dw.dxfString(  2, "DIMSTYLE" );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfHex( 5, 0xA );
        dw.dxfString( 100, "AcDbSymbolTable" );
    }

    dw.dxfInt( 70, 1 );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbDimStyleTable" );
        dw.dxfInt( 71, 0 );
    }


    dw.dxfString(  0, "DIMSTYLE" );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfHex( 105, 0x27 );
    }

    // dw.handle(105);
    // dw.dxfHex(330, 0xA);
    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbSymbolTableRecord" );
        dw.dxfString( 100, "AcDbDimStyleTableRecord" );
    }

    dw.dxfString(  2, "Standard" );

    if( version==DL_VERSION_R12 )
    {
        dw.dxfString(  3, "" );
        dw.dxfString(  4, "" );
        dw.dxfString(  5, "" );
        dw.dxfString(  6, "" );
        dw.dxfString(  7, "" );
        dw.dxfReal( 40, 1.0 );
    }

    dw.dxfReal( 41, dimasz );
    dw.dxfReal( 42, dimexo );
    dw.dxfReal( 43, 3.75 );
    dw.dxfReal( 44, dimexe );

    if( version==DL_VERSION_R12 )
    {
        dw.dxfReal( 45, 0.0 );
        dw.dxfReal( 46, 0.0 );
        dw.dxfReal( 47, 0.0 );
        dw.dxfReal( 48, 0.0 );
    }

    dw.dxfInt( 70, 0 );

    if( version==DL_VERSION_R12 )
    {
        dw.dxfInt( 71, 0 );
        dw.dxfInt( 72, 0 );
    }

    // DIMTIH:
    dw.dxfInt( 73, (int) dimtih );
    dw.dxfInt( 74, 0 );

    if( version==DL_VERSION_R12 )
    {
        dw.dxfInt( 75, 0 );
        dw.dxfInt( 76, 0 );
    }

    // DIMTAD:
    dw.dxfInt( 77, dimtad );
    dw.dxfInt( 78, 8 );
    dw.dxfReal( 140, dimtxt );
    dw.dxfReal( 141, 2.5 );

    if( version==DL_VERSION_R12 )
    {
        dw.dxfReal( 142, 0.0 );
    }

    dw.dxfReal( 143, 0.03937007874016 );

    if( version==DL_VERSION_R12 )
    {
        dw.dxfReal( 144, 1.0 );
        dw.dxfReal( 145, 0.0 );
        dw.dxfReal( 146, 1.0 );
    }

    dw.dxfReal( 147, dimgap );

    if( version==DL_VERSION_R12 )
    {
        dw.dxfInt( 170, 0 );
    }

    dw.dxfInt( 171, 3 );
    dw.dxfInt( 172, 1 );

    if( version==DL_VERSION_R12 )
    {
        dw.dxfInt( 173, 0 );
        dw.dxfInt( 174, 0 );
        dw.dxfInt( 175, 0 );
        dw.dxfInt( 176, 0 );
        dw.dxfInt( 177, 0 );
        dw.dxfInt( 178, 0 );
    }

    if( version==DL_VERSION_2000 )
    {
        dw.dxfInt( 271, 2 );
        dw.dxfInt( 272, 2 );
        dw.dxfInt( 274, 3 );
        dw.dxfInt( 278, 44 );
        dw.dxfInt( 283, 0 );
        dw.dxfInt( 284, 8 );
        dw.dxfHex( 340, styleHandleStd );
        // dw.dxfHex(340, 0x11);
    }

    // * /
    dw.dxfString(  0, "ENDTAB" );
}


/**
 * Writes a blockrecord section. This section is needed in DL_VERSION_R13.
 * Note that this method currently only writes a faked BLOCKRECORD section
 * to make the file readable by Aut*cad.
 */
void DL_Dxf::writeBlockRecord( DL_WriterA& dw )
{
    dw.dxfString(  0, "TABLE" );
    dw.dxfString(  2, "BLOCK_RECORD" );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfHex( 5, 1 );
    }

    // dw.dxfHex(330, 0);
    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbSymbolTable" );
    }

    dw.dxfInt( 70, 1 );

    dw.dxfString(  0, "BLOCK_RECORD" );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfHex( 5, 0x1F );
    }

    // int msh = dw.handle();
    // dw.setModelSpaceHandle(msh);
    // dw.dxfHex(330, 1);
    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbSymbolTableRecord" );
        dw.dxfString( 100, "AcDbBlockTableRecord" );
    }

    dw.dxfString(  2, "*Model_Space" );
    dw.dxfHex( 340, 0x22 );

    dw.dxfString(  0, "BLOCK_RECORD" );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfHex( 5, 0x1B );
    }

    // int psh = dw.handle();
    // dw.setPaperSpaceHandle(psh);
    // dw.dxfHex(330, 1);
    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbSymbolTableRecord" );
        dw.dxfString( 100, "AcDbBlockTableRecord" );
    }

    dw.dxfString(  2, "*Paper_Space" );
    dw.dxfHex( 340, 0x1E );

    dw.dxfString(  0, "BLOCK_RECORD" );

    if( version==DL_VERSION_2000 )
    {
        dw.dxfHex( 5, 0x23 );
    }

    // int ps0h = dw.handle();
    // dw.setPaperSpace0Handle(ps0h);
    // dw.dxfHex(330, 1);
    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbSymbolTableRecord" );
        dw.dxfString( 100, "AcDbBlockTableRecord" );
    }

    dw.dxfString(  2, "*Paper_Space0" );
    dw.dxfHex( 340, 0x26 );

    // dw.dxfString(  0, "ENDTAB");
}


/**
 * Writes a single block record with the given name.
 */
void DL_Dxf::writeBlockRecord( DL_WriterA& dw, const std::string& name )
{
    dw.dxfString(  0, "BLOCK_RECORD" );

    if( version==DL_VERSION_2000 )
    {
        dw.handle();
    }

    // dw->dxfHex(330, 1);
    if( version==DL_VERSION_2000 )
    {
        dw.dxfString( 100, "AcDbSymbolTableRecord" );
        dw.dxfString( 100, "AcDbBlockTableRecord" );
    }

    dw.dxfString(  2, name );
    dw.dxfHex( 340, 0 );
}


/**
 * Writes a objects section. This section is needed in DL_VERSION_R13.
 * Note that this method currently only writes a faked OBJECTS section
 * to make the file readable by Aut*cad.
 */
void DL_Dxf::writeObjects( DL_WriterA& dw, const std::string& appDictionaryName )
{
    dw.dxfString(  0, "SECTION" );
    dw.dxfString(  2, "OBJECTS" );


    dw.dxfString(  0, "DICTIONARY" );
    dw.dxfHex( 5, 0xC );
    dw.dxfString( 100, "AcDbDictionary" );
    dw.dxfInt( 280, 0 );
    dw.dxfInt( 281, 1 );
    dw.dxfString(  3, "ACAD_GROUP" );
    dw.dxfHex( 350, 0xD );
    dw.dxfString(  3, "ACAD_LAYOUT" );
    dw.dxfHex( 350, 0x1A );
    dw.dxfString(  3, "ACAD_MLINESTYLE" );
    dw.dxfHex( 350, 0x17 );
    dw.dxfString(  3, "ACAD_PLOTSETTINGS" );
    dw.dxfHex( 350, 0x19 );
    dw.dxfString(  3, "ACAD_PLOTSTYLENAME" );
    dw.dxfHex( 350, 0xE );
    dw.dxfString(  3, "AcDbVariableDictionary" );
    int acDbVariableDictionaryHandle = dw.handle( 350 );
    // int acDbVariableDictionaryHandle = dw.getNextHandle();
    // dw.dxfHex(350, acDbVariableDictionaryHandle);
    // dw.incHandle();

    if( appDictionaryName.length()!=0 )
    {
        dw.dxfString(  3, appDictionaryName );
        appDictionaryHandle = dw.handle( 350 );
        // appDictionaryHandle = dw.getNextHandle();
        // dw.dxfHex(350, appDictionaryHandle);
        // dw.incHandle();
    }

    dw.dxfString(  0, "DICTIONARY" );
    dw.dxfHex( 5, 0xD );
    // dw.handle();                                    // D
    // dw.dxfHex(330, 0xC);
    dw.dxfString( 100, "AcDbDictionary" );
    dw.dxfInt( 280, 0 );
    dw.dxfInt( 281, 1 );


    dw.dxfString(  0, "ACDBDICTIONARYWDFLT" );
    dw.dxfHex( 5, 0xE );
    // dicId4 = dw.handle();                           // E
    // dw.dxfHex(330, 0xC);                       // C
    dw.dxfString( 100, "AcDbDictionary" );
    dw.dxfInt( 281, 1 );
    dw.dxfString(  3, "Normal" );
    dw.dxfHex( 350, 0xF );
    // dw.dxfHex(350, dw.getNextHandle()+5);        // F
    dw.dxfString( 100, "AcDbDictionaryWithDefault" );
    dw.dxfHex( 340, 0xF );
    // dw.dxfHex(340, dw.getNextHandle()+5);        // F


    dw.dxfString(  0, "ACDBPLACEHOLDER" );
    dw.dxfHex( 5, 0xF );
    // dw.handle();                                    // F
    // dw.dxfHex(330, dicId4);                      // E


    dw.dxfString(  0, "DICTIONARY" );
    // dicId3 = dw.handle();                           // 17
    dw.dxfHex( 5, 0x17 );
    // dw.dxfHex(330, 0xC);                       // C
    dw.dxfString( 100, "AcDbDictionary" );
    dw.dxfInt( 280, 0 );
    dw.dxfInt( 281, 1 );
    dw.dxfString(  3, "Standard" );
    dw.dxfHex( 350, 0x18 );
    // dw.dxfHex(350, dw.getNextHandle()+5);        // 18


    dw.dxfString(  0, "MLINESTYLE" );
    dw.dxfHex( 5, 0x18 );
    // dw.handle();                                    // 18
    // dw.dxfHex(330, dicId3);                      // 17
    dw.dxfString( 100, "AcDbMlineStyle" );
    dw.dxfString(  2, "STANDARD" );
    dw.dxfInt( 70, 0 );
    dw.dxfString(  3, "" );
    dw.dxfInt( 62, 256 );
    dw.dxfReal( 51, 90.0 );
    dw.dxfReal( 52, 90.0 );
    dw.dxfInt( 71, 2 );
    dw.dxfReal( 49, 0.5 );
    dw.dxfInt( 62, 256 );
    dw.dxfString(  6, "BYLAYER" );
    dw.dxfReal( 49, -0.5 );
    dw.dxfInt( 62, 256 );
    dw.dxfString(  6, "BYLAYER" );


    dw.dxfString(  0, "DICTIONARY" );
    dw.dxfHex( 5, 0x19 );
    // dw.handle();                           // 17
    // dw.dxfHex(330, 0xC);                       // C
    dw.dxfString( 100, "AcDbDictionary" );
    dw.dxfInt( 280, 0 );
    dw.dxfInt( 281, 1 );


    dw.dxfString(  0, "DICTIONARY" );
    // dicId2 = dw.handle();                           // 1A
    dw.dxfHex( 5, 0x1A );
    // dw.dxfHex(330, 0xC);
    dw.dxfString( 100, "AcDbDictionary" );
    dw.dxfInt( 281, 1 );
    dw.dxfString(  3, "Layout1" );
    dw.dxfHex( 350, 0x1E );
    // dw.dxfHex(350, dw.getNextHandle()+2);        // 1E
    dw.dxfString(  3, "Layout2" );
    dw.dxfHex( 350, 0x26 );
    // dw.dxfHex(350, dw.getNextHandle()+4);        // 26
    dw.dxfString(  3, "Model" );
    dw.dxfHex( 350, 0x22 );
    // dw.dxfHex(350, dw.getNextHandle()+5);        // 22


    dw.dxfString(  0, "LAYOUT" );
    dw.dxfHex( 5, 0x1E );
    // dw.handle();                                    // 1E
    // dw.dxfHex(330, dicId2);                      // 1A
    dw.dxfString( 100, "AcDbPlotSettings" );
    dw.dxfString(  1, "" );
    dw.dxfString(  2, "none_device" );
    dw.dxfString(  4, "" );
    dw.dxfString(  6, "" );
    dw.dxfReal( 40, 0.0 );
    dw.dxfReal( 41, 0.0 );
    dw.dxfReal( 42, 0.0 );
    dw.dxfReal( 43, 0.0 );
    dw.dxfReal( 44, 0.0 );
    dw.dxfReal( 45, 0.0 );
    dw.dxfReal( 46, 0.0 );
    dw.dxfReal( 47, 0.0 );
    dw.dxfReal( 48, 0.0 );
    dw.dxfReal( 49, 0.0 );
    dw.dxfReal( 140, 0.0 );
    dw.dxfReal( 141, 0.0 );
    dw.dxfReal( 142, 1.0 );
    dw.dxfReal( 143, 1.0 );
    dw.dxfInt( 70, 688 );
    dw.dxfInt( 72, 0 );
    dw.dxfInt( 73, 0 );
    dw.dxfInt( 74, 5 );
    dw.dxfString(  7, "" );
    dw.dxfInt( 75, 16 );
    dw.dxfReal( 147, 1.0 );
    dw.dxfReal( 148, 0.0 );
    dw.dxfReal( 149, 0.0 );
    dw.dxfString( 100, "AcDbLayout" );
    dw.dxfString(  1, "Layout1" );
    dw.dxfInt( 70, 1 );
    dw.dxfInt( 71, 1 );
    dw.dxfReal( 10, 0.0 );
    dw.dxfReal( 20, 0.0 );
    dw.dxfReal( 11, 420.0 );
    dw.dxfReal( 21, 297.0 );
    dw.dxfReal( 12, 0.0 );
    dw.dxfReal( 22, 0.0 );
    dw.dxfReal( 32, 0.0 );
    dw.dxfReal( 14, 1.000000000000000E+20 );
    dw.dxfReal( 24, 1.000000000000000E+20 );
    dw.dxfReal( 34, 1.000000000000000E+20 );
    dw.dxfReal( 15, -1.000000000000000E+20 );
    dw.dxfReal( 25, -1.000000000000000E+20 );
    dw.dxfReal( 35, -1.000000000000000E+20 );
    dw.dxfReal( 146, 0.0 );
    dw.dxfReal( 13, 0.0 );
    dw.dxfReal( 23, 0.0 );
    dw.dxfReal( 33, 0.0 );
    dw.dxfReal( 16, 1.0 );
    dw.dxfReal( 26, 0.0 );
    dw.dxfReal( 36, 0.0 );
    dw.dxfReal( 17, 0.0 );
    dw.dxfReal( 27, 1.0 );
    dw.dxfReal( 37, 0.0 );
    dw.dxfInt( 76, 0 );
    // dw.dxfHex(330, dw.getPaperSpaceHandle());    // 1B
    dw.dxfHex( 330, 0x1B );


    dw.dxfString(  0, "LAYOUT" );
    dw.dxfHex( 5, 0x22 );
    // dw.handle();                                    // 22
    // dw.dxfHex(330, dicId2);                      // 1A
    dw.dxfString( 100, "AcDbPlotSettings" );
    dw.dxfString(  1, "" );
    dw.dxfString(  2, "none_device" );
    dw.dxfString(  4, "" );
    dw.dxfString(  6, "" );
    dw.dxfReal( 40, 0.0 );
    dw.dxfReal( 41, 0.0 );
    dw.dxfReal( 42, 0.0 );
    dw.dxfReal( 43, 0.0 );
    dw.dxfReal( 44, 0.0 );
    dw.dxfReal( 45, 0.0 );
    dw.dxfReal( 46, 0.0 );
    dw.dxfReal( 47, 0.0 );
    dw.dxfReal( 48, 0.0 );
    dw.dxfReal( 49, 0.0 );
    dw.dxfReal( 140, 0.0 );
    dw.dxfReal( 141, 0.0 );
    dw.dxfReal( 142, 1.0 );
    dw.dxfReal( 143, 1.0 );
    dw.dxfInt( 70, 1712 );
    dw.dxfInt( 72, 0 );
    dw.dxfInt( 73, 0 );
    dw.dxfInt( 74, 0 );
    dw.dxfString(  7, "" );
    dw.dxfInt( 75, 0 );
    dw.dxfReal( 147, 1.0 );
    dw.dxfReal( 148, 0.0 );
    dw.dxfReal( 149, 0.0 );
    dw.dxfString( 100, "AcDbLayout" );
    dw.dxfString(  1, "Model" );
    dw.dxfInt( 70, 1 );
    dw.dxfInt( 71, 0 );
    dw.dxfReal( 10, 0.0 );
    dw.dxfReal( 20, 0.0 );
    dw.dxfReal( 11, 12.0 );
    dw.dxfReal( 21, 9.0 );
    dw.dxfReal( 12, 0.0 );
    dw.dxfReal( 22, 0.0 );
    dw.dxfReal( 32, 0.0 );
    dw.dxfReal( 14, 0.0 );
    dw.dxfReal( 24, 0.0 );
    dw.dxfReal( 34, 0.0 );
    dw.dxfReal( 15, 0.0 );
    dw.dxfReal( 25, 0.0 );
    dw.dxfReal( 35, 0.0 );
    dw.dxfReal( 146, 0.0 );
    dw.dxfReal( 13, 0.0 );
    dw.dxfReal( 23, 0.0 );
    dw.dxfReal( 33, 0.0 );
    dw.dxfReal( 16, 1.0 );
    dw.dxfReal( 26, 0.0 );
    dw.dxfReal( 36, 0.0 );
    dw.dxfReal( 17, 0.0 );
    dw.dxfReal( 27, 1.0 );
    dw.dxfReal( 37, 0.0 );
    dw.dxfInt( 76, 0 );
    // dw.dxfHex(330, dw.getModelSpaceHandle());    // 1F
    dw.dxfHex( 330, 0x1F );


    dw.dxfString(  0, "LAYOUT" );
    // dw.handle();                                    // 26
    dw.dxfHex( 5, 0x26 );
    // dw.dxfHex(330, dicId2);                      // 1A
    dw.dxfString( 100, "AcDbPlotSettings" );
    dw.dxfString(  1, "" );
    dw.dxfString(  2, "none_device" );
    dw.dxfString(  4, "" );
    dw.dxfString(  6, "" );
    dw.dxfReal( 40, 0.0 );
    dw.dxfReal( 41, 0.0 );
    dw.dxfReal( 42, 0.0 );
    dw.dxfReal( 43, 0.0 );
    dw.dxfReal( 44, 0.0 );
    dw.dxfReal( 45, 0.0 );
    dw.dxfReal( 46, 0.0 );
    dw.dxfReal( 47, 0.0 );
    dw.dxfReal( 48, 0.0 );
    dw.dxfReal( 49, 0.0 );
    dw.dxfReal( 140, 0.0 );
    dw.dxfReal( 141, 0.0 );
    dw.dxfReal( 142, 1.0 );
    dw.dxfReal( 143, 1.0 );
    dw.dxfInt( 70, 688 );
    dw.dxfInt( 72, 0 );
    dw.dxfInt( 73, 0 );
    dw.dxfInt( 74, 5 );
    dw.dxfString(  7, "" );
    dw.dxfInt( 75, 16 );
    dw.dxfReal( 147, 1.0 );
    dw.dxfReal( 148, 0.0 );
    dw.dxfReal( 149, 0.0 );
    dw.dxfString( 100, "AcDbLayout" );
    dw.dxfString(  1, "Layout2" );
    dw.dxfInt( 70, 1 );
    dw.dxfInt( 71, 2 );
    dw.dxfReal( 10, 0.0 );
    dw.dxfReal( 20, 0.0 );
    dw.dxfReal( 11, 12.0 );
    dw.dxfReal( 21, 9.0 );
    dw.dxfReal( 12, 0.0 );
    dw.dxfReal( 22, 0.0 );
    dw.dxfReal( 32, 0.0 );
    dw.dxfReal( 14, 0.0 );
    dw.dxfReal( 24, 0.0 );
    dw.dxfReal( 34, 0.0 );
    dw.dxfReal( 15, 0.0 );
    dw.dxfReal( 25, 0.0 );
    dw.dxfReal( 35, 0.0 );
    dw.dxfReal( 146, 0.0 );
    dw.dxfReal( 13, 0.0 );
    dw.dxfReal( 23, 0.0 );
    dw.dxfReal( 33, 0.0 );
    dw.dxfReal( 16, 1.0 );
    dw.dxfReal( 26, 0.0 );
    dw.dxfReal( 36, 0.0 );
    dw.dxfReal( 17, 0.0 );
    dw.dxfReal( 27, 1.0 );
    dw.dxfReal( 37, 0.0 );
    dw.dxfInt( 76, 0 );
    // dw.dxfHex(330, dw.getPaperSpace0Handle());   // 23
    dw.dxfHex( 330, 0x23 );

    dw.dxfString(  0, "DICTIONARY" );
    // dw.dxfHex(5, 0x2C);
    // dicId5 =
    dw.dxfHex( 5, acDbVariableDictionaryHandle );
    // dw.handle();                           // 2C
    // dw.dxfHex(330, 0xC);                       // C
    dw.dxfString( 100, "AcDbDictionary" );
    dw.dxfInt( 281, 1 );
    dw.dxfString(  3, "DIMASSOC" );
    // dw.dxfHex(350, 0x2F);
    dw.dxfHex( 350, dw.getNextHandle() + 1 );        // 2E
    dw.dxfString(  3, "HIDETEXT" );
    // dw.dxfHex(350, 0x2E);
    dw.dxfHex( 350, dw.getNextHandle() );        // 2D


    dw.dxfString(  0, "DICTIONARYVAR" );
    // dw.dxfHex(5, 0x2E);
    dw.handle();                                    // 2E
    // dw.dxfHex(330, dicId5);                      // 2C
    dw.dxfString( 100, "DictionaryVariables" );
    dw.dxfInt( 280, 0 );
    dw.dxfInt(  1, 2 );


    dw.dxfString(  0, "DICTIONARYVAR" );
    // dw.dxfHex(5, 0x2D);
    dw.handle();                                    // 2D
    // dw.dxfHex(330, dicId5);                      // 2C
    dw.dxfString( 100, "DictionaryVariables" );
    dw.dxfInt( 280, 0 );
    dw.dxfInt(  1, 1 );
}


void DL_Dxf::writeAppDictionary( DL_WriterA& dw )
{
    dw.dxfString(  0, "DICTIONARY" );
    // dw.handle();
    dw.dxfHex( 5, appDictionaryHandle );
    dw.dxfString( 100, "AcDbDictionary" );
    dw.dxfInt( 281, 1 );
}


unsigned int DL_Dxf::writeDictionaryEntry( DL_WriterA& dw, const std::string& name )
{
    dw.dxfString(  3, name );
    return dw.handle(350);
}


void DL_Dxf::writeXRecord( DL_WriterA& dw, int handle, int value )
{
    dw.dxfString(  0, "XRECORD" );
    dw.dxfHex( 5, handle );
    dw.dxfHex( 330, appDictionaryHandle );
    dw.dxfString( 100, "AcDbXrecord" );
    dw.dxfInt( 280, 1 );
    dw.dxfInt( 90, value );
}


void DL_Dxf::writeXRecord( DL_WriterA& dw, int handle, double value )
{
    dw.dxfString(  0, "XRECORD" );
    dw.dxfHex( 5, handle );
    dw.dxfHex( 330, appDictionaryHandle );
    dw.dxfString( 100, "AcDbXrecord" );
    dw.dxfInt( 280, 1 );
    dw.dxfReal( 40, value );
}


void DL_Dxf::writeXRecord( DL_WriterA& dw, int handle, bool value )
{
    dw.dxfString(  0, "XRECORD" );
    dw.dxfHex( 5, handle );
    dw.dxfHex( 330, appDictionaryHandle );
    dw.dxfString( 100, "AcDbXrecord" );
    dw.dxfInt( 280, 1 );
    dw.dxfBool( 290, value );
}


void DL_Dxf::writeXRecord( DL_WriterA& dw, int handle, const std::string& value )
{
    dw.dxfString(  0, "XRECORD" );
    dw.dxfHex( 5, handle );
    dw.dxfHex( 330, appDictionaryHandle );
    dw.dxfString( 100, "AcDbXrecord" );
    dw.dxfInt( 280, 1 );
    dw.dxfString( 1000, value );
}


/**
 * Writes the end of the objects section. This section is needed in DL_VERSION_R13.
 * Note that this method currently only writes a faked OBJECTS section
 * to make the file readable by Aut*cad.
 */
void DL_Dxf::writeObjectsEnd( DL_WriterA& dw )
{
    dw.dxfString(  0, "ENDSEC" );
}


/**
 * Writes a comment to the DXF file.
 */
void DL_Dxf::writeComment( DL_WriterA& dw, const std::string& comment )
{
    dw.dxfString( 999, comment );
}


/**
 * Checks if the given variable is known by the given DXF version.
 */
bool DL_Dxf::checkVariable( const char* var, DL_Codes::version version )
{
    if( version>=DL_VERSION_2000 )
    {
        return true;
    }
    else if( version==DL_VERSION_R12 )
    {
        // these are all the variables recognized by dxf r12:
        if( !strcmp( var, "ACADVER" ) )
        {
            return true;
        }

        if( !strcmp( var, "ACADVER" ) )
        {
            return true;
        }

        if( !strcmp( var, "ANGBASE" ) )
        {
            return true;
        }

        if( !strcmp( var, "ANGDIR" ) )
        {
            return true;
        }

        if( !strcmp( var, "ATTDIA" ) )
        {
            return true;
        }

        if( !strcmp( var, "ATTMODE" ) )
        {
            return true;
        }

        if( !strcmp( var, "ATTREQ" ) )
        {
            return true;
        }

        if( !strcmp( var, "AUNITS" ) )
        {
            return true;
        }

        if( !strcmp( var, "AUPREC" ) )
        {
            return true;
        }

        if( !strcmp( var, "AXISMODE" ) )
        {
            return true;
        }

        if( !strcmp( var, "AXISUNIT" ) )
        {
            return true;
        }

        if( !strcmp( var, "BLIPMODE" ) )
        {
            return true;
        }

        if( !strcmp( var, "CECOLOR" ) )
        {
            return true;
        }

        if( !strcmp( var, "CELTYPE" ) )
        {
            return true;
        }

        if( !strcmp( var, "CHAMFERA" ) )
        {
            return true;
        }

        if( !strcmp( var, "CHAMFERB" ) )
        {
            return true;
        }

        if( !strcmp( var, "CLAYER" ) )
        {
            return true;
        }

        if( !strcmp( var, "COORDS" ) )
        {
            return true;
        }

        if( !strcmp( var, "DIMALT" ) )
        {
            return true;
        }

        if( !strcmp( var, "DIMALTD" ) )
        {
            return true;
        }

        if( !strcmp( var, "DIMALTF" ) )
        {
            return true;
        }

        if( !strcmp( var, "DIMAPOST" ) )
        {
            return true;
        }

        if( !strcmp( var, "DIMASO" ) )
        {
            return true;
        }

        if( !strcmp( var, "DIMASZ" ) )
        {
            return true;
        }

        if( !strcmp( var, "DIMBLK" ) )
        {
            return true;
        }

        if( !strcmp( var, "DIMBLK1" ) )
        {
            return true;
        }

        if( !strcmp( var, "DIMBLK2" ) )
        {
            return true;
        }

        if( !strcmp( var, "DIMCEN" ) )
        {
            return true;
        }

        if( !strcmp( var, "DIMCLRD" ) )
        {
            return true;
        }

        if( !strcmp( var, "DIMCLRE" ) )
        {
            return true;
        }

        if( !strcmp( var, "DIMCLRT" ) )
        {
            return true;
        }

        if( !strcmp( var, "DIMDLE" ) )
        {
            return true;
        }

        if( !strcmp( var, "DIMDLI" ) )
        {
            return true;
        }

        if( !strcmp( var, "DIMEXE" ) )
        {
            return true;
        }

        if( !strcmp( var, "DIMEXO" ) )
        {
            return true;
        }

        if( !strcmp( var, "DIMGAP" ) )
        {
            return true;
        }

        if( !strcmp( var, "DIMLFAC" ) )
        {
            return true;
        }

        if( !strcmp( var, "DIMLIM" ) )
        {
            return true;
        }

        if( !strcmp( var, "DIMPOST" ) )
        {
            return true;
        }

        if( !strcmp( var, "DIMRND" ) )
        {
            return true;
        }

        if( !strcmp( var, "DIMSAH" ) )
        {
            return true;
        }

        if( !strcmp( var, "DIMSCALE" ) )
        {
            return true;
        }

        if( !strcmp( var, "DIMSE1" ) )
        {
            return true;
        }

        if( !strcmp( var, "DIMSE2" ) )
        {
            return true;
        }

        if( !strcmp( var, "DIMSHO" ) )
        {
            return true;
        }

        if( !strcmp( var, "DIMSOXD" ) )
        {
            return true;
        }

        if( !strcmp( var, "DIMSTYLE" ) )
        {
            return true;
        }

        if( !strcmp( var, "DIMTAD" ) )
        {
            return true;
        }

        if( !strcmp( var, "DIMTFAC" ) )
        {
            return true;
        }

        if( !strcmp( var, "DIMTIH" ) )
        {
            return true;
        }

        if( !strcmp( var, "DIMTIX" ) )
        {
            return true;
        }

        if( !strcmp( var, "DIMTM" ) )
        {
            return true;
        }

        if( !strcmp( var, "DIMTOFL" ) )
        {
            return true;
        }

        if( !strcmp( var, "DIMTOH" ) )
        {
            return true;
        }

        if( !strcmp( var, "DIMTOL" ) )
        {
            return true;
        }

        if( !strcmp( var, "DIMTP" ) )
        {
            return true;
        }

        if( !strcmp( var, "DIMTSZ" ) )
        {
            return true;
        }

        if( !strcmp( var, "DIMTVP" ) )
        {
            return true;
        }

        if( !strcmp( var, "DIMTXT" ) )
        {
            return true;
        }

        if( !strcmp( var, "DIMZIN" ) )
        {
            return true;
        }

        if( !strcmp( var, "DWGCODEPAGE" ) )
        {
            return true;
        }

        if( !strcmp( var, "DRAGMODE" ) )
        {
            return true;
        }

        if( !strcmp( var, "ELEVATION" ) )
        {
            return true;
        }

        if( !strcmp( var, "EXTMAX" ) )
        {
            return true;
        }

        if( !strcmp( var, "EXTMIN" ) )
        {
            return true;
        }

        if( !strcmp( var, "FILLETRAD" ) )
        {
            return true;
        }

        if( !strcmp( var, "FILLMODE" ) )
        {
            return true;
        }

        if( !strcmp( var, "HANDLING" ) )
        {
            return true;
        }

        if( !strcmp( var, "HANDSEED" ) )
        {
            return true;
        }

        if( !strcmp( var, "INSBASE" ) )
        {
            return true;
        }

        if( !strcmp( var, "LIMCHECK" ) )
        {
            return true;
        }

        if( !strcmp( var, "LIMMAX" ) )
        {
            return true;
        }

        if( !strcmp( var, "LIMMIN" ) )
        {
            return true;
        }

        if( !strcmp( var, "LTSCALE" ) )
        {
            return true;
        }

        if( !strcmp( var, "LUNITS" ) )
        {
            return true;
        }

        if( !strcmp( var, "LUPREC" ) )
        {
            return true;
        }

        if( !strcmp( var, "MAXACTVP" ) )
        {
            return true;
        }

        if( !strcmp( var, "MENU" ) )
        {
            return true;
        }

        if( !strcmp( var, "MIRRTEXT" ) )
        {
            return true;
        }

        if( !strcmp( var, "ORTHOMODE" ) )
        {
            return true;
        }

        if( !strcmp( var, "OSMODE" ) )
        {
            return true;
        }

        if( !strcmp( var, "PDMODE" ) )
        {
            return true;
        }

        if( !strcmp( var, "PDSIZE" ) )
        {
            return true;
        }

        if( !strcmp( var, "PELEVATION" ) )
        {
            return true;
        }

        if( !strcmp( var, "PEXTMAX" ) )
        {
            return true;
        }

        if( !strcmp( var, "PEXTMIN" ) )
        {
            return true;
        }

        if( !strcmp( var, "PLIMCHECK" ) )
        {
            return true;
        }

        if( !strcmp( var, "PLIMMAX" ) )
        {
            return true;
        }

        if( !strcmp( var, "PLIMMIN" ) )
        {
            return true;
        }

        if( !strcmp( var, "PLINEGEN" ) )
        {
            return true;
        }

        if( !strcmp( var, "PLINEWID" ) )
        {
            return true;
        }

        if( !strcmp( var, "PSLTSCALE" ) )
        {
            return true;
        }

        if( !strcmp( var, "PUCSNAME" ) )
        {
            return true;
        }

        if( !strcmp( var, "PUCSORG" ) )
        {
            return true;
        }

        if( !strcmp( var, "PUCSXDIR" ) )
        {
            return true;
        }

        if( !strcmp( var, "PUCSYDIR" ) )
        {
            return true;
        }

        if( !strcmp( var, "QTEXTMODE" ) )
        {
            return true;
        }

        if( !strcmp( var, "REGENMODE" ) )
        {
            return true;
        }

        if( !strcmp( var, "SHADEDGE" ) )
        {
            return true;
        }

        if( !strcmp( var, "SHADEDIF" ) )
        {
            return true;
        }

        if( !strcmp( var, "SKETCHINC" ) )
        {
            return true;
        }

        if( !strcmp( var, "SKPOLY" ) )
        {
            return true;
        }

        if( !strcmp( var, "SPLFRAME" ) )
        {
            return true;
        }

        if( !strcmp( var, "SPLINESEGS" ) )
        {
            return true;
        }

        if( !strcmp( var, "SPLINETYPE" ) )
        {
            return true;
        }

        if( !strcmp( var, "SURFTAB1" ) )
        {
            return true;
        }

        if( !strcmp( var, "SURFTAB2" ) )
        {
            return true;
        }

        if( !strcmp( var, "SURFTYPE" ) )
        {
            return true;
        }

        if( !strcmp( var, "SURFU" ) )
        {
            return true;
        }

        if( !strcmp( var, "SURFV" ) )
        {
            return true;
        }

        if( !strcmp( var, "TDCREATE" ) )
        {
            return true;
        }

        if( !strcmp( var, "TDINDWG" ) )
        {
            return true;
        }

        if( !strcmp( var, "TDUPDATE" ) )
        {
            return true;
        }

        if( !strcmp( var, "TDUSRTIMER" ) )
        {
            return true;
        }

        if( !strcmp( var, "TEXTSIZE" ) )
        {
            return true;
        }

        if( !strcmp( var, "TEXTSTYLE" ) )
        {
            return true;
        }

        if( !strcmp( var, "THICKNESS" ) )
        {
            return true;
        }

        if( !strcmp( var, "TILEMODE" ) )
        {
            return true;
        }

        if( !strcmp( var, "TRACEWID" ) )
        {
            return true;
        }

        if( !strcmp( var, "UCSNAME" ) )
        {
            return true;
        }

        if( !strcmp( var, "UCSORG" ) )
        {
            return true;
        }

        if( !strcmp( var, "UCSXDIR" ) )
        {
            return true;
        }

        if( !strcmp( var, "UCSYDIR" ) )
        {
            return true;
        }

        if( !strcmp( var, "UNITMODE" ) )
        {
            return true;
        }

        if( !strcmp( var, "USERI1" ) )
        {
            return true;
        }

        if( !strcmp( var, "USERR1" ) )
        {
            return true;
        }

        if( !strcmp( var, "USRTIMER" ) )
        {
            return true;
        }

        if( !strcmp( var, "VISRETAIN" ) )
        {
            return true;
        }

        if( !strcmp( var, "WORLDVIEW" ) )
        {
            return true;
        }

        if( !strcmp( var, "FASTZOOM" ) )
        {
            return true;
        }

        if( !strcmp( var, "GRIDMODE" ) )
        {
            return true;
        }

        if( !strcmp( var, "GRIDUNIT" ) )
        {
            return true;
        }

        if( !strcmp( var, "SNAPANG" ) )
        {
            return true;
        }

        if( !strcmp( var, "SNAPBASE" ) )
        {
            return true;
        }

        if( !strcmp( var, "SNAPISOPAIR" ) )
        {
            return true;
        }

        if( !strcmp( var, "SNAPMODE" ) )
        {
            return true;
        }

        if( !strcmp( var, "SNAPSTYLE" ) )
        {
            return true;
        }

        if( !strcmp( var, "SNAPUNIT" ) )
        {
            return true;
        }

        if( !strcmp( var, "VIEWCTR" ) )
        {
            return true;
        }

        if( !strcmp( var, "VIEWDIR" ) )
        {
            return true;
        }

        if( !strcmp( var, "VIEWSIZE" ) )
        {
            return true;
        }

        return false;
    }

    return false;
}


/**
 * @returns the library version as int (4 bytes, each byte one version number).
 * e.g. if str = "2.0.2.0" getLibVersion returns 0x02000200
 */
int DL_Dxf::getLibVersion( const std::string& str )
{
    std::size_t d[4];
    int idx = 0;
    // char v[4][5];
    std::string v[4];
    int ret = 0;

    for( unsigned int i = 0; i<str.length() && idx<3; ++i )
    {
        if( str[i]=='.' )
        {
            d[idx] = i;
            idx++;
        }
    }

    if( idx>=2 )
    {
        d[3] = str.length();

        v[0] = str.substr( 0, d[0] );
        v[1] = str.substr( d[0] + 1, d[1] - d[0] - 1 );
        v[2] = str.substr( d[1] + 1, d[2] - d[1] - 1 );

        if( idx>=3 )
        {
            v[3] = str.substr( d[2] + 1, d[3] - d[2] - 1 );
        }
        else
        {
            v[3] = "0";
        }

        ret = ( atoi( v[0].c_str() ) << (3 * 8) ) +
              ( atoi( v[1].c_str() ) << (2 * 8) ) +
              ( atoi( v[2].c_str() ) << (1 * 8) ) +
              ( atoi( v[3].c_str() ) << (0 * 8) );

        return ret;
    }
    else
    {
        std::cerr << "DL_Dxf::getLibVersion: invalid version number: " << str << "\n";
        return 0;
    }
}


/**
 * Converts the given string into a double or returns the given
 * default valud (def) if value is NULL or empty.
 */
// double DL_Dxf::toReal(const char* value, double def) {
// if (value!=NULL && value[0] != '\0') {
// printf("toReal: not empty: %s\n", value);
// printf("toReal: val: %f\n", atof(value));
// printf("toReal: 0: %d\n", value[0]);
// printf("toReal: 1: %d\n", value[1]);
// printf("toReal: 2: %d\n", value[2]);
// double ret;
// if (strchr(value, ',') != NULL) {
// char* tmp = new char[strlen(value)+1];
// strcpy(tmp, value);
// DL_WriterA::strReplace(tmp, ',', '.');
// ret = atof(tmp);
// delete[] tmp;
// }
// else {
// ret = atof(value);
// }
// return ret;
// } else {
// return def;
// }
// }


/**
 * Some test routines.
 */
void DL_Dxf::test()
{
    char* buf1;
    char* buf2;
    char* buf3;
    char* buf4;
    char* buf5;
    char* buf6;

    buf1 = new char[10];
    buf2 = new char[10];
    buf3 = new char[10];
    buf4 = new char[10];
    buf5 = new char[10];
    buf6 = new char[10];

    strcpy( buf1, "  10\n" );
    strcpy( buf2, "10" );
    strcpy( buf3, "10\n" );
    strcpy( buf4, "  10 \n" );
    strcpy( buf5, "  10 \r" );
    strcpy( buf6, "\t10 \n" );

    std::cout << "1 buf1: '" << buf1 << "'\n";
    stripWhiteSpace( &buf1 );
    std::cout << "2 buf1: '" << buf1 << "'\n";
    // assert(!strcmp(buf1, "10"));

    std::cout << "1 buf2: '" << buf2 << "'\n";
    stripWhiteSpace( &buf2 );
    std::cout << "2 buf2: '" << buf2 << "'\n";

    std::cout << "1 buf3: '" << buf3 << "'\n";
    stripWhiteSpace( &buf3 );
    std::cout << "2 buf3: '" << buf3 << "'\n";

    std::cout << "1 buf4: '" << buf4 << "'\n";
    stripWhiteSpace( &buf4 );
    std::cout << "2 buf4: '" << buf4 << "'\n";

    std::cout << "1 buf5: '" << buf5 << "'\n";
    stripWhiteSpace( &buf5 );
    std::cout << "2 buf5: '" << buf5 << "'\n";

    std::cout << "1 buf6: '" << buf6 << "'\n";
    stripWhiteSpace( &buf6 );
    std::cout << "2 buf6: '" << buf6 << "'\n";

    delete[] buf1;
    delete[] buf2;
    delete[] buf3;
    delete[] buf4;
    delete[] buf5;
    delete[] buf6;
}
