/****************************************************************************
** Copyright (C) 2001-2013 RibbonSoft, GmbH. All rights reserved.
**
** This file is part of the dxflib project.
**
** This file is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** Licensees holding valid dxflib Professional Edition licenses may use
** this file in accordance with the dxflib Commercial License
** Agreement provided with the Software.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
** See http://www.ribbonsoft.com for further details.
**
** Contact info@ribbonsoft.com if any conditions of this licensing are
** not clear to you.
**
**********************************************************************/
#include "dl_dxf.h"
#include <algorithm>
#include <string>
#include <cstdio>
#include <cassert>
#include <cmath>
#include "dl_attributes.h"
#include "dl_codes.h"
#include "dl_creationadapter.h"
#include "dl_writer_ascii.h"
#include "iostream"
/**
* Default constructor.
*/
DL_Dxf::DL_Dxf() {
m_version = DL_VERSION_2000;
m_vertices = NULL;
m_maxVertices = 0;
m_vertexIndex = 0;
m_knots = NULL;
m_maxKnots = 0;
m_knotIndex = 0;
m_weights = NULL;
m_weightIndex = 0;
m_controlPoints = NULL;
m_maxControlPoints = 0;
m_controlPointIndex = 0;
m_fitPoints = NULL;
m_maxFitPoints = 0;
m_fitPointIndex = 0;
m_leaderVertices = NULL;
m_maxLeaderVertices = 0;
m_leaderVertexIndex = 0;
}
/**
* Destructor.
*/
DL_Dxf::~DL_Dxf() {
if (m_vertices!=NULL) {
delete[] m_vertices;
}
if (m_knots!=NULL) {
delete[] m_knots;
}
if (m_controlPoints!=NULL) {
delete[] m_controlPoints;
}
if (m_fitPoints!=NULL) {
delete[] m_fitPoints;
}
if (m_weights!=NULL) {
delete[] m_weights;
}
if (m_leaderVertices!=NULL) {
delete[] m_leaderVertices;
}
}
/**
* @brief Reads the given file and calls the appropriate functions in
* the given creation interface for every entity found in the file.
*
* @param file Input
* Path and name of file to read
* @param creationInterface
* Pointer to the class which takes care of the entities in the file.
*
* @retval true If \p file could be opened.
* @retval false If \p file could not be opened.
*/
bool DL_Dxf::in(const std::string& file, DL_CreationInterface* creationInterface) {
FILE *fp;
m_firstCall = true;
m_currentObjectType = DL_UNKNOWN;
fopen_s(&fp, file.c_str(), "rt");
if (fp) {
while (readDxfGroups(fp, creationInterface)) {}
fclose(fp);
return true;
}
return false;
}
/**
* Reads a DXF file from an existing stream.
*
* @param stream The string stream.
* @param creationInterface
* Pointer to the class which takes care of the entities in the file.
*
* @retval true If \p file could be opened.
* @retval false If \p file could not be opened.
*/
bool DL_Dxf::in(std::stringstream& stream,
DL_CreationInterface* creationInterface) {
if (stream.good()) {
m_firstCall=true;
m_currentObjectType = DL_UNKNOWN;
while (readDxfGroups(stream, creationInterface)) {}
return true;
}
return false;
}
/**
* @brief Reads a group couplet from a DXF file. Calls another function
* to process it.
*
* A group couplet consists of two lines that represent a single
* piece of data. An integer constant on the first line indicates
* the type of data. The value is on the next line.\n
*
* This function reads a couplet, determines the type of data, and
* passes the value to the the appropriate handler function of
* \p creationInterface.\n
*
* \p fp is advanced so that the next call to \p readDXFGroups() reads
* the next couplet in the file.
*
* @param fp Handle of input file
* @param creationInterface Handle of class which processes entities
* in the file
*
* @retval true If EOF not reached.
* @retval false If EOF reached.
*/
bool DL_Dxf::readDxfGroups(FILE *fp, DL_CreationInterface* creationInterface) {
static int line = 1;
// Read one group of the DXF file and strip the lines:
if (DL_Dxf::getStrippedLine(m_groupCodeTmp, DL_DXF_MAXLINE, fp) &&
DL_Dxf::getStrippedLine(m_groupValue, DL_DXF_MAXLINE, fp, false) ) {
m_groupCode = (unsigned int)toInt(m_groupCodeTmp);
//creationInterface->processCodeValuePair(groupCode, groupValue);
line+=2;
processDXFGroup(creationInterface, m_groupCode, m_groupValue);
}
return !feof(fp);
}
/**
* Same as above but for stringstreams.
*/
bool DL_Dxf::readDxfGroups(std::stringstream& stream,
DL_CreationInterface* creationInterface) {
static int line = 1;
// Read one group of the DXF file and chop the lines:
if (DL_Dxf::getStrippedLine(m_groupCodeTmp, DL_DXF_MAXLINE, stream) &&
DL_Dxf::getStrippedLine(m_groupValue, DL_DXF_MAXLINE, stream, false) ) {
m_groupCode = (unsigned int)toInt(m_groupCodeTmp);
line+=2;
processDXFGroup(creationInterface, m_groupCode, m_groupValue);
}
return !stream.eof();
}
/**
* @brief Reads line from file & strips whitespace at start and newline
* at end.
*
* @param s Output\n
* Pointer to character array that chopped line will be returned in.
* @param size Size of \p s. (Including space for NULL.)
* @param fp Input\n
* Handle of input file.
*
* @retval true if line could be read
* @retval false if \p fp is already at end of file
*
* @todo Change function to use safer FreeBSD strl* functions
* @todo Is it a problem if line is blank (i.e., newline only)?
* Then, when function returns, (s==NULL).
*/
bool DL_Dxf::getStrippedLine(std::string& s, unsigned int size, FILE *fp, bool stripSpace) {
if (!feof(fp)) {
// The whole line in the file. Includes space for NULL.
char* wholeLine = new char[size];
// Only the useful part of the line
char* line;
line = fgets(wholeLine, size, fp);
if (line!=NULL && line[0] != '\0') { // Evaluates to fgets() retval
// line == wholeLine at this point.
// Both guaranteed to be NULL terminated.
// Strip leading whitespace and trailing CR/LF.
stripWhiteSpace(&line, stripSpace);
s = line;
assert(size > s.length());
}
delete[] wholeLine; // Done with wholeLine
return true;
} else {
s = "";
return false;
}
}
/**
* Same as above but for stringstreams.
*/
bool DL_Dxf::getStrippedLine(std::string &s, unsigned int size,
std::stringstream& stream, bool stripSpace) {
if (!stream.eof()) {
// Only the useful part of the line
char* line = new char[size+1];
char* oriLine = line;
stream.getline(line, size);
stripWhiteSpace(&line, stripSpace);
s = line;
assert(size > s.length());
delete[] oriLine;
return true;
} else {
s[0] = '\0';
return false;
}
}
/**
* @brief Strips leading whitespace and trailing Carriage Return (CR)
* and Line Feed (LF) from NULL terminated string.
*
* @param s Input and output.
* NULL terminates string.
*
* @retval true if \p s is non-NULL
* @retval false if \p s is NULL
*/
bool DL_Dxf::stripWhiteSpace(char** s, bool stripSpace) {
// last non-NULL char:
int lastChar = strlen(*s) - 1;
// Is last character CR or LF?
while ( (lastChar >= 0) &&
(((*s)[lastChar] == 10) || ((*s)[lastChar] == 13) ||
(stripSpace && ((*s)[lastChar] == ' ' || ((*s)[lastChar] == '\t')))) ) {
(*s)[lastChar] = '\