Hi, I have a very big problem with dxf files. My custom entity consists of other primitive entities (lets say: lines, multitexts and circles). But types and quantities are variable. So one instance of my entity will consist of 2 lines and the other of line and circle.
I'm trying to save and load the info using dxf file. What I learnt so far is:
1. When you save embedded object, first you have to call: filer->writeEmbeddedObjectStart() and then you can call object's dxfOutFields
2. When you load embedded object, first you have to call: if(filler->atEmbeddedObjectStart())... and then you can call object's dxfInFields
3. You cannot have other values between two embedded objects! (why?)
When I'm trying to put some info between two embedded objects I get kdxfInvalid instead of actual value type. In my case this would be desireable because firts I would save info about object type (as int, ie. 1 for line, 2 for circle etc) and then the object. But it doesn't work. So I have to first save info about all object types and then objects.
After all objects are saved, I would like to have some more data, but the situation is the same. When I try to read data (after last object), I get kdxfInvalid instead of actual value type. Why is that?
Here is fragment of my code:
Acad::ErrorStatus ARXEntity::dxfOutFields(AcDbDxfFiler* filer) const { assertReadEnabled(); ErrorStatus es = AcDbEntity::dxfOutFields(filer); if(es != Acad::eOk) return es; filer->writeItem(AcDb::kDxfSubclass, L"ARXEntity"); //entities count filer->writeInt32(AcDb::kDxfInt32, m_entities->size()); //entities types for(Entities::iterator it = m_entities->begin(); it != m_entities->end(); ++it) { filer->writeInt32(AcDb::kDxfInt32, int(it->second)); } //entities for(Entities::iterator it = m_entities->begin(); it != m_entities->end(); ++it) { filer->writeEmbeddedObjectStart(); it->first->dxfOutFields(filer); } //grips filer->writeInt32(AcDb::kDxfInt32, m_gripPoints->size()); for(GripPoints::iterator it = m_gripPoints->begin(); it != m_gripPoints->end(); ++it) { filer->writePoint3d(AcDb::kDxfXCoord, *it); } return filer->filerStatus(); } Acad::ErrorStatus ARXEntity::dxfInFields(AcDbDxfFiler* filer) { assertWriteEnabled(); Acad::ErrorStatus es = AcDbEntity::dxfInFields(filer); if(es != Acad::eOk) return es; if(!filer->atSubclassData(L"ARXEntity")) return Acad::eBadDxfSequence; resbuf rb; //entities count es = filer->readResBuf(&rb); if(rb.restype != kDxfInt32) ThrowDXFException(kDxfInt32, rb.restype); int entCount = rb.resval.rlong; if((entCount) && (!m_entities)) m_entities = new Entities(); //entities types std::vector<claas::EntityType::Enum> eTypes; for(size_t i = 0; i < entCount; ++i) { es = filer->readResBuf(&rb); if(rb.restype != kDxfInt32) ThrowDXFException(kDxfInt32, rb.restype); eTypes.push_back(EntityType(rb.resval.rlong)); } AcDbEntity * ent; //entities for(size_t i = 0; i < entCount; ++i) { if(filer->atEmbeddedObjectStart()) { switch(eTypes[i]) { case EntityType::Circle: ent = new AcDbCircle; ((AcDbCircle *)ent)->dxfInFields(filer); break; case EntityType::Line: ent = new AcDbLine(); ((AcDbLine *)ent)->dxfInFields(filer); break; case EntityType::MultilineText: ent = new AcDbMText(); ((AcDbMText *) ent)->dxfInFields(filer); break; } else throw "No object start"; (*m_entities)[ent] = eTypes[i]; } //grips es = filer->readResBuf(&rb); if(rb.restype != kDxfInt32) ThrowDXFException(kDxfInt32, rb.restype); //here my rb.restype is -9999 instead of 90, why? int gripsLen = rb.resval.rlong; //some other code return filer->filerStatus(); }
So, what's wrong with this code?
There needs to be some marker group to indicate when one object's data ends and another object's data starts so that readResBuf() knows when to return a non-eOk value to indicate that an object's data has ended. These would be the group codes that indicate the start of xdata, or another object (group 0), or an embedded entity (101), or a different class's data (i.e. group 100). This is necessary because objects' dxfInFields() methods rely on readResBuf() returning non-eOk to tell when their data is done and it's time to return.
I've never tested it, but you should be able to use a group 100 as a separator between your embedded entity data and the data you want to write. Something like this:
In your dxfOutFields:
write your subclassdata marker
write your data
call writeEmbeddedObjectStart
call dxfOutFields on the embedded object
write a subclass data marker (k.e. AcZDb::kDxfSubclass)
write data you want
call write EmbeddedObjectStart
call dxfOutFields on next embedded object
write a subclass data marker
wirte data you want
repeat until done
In dxfInFields
call your base class's dxfInFields()
call atSubclassData() to get past your first subclass data marker
read your data until readResBuf() returns non-eOk
check atEmbeddedObjectStart and if so, then call dxfInFields on embedded object
call atSubclassData to get past the subclass data marker you wrote after the embedded object's data
read your data until readResBuf() returns non-eOk
check atEmbeddedObjectStart and if so, then call dxfInFields on embedded object
call atSubclassData to get past the subclass data marker you wrote after the embedded object's data
read your data until readResBuf() returns non-eOk.
repeat until done
When you write the subclassData markers, you can use any string you want. It can be the same for all of them, or different for each.