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


check vertexIndex which might be -1 for broken DXF

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

A code execution vulnerability exists in the DL_Dxf::handleLWPolylineData functionality of Ribbonsoft dxflib 3.17.0. A specially-crafted .dxf file can lead to a heap buffer overflow. An attacker can provide a malicious file to trigger this vulnerability.


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


DL_Dxf::~DL_Dxf() {
    if (vertices!=NULL) {
        delete[] vertices;
    }
    if (knots!=NULL) {
        delete[] knots;
    }
    if (controlPoints!=NULL) {
        delete[] controlPoints;
    }
    if (fitPoints!=NULL) {
        delete[] fitPoints;
    }
    if (weights!=NULL) {
        delete[] weights;
    }
    if (leaderVertices!=NULL) {
        delete[] leaderVertices;
    }
}


void DL_Dxf::writeDimOrdinate(DL_WriterA& dw,
                             const DL_DimensionData& data,
                             const DL_DimOrdinateData& 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);

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

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

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

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

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

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


unsigned long DL_Dxf::writeImage(DL_WriterA& dw,
                       const DL_ImageData& data,
                       const DL_Attributes& attrib) {

    /*if (data.file.empty()) {
        std::cerr << "DL_Dxf::writeImage: "
        << "Image file must not be empty\n";
        return;
}*/

    dw.entity("IMAGE");

    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbEntity");
    }
    dw.entityAttributes(attrib);
    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbRasterImage");
        dw.dxfInt(90, 0);
    }
    // insertion point
    dw.dxfReal(10, data.ipx);
    dw.dxfReal(20, data.ipy);
    dw.dxfReal(30, data.ipz);

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

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

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

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

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

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

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

    return handle;
}


void DL_Dxf::writeDimAligned(DL_WriterA& dw,
                             const DL_DimensionData& data,
                             const DL_DimAlignedData& 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.epx1);
    dw.dxfReal(23, edata.epy1);
    dw.dxfReal(33, 0.0);

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

    writeDimStyleOverrides(dw, data);
}


void DL_Dxf::writeLine(DL_WriterA& dw,
                       const DL_LineData& data,
                       const DL_Attributes& attrib) {
    dw.entity("LINE");
    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.x1, data.y1, data.z1);
    dw.coord(DL_LINE_END_CODE, data.x2, data.y2, data.z2);
}


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

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

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

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

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

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

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

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

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

}


