static int follow_automount(struct path *path, unsigned flags,
			    bool *need_mntput)
{
	struct vfsmount *mnt;
	int err;

	if (!path->dentry->d_op || !path->dentry->d_op->d_automount)
		return -EREMOTE;

	/* We don't want to mount if someone's just doing a stat -
	 * unless they're stat'ing a directory and appended a '/' to
	 * the name.
	 *
	 * We do, however, want to mount if someone wants to open or
	 * create a file of any type under the mountpoint, wants to
	 * traverse through the mountpoint or wants to open the
	 * mounted directory.  Also, autofs may mark negative dentries
	 * as being automount points.  These will need the attentions
	 * of the daemon to instantiate them before they can be used.
	 */
	if (!(flags & (LOOKUP_PARENT | LOOKUP_DIRECTORY |
		     LOOKUP_OPEN | LOOKUP_CREATE | LOOKUP_AUTOMOUNT)) &&
	    path->dentry->d_inode)
		return -EISDIR;

	current->total_link_count++;
	if (current->total_link_count >= 40)
		return -ELOOP;

	mnt = path->dentry->d_op->d_automount(path);
	if (IS_ERR(mnt)) {
		/*
		 * The filesystem is allowed to return -EISDIR here to indicate
		 * it doesn't want to automount.  For instance, autofs would do
		 * this so that its userspace daemon can mount on this dentry.
		 *
		 * However, we can only permit this if it's a terminal point in
		 * the path being looked up; if it wasn't then the remainder of
		 * the path is inaccessible and we should say so.
		 */
		if (PTR_ERR(mnt) == -EISDIR && (flags & LOOKUP_PARENT))
			return -EREMOTE;
		return PTR_ERR(mnt);
	}

	if (!mnt) /* mount collision */
		return 0;

	if (!*need_mntput) {
		/* lock_mount() may release path->mnt on error */
		mntget(path->mnt);
		*need_mntput = true;
	}
	err = finish_automount(mnt, path);

	switch (err) {
	case -EBUSY:
		/* Someone else made a mount here whilst we were busy */
		return 0;
	case 0:
		path_put(path);
		path->mnt = mnt;
		path->dentry = dget(mnt->mnt_root);
		return 0;
	default:
		return err;
	}

}


SYSCALL_DEFINE3(mkdirat, int, dfd, const char __user *, pathname, umode_t, mode)
{
	struct dentry *dentry;
	struct path path;
	int error;
	unsigned int lookup_flags = LOOKUP_DIRECTORY;

retry:
	dentry = user_path_create(dfd, pathname, &path, lookup_flags);
	if (IS_ERR(dentry))
		return PTR_ERR(dentry);

	if (!IS_POSIXACL(path.dentry->d_inode))
		mode &= ~current_umask();
	error = security_path_mkdir(&path, dentry, mode);
	if (!error)
		error = vfs_mkdir(path.dentry->d_inode, dentry, mode);
	done_path_create(&path, dentry);
	if (retry_estale(error, lookup_flags)) {
		lookup_flags |= LOOKUP_REVAL;
		goto retry;
	}
	return error;
}

Safer ABI for O_TMPFILE

[suggested by Rasmus Villemoes] make O_DIRECTORY | O_RDWR part of O_TMPFILE;
that will fail on old kernels in a lot more cases than what I came up with.
And make sure O_CREAT doesn't get there...

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>


ftp_close(ftpbuf_t *ftp)
{
	if (ftp == NULL) {
		return NULL;
	}
	if (ftp->data) {
		data_close(ftp, ftp->data);
	}
	if (ftp->fd != -1) {
#if HAVE_OPENSSL_EXT
		if (ftp->ssl_active) {
			SSL_shutdown(ftp->ssl_handle);
			SSL_free(ftp->ssl_handle);
		}
#endif
		closesocket(ftp->fd);
	}
	ftp_gc(ftp);
	efree(ftp);
	return NULL;
}

Clean up this weird safe_emalloc() call


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);
}

check vertexIndex which might be -1 for broken DXF


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);
}


int DL_Dxf::getLibVersion(const std::string& str) {
    int 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;
    }
}


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;
}


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;
}


void DL_Dxf::writePolylineEnd(DL_WriterA& dw) {
    if (version==DL_VERSION_2000) {
    } else {
        dw.entity("SEQEND");
    }
}


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);
}


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, "}");
    }
}


void DL_Dxf::addImageDef(DL_CreationInterface* creationInterface) {
    DL_ImageDefData id(// handle
        getStringValue(5, ""),
        getStringValue(1, ""));

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


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");
}


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();
}


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);
}


