2 //-----------------------------------------------------------------------------
3 #include "gdcmDicomDir.h"
4 #include "gdcmDicomDirStudy.h"
5 #include "gdcmDicomDirSerie.h"
6 #include "gdcmDicomDirImage.h"
7 #include "gdcmDirList.h"
13 #include <sys/types.h>
17 //-----------------------------------------------------------------------------
18 // For full DICOMDIR description, see:
19 // PS 3.3-2003, pages 731-750
20 //-----------------------------------------------------------------------------
21 // Constructor / Destructor
23 * \ingroup gdcmDicomDir
24 * \brief Constructor : Parses recursively the directory and creates the DicomDir
25 * \ or uses an already built DICOMDIR, depending on 'parseDir' value
26 * @param Name name of the root directory (parseDir = true)
27 * name of the DICOMDIR (parseDir = false)
28 * @param parseDir - true if user passed an entry point
29 * and wants to explore recursively the directories
30 * - false if user passed an already built DICOMDIR file
32 * @param exception_on_error whether we want to throw an exception or not
34 gdcmDicomDir::gdcmDicomDir(const char *Name, bool parseDir,
35 bool exception_on_error):
36 gdcmParser(Name,exception_on_error,true) // true : enable SeQuences
38 // que l'on ai passe un root directory ou un DICOMDIR
39 // et quelle que soit la valeur de parseDir,
40 // on a lance gdcmParser
45 startMethodArgDelete= NULL;
46 progressMethodArgDelete=NULL;
47 endMethodArgDelete= NULL;
57 // gdcmParser already executed
58 // if user passed a root directory, sure we didn't get anything
60 if( GetListEntry().begin()==GetListEntry().end() )
62 // if parseDir == false, it should be tagged as an error
63 dbg.Verbose(0, "gdcmDicomDir::gdcmDicomDir : entry list empty");
65 if(strlen(Name)==1 && Name[0]=='.') { // user passed '.' as Name
66 // we get current directory name
67 char*dummy=(char*) malloc(1000); // TODO : check with Windoze
68 getcwd(dummy,(size_t)1000);
69 SetFileName(dummy); // will be converted into a string
70 free(dummy); // no longer needed
75 dbg.Verbose(0, "gdcmDicomDir::gdcmDicomDir : Parse directory and create the DicomDir");
81 CheckBoundaries(); // JPR
87 * \ingroup gdcmDicomDir
88 * \brief Constructor : creates an empty gdcmDicomDir
89 * @param exception_on_error whether we want to throw an exception or not
91 gdcmDicomDir::gdcmDicomDir(bool exception_on_error):
92 gdcmParser(exception_on_error)
97 startMethodArgDelete= NULL;
98 progressMethodArgDelete=NULL;
99 endMethodArgDelete= NULL;
106 std::string pathBidon = ""; // Sorry, NULL not allowed ...
107 SetElement(pathBidon, GDCM_DICOMDIR_META, NULL); // Set the META elements
109 AddDicomDirMetaToEnd(listEntries.begin(),--listEntries.end());
114 * \ingroup gdcmDicomDir
115 * \brief Canonical destructor
117 gdcmDicomDir::~gdcmDicomDir()
119 SetStartMethod(NULL);
120 SetProgressMethod(NULL);
126 for(ListDicomDirPatient::iterator cc=patients.begin();cc!=patients.end();++cc)
132 //-----------------------------------------------------------------------------
135 * \ingroup gdcmDicomDir
136 * \brief Canonical Printer
138 void gdcmDicomDir::Print(std::ostream &os)
142 metaElems->SetPrintLevel(printLevel);
143 metaElems->Print(os);
145 for(ListDicomDirPatient::iterator cc=patients.begin();cc!=patients.end();++cc)
147 (*cc)->SetPrintLevel(printLevel);
152 //-----------------------------------------------------------------------------
155 * \ingroup gdcmDicomDir
156 * \brief This predicate, based on hopefully reasonable heuristics,
157 * decides whether or not the current header was properly parsed
158 * and contains the mandatory information for being considered as
159 * a well formed and usable DicomDir.
160 * @return true when gdcmParser is the one of a reasonable DicomDir,
163 bool gdcmDicomDir::IsReadable(void)
165 if(!gdcmParser::IsReadable())
169 if(patients.size()<=0)
176 * \ingroup gdcmDicomDir
177 * \brief fills the whole structure, starting from a root Directory
179 void gdcmDicomDir::ParseDirectory(void)
181 CreateDicomDirChainedList(GetFileName());
186 * \ingroup gdcmDicomDir
187 * \brief Set the start method to call when the parsing of the directory starts
188 * @param method Method to call
189 * @param arg Argument to pass to the method
190 * \warning In python : the arg parameter isn't considered
192 void gdcmDicomDir::SetStartMethod(gdcmMethod *method,void *arg,gdcmMethod *argDelete)
194 if((startArg)&&(startMethodArgDelete))
195 startMethodArgDelete(startArg);
199 startMethodArgDelete=argDelete;
203 * \ingroup gdcmDicomDir
204 * \brief Set the method to delete the argument
205 * The argument is destroyed when the method is changed or when the
207 * @param method Method to call to delete the argument
209 void gdcmDicomDir::SetStartMethodArgDelete(gdcmMethod *method)
211 startMethodArgDelete=method;
215 * \ingroup gdcmDicomDir
216 * \brief Set the progress method to call when the parsing of the directory progress
217 * @param method Method to call
218 * @param arg Argument to pass to the method
219 * \warning In python : the arg parameter isn't considered
221 void gdcmDicomDir::SetProgressMethod(gdcmMethod *method,void *arg,gdcmMethod *argDelete)
223 if((progressArg)&&(progressMethodArgDelete))
224 progressMethodArgDelete(progressArg);
226 progressMethod=method;
228 progressMethodArgDelete=argDelete;
232 * \ingroup gdcmDicomDir
233 * \brief Set the method to delete the argument
234 * The argument is destroyed when the method is changed or when the
236 * @param method Method to call to delete the argument
238 void gdcmDicomDir::SetProgressMethodArgDelete(gdcmMethod *method)
240 progressMethodArgDelete=method;
244 * \ingroup gdcmDicomDir
245 * \brief Set the end method to call when the parsing of the directory ends
246 * @param method Method to call
247 * @param arg Argument to pass to the method
248 * \warning In python : the arg parameter isn't considered
250 void gdcmDicomDir::SetEndMethod(gdcmMethod *method,void *arg,gdcmMethod *argDelete)
252 if((endArg)&&(endMethodArgDelete))
253 endMethodArgDelete(endArg);
257 endMethodArgDelete=argDelete;
261 * \ingroup gdcmDicomDir
262 * \brief Set the method to delete the argument
263 * The argument is destroyed when the method is changed or when the class
265 * @param method Method to call to delete the argument
267 void gdcmDicomDir::SetEndMethodArgDelete(gdcmMethod *method)
269 endMethodArgDelete=method;
273 * \ingroup gdcmDicomDir
274 * \brief writes on disc a DICOMDIR
275 * \ warning does NOT add the missing elements in the header :
276 * it's up to the user doing it !
277 * \todo : to be re-written using the DICOMDIR tree-like structure
278 * *not* the chained list
279 * (does NOT exist if the DICOMDIR is user-forged !)
280 * @param fileName file to be written to
281 * @return false only when fail to open
284 bool gdcmDicomDir::Write(std::string fileName)
288 fp1=fopen(fileName.c_str(),"wb");
291 printf("Failed to open(write) File [%s] \n",fileName.c_str());
296 filePreamble=(char*)calloc(128,1);
297 fwrite(filePreamble,128,1,fp1);
298 fwrite("DICM",4,1,fp1);
300 //UpdateDirectoryRecordSequenceLength(); // a reecrire en utilisant
301 // la structure arborescente JPR
302 WriteDicomDirEntries(fp1);
309 * \ingroup gdcmParser
310 * \brief writes on disc according to the DICOMDIR format
311 * using the tree-like structure
312 * @param _fp already open file pointer
315 void gdcmDicomDir::WriteDicomDirEntries(FILE *_fp)
317 // TODO (?) tester les echecs en ecriture
318 // (apres chaque fwrite, dans le WriteEntry)
320 gdcmDicomDirMeta *ptrMeta;
321 ListDicomDirPatient::iterator itPatient;
322 ListDicomDirStudy::iterator itStudy;
323 ListDicomDirSerie::iterator itSerie;
324 ListDicomDirImage::iterator itImage;
327 ptrMeta= GetDicomDirMeta();
328 for(i=ptrMeta->debut();i!=ptrMeta->fin();++i) {
329 WriteEntry(*i,_fp,DICOMDIR);
332 itPatient = GetDicomDirPatients().begin();
333 while ( itPatient != GetDicomDirPatients().end() ) {
334 for(i=(*itPatient)->debut();i!=(*itPatient)->fin();++i) {
335 WriteEntry(*i,_fp,DICOMDIR);
337 itStudy = ((*itPatient)->GetDicomDirStudies()).begin();
338 while (itStudy != (*itPatient)->GetDicomDirStudies().end() ) {
339 for(i=(*itStudy)->debut();i!=(*itStudy)->fin();++i) {
340 WriteEntry(*i,_fp,DICOMDIR);
342 itSerie = ((*itStudy)->GetDicomDirSeries()).begin();
343 while (itSerie != (*itStudy)->GetDicomDirSeries().end() ) {
344 for(i=(*itSerie)->debut();i!=(*itSerie)->fin();++i) {
345 WriteEntry(*i,_fp,DICOMDIR);
347 itImage = ((*itSerie)->GetDicomDirImages()).begin();
348 while (itImage != (*itSerie)->GetDicomDirImages().end() ) {
349 for(i=(*itImage)->debut();i!=(*itImage)->fin();++i) {
350 WriteEntry(*i,_fp,DICOMDIR);
362 //-----------------------------------------------------------------------------
366 * \ingroup gdcmDicomDir
367 * \brief create a gdcmHeader-like chained list from a root Directory
368 * @param path entry point of the tree-like structure
370 void gdcmDicomDir::CreateDicomDirChainedList(std::string path)
374 gdcmDirList fileList(path,1); // gets recursively the file list
375 unsigned int count=0;
382 for(gdcmDirList::iterator it=fileList.begin();
386 progress=(float)(count+1)/(float)fileList.size();
387 CallProgressMethod();
391 header=new gdcmHeader(it->c_str());
392 if(header->IsReadable())
393 list.push_back(header); // adds the file header to the chained list
399 // sorts Patient/Study/Serie/
400 std::sort(list.begin(),list.end(),gdcmDicomDir::HeaderLessThan);
402 std::string tmp=fileList.GetDirName();
404 //for each Header of the chained list, add/update the Patient/Study/Serie/Image info
405 SetElements(tmp,list);
412 void gdcmDicomDir::CheckBoundaries()
415 cout <<"entree ds CheckBoundaries " <<endl;
416 ListDicomDirPatient::iterator itPatient;
417 ListDicomDirStudy::iterator itStudy;
418 ListDicomDirSerie::iterator itSerie;
419 ListDicomDirImage::iterator itImage;
420 ListTag::iterator i,j;
422 GetDicomDirMeta()->ResetBoundaries(0);
424 itPatient = GetDicomDirPatients().begin();
425 while ( itPatient != GetDicomDirPatients().end() ) {
426 (*itPatient)->ResetBoundaries(1);
427 itStudy = ((*itPatient)->GetDicomDirStudies()).begin();
428 while (itStudy != (*itPatient)->GetDicomDirStudies().end() ) {
429 (*itStudy)->ResetBoundaries(1);
430 itSerie = ((*itStudy)->GetDicomDirSeries()).begin();
431 while (itSerie != (*itStudy)->GetDicomDirSeries().end() ) {
432 (*itSerie)->ResetBoundaries(1);
433 itImage = ((*itSerie)->GetDicomDirImages()).begin();
434 while (itImage != (*itSerie)->GetDicomDirImages().end() ) {
435 (*itImage)->ResetBoundaries(1);
448 * \ingroup gdcmDicomDir
449 * \brief adds a new Patient to a partially created DICOMDIR
451 gdcmDicomDirPatient * gdcmDicomDir::NewPatient(void) {
452 std::list<gdcmElement> elemList;
453 std::list<gdcmElement>::iterator it;
455 gdcmDictEntry *dictEntry;
456 gdcmHeaderEntry *entry;
458 elemList=gdcmGlobal::GetDicomDirElements()->GetDicomDirPatientElements();
459 std::list<gdcmHeaderEntry *>::iterator debInsertion, finInsertion, i,j;
461 debInsertion = metaElems->fin();
463 finInsertion=debInsertion;
465 // for all the DicomDirPatient Elements
466 for(it=elemList.begin();it!=elemList.end();++it)
470 dictEntry=GetPubDict()->GetDictEntryByNumber(tmpGr,tmpEl);
471 entry=new gdcmHeaderEntry(dictEntry);
472 entry->SetOffset(0); // just to avoid missprinting
473 entry->SetValue(it->value);
475 if(dictEntry->GetGroup()==0xfffe)
477 entry->SetLength(entry->GetValue().length());
479 else if( (dictEntry->GetVR()=="UL") || (dictEntry->GetVR()=="SL") )
483 else if( (dictEntry->GetVR()=="US") || (dictEntry->GetVR()=="SS") )
487 else if(dictEntry->GetVR()=="SQ")
489 entry->SetLength(0xffffffff);
493 entry->SetLength(entry->GetValue().length());
496 tagHT.insert( PairHT(entry->GetKey(),entry) ); // add in the (multimap) H Table
497 listEntries.insert(debInsertion ,entry); // en tete de liste des Patients
504 gdcmDicomDirPatient *p = new gdcmDicomDirPatient(i, --debInsertion,
505 &tagHT, &listEntries);
506 patients.push_front(p);
511 * \ingroup gdcmDicomDir
512 * \brief CallStartMethod
514 void gdcmDicomDir::CallStartMethod(void)
519 startMethod(startArg);
522 * \ingroup gdcmDicomDir
523 * \brief CallProgressMethod
525 void gdcmDicomDir::CallProgressMethod(void)
528 progressMethod(progressArg);
531 * \ingroup gdcmDicomDir
532 * \brief CallEndMethod
534 void gdcmDicomDir::CallEndMethod(void)
541 //-----------------------------------------------------------------------------
544 * \ingroup gdcmDicomDir
545 * \brief create a 'gdcmDicomDir' from a DICOMDIR gdcmHeader
547 void gdcmDicomDir::CreateDicomDir()
549 // The list is parsed.
550 // When a DicomDir tag ("PATIENT", "STUDY", "SERIE", "IMAGE") is found :
551 // 1 - we save the beginning iterator
552 // 2 - we continue to parse
553 // 3 - we find an other tag
554 // + we create the object for the precedent tag
557 gdcmDicomDirType type=gdcmDicomDir::GDCM_DICOMDIR_META;
558 ListTag::iterator begin;
559 ListTag::iterator end, end2;
561 begin=listEntries.begin();
563 for(ListTag::iterator i=end;i !=listEntries.end();++i)
565 std::string v=(*i)->GetValue();
569 AddObjectToEnd(type,begin,--end2);
570 type=gdcmDicomDir::GDCM_DICOMDIR_PATIENT;
577 AddObjectToEnd(type,begin,--end2);
578 type=gdcmDicomDir::GDCM_DICOMDIR_STUDY;
585 AddObjectToEnd(type,begin,--end2);
586 type=gdcmDicomDir::GDCM_DICOMDIR_SERIE;
593 AddObjectToEnd(type,begin,--end2);
594 type=gdcmDicomDir::GDCM_DICOMDIR_IMAGE;
599 end=end2=listEntries.end();
601 AddObjectToEnd(type,begin,--end2);
604 * \ingroup gdcmDicomDir
605 * \brief AddObjectToEnd
610 void gdcmDicomDir::AddObjectToEnd(gdcmDicomDirType type,ListTag::iterator begin,ListTag::iterator end)
617 case gdcmDicomDir::GDCM_DICOMDIR_META:
618 AddDicomDirMetaToEnd(begin,end);
620 case gdcmDicomDir::GDCM_DICOMDIR_PATIENT:
621 AddDicomDirPatientToEnd(begin,end);
623 case gdcmDicomDir::GDCM_DICOMDIR_STUDY:
624 AddDicomDirStudyToEnd(begin,end);
626 case gdcmDicomDir::GDCM_DICOMDIR_SERIE:
627 AddDicomDirSerieToEnd(begin,end);
629 case gdcmDicomDir::GDCM_DICOMDIR_IMAGE:
630 AddDicomDirImageToEnd(begin,end);
636 * \ingroup gdcmDicomDir
637 * \brief Well ... Not realy to end, there is only one occurence
641 void gdcmDicomDir::AddDicomDirMetaToEnd(ListTag::iterator begin,ListTag::iterator end)
645 metaElems = new gdcmDicomDirMeta(begin,end,&tagHT,&listEntries);
649 * \ingroup gdcmDicomDir
650 * \brief AddDicomDirPatientToEnd
654 void gdcmDicomDir::AddDicomDirPatientToEnd(ListTag::iterator begin,ListTag::iterator end)
656 patients.push_back(new gdcmDicomDirPatient(begin,end,&tagHT, &listEntries));
660 * \ingroup gdcmDicomDir
661 * \brief AddDicomDirStudyToEnd
665 void gdcmDicomDir::AddDicomDirStudyToEnd(ListTag::iterator begin,ListTag::iterator end)
667 if(patients.size()>0)
669 ListDicomDirPatient::iterator itp=patients.end();
671 (*itp)->AddDicomDirStudy(new gdcmDicomDirStudy(begin,end,&tagHT, &listEntries));
675 * \ingroup gdcmDicomDir
676 * \brief AddDicomDirSerieToEnd
680 void gdcmDicomDir::AddDicomDirSerieToEnd(ListTag::iterator begin,ListTag::iterator end)
682 if(patients.size()>0)
684 ListDicomDirPatient::iterator itp=patients.end();
687 if((*itp)->GetDicomDirStudies().size()>0)
689 ListDicomDirStudy::iterator itst=(*itp)->GetDicomDirStudies().end();
691 (*itst)->AddDicomDirSerie(new gdcmDicomDirSerie(begin,end,&tagHT, &listEntries));
697 * \ingroup gdcmDicomDir
698 * \brief AddDicomDirImageToEnd
702 void gdcmDicomDir::AddDicomDirImageToEnd(ListTag::iterator begin,ListTag::iterator end)
704 if(patients.size()>0)
706 ListDicomDirPatient::iterator itp=patients.end();
709 if((*itp)->GetDicomDirStudies().size()>0)
711 ListDicomDirStudy::iterator itst=(*itp)->GetDicomDirStudies().end();
714 if((*itst)->GetDicomDirSeries().size()>0)
716 ListDicomDirSerie::iterator its=(*itst)->GetDicomDirSeries().end();
718 (*its)->AddDicomDirImage(new gdcmDicomDirImage(begin,end,&tagHT, &listEntries));
725 * \ingroup gdcmDicomDir
726 * \brief for each Header of the chained list, add/update the Patient/Study/Serie/Image info
727 * @param path path of the root directory
728 * @param list chained list of Headers
730 void gdcmDicomDir::SetElements(std::string &path, ListHeader &list)
732 std::string patPrevName="", patPrevID="";
733 std::string studPrevInstanceUID="", studPrevID="";
734 std::string serPrevInstanceUID="", serPrevID="";
736 std::string patCurName, patCurID;
737 std::string studCurInstanceUID, studCurID;
738 std::string serCurInstanceUID, serCurID;
740 SetElement(path,GDCM_DICOMDIR_META,NULL);
742 ListTag::iterator debPat=listEntries.begin();
743 for(ListHeader::iterator it=list.begin();it!=list.end();++it)
745 // get the current file characteristics
746 patCurName= (*it)->GetEntryByNumber(0x0010,0x0010);
747 patCurID= (*it)->GetEntryByNumber(0x0010,0x0011);
748 studCurInstanceUID=(*it)->GetEntryByNumber(0x0020,0x000d);
749 studCurID= (*it)->GetEntryByNumber(0x0020,0x0010);
750 serCurInstanceUID= (*it)->GetEntryByNumber(0x0020,0x000e);
751 serCurID= (*it)->GetEntryByNumber(0x0020,0x0011);
753 if(patCurName!=patPrevName || patCurID!=patPrevID)
754 SetElement(path,GDCM_DICOMDIR_PATIENT,*it);
756 // if new Study Deal with 'STUDY' Elements
757 if(studCurInstanceUID!=studPrevInstanceUID || studCurID!=studPrevID)
758 SetElement(path,GDCM_DICOMDIR_STUDY,*it);
760 // if new Serie Deal with 'SERIE' Elements
761 if(serCurInstanceUID!=serPrevInstanceUID || serCurID!=serPrevID)
762 SetElement(path,GDCM_DICOMDIR_SERIE,*it);
764 // Always Deal with 'IMAGE' Elements
765 SetElement(path,GDCM_DICOMDIR_IMAGE,*it);
767 patPrevName= patCurName;
769 studPrevInstanceUID=studCurInstanceUID;
770 studPrevID= studCurID;
771 serPrevInstanceUID= serCurInstanceUID;
777 * \ingroup gdcmDicomDir
778 * \brief adds to the HTable and at the end of the Chained List
779 * the gdcmEntries (Dicom Elements) corresponding to the given type
780 * @param path full path file name(only used when type = GDCM_DICOMDIR_IMAGE
781 * @param type gdcmObject type to create (GDCM_DICOMDIR_PATIENT, GDCM_DICOMDIR_STUDY, GDCM_DICOMDIR_SERIE ...)
782 * @param header gdcmHeader of the current file
784 void gdcmDicomDir::SetElement(std::string &path,gdcmDicomDirType type,gdcmHeader *header)
786 std::list<gdcmElement> elemList;
787 std::list<gdcmElement>::iterator it;
788 guint16 tmpGr, tmpEl;
789 gdcmDictEntry *dictEntry;
790 gdcmHeaderEntry *entry;
795 case GDCM_DICOMDIR_PATIENT:
796 elemList=gdcmGlobal::GetDicomDirElements()->GetDicomDirPatientElements();
798 case GDCM_DICOMDIR_STUDY:
799 elemList=gdcmGlobal::GetDicomDirElements()->GetDicomDirStudyElements();
801 case GDCM_DICOMDIR_SERIE:
802 elemList=gdcmGlobal::GetDicomDirElements()->GetDicomDirSerieElements();
804 case GDCM_DICOMDIR_IMAGE:
805 elemList=gdcmGlobal::GetDicomDirElements()->GetDicomDirImageElements();
807 case GDCM_DICOMDIR_META:
808 elemList=gdcmGlobal::GetDicomDirElements()->GetDicomDirMetaElements();
814 for(it=elemList.begin();it!=elemList.end();++it)
818 dictEntry=GetPubDict()->GetDictEntryByNumber(tmpGr,tmpEl);
819 entry=new gdcmHeaderEntry(dictEntry);
820 entry->SetOffset(0); // just to avoid missprinting
823 val=header->GetEntryByNumber(tmpGr,tmpEl);
827 if(val==GDCM_UNFOUND)
829 if((tmpGr==0x0004) &&(tmpEl==0x1130) ) // File-set ID
831 // force to the *end* File Name
834 else if( (tmpGr==0x0004) && (tmpEl==0x1500) ) // Only used for image
836 if(header->GetFileName().substr(0,path.length())!=path)
838 dbg.Verbose(0, "gdcmDicomDir::SetElement : the base path of file name is incorrect");
839 val=header->GetFileName();
842 val=&(header->GetFileName().c_str()[path.length()]);
852 if (header->GetEntryLengthByNumber(tmpGr,tmpEl)== 0)
856 entry->SetValue(val);
860 if(dictEntry->GetGroup()==0xfffe)
862 entry->SetLength(entry->GetValue().length());
864 else if( (dictEntry->GetVR()=="UL") || (dictEntry->GetVR()=="SL") )
868 else if( (dictEntry->GetVR()=="US") || (dictEntry->GetVR()=="SS") )
872 else if(dictEntry->GetVR()=="SQ")
874 entry->SetLength(0xffffffff);
878 entry->SetLength(entry->GetValue().length());
881 //AddHeaderEntry(entry); // both in H Table and in chained list
882 tagHT.insert( PairHT( entry->GetKey(),entry) );
883 listEntries.push_back(entry);
884 //wasUpdated = 1; // is private
888 bool gdcmDicomDir::HeaderLessThan(gdcmHeader *header1,gdcmHeader *header2)
890 return(*header1<*header2);
895 void gdcmDicomDir::UpdateDirectoryRecordSequenceLength() {
897 ListTag::iterator it;
900 for(it=listEntries.begin();it!=listEntries.end();++it) {
901 gr = (*it)->GetGroup();
902 el = (*it)->GetElement();
905 if ( (vr == "OB") || (vr == "OW") || (vr == "SQ") ) {
906 offset += 4; // explicit VR AND OB, OW, SQ : 4 more bytes
908 offset += 2 + 2 + 4 + (*it)->GetLength();
910 offset += 4; // delimiters don't have a value.
913 bool res=SetEntryLengthByNumber(offset, 0x0004, 0x1220); // Hope there is no dupps.
917 //-----------------------------------------------------------------------------