]> Creatis software - gdcm.git/blob - src/gdcmHeader.cxx
* src/gdcmHeader.cxx :
[gdcm.git] / src / gdcmHeader.cxx
1 #include "gdcmlib.h"
2 extern "C" {
3 #include "glib.h"
4 }
5 #include <stdio.h>
6 // For nthos:
7 #ifdef _MSC_VER
8 #include <winsock.h>
9 #else
10 #include <netinet/in.h>
11 #endif
12 #include <map>
13 #include <sstream>
14 #include "gdcmUtil.h"
15
16 #define HEADER_LENGHT_TO_READ 256 // on ne lit plus que le debut
17
18 //FIXME: this looks dirty to me...
19 #define str2num(str, typeNum) *((typeNum *)(str))
20
21 VRHT * gdcmHeader::dicom_vr = (VRHT*)0;
22 gdcmDictSet* gdcmHeader::Dicts = new gdcmDictSet();
23
24 void gdcmHeader::Initialise(void) {
25         if (!gdcmHeader::dicom_vr)
26                 InitVRDict();
27         PixelPosition = (size_t)0;
28         PixelsTrouves = false;
29         RefPubDict = gdcmHeader::Dicts->GetDefaultPublicDict();
30         RefShaDict = (gdcmDict*)0;
31 }
32
33 gdcmHeader::gdcmHeader (char* InFilename) {
34         filename = InFilename;
35         Initialise();
36         fp=fopen(InFilename,"rw");
37         dbg.Error(!fp, "gdcmHeader::gdcmHeader cannot open file", InFilename);
38         BuildHeader();
39         fclose(fp);
40 }
41
42 gdcmHeader::~gdcmHeader (void) {
43         return;
44 }
45
46 void gdcmHeader::InitVRDict (void) {
47         if (dicom_vr) {
48                 dbg.Verbose(0, "gdcmHeader::InitVRDict:", "VR dictionary allready set");
49                 return;
50         }
51         VRHT *vr = new VRHT;
52         (*vr)["AE"] = "Application Entity";       // 16 car max
53         (*vr)["AS"] = "Age String";               // 4 car fixe
54         (*vr)["AT"] = "Attribute Tag";            // 2 unsigned short int
55         (*vr)["CS"] = "Code String";              // 16 car max
56         (*vr)["DA"] = "Date";                     // 8 car fixe
57         (*vr)["DS"] = "Decimal String";           // Decimal codé Binaire 16 max
58         (*vr)["DT"] = "Date Time";                // 26 car max
59         (*vr)["FL"] = "Floating Point Single";    // 4 octets IEEE 754:1985
60         (*vr)["FD"] = "Floating Point Double";    // 8 octets IEEE 754:1985
61         (*vr)["IS"] = "Integer String";           // en format externe 12 max
62         (*vr)["LO"] = "Long String";              // 64 octets max
63         (*vr)["LT"] = "Long Text";                // 10240 max
64         (*vr)["OB"] = "Other Byte String";
65         (*vr)["OW"] = "Other Word String";
66         (*vr)["PN"] = "Person Name";
67         (*vr)["SH"] = "Short String";             // 16 car max
68         (*vr)["SL"] = "Signed Long";
69         (*vr)["SQ"] = "Sequence of Items";        // Not Applicable
70         (*vr)["SS"] = "Signed Short";             // 2 octets
71         (*vr)["ST"] = "Short Text";               // 1024 car max
72         (*vr)["TM"] = "Time";                     // 16 car max
73         (*vr)["UI"] = "Unique Identifier";        // 64 car max
74         (*vr)["UN"] = "Unknown";
75         (*vr)["UT"] = "Unlimited Text";           //  2 puissance 32 -1 car max
76         (*vr)["UL"] = "Unsigned Long ";           // 4 octets fixe
77         (*vr)["US"] = "Unsigned Short ";          // 2 octets fixe
78    dicom_vr = vr;       
79 }
80
81 /**
82  * \ingroup gdcmHeader
83  * \brief   La seule maniere sure que l'on aie pour determiner 
84  *          si on est en   LITTLE_ENDIAN,       BIG-ENDIAN, 
85  *          BAD-LITTLE-ENDIAN, BAD-BIG-ENDIAN
86  *          est de trouver l'element qui donne la longueur d'un 'GROUP'
87  *          (on sait que la longueur de cet element vaut 0x00000004)
88  *          et de regarder comment cette longueur est codee en memoire  
89  *          
90  *          Le probleme vient de ce que parfois, il n'y en a pas ...
91  *          
92  *          On fait alors le pari qu'on a a faire a du LITTLE_ENDIAN propre.
93  *          (Ce qui est la norme -pas respectee- depuis ACR-NEMA)
94  *          Si ce n'est pas le cas, on ne peut rien faire.
95  *
96  *          (il faudrait avoir des fonctions auxquelles 
97  *          on passe le code Swap en parametre, pour faire des essais 'manuels')
98  */
99 void gdcmHeader::CheckSwap()
100 {
101         guint32  s;
102         guint32  x=4;  // x : pour ntohs
103         bool net2host; // true when HostByteOrder is the same as NetworkByteOrder
104          
105         int lgrLue;
106         char * entCur;
107         char deb[HEADER_LENGHT_TO_READ];
108          
109         // First, compare HostByteOrder and NetworkByteOrder in order to
110         // determine if we shall need to swap bytes (i.e. the Endian type).
111         if (x==ntohs(x))
112                 net2host = true;
113         else
114                 net2host = false;
115         
116         // The easiest case is the one of a DICOM header, since it possesses a
117         // file preamble where it suffice to look for the sting "DICM".
118         lgrLue = fread(deb, 1, HEADER_LENGHT_TO_READ, fp);
119         
120         entCur = deb + 128;
121         if(memcmp(entCur, "DICM", (size_t)4) == 0) {
122                 filetype = TrueDicom;
123                 dbg.Verbose(0, "gdcmHeader::CheckSwap:", "looks like DICOM Version3");
124         } else {
125                 filetype = Unknown;
126                 dbg.Verbose(0, "gdcmHeader::CheckSwap:", "not a DICOM Version3 file");
127         }
128
129         if(filetype == TrueDicom) {
130                 // Next, determine the value representation (VR). Let's skip to the
131                 // first element (0002, 0000) and check there if we find "UL", in
132                 // which case we (almost) know it is explicit VR.
133                 // WARNING: if it happens to be implicit VR then what we will read
134                 // is the length of the group. If this ascii representation of this
135                 // length happens to be "UL" then we shall believe it is explicit VR.
136                 // FIXME: in order to fix the above warning, we could read the next
137                 // element value (or a couple of elements values) in order to make
138                 // sure we are not commiting a big mistake.
139                 // We need to skip :
140                 // * the 128 bytes of File Preamble (often padded with zeroes),
141                 // * the 4 bytes of "DICM" string,
142                 // * the 4 bytes of the first tag (0002, 0000),
143                 // i.e. a total of  136 bytes.
144                 entCur = deb + 136;
145                 if(memcmp(entCur, "UL", (size_t)2) == 0) {
146                         filetype = ExplicitVR;
147                         dbg.Verbose(0, "gdcmHeader::CheckSwap:",
148                                     "explicit Value Representation");
149                 } else {
150                         filetype = ImplicitVR;
151                         dbg.Verbose(0, "gdcmHeader::CheckSwap:",
152                                     "not an explicit Value Representation");
153                 }
154
155                 if (net2host) {
156                         sw = 4321;
157                         dbg.Verbose(0, "gdcmHeader::CheckSwap:",
158                                        "HostByteOrder != NetworkByteOrder");
159                 } else {
160                         sw = 0;
161                         dbg.Verbose(0, "gdcmHeader::CheckSwap:",
162                                        "HostByteOrder = NetworkByteOrder");
163                 }
164                 
165                 // Position the file position indicator at first tag (i.e.
166                 // after the file preamble and the "DICM" string).
167                 rewind(fp);
168                 fseek (fp, 132L, SEEK_SET);
169                 return;
170         } // End of TrueDicom
171
172         // Alas, this is not a DicomV3 file and whatever happens there is no file
173         // preamble. We can reset the file position indicator to where the data
174         // is (i.e. the beginning of the file).
175         rewind(fp);
176
177         // Our next best chance would be to be considering a 'clean' ACR/NEMA file.
178         // By clean we mean that the length of the first tag is written down.
179         // If this is the case and since the length of the first group HAS to be
180         // four (bytes), then determining the proper swap code is straightforward.
181
182         entCur = deb + 4;
183         s = str2num(entCur, int);
184         
185         switch (s) {
186         case 0x00040000 :
187                 sw=3412;
188                 filetype = ACR;
189                 return;
190         case 0x04000000 :
191                 sw=4321;
192                 filetype = ACR;
193                 return;
194         case 0x00000400 :
195                 sw=2143;
196                 filetype = ACR;
197                 return;
198         case 0x00000004 :
199                 sw=0;
200                 filetype = ACR;
201                 return;
202         default :
203                 dbg.Verbose(0, "gdcmHeader::CheckSwap:",
204                                "ACE/NEMA unfound swap info (time to raise bets)");
205         }
206
207         // We are out of luck. It is not a DicomV3 nor a 'clean' ACR/NEMA file.
208         // It is time for despaired wild guesses. So, let's assume this file
209         // happens to be 'dirty' ACR/NEMA, i.e. the length of the group it
210         // not present. Then the only info we have is the net2host one.
211         //FIXME  Si c'est du RAW, ca degagera + tard
212         
213         if (! net2host )
214                 sw = 0;
215         else
216                 sw = 4321;
217         return;
218 }
219
220 /**
221  * \ingroup   gdcmHeader
222  * \brief     recupere la longueur d'un champ DICOM.
223  *            Preconditions:
224  *            1/ le fichier doit deja avoir ete ouvert,
225  *            2/ CheckSwap() doit avoir ete appele
226  *            3/ la  partie 'group'  ainsi que la  partie 'elem' 
227  *               de l'acr_element doivent avoir ete lues.
228  *
229  *            ACR-NEMA : we allways get
230  *                 GroupNumber   (2 Octets) 
231  *                 ElementNumber (2 Octets) 
232  *                 ElementSize   (4 Octets)
233  *            DICOM en implicit Value Representation :
234  *                 GroupNumber   (2 Octets) 
235  *                 ElementNumber (2 Octets) 
236  *                 ElementSize   (4 Octets)
237  *
238  *            DICOM en explicit Value Representation :
239  *                 GroupNumber         (2 Octets) 
240  *                 ElementNumber       (2 Octets) 
241  *                 ValueRepresentation (2 Octets) 
242  *                 ElementSize         (2 Octets)
243  *
244  *            ATTENTION : dans le cas ou ValueRepresentation = OB, OW, SQ, UN
245  *                 GroupNumber         (2 Octets) 
246  *                 ElementNumber       (2 Octets) 
247  *                 ValueRepresentation (2 Octets)
248  *                 zone reservee       (2 Octets) 
249  *                 ElementSize         (4 Octets)
250  *
251  * @param sw  code swap
252  * @param skippedLength  pointeur sur nombre d'octets que l'on a saute qd
253  *                       la lecture est finie
254  * @param longueurLue    pointeur sur longueur (en nombre d'octets) 
255  *                       effectivement lue
256  * @return               longueur retenue pour le champ 
257  */
258
259 void gdcmHeader::FindVR( ElValue *pleCourant) {
260         char VR[3];
261         int lgrLue;
262         long PositionOnEntry = ftell(fp);
263         
264         if (filetype != ExplicitVR)
265                 return;
266
267         lgrLue=fread (&VR, (size_t)2,(size_t)1, fp);
268         VR[2]=0;
269                 
270         // Warning: we believe this is explicit VR (Value Representation) because
271         // we used a heuristic that found "UL" in the first tag. Alas this
272         // doesn't guarantee that all the tags will be in explicit VR. In some
273         // cases (see e-film filtered files) one finds implicit VR tags mixed
274         // within an explicit VR file. Hence we make sure the present tag
275         // is in explicit VR and try to fix things if it happens not to be
276         // the case.
277
278         // FIXME There should be only one occurence returned. Avoid the
279         // first extraction by calling proper method.
280         VRAtr FoundVR = dicom_vr->find(string(VR))->first;
281         if ( ! FoundVR.empty()) {
282                 pleCourant->SetVR(FoundVR);
283                 return; 
284         }
285         
286         // We thought this was explicit VR, but we end up with an
287         // implicit VR tag. Let's backtrack.
288         pleCourant->SetVR("Implicit");
289         fseek(fp, PositionOnEntry, SEEK_SET);
290 }
291
292 void gdcmHeader::FindLength( ElValue *pleCourant) {
293         int lgrLue;
294         guint32 l_gr;
295         unsigned short int l_gr_2;
296         
297         string vr = pleCourant->GetVR();
298         
299         if ( (filetype == ExplicitVR) && (vr != "Implicit") ) {
300                 if (   ( vr == "OB" ) || ( vr == "OW" )
301                          || ( vr == "SQ" ) || ( vr == "UN" ) ) {
302                         
303                         // The following two bytes are reserved, so we skip them,
304                         // and we proceed on reading the length on 4 bytes.
305                         fseek(fp, 2L,SEEK_CUR);
306                         lgrLue=fread (&l_gr, (size_t)4,(size_t)1, fp);
307                         l_gr = SwapLong((guint32)l_gr);
308                         
309                 } else {
310                         // Length is encoded on 2 bytes.
311                         lgrLue=fread (&l_gr_2, (size_t)2,(size_t)1, fp);
312                         
313                         l_gr_2 = SwapShort((unsigned short)l_gr_2);
314                         
315                         if ( l_gr_2 == 0xffff) {
316                                 l_gr = 0;
317                         } else {
318                                 l_gr = l_gr_2;
319                         }
320                 }
321         } else {
322                 // Either implicit VR or an explicit VR that (at least for this
323                 // element) lied a little bit. Length is on 4 bytes.
324                 lgrLue=fread (&l_gr, (size_t)4,(size_t)1, fp);
325                 l_gr= SwapLong((long)l_gr);
326         }
327
328         // Traitement des curiosites sur la longueur
329         if ( l_gr == 0xffffffff)
330                 l_gr=0; 
331         
332         pleCourant->SetLength(l_gr);
333 }
334
335 /**
336  * \ingroup gdcmHeader
337  * \brief   remet les octets dans un ordre compatible avec celui du processeur
338
339  * @return  longueur retenue pour le champ 
340  */
341 guint32 gdcmHeader::SwapLong(guint32 a) {
342         // FIXME: il pourrait y avoir un pb pour les entiers negatifs ...
343         switch (sw) {
344         case    0 :
345                 break;
346         case 4321 :
347                 a=(   ((a<<24) & 0xff000000) | ((a<<8)  & 0x00ff0000)    | 
348                       ((a>>8)  & 0x0000ff00) | ((a>>24) & 0x000000ff) );
349                 break;
350         
351         case 3412 :
352                 a=(   ((a<<16) & 0xffff0000) | ((a>>16) & 0x0000ffff) );
353                 break;
354         
355         case 2143 :
356                 a=(    ((a<<8) & 0xff00ff00) | ((a>>8) & 0x00ff00ff)  );
357                 break;
358         default :
359                 dbg.Error(" gdcmHeader::SwapLong : unset swap code");
360                 a=0;
361         }
362         return(a);
363 }
364
365 /**
366  * \ingroup gdcmHeader
367  * \brief   remet les octets dans un ordre compatible avec celui du processeur
368
369  * @return  longueur retenue pour le champ 
370  */
371 short int gdcmHeader::SwapShort(short int a) {
372         if ( (sw==4321)  || (sw==2143) )
373                 a =(((a<<8) & 0x0ff00) | ((a>>8)&0x00ff));
374         return (a);
375 }
376
377 /**
378  * \ingroup       gdcmHeader
379  * \brief         lit le dicom_element suivant.
380  *                      (le fichier doit deja avoir ete ouvert,
381  *                       _IdAcrCheckSwap(ID_DCM_HDR *e) avoir ete appele)
382  * @param e      ID_DCM_HDR  dans lequel effectuer la recherche.
383  * @param sw            code swap.
384  * @return              En cas de succes, 1 
385  *                      0 en cas d'echec.
386  */
387
388 ElValue * gdcmHeader::ReadNextElement(void) {
389         guint16 g;
390         guint16 n;
391         guint32 l;
392         size_t lgrLue;
393         ElValue * nouvDcmElem;
394         
395         // ------------------------- Lecture Num group : g
396         lgrLue=fread (&g, (size_t)2,(size_t)1, fp);
397         
398         if (feof(fp))  {
399                 dbg.Verbose(1, "ReadNextElement: EOF encountered");
400                 return (NULL);
401         }
402         if (ferror(fp)){
403                 dbg.Verbose(1, "ReadNextElement: failed to read NumGr");
404                 return (NULL);
405         }
406         
407         if (sw) g= SwapShort(((short)g));
408         
409         // ------------------------- Lecture Num Elem : n
410         lgrLue=fread (&n, (size_t)2,(size_t)1, fp);
411         
412         if (feof(fp))  {
413                 dbg.Verbose(1, "ReadNextElement: EOF encountered");
414                 return (NULL);
415         }
416         if (ferror(fp)){
417                 dbg.Verbose(1, "ReadNextElement: failed to read NumElem");
418                 return (NULL);
419         }
420         
421         if(sw) n= SwapShort(((short)n));
422
423         // Find out if the tag we encountered is in the dictionaries:
424         gdcmDictEntry * NewTag = IsInDicts(g, n);
425         if (!NewTag)
426                 NewTag = new gdcmDictEntry(g, n, "Unknown", "Unknown", "Unkown");
427
428         nouvDcmElem = new ElValue(NewTag);
429         if (!nouvDcmElem) {
430                 dbg.Verbose(1, "ReadNextElement: failed to allocate ElValue");
431                 return(NULL);
432         }
433
434         FindVR(nouvDcmElem);
435         FindLength(nouvDcmElem);
436         nouvDcmElem->SetOffset(ftell(fp));
437         l = nouvDcmElem->GetLength();
438
439         //FIXMEif(!memcmp( VR,"SQ",(size_t)2 )) { // ca annonce une SEQUENCE d'items ?!
440         //FIXME l_gr=0;                         // on lira donc les items de la sequence 
441         //FIXME}
442         //FIXMEreturn(l_gr);
443
444    // Une sequence contient un ensemble de group element repetes n fois
445         // et g=fffe indique la fin (contient une longueur bidon).
446         if(g==0xfffe) l=0;  // pour sauter les indicateurs de 'SQ'
447         
448         
449         // ------------------------- Lecture Valeur element 
450         
451         // FIXME The exact size should be l if we move to strings or whatever
452         // CLEAN ME NEWValue used to be nouvDcmElem->valeurElem
453         char* NewValue = (char*)g_malloc(l+1);
454         if(NewValue) {
455                 NewValue[l]= 0;
456         } else {
457                 return (NULL);
458         }
459         
460         // FIXME les elements trop long (seuil a fixer a la main) ne devraient
461         // pas etre charge's !!!! Voir TODO.
462         lgrLue=fread (NewValue, (size_t)l,(size_t)1, fp);
463         
464         if ( IsAnInteger(g, n, NewTag->GetVR(), l) ) {
465                 // CLEANME THe following is really UGLY ! 
466                 if( l == 4 ) {
467                         *(guint32 *) NewValue = SwapLong  ((*(guint32 *) NewValue)); 
468                 } else {
469                         if( l == 2 )
470                                 *(guint16 *) NewValue = SwapShort ((*(guint16 *)NewValue));
471                 }
472                 //FIXME: don't we have to distinguish guin16 and guint32
473                 //FIXME: make the following an util fonction
474                 ostringstream s;
475                 s << *(guint32 *) NewValue;
476                 nouvDcmElem->value = s.str();
477                 g_free(NewValue);
478         } else
479                 nouvDcmElem->value = NewValue;
480         return nouvDcmElem;
481 }
482
483 bool gdcmHeader::IsAnInteger(guint16 group, guint16 element,
484                                      string vr, guint32 length ) {
485         // When we have some semantics on the element we just read, and we
486         // a priori now we are dealing with an integer, then we can swap it's
487         // element value properly.
488         if ( element == 0 )  {  // This is the group length of the group
489                 if (length != 4)
490                         dbg.Error("gdcmHeader::ShouldBeSwaped", "should be four");
491                 return true;
492         }
493         
494         if ( group % 2 != 0 )
495                 // We only have some semantics on documented elements, which are
496                 // the even ones.
497                 return false;
498         
499         if ( (length != 4) && ( length != 2) )
500                 // Swapping only make sense on integers which are 2 or 4 bytes long.
501                 return false;
502         
503         if ( (vr == "UL") || (vr == "US") || (vr == "SL") || (vr == "SS") )
504                 return true;
505         
506         if ( (group == 0x0028) && (element == 0x0005) )
507                 // This tag is retained from ACR/NEMA
508                 // CHECKME Why should "Image Dimensions" be a single integer ?
509                 return true;
510         
511         if ( (group == 0x0028) && (element == 0x0200) )
512                 // This tag is retained from ACR/NEMA
513                 return true;
514         
515         return false;
516 }
517
518 /**
519  * \ingroup gdcmHeader
520  * \brief   If we encountered the offset of the pixels in the file
521  *          (Pixel Data) then keep the info aside.
522  */
523 void gdcmHeader::SetAsidePixelData(ElValue* elem) {
524         // They are two cases :
525         // * the pixel data (i.e. the image or the volume) is pointed by it's
526         //   default official tag (0x7fe0,0x0010),
527         // * the writer of this file decided to put the image "address" (i.e the
528         //   offset from the begining of the file) at a different tag.
529         //   Then the "Pixel Data" offset might be found by indirection through
530         //   the "Image Location" tag (0x0028,  0x0200). In other terms the Image
531         //   Location tag contains the group where the "Pixel Data" offset is and
532         //   inside this group the element is conventionally at element 0x0010
533         //   (when the norm is respected).
534         // 
535         // Hence getting our hands on the Pixel Data is a two stage process:
536         //  1/ * find if the "Pixel Data" tag exists.
537         //     * if it does not exist, look for the "Pixel Location" tag.
538         //  2/ look at the proper tag ("Pixel Data" or "Pixel Location" when
539         //     it exists) what the offset is.
540         cout << "aaaaaaaaaaaaaaaaaaaaa";
541         // PubElVals.PrintByName(cout);
542         ostringstream val;
543         val << hex << GetPubElValByName("Image Location");
544         cout << GetPubElValByName("Image Location") << endl;
545         cout <<hex << GetPubElValByName("Image Location") << dec << endl;
546         cout << "aaaa" << hex << val << dec << endl;
547         if (val)
548                 //grPixel  = val.hex().str();
549                 grPixel = 0;
550         else
551                 grPixel  = 0x7FE0;
552         return;
553
554         guint16 g;
555         guint16 n;
556         g = elem->GetGroup();
557         n = elem->GetElement();
558         if (!grPixelTrouve) {   // on n a pas encore trouve les pixels
559                 if (g > 0x0028) {
560                         if (n > 0x0200 || g == 0x7FE0 ) {  // on a depasse (28,200)
561                                 grPixel  = 0x7FE0;
562                                 numPixel = 0x0010;
563                                 grPixelTrouve = true;
564                         }
565                 } else {         // on est sur (28,200)
566                         if (g == 0x0028) {
567                                 if (n == 0x0200) {
568                                         grPixelTrouve = 1;
569                                         char* NewValue = (char*)g_malloc(elem->GetLength()+1);
570                                         // FIXME: not very elegant conversion
571                                         for(int i=0;i<4;i++)
572                                                 *((char*)(&grPixel)+i) = *(NewValue+i); 
573                                         elem->SetValue(NewValue);
574                                         
575                                         if (grPixel != 0x7FE0)   // Vieux pb Philips
576                                                 numPixel = 0x1010;    // encore utile ??
577                                         else
578                                                 numPixel = 0x0010;
579                                 }
580                         }
581                 }
582         } else {     // on vient de trouver les pixels
583                 if (g == grPixel) {
584                         if (n == numPixel) {
585                                 PixelPosition = elem->Offset; 
586                                 PixelsTrouves = true;
587                                 dbg.Verbose(0, "gdcmHeader::SetAsidePixelData:",
588                                             "Pixel data found");
589                         }
590                 }
591         }
592 }
593
594 gdcmDictEntry * gdcmHeader::IsInDicts(guint32 group, guint32 element) {
595         gdcmDictEntry * found = (gdcmDictEntry*)0;
596         if (!RefPubDict && !RefShaDict) {
597                 //FIXME build a default dictionary !
598                 printf("FIXME in gdcmHeader::IsInDicts\n");
599         }
600         if (RefPubDict) {
601                 found = RefPubDict->GetTag(group, element);
602                 if (found)
603                         return found;
604         }
605         if (RefShaDict) {
606                 found = RefShaDict->GetTag(group, element);
607                 if (found)
608                         return found;
609         }
610         return found;
611 }
612
613 string gdcmHeader::GetPubElValByNumber(guint16 group, guint16 element) {
614         return PubElVals.GetElValue(group, element);
615 }
616
617 string gdcmHeader::GetPubElValByName(string TagName) {
618         return PubElVals.GetElValue(TagName);
619 }
620
621 /**
622  * \ingroup       gdcmHeader
623  * \brief         renvoie un pointeur sur le ID_DCM_HDR correspondant au fichier
624  * @param filename      Nom du fichier ACR / LibIDO / DICOM
625  * @return       le ID_DCM_HDR 
626  */
627  
628 void gdcmHeader::BuildHeader(void) {
629         ElValue * newElValue = (ElValue *)0;
630         
631         rewind(fp);
632         CheckSwap();
633         while ( (newElValue = ReadNextElement()) ) {
634                 PubElVals.Add(newElValue);
635         }
636         SetAsidePixelData((ElValue*)0);
637 }
638
639 void gdcmHeader::PrintPubElVal(ostream & os) {
640         PubElVals.Print(os);
641 }
642
643 void gdcmHeader::PrintPubDict(ostream & os) {
644         RefPubDict->Print(os);
645 }