void DL_Dxf::addTrace(DL_CreationInterface* creationInterface) {
    DL_TraceData td;

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


void DL_Dxf::writeHatchLoop1(DL_WriterA& dw,
                             const DL_HatchLoopData& data) {

    dw.dxfInt(92, 1);
    dw.dxfInt(93, data.numEdges);
    //dw.dxfInt(97, 0);
}


void DL_Dxf::writeObjects(DL_WriterA& dw, const std::string& appDictionaryName) {
    dw.dxfString(  0, "SECTION");
    dw.dxfString(  2, "OBJECTS");

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


void DL_Dxf::addLinetype(DL_CreationInterface* creationInterface) {
    std::string name = getStringValue(2, "");
    if (name.length()==0) {
        return;
    }
    int numDashes = getIntValue(73, 0);
    //double dashes[numDashes];

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

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


void DL_Dxf::writeControlPoint(DL_WriterA& dw,
                               const DL_ControlPointData& data) {

    dw.dxfReal(10, data.x);
    dw.dxfReal(20, data.y);
    dw.dxfReal(30, data.z);
}


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

    creationInterface->addText(d);
}


void DL_Dxf::writeHatch2(DL_WriterA& dw,
                         const DL_HatchData& data,
                         const DL_Attributes& /*attrib*/) {

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

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


void DL_Dxf::addEllipse(DL_CreationInterface* creationInterface) {
    DL_EllipseData d(getRealValue(10, 0.0),
                     getRealValue(20, 0.0),
                     getRealValue(30, 0.0),
                     getRealValue(11, 0.0),
                     getRealValue(21, 0.0),
                     getRealValue(31, 0.0),
                     getRealValue(40, 1.0),
                     getRealValue(41, 0.0),
                     getRealValue(42, 2*M_PI));

    creationInterface->addEllipse(d);
}


bool DL_Dxf::handleHatchData(DL_CreationInterface* creationInterface) {
    // New polyline loop, group code 92
    // or new loop with individual edges, group code 93
    if (groupCode==92 || groupCode==93) {
        if (firstHatchLoop) {
            hatchEdges.clear();
            firstHatchLoop = false;
        }
        if (groupCode==92 && (toInt(groupValue)&2)==2) {
            addHatchLoop();
        }
        if (groupCode==93) {
            addHatchLoop();
        }
        return true;
    }

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

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

    // polyline boundary:
    if ((getIntValue(92, 0)&2)==2) {
        switch (groupCode) {
        case 10:
            hatchEdge.type = 0;
            hatchEdge.vertices.push_back(std::vector<double>());
            hatchEdge.vertices.back().push_back(toReal(groupValue));
            return true;
        case 20:
            if (!hatchEdge.vertices.empty()) {
                hatchEdge.vertices.back().push_back(toReal(groupValue));
                hatchEdge.defined = true;
            }
            return true;
        case 42:
            if (!hatchEdge.vertices.empty()) {
                hatchEdge.vertices.back().push_back(toReal(groupValue));
                hatchEdge.defined = true;
            }
            return true;
        }
    }
    else {
        // Line edge:
        if (hatchEdge.type==1) {
            switch (groupCode) {
            case 10:
                hatchEdge.x1 = toReal(groupValue);
                return true;
            case 20:
                hatchEdge.y1 = toReal(groupValue);
                return true;
            case 11:
                hatchEdge.x2 = toReal(groupValue);
                return true;
            case 21:
                hatchEdge.y2 = toReal(groupValue);
                hatchEdge.defined = true;
                return true;
            }
        }

        // Arc edge:
        if (hatchEdge.type==2) {
            switch(groupCode) {
            case 10:
                hatchEdge.cx = toReal(groupValue);
                return true;
            case 20:
                hatchEdge.cy = toReal(groupValue);
                return true;
            case 40:
                hatchEdge.radius = toReal(groupValue);
                return true;
            case 50:
                hatchEdge.angle1 = toReal(groupValue)/360.0*2*M_PI;
                return true;
            case 51:
                hatchEdge.angle2 = toReal(groupValue)/360.0*2*M_PI;
                return true;
            case 73:
                hatchEdge.ccw = (bool)toInt(groupValue);
                hatchEdge.defined = true;
                return true;
            }
        }

        // Ellipse arc edge:
        if (hatchEdge.type==3) {
            switch (groupCode) {
            case 10:
                hatchEdge.cx = toReal(groupValue);
                return true;
            case 20:
                hatchEdge.cy = toReal(groupValue);
                return true;
            case 11:
                hatchEdge.mx = toReal(groupValue);
                return true;
            case 21:
                hatchEdge.my = toReal(groupValue);
                return true;
            case 40:
                hatchEdge.ratio = toReal(groupValue);
                return true;
            case 50:
                hatchEdge.angle1 = toReal(groupValue)/360.0*2*M_PI;
                return true;
            case 51:
                hatchEdge.angle2 = toReal(groupValue)/360.0*2*M_PI;
                return true;
            case 73:
                hatchEdge.ccw = (bool)toInt(groupValue);
                hatchEdge.defined = true;
                return true;
            }
        }

        // Spline edge:
        if (hatchEdge.type==4) {
            switch (groupCode) {
            case 94:
                hatchEdge.degree = toInt(groupValue);
                return true;
            case 73:
                hatchEdge.rational = toBool(groupValue);
                return true;
            case 74:
                hatchEdge.periodic = toBool(groupValue);
                return true;
            case 95:
                hatchEdge.nKnots = toInt(groupValue);
                return true;
            case 96:
                hatchEdge.nControl = toInt(groupValue);
                return true;
            case 97:
                hatchEdge.nFit = toInt(groupValue);
                return true;
            case 40:
                if (hatchEdge.knots.size() < hatchEdge.nKnots) {
                    hatchEdge.knots.push_back(toReal(groupValue));
                }
                return true;
            case 10:
                if (hatchEdge.controlPoints.size() < hatchEdge.nControl) {
                    std::vector<double> v;
                    v.push_back(toReal(groupValue));
                    hatchEdge.controlPoints.push_back(v);
                }
                return true;
            case 20:
                if (!hatchEdge.controlPoints.empty() && hatchEdge.controlPoints.back().size()==1) {
                    hatchEdge.controlPoints.back().push_back(toReal(groupValue));
                }
                hatchEdge.defined = true;
                return true;
            case 42:
                if (hatchEdge.weights.size() < hatchEdge.nControl) {
                    hatchEdge.weights.push_back(toReal(groupValue));
                }
                return true;
            case 11:
                if (hatchEdge.fitPoints.size() < hatchEdge.nFit) {
                    std::vector<double> v;
                    v.push_back(toReal(groupValue));
                    hatchEdge.fitPoints.push_back(v);
                }
                return true;
            case 21:
                if (!hatchEdge.fitPoints.empty() && hatchEdge.fitPoints.back().size()==1) {
                    hatchEdge.fitPoints.back().push_back(toReal(groupValue));
                }
                hatchEdge.defined = true;
                return true;
            case 12:
                hatchEdge.startTangentX = toReal(groupValue);
                return true;
            case 22:
                hatchEdge.startTangentY = toReal(groupValue);
                return true;
            case 13:
                hatchEdge.endTangentX = toReal(groupValue);
                return true;
            case 23:
                hatchEdge.endTangentY = toReal(groupValue);
                return true;
            }
        }
    }

    return false;
}


void DL_Dxf::addDimAligned(DL_CreationInterface* creationInterface) {
    DL_DimensionData d = getDimData();

    // aligned dimension:
    DL_DimAlignedData da(
        // extension point 1
        getRealValue(13, 0.0),
        getRealValue(23, 0.0),
        getRealValue(33, 0.0),
        // extension point 2
        getRealValue(14, 0.0),
        getRealValue(24, 0.0),
        getRealValue(34, 0.0));
    creationInterface->addDimAlign(d, da);
}


void DL_Dxf::writeMText(DL_WriterA& dw,
                        const DL_MTextData& data,
                        const DL_Attributes& attrib) {

    dw.entity("MTEXT");
    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbEntity");
    }
    dw.entityAttributes(attrib);
    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbMText");
    }
    dw.dxfReal(10, data.ipx);
    dw.dxfReal(20, data.ipy);
    dw.dxfReal(30, data.ipz);
    dw.dxfReal(40, data.height);
    dw.dxfReal(41, data.width);

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

    // Creare text chunks of 250 characters each:
    int length = data.text.length();
    char chunk[251];
    int i;
    for (i=250; i<length; i+=250) {
        strncpy(chunk, &data.text.c_str()[i-250], 250);
        chunk[250]='\0';
        dw.dxfString(3, chunk);
    }
    strncpy(chunk, &data.text.c_str()[i-250], 250);
    chunk[250]='\0';
    dw.dxfString(1, chunk);

    dw.dxfString(7, data.style);

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

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


void DL_Dxf::addBlock(DL_CreationInterface* creationInterface) {
    std::string name = getStringValue(2, "");
    if (name.length()==0) {
        return;
    }

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

    creationInterface->addBlock(d);
}


bool DL_Dxf::handleXRecordData(DL_CreationInterface* creationInterface) {
    if (groupCode==105) {
        return false;
    }

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

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

    if (!xRecordValues) {
        return false;
    }

    // string:
    if (groupCode<=9 ||
        groupCode==100 || groupCode==102 || groupCode==105 ||
        (groupCode>=300 && groupCode<=369) ||
        (groupCode>=1000 && groupCode<=1009)) {

        creationInterface->addXRecordString(groupCode, groupValue);
        return true;
    }

    // int:
    else if ((groupCode>=60 && groupCode<=99) || (groupCode>=160 && groupCode<=179) || (groupCode>=270 && groupCode<=289)) {
        creationInterface->addXRecordInt(groupCode, toInt(groupValue));
        return true;
    }

    // bool:
    else if (groupCode>=290 && groupCode<=299) {
        creationInterface->addXRecordBool(groupCode, toBool(groupValue));
        return true;
    }

    // double:
    else if ((groupCode>=10 && groupCode<=59) || (groupCode>=110 && groupCode<=149) || (groupCode>=210 && groupCode<=239)) {
        creationInterface->addXRecordReal(groupCode, toReal(groupValue));
        return true;
    }

    return false;
}


void DL_Dxf::addComment(DL_CreationInterface* creationInterface, const std::string& comment) {
    creationInterface->addComment(comment);
}


udf: Restore i_lenAlloc when inode expansion fails

When we fail to expand inode from inline format to a normal format, we
restore inode to contain the original inline formatting but we forgot to
set i_lenAlloc back. The mismatch between i_lenAlloc and i_size was then
causing further problems such as warnings and lost data down the line.

Reported-by: butt3rflyh4ck <butterflyhuangxx@gmail.com>
CC: stable@vger.kernel.org
Fixes: 7e49b6f2480c ("udf: Convert UDF to new truncate calling sequence")
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Jan Kara <jack@suse.cz>

int udf_expand_file_adinicb(struct inode *inode)
{
	struct page *page;
	char *kaddr;
	struct udf_inode_info *iinfo = UDF_I(inode);
	int err;

	WARN_ON_ONCE(!inode_is_locked(inode));
	if (!iinfo->i_lenAlloc) {
		if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD))
			iinfo->i_alloc_type = ICBTAG_FLAG_AD_SHORT;
		else
			iinfo->i_alloc_type = ICBTAG_FLAG_AD_LONG;
		/* from now on we have normal address_space methods */
		inode->i_data.a_ops = &udf_aops;
		up_write(&iinfo->i_data_sem);
		mark_inode_dirty(inode);
		return 0;
	}
	/*
	 * Release i_data_sem so that we can lock a page - page lock ranks
	 * above i_data_sem. i_mutex still protects us against file changes.
	 */
	up_write(&iinfo->i_data_sem);

	page = find_or_create_page(inode->i_mapping, 0, GFP_NOFS);
	if (!page)
		return -ENOMEM;

	if (!PageUptodate(page)) {
		kaddr = kmap_atomic(page);
		memset(kaddr + iinfo->i_lenAlloc, 0x00,
		       PAGE_SIZE - iinfo->i_lenAlloc);
		memcpy(kaddr, iinfo->i_data + iinfo->i_lenEAttr,
			iinfo->i_lenAlloc);
		flush_dcache_page(page);
		SetPageUptodate(page);
		kunmap_atomic(kaddr);
	}
	down_write(&iinfo->i_data_sem);
	memset(iinfo->i_data + iinfo->i_lenEAttr, 0x00,
	       iinfo->i_lenAlloc);
	iinfo->i_lenAlloc = 0;
	if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD))
		iinfo->i_alloc_type = ICBTAG_FLAG_AD_SHORT;
	else
		iinfo->i_alloc_type = ICBTAG_FLAG_AD_LONG;
	/* from now on we have normal address_space methods */
	inode->i_data.a_ops = &udf_aops;
	set_page_dirty(page);
	unlock_page(page);
	up_write(&iinfo->i_data_sem);
	err = filemap_fdatawrite(inode->i_mapping);
	if (err) {
		/* Restore everything back so that we don't lose data... */
		lock_page(page);
		down_write(&iinfo->i_data_sem);
		kaddr = kmap_atomic(page);
		memcpy(iinfo->i_data + iinfo->i_lenEAttr, kaddr, inode->i_size);
		kunmap_atomic(kaddr);
		unlock_page(page);
		iinfo->i_alloc_type = ICBTAG_FLAG_AD_IN_ICB;
		inode->i_data.a_ops = &udf_adinicb_aops;
		iinfo->i_lenAlloc = inode->i_size;
		up_write(&iinfo->i_data_sem);
	}
	put_page(page);
	mark_inode_dirty(inode);

	return err;
}