void DL_Dxf::writePoint(DL_WriterA& dw,
                        const DL_PointData& data,
                        const DL_Attributes& attrib) {
    dw.entity("POINT");
    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbEntity");
    }
    dw.entityAttributes(attrib);
    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbPoint");
    }
    dw.coord(DL_POINT_COORD_CODE, data.x, data.y, data.z);
}


void DL_Dxf::writeDimLinear(DL_WriterA& dw,
                            const DL_DimensionData& data,
                            const DL_DimLinearData& edata,
                            const DL_Attributes& attrib) {

    dw.entity("DIMENSION");

    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbEntity");
    }
    dw.entityAttributes(attrib);
    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);
}


void DL_Dxf::addPoint(DL_CreationInterface* creationInterface) {
    DL_PointData d(getRealValue(10, 0.0),
                   getRealValue(20, 0.0),
                   getRealValue(30, 0.0));
    creationInterface->addPoint(d);
}


void DL_Dxf::writeLeader(DL_WriterA& dw,
                         const DL_LeaderData& data,
                         const DL_Attributes& attrib) {
    if (version>DL_VERSION_R12) {
        dw.entity("LEADER");
        if (version==DL_VERSION_2000) {
            dw.dxfString(100, "AcDbEntity");
        }
        dw.entityAttributes(attrib);
        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);
    }
}


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);
}


void DL_Dxf::writeObjectsEnd(DL_WriterA& dw) {
    dw.dxfString(  0, "ENDSEC");
}


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


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


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));

    creationInterface->addVertex(d);
}


void DL_Dxf::writeSpline(DL_WriterA& dw,
                         const DL_SplineData& data,
                         const DL_Attributes& attrib) {

    dw.entity("SPLINE");
    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbEntity");
    }
    dw.entityAttributes(attrib);
    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
}


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);
            }
        }
    }
}


void DL_Dxf::writeTrace(DL_WriterA& dw,
                        const DL_TraceData& data,
                        const DL_Attributes& attrib) {
    dw.entity("TRACE");
    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbEntity");
    }
    dw.entityAttributes(attrib);
    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);
}


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;
}


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;
}


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);
    }
}


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);
}


void DL_Dxf::writeDimStyle(DL_WriterA& dw,
                    double dimasz, double dimexe, double dimexo,
                    double dimgap, double dimtxt) {

    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);
    }
    dw.dxfInt( 73, 0);
    dw.dxfInt( 74, 0);
    if (version==DL_VERSION_R12) {
        dw.dxfInt( 75, 0);
        dw.dxfInt( 76, 0);
    }
    dw.dxfInt( 77, 1);
    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");
}


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);
}


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;
}


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();
}


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);
}


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)));
}


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);
}


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);
}


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, "");
}


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);
}


void DL_Dxf::writeLayer(DL_WriterA& dw,
                        const DL_LayerData& data,
                        const DL_Attributes& attrib) {

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

    int color = attrib.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 && attrib.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);
    }
}


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);
}


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;
    }
}


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);
    }
}


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);
}


void DL_Dxf::writePolyline(DL_WriterA& dw,
                           const DL_PolylineData& data,
                           const DL_Attributes& attrib) {
    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);
    }
}


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);
}


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;
    }
}


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;
}


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(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::write3dFace(DL_WriterA& dw,
                   const DL_3dFaceData& data,
                   const DL_Attributes& attrib) {
    dw.entity("3DFACE");
    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbEntity");
    }
    dw.entityAttributes(attrib);
    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]);
}


void DL_Dxf::writeXLine(DL_WriterA& dw,
                       const DL_XLineData& data,
                       const DL_Attributes& attrib) {
    dw.entity("XLINE");
    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbEntity");
    }
    dw.entityAttributes(attrib);
    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);
}


void DL_Dxf::writeKnot(DL_WriterA& dw,
                       const DL_KnotData& data) {

    dw.dxfReal(40, data.k);
}


void DL_Dxf::writeInsert(DL_WriterA& dw,
                         const DL_InsertData& data,
                         const DL_Attributes& attrib) {

    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(attrib);
    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);
    }
}


void DL_Dxf::writeSolid(DL_WriterA& dw,
                   const DL_SolidData& data,
                   const DL_Attributes& attrib) {
    dw.entity("SOLID");
    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbEntity");
    }
    dw.entityAttributes(attrib);
    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);
}


