Skip to content

Commit cb6884b

Browse files
committed
Encode characters outside the valid range for IFC SPF files and escape backslashes when writing IFC files
1 parent da616ff commit cb6884b

File tree

5 files changed

+88
-8
lines changed

5 files changed

+88
-8
lines changed

src/ifcparse/IfcCharacterDecoder.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
#define CLEAR_HEX(C) (C &= ~(HEX(1)&HEX(2)&HEX(3)&HEX(4)&HEX(5)&HEX(6)&HEX(7)&HEX(8)))
6868

6969
using namespace IfcParse;
70+
using namespace IfcWrite;
7071

7172
void IfcCharacterDecoder::addChar(std::stringstream& s,const UChar32& ch) {
7273
#ifdef HAVE_ICU
@@ -296,3 +297,61 @@ std::string IfcCharacterDecoder::compatibility_charset = "";
296297
#else
297298
char IfcCharacterDecoder::substitution_character = '_';
298299
#endif
300+
301+
302+
IfcCharacterEncoder::IfcCharacterEncoder(const std::string& input) {
303+
if ( !converter) converter = ucnv_open("utf-8", &status);
304+
str = input;
305+
}
306+
307+
IfcCharacterEncoder::~IfcCharacterEncoder() {
308+
if ( !converter) ucnv_close(converter);
309+
converter = 0;
310+
}
311+
312+
IfcCharacterEncoder::operator std::string() {
313+
#ifdef HAVE_ICU
314+
// Either 2 or 4 to uses \X2 or \X4 respectively.
315+
// Currently hardcoded to 4, but \X2 might be
316+
// sufficient for nearly all purposes.
317+
const int num_bytes = 4;
318+
const std::string num_bytes_str = std::string(1,num_bytes + 0x30);
319+
320+
std::ostringstream oss;
321+
UChar32 ch;
322+
323+
const char* source = str.c_str();
324+
const char* limit = &*str.end();
325+
326+
bool in_extended = false;
327+
328+
while(source < limit) {
329+
ch = ucnv_getNextUChar(converter, &source, limit, &status);
330+
const bool within_spf_range = ch >= 0x20 && ch <= 0x7e;
331+
if ( in_extended && within_spf_range ) {
332+
oss << "\\X0\\";
333+
} else if ( !in_extended && !within_spf_range ) {
334+
oss << "\\X" << num_bytes_str << "\\";
335+
}
336+
if ( within_spf_range ) {
337+
oss.put(ch);
338+
if ( ch == '\\' ) oss.put(ch);
339+
} else {
340+
oss << std::hex << std::setw(num_bytes*2) << std::uppercase << std::setfill('0') << (int) ch;
341+
}
342+
in_extended = !within_spf_range;
343+
}
344+
345+
if ( in_extended ) oss << "\\X0\\";
346+
347+
return oss.str();
348+
#else
349+
return str;
350+
#endif
351+
}
352+
353+
354+
#ifdef HAVE_ICU
355+
UErrorCode IfcCharacterEncoder::status = U_ZERO_ERROR;
356+
UConverter* IfcCharacterEncoder::converter = 0;
357+
#endif

src/ifcparse/IfcCharacterDecoder.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,21 @@ namespace IfcParse {
7373

7474
}
7575

76+
namespace IfcWrite {
77+
78+
class IfcCharacterEncoder {
79+
private:
80+
#ifdef HAVE_ICU
81+
static UErrorCode status;
82+
static UConverter* converter;
83+
std::string str;
84+
#endif
85+
public:
86+
IfcCharacterEncoder(const std::string& input);
87+
~IfcCharacterEncoder();
88+
operator std::string();
89+
};
90+
91+
}
92+
7693
#endif

src/ifcparse/IfcParse.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -468,7 +468,7 @@ TokenArgument::operator IfcUtil::IfcSchemaEntity() const { return token.first->f
468468
TokenArgument::operator IfcEntities() const { throw IfcException("Argument is not a list of entities"); }
469469
unsigned int TokenArgument::Size() const { return 1; }
470470
ArgumentPtr TokenArgument::operator [] (unsigned int i) const { throw IfcException("Argument is not a list of arguments"); }
471-
std::string TokenArgument::toString(bool upper) const { return TokenFunc::toString(token); }
471+
std::string TokenArgument::toString(bool upper) const { if ( upper && TokenFunc::isString(token) ) return IfcWrite::IfcCharacterEncoder(TokenFunc::toString(token)); else return TokenFunc::toString(token); }
472472
bool TokenArgument::isNull() const { return TokenFunc::isOperator(token,'$'); }
473473
//
474474
// Functions for casting the EntityArgument to other types
@@ -488,10 +488,11 @@ ArgumentPtr EntityArgument::operator [] (unsigned int i) const { throw IfcExcept
488488
std::string EntityArgument::toString(bool upper) const {
489489
ArgumentPtr arg = entity->wrappedValue();
490490
IfcParse::TokenArgument* token_arg = dynamic_cast<IfcParse::TokenArgument*>(arg);
491-
std::string token_string = ( token_arg ) ? TokenFunc::toString(token_arg->token) : "";
491+
std::string token_string = ( token_arg ) ? TokenFunc::toString(token_arg->token) : std::string();
492492
std::string dt = Ifc2x3::Type::ToString(entity->type());
493493
if ( upper ) {
494494
for (std::string::iterator p = dt.begin(); p != dt.end(); ++p ) *p = toupper(*p);
495+
token_string = IfcWrite::IfcCharacterEncoder(token_string);
495496
}
496497
return dt + "(" + token_string + ")";
497498
}

src/ifcparse/IfcWrite.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "IfcParse.h"
2121
#include "IfcWrite.h"
2222
#include "IfcWritableEntity.h"
23+
#include "IfcCharacterDecoder.h"
2324

2425
using namespace IfcWrite;
2526

@@ -284,10 +285,12 @@ std::string IfcWriteIntegralArgument::toString(bool upper) const {
284285
case Argument_DOUBLE:
285286
ss << *(double*) data;
286287
break;
287-
case Argument_STRING:
288-
ss << '\'' << *(std::string*) data << '\'';
289-
break;
290-
case Argument_VECTOR_INT:
288+
case Argument_STRING: {
289+
std::string d = *(std::string*) data;
290+
if ( upper ) d = IfcCharacterEncoder(d);
291+
ss << '\'' << d << '\'';
292+
break;
293+
} case Argument_VECTOR_INT:
291294
ss << "(";
292295
{const std::vector<int>& v = *(std::vector<int>*) data;
293296
for ( std::vector<int>::const_iterator it = v.begin(); it != v.end(); ++ it ) {
@@ -361,7 +364,7 @@ IfcSelectHelper::IfcSelectHelper(int v, Ifc2x3::Type::Enum t) {
361364
IfcWriteArgument* a = new IfcWriteIntegralArgument(v);
362365
this->entity = new IfcSelectHelperEntity(t,a);
363366
}
364-
IfcSelectHelper::IfcSelectHelper(float v, Ifc2x3::Type::Enum t) {
367+
IfcSelectHelper::IfcSelectHelper(double v, Ifc2x3::Type::Enum t) {
365368
IfcWriteArgument* a = new IfcWriteIntegralArgument(v);
366369
this->entity = new IfcSelectHelperEntity(t,a);
367370
}

src/ifcparse/IfcWrite.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ namespace IfcWrite {
170170
IfcSelectHelper(const std::string& v, Ifc2x3::Type::Enum t=Ifc2x3::Type::IfcText);
171171
IfcSelectHelper(const char* const v, Ifc2x3::Type::Enum t=Ifc2x3::Type::IfcText);
172172
IfcSelectHelper(int v, Ifc2x3::Type::Enum t=Ifc2x3::Type::IfcInteger);
173-
IfcSelectHelper(float v, Ifc2x3::Type::Enum t=Ifc2x3::Type::IfcReal);
173+
IfcSelectHelper(double v, Ifc2x3::Type::Enum t=Ifc2x3::Type::IfcReal);
174174
IfcSelectHelper(bool v, Ifc2x3::Type::Enum t=Ifc2x3::Type::IfcBoolean);
175175
bool is(Ifc2x3::Type::Enum t) const;
176176
Ifc2x3::Type::Enum type() const;

0 commit comments

Comments
 (0)