A flaw null pointer dereference in the Linux kernel UDF file system functionality was found in the way user triggers udf_file_write_iter function for the malicious UDF image. A local user could use this flaw to crash the system. Actual from Linux kernel 4.2-rc1 till 5.17-rc2.


static MagickBooleanType SetGrayscaleImage(Image *image,
  ExceptionInfo *exception)
{
  CacheView
    *image_view;

  MagickBooleanType
    status;

  PixelInfo
    *colormap;

  register ssize_t
    i;

  ssize_t
    *colormap_index,
    j,
    y;

  assert(image != (Image *) NULL);
  assert(image->signature == MagickCoreSignature);
  if (image->type != GrayscaleType)
    (void) TransformImageColorspace(image,GRAYColorspace,exception);
  if (image->storage_class == PseudoClass)
    colormap_index=(ssize_t *) AcquireQuantumMemory(MagickMax(image->colors+1,
      MaxMap),sizeof(*colormap_index));
  else
    colormap_index=(ssize_t *) AcquireQuantumMemory(MagickMax(MaxColormapSize+1,
      MaxMap),sizeof(*colormap_index));
  if (colormap_index == (ssize_t *) NULL)
    ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
      image->filename);
  if (image->storage_class != PseudoClass)
    {
      (void) memset(colormap_index,(-1),MaxColormapSize*
        sizeof(*colormap_index));
      if (AcquireImageColormap(image,MaxColormapSize,exception) == MagickFalse)
        {
          colormap_index=(ssize_t *) RelinquishMagickMemory(colormap_index);
          ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
            image->filename);
        }
      image->colors=0;
      status=MagickTrue;
      image_view=AcquireAuthenticCacheView(image,exception);