DL_WriterA* DL_Dxf::out(const char* file, DL_Codes::version version) {
    char* f = new char[strlen(file)+1];
    strcpy(f, file);
    this->version = version;

    DL_WriterA* dw = new DL_WriterA(f, version);
    if (dw->openFailed()) {
        delete dw;
        delete[] f;
        return NULL;
    } else {
        delete[] f;
        return dw;
    }
}


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, "}");
    }
}


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


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);
}


void DL_Dxf::writeDimAngular3P(DL_WriterA& dw,
                               const DL_DimensionData& data,
                               const DL_DimAngular3PData& edata,
                               const DL_Attributes& attrib) {

    dw.entity("DIMENSION");

    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbEntity");
    }
    dw.entityAttributes(attrib);
    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);
}


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;
}


void DL_Dxf::writeText(DL_WriterA& dw,
                       const DL_TextData& data,
                       const DL_Attributes& attrib) {

    dw.entity("TEXT");
    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbEntity");
    }
    dw.entityAttributes(attrib);
    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);
}


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;
}


void DL_Dxf::writeComment(DL_WriterA& dw, const std::string& comment) {
    dw.dxfString(999, comment);
}


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");
}


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);
}


void DL_Dxf::endEntity(DL_CreationInterface* creationInterface) {
    creationInterface->endEntity();
}


void DL_Dxf::writeEllipse(DL_WriterA& dw,
                          const DL_EllipseData& data,
                          const DL_Attributes& attrib) {

    if (version>DL_VERSION_R12) {
        dw.entity("ELLIPSE");
        if (version==DL_VERSION_2000) {
            dw.dxfString(100, "AcDbEntity");
        }
        dw.entityAttributes(attrib);
        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);
    }
}


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();
    }
}


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


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;
}


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);
}


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();
}


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);
}


void DL_Dxf::writeDimRadial(DL_WriterA& dw,
                            const DL_DimensionData& data,
                            const DL_DimRadialData& edata,
                            const DL_Attributes& attrib) {

    dw.entity("DIMENSION");

    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbEntity");
    }
    dw.entityAttributes(attrib);
    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);
}


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);
}


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::writeRay(DL_WriterA& dw,
                        const DL_RayData& data,
                        const DL_Attributes& attrib) {
    dw.entity("RAY");
    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbEntity");
    }
    dw.entityAttributes(attrib);
    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);
}


void DL_Dxf::writeArc(DL_WriterA& dw,
                      const DL_ArcData& data,
                      const DL_Attributes& attrib) {
    dw.entity("ARC");
    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbEntity");
    }
    dw.entityAttributes(attrib);
    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);
}


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);
}


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;
}


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);
}


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);
}


bool DL_Dxf::handleLinetypeData(DL_CreationInterface* creationInterface) {
    if (groupCode == 49) {
        creationInterface->addLinetypeDash(toReal(groupValue));
        return true;
    }

    return false;
}


void DL_Dxf::endSequence(DL_CreationInterface* creationInterface) {
    creationInterface->endSequence();
}


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);
}


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");
}


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 && vertexIndex>=0) {
                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;
}


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);
}


void DL_Dxf::writeDimAngular2L(DL_WriterA& dw,
                             const DL_DimensionData& data,
                             const DL_DimAngular2LData& edata,
                             const DL_Attributes& attrib) {

    dw.entity("DIMENSION");

    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbEntity");
    }
    dw.entityAttributes(attrib);
    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);
}


void DL_Dxf::writeAttribute(DL_WriterA& dw,
                   const DL_AttributeData& data,
                   const DL_Attributes& attrib) {

    dw.entity("ATTRIB");
    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbEntity");
    }
    dw.entityAttributes(attrib);
    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::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);
}


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);
}


void DL_Dxf::writeDimDiametric(DL_WriterA& dw,
                               const DL_DimensionData& data,
                               const DL_DimDiametricData& edata,
                               const DL_Attributes& attrib) {

    dw.entity("DIMENSION");

    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbEntity");
    }
    dw.entityAttributes(attrib);
    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);
}


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();
}


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;
}


void DL_Dxf::endBlock(DL_CreationInterface* creationInterface) {
    creationInterface->endBlock();
}


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);
}


void DL_Dxf::writeCircle(DL_WriterA& dw,
                         const DL_CircleData& data,
                         const DL_Attributes& attrib) {
    dw.entity("CIRCLE");
    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbEntity");
    }
    dw.entityAttributes(attrib);
    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbCircle");
    }
    dw.coord(10, data.cx, data.cy, data.cz);
    dw.dxfReal(40, data.radius);
}


bool DL_Dxf::processDXFGroup(DL_CreationInterface* creationInterface,
                             int groupCode, const std::string& groupValue) {

    //printf("%d\n", groupCode);
    //printf("%s\n", groupValue.c_str());

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

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

            addComment(creationInterface, groupValue);
        }
    }

    // Indicates start of new entity or variable:
    else if (groupCode==0 || groupCode==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 (groupValue[0]=='$') {
            currentObjectType = DL_SETTING;
            settingKey = groupValue;
        }

        // Read Layers:
        else if (groupValue=="LAYER") {
            currentObjectType = DL_LAYER;
        }

        // Read Linetypes:
        else if (groupValue=="LTYPE") {
            currentObjectType = DL_LINETYPE;
        }

        // Read Blocks:
        else if (groupValue=="BLOCK") {
            currentObjectType = DL_BLOCK;
        } else if (groupValue=="ENDBLK") {
            currentObjectType = DL_ENDBLK;
        }

        // Read text styles:
        else if (groupValue=="STYLE") {
            currentObjectType = DL_STYLE;
        }

        // Read entities:
        else if (groupValue=="POINT") {
            currentObjectType = DL_ENTITY_POINT;
        } else if (groupValue=="LINE") {
            currentObjectType = DL_ENTITY_LINE;
        } else if (groupValue=="XLINE") {
            currentObjectType = DL_ENTITY_XLINE;
        } else if (groupValue=="RAY") {
            currentObjectType = DL_ENTITY_RAY;
        } else if (groupValue=="POLYLINE") {
            currentObjectType = DL_ENTITY_POLYLINE;
        } else if (groupValue=="LWPOLYLINE") {
            currentObjectType = DL_ENTITY_LWPOLYLINE;
        } else if (groupValue=="VERTEX") {
            currentObjectType = DL_ENTITY_VERTEX;
        } else if (groupValue=="SPLINE") {
            currentObjectType = DL_ENTITY_SPLINE;
        } else if (groupValue=="ARC") {
            currentObjectType = DL_ENTITY_ARC;
        } else if (groupValue=="ELLIPSE") {
            currentObjectType = DL_ENTITY_ELLIPSE;
        } else if (groupValue=="CIRCLE") {
            currentObjectType = DL_ENTITY_CIRCLE;
        } else if (groupValue=="INSERT") {
            currentObjectType = DL_ENTITY_INSERT;
        } else if (groupValue=="TEXT") {
            currentObjectType = DL_ENTITY_TEXT;
        } else if (groupValue=="MTEXT") {
            currentObjectType = DL_ENTITY_MTEXT;
        } else if (groupValue=="ARCALIGNEDTEXT") {
            currentObjectType = DL_ENTITY_ARCALIGNEDTEXT;
        } else if (groupValue=="ATTRIB") {
            currentObjectType = DL_ENTITY_ATTRIB;
        } else if (groupValue=="DIMENSION") {
            currentObjectType = DL_ENTITY_DIMENSION;
        } else if (groupValue=="LEADER") {
            currentObjectType = DL_ENTITY_LEADER;
        } else if (groupValue=="HATCH") {
            currentObjectType = DL_ENTITY_HATCH;
        } else if (groupValue=="IMAGE") {
            currentObjectType = DL_ENTITY_IMAGE;
        } else if (groupValue=="IMAGEDEF") {
            currentObjectType = DL_ENTITY_IMAGEDEF;
        } else if (groupValue=="TRACE") {
           currentObjectType = DL_ENTITY_TRACE;
        } else if (groupValue=="SOLID") {
           currentObjectType = DL_ENTITY_SOLID;
        } else if (groupValue=="3DFACE") {
           currentObjectType = DL_ENTITY_3DFACE;
        } else if (groupValue=="SEQEND") {
            currentObjectType = DL_ENTITY_SEQEND;
        } else if (groupValue=="XRECORD") {
            currentObjectType = DL_XRECORD;
        } else if (groupValue=="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 (groupCode<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[groupCode] = groupValue;
            }
        }

        return false;
    }
    return false;
}


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(attrib);
        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);
        }
    }
}


void DL_Dxf::writeHatch1(DL_WriterA& dw,
                         const DL_HatchData& data,
                         const DL_Attributes& attrib) {

    dw.entity("HATCH");
    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbEntity");
    }
    dw.entityAttributes(attrib);
    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);
}


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");
}


void DL_Dxf::writeHatchLoop2(DL_WriterA& dw,
                             const DL_HatchLoopData& /*data*/) {

    dw.dxfInt(97, 0);
}


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;
    }
}


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");
}


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();
    }
}


void DL_Dxf::addHatchLoop() {
    addHatchEdge();
    hatchEdges.push_back(std::vector<DL_HatchEdgeData>());
}


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);
}