#if defined(MAGICKCORE_OPENMP_SUPPORT)
      #pragma omp parallel for schedule(static) shared(status) \
        magick_number_threads(image,image,image->rows,1)
#endif
      for (y=0; y < (ssize_t) image->rows; y++)
      {
        register Quantum
          *magick_restrict q;

        register ssize_t
          x;

        if (status == MagickFalse)
          continue;
        q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
          exception);
        if (q == (Quantum *) NULL)
          {
            status=MagickFalse;
            continue;
          }
        for (x=0; x < (ssize_t) image->columns; x++)
        {
          register size_t
            intensity;

          intensity=ScaleQuantumToMap(GetPixelRed(image,q));
          if (colormap_index[intensity] < 0)
            {
#if defined(MAGICKCORE_OPENMP_SUPPORT)
              #pragma omp critical (MagickCore_SetGrayscaleImage)
#endif
              if (colormap_index[intensity] < 0)
                {
                  colormap_index[intensity]=(ssize_t) image->colors;
                  image->colormap[image->colors].red=(double)
                    GetPixelRed(image,q);
                  image->colormap[image->colors].green=(double)
                    GetPixelGreen(image,q);
                  image->colormap[image->colors].blue=(double)
                    GetPixelBlue(image,q);
                  image->colors++;
               }
            }
          SetPixelIndex(image,(Quantum) colormap_index[intensity],q);
          q+=GetPixelChannels(image);
        }
        if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
          status=MagickFalse;
      }
      image_view=DestroyCacheView(image_view);
    }
  for (i=0; i < (ssize_t) image->colors; i++)
    image->colormap[i].alpha=(double) i;
  qsort((void *) image->colormap,image->colors,sizeof(PixelInfo),
    IntensityCompare);
  colormap=(PixelInfo *) AcquireQuantumMemory(image->colors,sizeof(*colormap));
  if (colormap == (PixelInfo *) NULL)
    {
      colormap_index=(ssize_t *) RelinquishMagickMemory(colormap_index);
      ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
        image->filename);
    }
  j=0;
  colormap[j]=image->colormap[0];
  for (i=0; i < (ssize_t) image->colors; i++)
  {
    if (IsPixelInfoEquivalent(&colormap[j],&image->colormap[i]) == MagickFalse)
      {
        j++;
        colormap[j]=image->colormap[i];
      }
    colormap_index[(ssize_t) image->colormap[i].alpha]=j;
  }
  image->colors=(size_t) (j+1);
  image->colormap=(PixelInfo *) RelinquishMagickMemory(image->colormap);
  image->colormap=colormap;
  status=MagickTrue;
  image_view=AcquireAuthenticCacheView(image,exception);
#if defined(MAGICKCORE_OPENMP_SUPPORT)
  #pragma omp parallel for schedule(static) shared(status) \
    magick_number_threads(image,image,image->rows,1)
#endif
  for (y=0; y < (ssize_t) image->rows; y++)
  {
    register Quantum
      *magick_restrict q;

    register ssize_t
      x;

    if (status == MagickFalse)
      continue;
    q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
    if (q == (Quantum *) NULL)
      {
        status=MagickFalse;
        continue;
      }
    for (x=0; x < (ssize_t) image->columns; x++)
    {
      SetPixelIndex(image,(Quantum) colormap_index[ScaleQuantumToMap(
        GetPixelIndex(image,q))],q);
      q+=GetPixelChannels(image);
    }
    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
      status=MagickFalse;
  }
  image_view=DestroyCacheView(image_view);
  colormap_index=(ssize_t *) RelinquishMagickMemory(colormap_index);
  image->type=GrayscaleType;
  if (SetImageMonochrome(image,exception) != MagickFalse)
    image->type=BilevelType;
  return(status);
}

In ImageMagick 7.0.8-40 Q16, there is a heap-based buffer over-read in the function WritePNMImage of coders/pnm.c, which allows an attacker to cause a denial of service or possibly information disclosure via a crafted image file. This is related to SetGrayscaleImage in MagickCore/quantize.c.