]> Creatis software - gdcm.git/blob - src/gdcmFileHelper.cxx
Remove useless accesses to the Dicom Dictionnary std::map
[gdcm.git] / src / gdcmFileHelper.cxx
1 /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: gdcmFileHelper.cxx,v $
5   Language:  C++
6
7   Date:      $Date: 2006/04/11 16:03:26 $
8   Version:   $Revision: 1.99 $
9                                                                                 
10   Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
11   l'Image). All rights reserved. See Doc/License.txt or
12   http://www.creatis.insa-lyon.fr/Public/Gdcm/License.html for details.
13                                                                                 
14      This software is distributed WITHOUT ANY WARRANTY; without even
15      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
16      PURPOSE.  See the above copyright notices for more information.
17                                                                                 
18 =========================================================================*/
19
20 #include "gdcmFileHelper.h"
21 #include "gdcmGlobal.h"
22 #include "gdcmTS.h"
23 #include "gdcmDocument.h"
24 #include "gdcmDebug.h"
25 #include "gdcmUtil.h"
26 #include "gdcmSeqEntry.h"
27 #include "gdcmSQItem.h"
28 #include "gdcmDataEntry.h"
29 #include "gdcmDocEntry.h"
30 #include "gdcmFile.h"
31 #include "gdcmPixelReadConvert.h"
32 #include "gdcmPixelWriteConvert.h"
33 #include "gdcmDocEntryArchive.h"
34 #include "gdcmDictSet.h"
35 #include "gdcmOrientation.h"
36  
37 #if defined(__BORLANDC__)
38    #include <mem.h> // for memset
39 #endif 
40
41 #include <fstream>
42
43 /*
44 // ----------------------------- WARNING -------------------------
45
46 These lines will be moved to the document-to-be 'User's Guide'
47
48 // To read an image, user needs a gdcm::File
49 gdcm::File *f = new gdcm::File(fileName);
50 // or (advanced) :
51 // user may also decide he doesn't want to load some parts of the header
52 gdcm::File *f = new gdcm::File();
53 f->SetFileName(fileName);
54    f->SetLoadMode(LD_NOSEQ);             // or      
55    f->SetLoadMode(LD_NOSHADOW);          // or
56    f->SetLoadMode(LD_NOSEQ | LD_NOSHADOW); // or
57    f->SetLoadMode(LD_NOSHADOWSEQ);
58 f->Load();
59
60 // user can now check some values
61 std::string v = f->GetEntryValue(groupNb,ElementNb);
62
63 // to get the pixels, user needs a gdcm::FileHelper
64 gdcm::FileHelper *fh = new gdcm::FileHelper(f);
65 // user may ask not to convert Palette to RGB
66 uint8_t *pixels = fh->GetImageDataRaw();
67 int imageLength = fh->GetImageDataRawSize();
68 // He can now use the pixels, create a new image, ...
69 uint8_t *userPixels = ...
70
71 To re-write the image, user re-uses the gdcm::FileHelper
72
73 fh->SetImageData( userPixels, userPixelsLength);
74 fh->SetTypeToRaw(); // Even if it was possible to convert Palette to RGB
75                      // (WriteMode is set)
76  
77 fh->SetWriteTypeToDcmExpl();  // he wants Explicit Value Representation
78                               // Little Endian is the default
79                               // no other value is allowed
80                                 (-->SetWriteType(ExplicitVR);)
81                                    -->WriteType = ExplicitVR;
82 fh->Write(newFileName);      // overwrites the file, if any
83
84 // or :
85 fh->WriteDcmExplVR(newFileName);
86
87
88 // ----------------------------- WARNING -------------------------
89
90 These lines will be moved to the document-to-be 'Developer's Guide'
91
92 WriteMode : WMODE_RAW / WMODE_RGB
93 WriteType : ImplicitVR, ExplicitVR, ACR, ACR_LIBIDO
94
95 fh1->Write(newFileName);
96    SetWriteFileTypeToImplicitVR() / SetWriteFileTypeToExplicitVR();
97    (modifies TransferSyntax)
98    SetWriteToRaw(); / SetWriteToRGB();
99       (modifies, when necessary : photochromatic interpretation, 
100          samples per pixel, Planar configuration, 
101          bits allocated, bits stored, high bit -ACR 24 bits-
102          Pixels element VR, pushes out the LUT )
103    CheckWriteIntegrity();
104       (checks user given pixels length)
105    FileInternal->Write(fileName,WriteType)
106    fp = opens file(fileName);
107    ComputeGroup0002Length( );
108    BitsAllocated 12->16
109       RemoveEntry(palettes, etc)
110       Document::WriteContent(fp, writetype);
111    RestoreWrite();
112       (moves back to the File all the archived elements)
113    RestoreWriteFileType();
114       (pushes back group 0002, with TransferSyntax)
115 */
116
117
118
119
120 namespace gdcm 
121 {
122 typedef std::map<uint16_t, int> GroupHT;    //  Hash Table
123 //-------------------------------------------------------------------------
124 // Constructor / Destructor
125 /**
126  * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3
127  *        file (gdcm::File only deals with the ... header)
128  *        Opens (in read only and when possible) an existing file and checks
129  *        for DICOM compliance. Returns NULL on failure.
130  *        It will be up to the user to load the pixels into memory
131  *        ( GetImageDataSize() + GetImageData() methods)
132  * \note  the in-memory representation of all available tags found in
133  *        the DICOM header is post-poned to first header information access.
134  *        This avoid a double parsing of public part of the header when
135  *        one sets an a posteriori shadow dictionary (efficiency can be
136  *        seen as a side effect).   
137  */
138 FileHelper::FileHelper( )
139
140    FileInternal = File::New( );
141    Initialize();
142 }
143
144 /**
145  * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3
146  *        file (File only deals with the ... header)
147  *        Opens (in read only and when possible) an existing file and checks
148  *        for DICOM compliance. Returns NULL on failure.
149  *        It will be up to the user to load the pixels into memory
150  *        ( GetImageDataSize() + GetImageData() methods)
151  * \note  the in-memory representation of all available tags found in
152  *        the DICOM header is post-poned to first header information access.
153  *        This avoid a double parsing of public part of the header when
154  *        user sets an a posteriori shadow dictionary (efficiency can be
155  *        seen as a side effect).   
156  * @param header already built Header
157  */
158 FileHelper::FileHelper(File *header)
159 {
160    gdcmAssertMacro(header);
161
162    FileInternal = header;
163    FileInternal->Register();
164    Initialize();
165    if ( FileInternal->IsReadable() )
166    {
167       PixelReadConverter->GrabInformationsFromFile( FileInternal, this );
168    }
169 }
170
171 /**
172  * \brief canonical destructor
173  * \note  If the header (gdcm::File) was created by the FileHelper constructor,
174  *        it is destroyed by the FileHelper
175  */
176 FileHelper::~FileHelper()
177
178    if ( PixelReadConverter )
179    {
180       delete PixelReadConverter;
181    }
182    if ( PixelWriteConverter )
183    {
184       delete PixelWriteConverter;
185    }
186    if ( Archive )
187    {
188       delete Archive;
189    }
190
191    FileInternal->Unregister();
192 }
193
194 //-----------------------------------------------------------------------------
195 // Public
196
197 /**
198  * \brief Sets the LoadMode of the internal gdcm::File as a boolean string. 
199  *        NO_SEQ, NO_SHADOW, NO_SHADOWSEQ ... (nothing more, right now)
200  *        WARNING : before using NO_SHADOW, be sure *all* your files
201  *        contain accurate values in the 0x0000 element (if any) 
202  *        of *each* Shadow Group. The parser will fail if the size is wrong !
203  * @param   loadMode Load mode to be used    
204  */
205 void FileHelper::SetLoadMode(int loadMode) 
206
207    GetFile()->SetLoadMode( loadMode ); 
208 }
209 /**
210  * \brief Sets the LoadMode of the internal gdcm::File
211  * @param  fileName name of the file to be open  
212  */
213 void FileHelper::SetFileName(std::string const &fileName)
214 {
215    FileInternal->SetFileName( fileName );
216 }
217
218 /**
219  * \brief   Loader  
220  * @return false if file cannot be open or no swap info was found,
221  *         or no tag was found.
222  */
223 bool FileHelper::Load()
224
225    if ( !FileInternal->Load() )
226       return false;
227
228    PixelReadConverter->GrabInformationsFromFile( FileInternal, this );
229    return true;
230 }
231
232 /**
233  * \brief   Accesses an existing DataEntry through it's (group, element) 
234  *          and modifies it's content with the given value.
235  * @param   content new value (string) to substitute with
236  * @param   group  group number of the Dicom Element to modify
237  * @param   elem element number of the Dicom Element to modify
238  * \return  false if DataEntry not found
239  */
240 bool FileHelper::SetEntryString(std::string const &content,
241                                 uint16_t group, uint16_t elem)
242
243    return FileInternal->SetEntryString(content, group, elem);
244 }
245
246
247 /**
248  * \brief   Accesses an existing DataEntry through it's (group, element) 
249  *          and modifies it's content with the given value.
250  * @param   content new value (void*  -> uint8_t*) to substitute with
251  * @param   lgth new value length
252  * @param   group  group number of the Dicom Element to modify
253  * @param   elem element number of the Dicom Element to modify
254  * \return  false if DataEntry not found
255  */
256 bool FileHelper::SetEntryBinArea(uint8_t *content, int lgth,
257                                  uint16_t group, uint16_t elem)
258 {
259    return FileInternal->SetEntryBinArea(content, lgth, group, elem);
260 }
261
262 /**
263  * \brief   Modifies the value of a given DataEntry when it exists.
264  *          Creates it with the given value when unexistant.
265  * @param   content (string) value to be set
266  * @param   group   Group number of the Entry 
267  * @param   elem  Element number of the Entry
268  * \return  pointer to the modified/created DataEntry (NULL when creation
269  *          failed).
270  */ 
271 DataEntry *FileHelper::InsertEntryString(std::string const &content,
272                                                 uint16_t group, uint16_t elem)
273 {
274    return FileInternal->InsertEntryString(content, group, elem);
275 }
276
277 /**
278  * \brief   Modifies the value of a given DataEntry when it exists.
279  *          Creates it with the given value when unexistant.
280  *          A copy of the binArea is made to be kept in the Document.
281  * @param   binArea (binary)value to be set
282  * @param   lgth new value length
283  * @param   group   Group number of the Entry 
284  * @param   elem  Element number of the Entry
285  * \return  pointer to the modified/created DataEntry (NULL when creation
286  *          failed).
287  */
288 DataEntry *FileHelper::InsertEntryBinArea(uint8_t *binArea, int lgth,
289                                           uint16_t group, uint16_t elem)
290 {
291    return FileInternal->InsertEntryBinArea(binArea, lgth, group, elem);
292 }
293
294 /**
295  * \brief   Adds an empty SeqEntry 
296  *          (remove any existing entry with same group,elem)
297  * @param   group   Group number of the Entry 
298  * @param   elem  Element number of the Entry
299  * \return  pointer to the created SeqEntry (NULL when creation
300  *          failed).
301  */
302 SeqEntry *FileHelper::InsertSeqEntry(uint16_t group, uint16_t elem)
303 {
304    return FileInternal->InsertSeqEntry(group, elem);
305 }
306
307 /**
308  * \brief   Get the size of the image data
309  *          If the image can be RGB (with a lut or by default), the size 
310  *          corresponds to the RGB image
311  *         (use GetImageDataRawSize if you want to be sure to get *only*
312  *          the size of the pixels)
313  * @return  The image size
314  */
315 size_t FileHelper::GetImageDataSize()
316 {
317    if ( PixelWriteConverter->GetUserData() )
318    {
319       return PixelWriteConverter->GetUserDataSize();
320    }
321    return PixelReadConverter->GetRGBSize();
322 }
323
324 /**
325  * \brief   Get the size of the image data.
326  *          If the image could be converted to RGB using a LUT, 
327  *          this transformation is not taken into account by GetImageDataRawSize
328  *          (use GetImageDataSize if you wish)
329  * @return  The raw image size
330  */
331 size_t FileHelper::GetImageDataRawSize()
332 {
333    if ( PixelWriteConverter->GetUserData() )
334    {
335       return PixelWriteConverter->GetUserDataSize();
336    }
337    return PixelReadConverter->GetRawSize();
338 }
339
340 /**
341  * \brief brings pixels into memory :  
342  *          - Allocates necessary memory,
343  *          - Reads the pixels from disk (uncompress if necessary),
344  *          - Transforms YBR pixels, if any, into RGB pixels,
345  *          - Transforms 3 planes R, G, B, if any, into a single RGB Plane
346  *          - Transforms single Grey plane + 3 Palettes into a RGB Plane
347  *          - Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
348  * @return  Pointer to newly allocated pixel data.
349  *          (uint8_t is just for prototyping. feel free to cast)
350  *          NULL if alloc fails 
351  */
352 uint8_t *FileHelper::GetImageData()
353 {
354    if ( PixelWriteConverter->GetUserData() )
355    {
356       return PixelWriteConverter->GetUserData();
357    }
358
359    if ( ! GetRaw() )
360    {
361       // If the decompression failed nothing can be done.
362       return 0;
363    }
364
365    if ( FileInternal->HasLUT() && PixelReadConverter->BuildRGBImage() )
366    {
367       return PixelReadConverter->GetRGB();
368    }
369    else
370    {
371       // When no LUT or LUT conversion fails, return the Raw
372       return PixelReadConverter->GetRaw();
373    }
374 }
375
376 /**
377  * \brief brings pixels into memory :  
378  *          - Allocates necessary memory, 
379  *          - Transforms YBR pixels (if any) into RGB pixels
380  *          - Transforms 3 planes R, G, B  (if any) into a single RGB Plane
381  *          - Copies the pixel data (image[s]/volume[s]) to newly allocated zone. 
382  *          - DOES NOT transform Grey plane + 3 Palettes into a RGB Plane
383  * @return  Pointer to newly allocated pixel data.
384  *          (uint8_t is just for prototyping. feel free to cast)
385  *          NULL if alloc fails
386  */
387 uint8_t *FileHelper::GetImageDataRaw ()
388 {
389    return GetRaw();
390 }
391
392 #ifndef GDCM_LEGACY_REMOVE
393 /*
394  * \brief   Useless function, since PixelReadConverter forces us 
395  *          copy the Pixels anyway.  
396  *          Reads the pixels from disk (uncompress if necessary),
397  *          Transforms YBR pixels, if any, into RGB pixels
398  *          Transforms 3 planes R, G, B, if any, into a single RGB Plane
399  *          Transforms single Grey plane + 3 Palettes into a RGB Plane   
400  *          Copies at most MaxSize bytes of pixel data to caller allocated
401  *          memory space.
402  * \warning This function allows people that want to build a volume
403  *          from an image stack *not to* have, first to get the image pixels, 
404  *          and then move them to the volume area.
405  *          It's absolutely useless for any VTK user since vtk chooses 
406  *          to invert the lines of an image, that is the last line comes first
407  *          (for some axis related reasons?). Hence he will have 
408  *          to load the image line by line, starting from the end.
409  *          VTK users have to call GetImageData
410  *     
411  * @param   destination Address (in caller's memory space) at which the
412  *          pixel data should be copied
413  * @param   maxSize Maximum number of bytes to be copied. When MaxSize
414  *          is not sufficient to hold the pixel data the copy is not
415  *          executed (i.e. no partial copy).
416  * @return  On success, the number of bytes actually copied. Zero on
417  *          failure e.g. MaxSize is lower than necessary.
418  */
419 size_t FileHelper::GetImageDataIntoVector (void *destination, size_t maxSize)
420 {
421    if ( ! GetRaw() )
422    {
423       // If the decompression failed nothing can be done.
424       return 0;
425    }
426
427    if ( FileInternal->HasLUT() && PixelReadConverter->BuildRGBImage() )
428    {
429       if ( PixelReadConverter->GetRGBSize() > maxSize )
430       {
431          gdcmWarningMacro( "Pixel data bigger than caller's expected MaxSize");
432          return 0;
433       }
434       memcpy( destination,
435               (void*)PixelReadConverter->GetRGB(),
436               PixelReadConverter->GetRGBSize() );
437       return PixelReadConverter->GetRGBSize();
438    }
439
440    // Either no LUT conversion necessary or LUT conversion failed
441    if ( PixelReadConverter->GetRawSize() > maxSize )
442    {
443       gdcmWarningMacro( "Pixel data bigger than caller's expected MaxSize");
444       return 0;
445    }
446    memcpy( destination,
447            (void *)PixelReadConverter->GetRaw(),
448            PixelReadConverter->GetRawSize() );
449    return PixelReadConverter->GetRawSize();
450 }
451 #endif
452
453 /**
454  * \brief   Points the internal pointer to the callers inData
455  *          image representation, BUT WITHOUT COPYING THE DATA.
456  *          'image' Pixels are presented as C-like 2D arrays : line per line.
457  *          'volume'Pixels are presented as C-like 3D arrays : plane per plane 
458  * \warning Since the pixels are not copied, it is the caller's responsability
459  *          not to deallocate its data before gdcm uses them (e.g. with
460  *          the Write() method )
461  * @param inData user supplied pixel area (uint8_t* is just for the compiler.
462  *               user is allowed to pass any kind of pixelsn since the size is
463  *               given in bytes) 
464  * @param expectedSize total image size, *in Bytes*
465  */
466 void FileHelper::SetImageData(uint8_t *inData, size_t expectedSize)
467 {
468    SetUserData(inData, expectedSize);
469 }
470
471 /**
472  * \brief   Set the image data defined by the user
473  * \warning When writting the file, this data are get as default data to write
474  * @param inData user supplied pixel area (uint8_t* is just for the compiler.
475  *               user is allowed to pass any kind of pixels since the size is
476  *               given in bytes) 
477  * @param expectedSize total image size, *in Bytes* 
478  */
479 void FileHelper::SetUserData(uint8_t *inData, size_t expectedSize)
480 {
481    PixelWriteConverter->SetUserData(inData, expectedSize);
482 }
483
484 /**
485  * \brief   Get the image data defined by the user
486  * \warning When writting the file, this data are get as default data to write
487  */
488 uint8_t *FileHelper::GetUserData()
489 {
490    return PixelWriteConverter->GetUserData();
491 }
492
493 /**
494  * \brief   Get the image data size defined by the user
495  * \warning When writting the file, this data are get as default data to write
496  */
497 size_t FileHelper::GetUserDataSize()
498 {
499    return PixelWriteConverter->GetUserDataSize();
500 }
501
502 /**
503  * \brief   Get the image data from the file.
504  *          If a LUT is found, the data are expanded to be RGB
505  */
506 uint8_t *FileHelper::GetRGBData()
507 {
508    return PixelReadConverter->GetRGB();
509 }
510
511 /**
512  * \brief   Get the image data size from the file.
513  *          If a LUT is found, the data are expanded to be RGB
514  */
515 size_t FileHelper::GetRGBDataSize()
516 {
517    return PixelReadConverter->GetRGBSize();
518 }
519
520 /**
521  * \brief   Get the image data from the file.
522  *          Even when a LUT is found, the data are not expanded to RGB!
523  */
524 uint8_t *FileHelper::GetRawData()
525 {
526    return PixelReadConverter->GetRaw();
527 }
528
529 /**
530  * \brief   Get the image data size from the file.
531  *          Even when a LUT is found, the data are not expanded to RGB!
532  */
533 size_t FileHelper::GetRawDataSize()
534 {
535    return PixelReadConverter->GetRawSize();
536 }
537
538 /**
539  * \brief Access to the underlying \ref PixelReadConverter RGBA LUT
540  */
541 uint8_t* FileHelper::GetLutRGBA()
542 {
543    if ( PixelReadConverter->GetLutRGBA() ==0 )
544       PixelReadConverter->BuildLUTRGBA();
545    return PixelReadConverter->GetLutRGBA();
546 }
547
548 /**
549  * \brief Access to the underlying \ref PixelReadConverter RGBA LUT Item Number
550  */
551 int FileHelper::GetLutItemNumber()
552 {
553    return PixelReadConverter->GetLutItemNumber();
554 }
555
556 /**
557  * \brief Access to the underlying \ref PixelReadConverter RGBA LUT Item Size
558  */
559 int FileHelper::GetLutItemSize()
560 {
561    return PixelReadConverter->GetLutItemSize();
562 }
563
564 /**
565  * \brief Writes on disk A SINGLE Dicom file
566  *        NO test is performed on  processor "Endiannity".
567  *        It's up to the user to call his Reader properly
568  * @param fileName name of the file to be created
569  *                 (any already existing file is over written)
570  * @return false if write fails
571  */
572 bool FileHelper::WriteRawData(std::string const &fileName)
573 {
574    std::ofstream fp1(fileName.c_str(), std::ios::out | std::ios::binary );
575    if (!fp1)
576    {
577       gdcmWarningMacro( "Fail to open (write) file:" << fileName.c_str());
578       return false;
579    }
580
581    if ( PixelWriteConverter->GetUserData() )
582    {
583       fp1.write( (char *)PixelWriteConverter->GetUserData(), 
584                  PixelWriteConverter->GetUserDataSize() );
585    }
586    else if ( PixelReadConverter->GetRGB() )
587    {
588       fp1.write( (char *)PixelReadConverter->GetRGB(), 
589                  PixelReadConverter->GetRGBSize());
590    }
591    else if ( PixelReadConverter->GetRaw() )
592    {
593       fp1.write( (char *)PixelReadConverter->GetRaw(), 
594                  PixelReadConverter->GetRawSize());
595    }
596    else
597    {
598       gdcmErrorMacro( "Nothing written." );
599    }
600
601    fp1.close();
602
603    return true;
604 }
605
606 /**
607  * \brief Writes on disk A SINGLE Dicom file, 
608  *        using the Implicit Value Representation convention
609  *        NO test is performed on  processor "Endianity".
610  * @param fileName name of the file to be created
611  *                 (any already existing file is overwritten)
612  * @return false if write fails
613  */
614
615 bool FileHelper::WriteDcmImplVR (std::string const &fileName)
616 {
617    SetWriteTypeToDcmImplVR();
618    return Write(fileName);
619 }
620
621 /**
622 * \brief Writes on disk A SINGLE Dicom file, 
623  *        using the Explicit Value Representation convention
624  *        NO test is performed on  processor "Endiannity". 
625  * @param fileName name of the file to be created
626  *                 (any already existing file is overwritten)
627  * @return false if write fails
628  */
629
630 bool FileHelper::WriteDcmExplVR (std::string const &fileName)
631 {
632    SetWriteTypeToDcmExplVR();
633    return Write(fileName);
634 }
635
636 /**
637  * \brief Writes on disk A SINGLE Dicom file, 
638  *        using the ACR-NEMA convention
639  *        NO test is performed on  processor "Endiannity".
640  *        (a l'attention des logiciels cliniques 
641  *        qui ne prennent en entrée QUE des images ACR ...
642  * \warning if a DICOM_V3 header is supplied,
643  *         groups < 0x0008 and shadow groups are ignored
644  * \warning NO TEST is performed on processor "Endiannity".
645  * @param fileName name of the file to be created
646  *                 (any already existing file is overwritten)
647  * @return false if write fails
648  */
649
650 bool FileHelper::WriteAcr (std::string const &fileName)
651 {
652    SetWriteTypeToAcr();
653    return Write(fileName);
654 }
655
656 /**
657  * \brief Writes on disk A SINGLE Dicom file, 
658  * @param fileName name of the file to be created
659  *                 (any already existing file is overwritten)
660  * @return false if write fails
661  */
662 bool FileHelper::Write(std::string const &fileName)
663 {
664
665    CheckMandatoryElements(); //called once, here !
666    
667    bool flag = false;
668    DocEntry *e;   
669    switch(WriteType)
670    {
671       case ImplicitVR:
672          SetWriteFileTypeToImplicitVR();
673          break;
674  
675       case Unknown:  // should never happen; ExplicitVR is the default value
676       case ExplicitVR:
677
678    // User should ask gdcm to write an image in Explicit VR mode
679    // only when he is sure *all* the VR of *all* the DataElements is known.
680    // i.e : when there are *only* Public Groups
681    // or *all* the Shadow Groups are fully described in the relevant Shadow
682    // Dictionnary
683    // Let's just *dream* about it; *never* trust a user !
684    // We turn to Implicit VR if at least the VR of one element is unknown.
685    
686  
687          e = FileInternal->GetFirstEntry();
688          while (e != 0)
689          {
690             if (e->GetVR() == "  ")  
691             {
692                SetWriteTypeToDcmImplVR();
693                SetWriteFileTypeToImplicitVR();
694                flag = true;
695                break;         
696             } 
697             e = FileInternal->GetNextEntry();
698          }        
699
700          if (!flag)
701          {
702             SetWriteFileTypeToExplicitVR();
703          }
704          break;
705
706   SetWriteFileTypeToExplicitVR(); // to see JPRx
707   break;
708       case ACR:
709       case ACR_LIBIDO:
710       // NOTHING is done here just for LibIDO.
711       // Just to avoid further trouble if user creates a file ex-nihilo,
712       // wants to write it as an ACR-NEMA file,
713       // and forgets to create any Entry belonging to group 0008
714       // (shame on him !)
715       // We add Recognition Code (RET)
716         if ( ! FileInternal->GetDataEntry(0x0008, 0x0010) )
717             FileInternal->InsertEntryString("ACR-NEMA V1.0 ", 
718                                              0x0008, 0x0010, "LO");
719          SetWriteFileTypeToACR();
720         // SetWriteFileTypeToImplicitVR(); // ACR IS implicit VR !
721          break;
722  
723       /// \todo FIXME : JPEG may be either ExplicitVR or ImplicitVR
724       case JPEG:
725          SetWriteFileTypeToJPEG();
726          break;
727    }
728  
729    // --------------------------------------------------------------
730    // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
731    //
732    // if recognition code tells us we dealt with a LibIDO image
733    // we reproduce on disk the switch between lineNumber and columnNumber
734    // just before writting ...
735    /// \todo the best trick would be *change* the recognition code
736    ///       but pb expected if user deals with, e.g. COMPLEX images
737    
738    if ( WriteType == ACR_LIBIDO )
739    {
740       SetWriteToLibido();
741    }
742    else
743    {
744       SetWriteToNoLibido();
745    }
746    // ----------------- End of Special Patch ----------------
747   
748    switch(WriteMode)
749    {
750       case WMODE_RAW :
751          SetWriteToRaw(); // modifies and pushes to the archive, when necessary
752          break;
753       case WMODE_RGB :
754          SetWriteToRGB(); // modifies and pushes to the archive, when necessary
755          break;
756    }
757
758    bool check = CheckWriteIntegrity(); // verifies length
759    if (WriteType == JPEG ) check = true;
760    if (check)
761    {
762       check = FileInternal->Write(fileName,WriteType);
763    }
764
765    RestoreWrite(); 
766   // RestoreWriteFileType();
767   // RestoreWriteMandatory();
768    
769
770    // --------------------------------------------------------------
771    // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
772    // 
773    // ...and we restore the header to be Dicom Compliant again 
774    // just after writting
775    RestoreWriteOfLibido();
776    // ----------------- End of Special Patch ----------------
777
778    return check;
779 }
780
781 //-----------------------------------------------------------------------------
782 // Protected
783 /**
784  * \brief Checks the write integrity
785  *
786  * The tests made are :
787  *  - verify the size of the image to write with the possible write
788  *    when the user set an image data
789  * @return true if check is successfull
790  */
791 bool FileHelper::CheckWriteIntegrity()
792 {
793    if ( PixelWriteConverter->GetUserData() )
794    {
795       int numberBitsAllocated = FileInternal->GetBitsAllocated();
796       if ( numberBitsAllocated == 0 || numberBitsAllocated == 12 )
797       {
798          gdcmWarningMacro( "numberBitsAllocated changed from " 
799                           << numberBitsAllocated << " to 16 " 
800                           << " for consistency purpose" );
801          numberBitsAllocated = 16;
802       }
803
804       size_t decSize = FileInternal->GetXSize()
805                      * FileInternal->GetYSize() 
806                      * FileInternal->GetZSize()
807                      * FileInternal->GetTSize()     
808                      * FileInternal->GetSamplesPerPixel()
809                      * ( numberBitsAllocated / 8 );
810       size_t rgbSize = decSize;
811       if ( FileInternal->HasLUT() )
812          rgbSize = decSize * 3;
813
814       switch(WriteMode)
815       {
816          case WMODE_RAW :
817             if ( decSize!=PixelWriteConverter->GetUserDataSize() )
818             {
819                gdcmWarningMacro( "Data size (Raw) is incorrect. Should be " 
820                            << decSize << " / Found :" 
821                            << PixelWriteConverter->GetUserDataSize() );
822                return false;
823             }
824             break;
825          case WMODE_RGB :
826             if ( rgbSize!=PixelWriteConverter->GetUserDataSize() )
827             {
828                gdcmWarningMacro( "Data size (RGB) is incorrect. Should be " 
829                           << decSize << " / Found " 
830                           << PixelWriteConverter->GetUserDataSize() );
831                return false;
832             }
833             break;
834       }
835    }
836
837    return true;
838 }
839
840 /**
841  * \brief Updates the File to write RAW data (as opposed to RGB data)
842  *       (modifies, when necessary, photochromatic interpretation, 
843  *       bits allocated, Pixels element VR)
844  */ 
845 void FileHelper::SetWriteToRaw()
846 {
847    if ( FileInternal->GetNumberOfScalarComponents() == 3 
848     && !FileInternal->HasLUT() )
849    {
850       SetWriteToRGB();
851    } 
852    else
853    {
854       DataEntry *photInt = CopyDataEntry(0x0028,0x0004,"CS");
855       if (FileInternal->HasLUT() )
856       {
857          photInt->SetString("PALETTE COLOR ");
858       }
859       else
860       {
861          photInt->SetString("MONOCHROME2 ");
862       }
863
864       PixelWriteConverter->SetReadData(PixelReadConverter->GetRaw(),
865                                        PixelReadConverter->GetRawSize());
866
867       std::string vr = "OB";
868       if ( FileInternal->GetBitsAllocated()>8 )
869          vr = "OW";
870       if ( FileInternal->GetBitsAllocated()==24 ) // For RGB ACR files 
871          vr = "OB";
872       DataEntry *pixel = 
873          CopyDataEntry(GetFile()->GetGrPixel(),GetFile()->GetNumPixel(),vr);
874       pixel->SetFlag(DataEntry::FLAG_PIXELDATA);
875       pixel->SetBinArea(PixelWriteConverter->GetData(),false);
876       pixel->SetLength(PixelWriteConverter->GetDataSize());
877
878       Archive->Push(photInt);
879       Archive->Push(pixel);
880
881       photInt->Delete();
882       pixel->Delete();
883    }
884 }
885
886 /**
887  * \brief Updates the File to write RGB data (as opposed to RAW data)
888  *       (modifies, when necessary, photochromatic interpretation, 
889  *       samples per pixel, Planar configuration, 
890  *       bits allocated, bits stored, high bit -ACR 24 bits-
891  *       Pixels element VR, pushes out the LUT, )
892  */ 
893 void FileHelper::SetWriteToRGB()
894 {
895    if ( FileInternal->GetNumberOfScalarComponents()==3 )
896    {
897       PixelReadConverter->BuildRGBImage();
898       
899       DataEntry *spp = CopyDataEntry(0x0028,0x0002,"US");
900       spp->SetString("3 ");
901
902       DataEntry *planConfig = CopyDataEntry(0x0028,0x0006,"US");
903       planConfig->SetString("0 ");
904
905       DataEntry *photInt = CopyDataEntry(0x0028,0x0004,"CS");
906       photInt->SetString("RGB ");
907
908       if ( PixelReadConverter->GetRGB() )
909       {
910          PixelWriteConverter->SetReadData(PixelReadConverter->GetRGB(),
911                                           PixelReadConverter->GetRGBSize());
912       }
913       else // Raw data
914       {
915          PixelWriteConverter->SetReadData(PixelReadConverter->GetRaw(),
916                                           PixelReadConverter->GetRawSize());
917       }
918
919       std::string vr = "OB";
920       if ( FileInternal->GetBitsAllocated()>8 )
921          vr = "OW";
922       if ( FileInternal->GetBitsAllocated()==24 ) // For RGB ACR files 
923          vr = "OB";
924       DataEntry *pixel = 
925          CopyDataEntry(GetFile()->GetGrPixel(),GetFile()->GetNumPixel(),vr);
926       pixel->SetFlag(DataEntry::FLAG_PIXELDATA);
927       pixel->SetBinArea(PixelWriteConverter->GetData(),false);
928       pixel->SetLength(PixelWriteConverter->GetDataSize());
929
930       Archive->Push(spp);
931       Archive->Push(planConfig);
932       Archive->Push(photInt);
933       Archive->Push(pixel);
934
935       spp->Delete();
936       planConfig->Delete();
937       photInt->Delete();
938       pixel->Delete();
939
940       // Remove any LUT
941       Archive->Push(0x0028,0x1101);
942       Archive->Push(0x0028,0x1102);
943       Archive->Push(0x0028,0x1103);
944       Archive->Push(0x0028,0x1201);
945       Archive->Push(0x0028,0x1202);
946       Archive->Push(0x0028,0x1203);
947
948       // push out Palette Color Lookup Table UID, if any
949       Archive->Push(0x0028,0x1199);
950
951       // For old '24 Bits' ACR-NEMA
952       // Thus, we have a RGB image and the bits allocated = 24 and 
953       // samples per pixels = 1 (in the read file)
954       if ( FileInternal->GetBitsAllocated()==24 ) 
955       {
956          DataEntry *bitsAlloc = CopyDataEntry(0x0028,0x0100,"US");
957          bitsAlloc->SetString("8 ");
958
959          DataEntry *bitsStored = CopyDataEntry(0x0028,0x0101,"US");
960          bitsStored->SetString("8 ");
961
962          DataEntry *highBit = CopyDataEntry(0x0028,0x0102,"US");
963          highBit->SetString("7 ");
964
965          Archive->Push(bitsAlloc);
966          Archive->Push(bitsStored);
967          Archive->Push(highBit);
968
969          bitsAlloc->Delete();
970          bitsStored->Delete();
971          highBit->Delete();
972       }
973    }
974    else
975    {
976       SetWriteToRaw();
977    }
978 }
979
980 /**
981  * \brief Restore the File write mode  
982  */ 
983 void FileHelper::RestoreWrite()
984 {
985
986    Archive->Restore(0x0028,0x0002);
987    Archive->Restore(0x0028,0x0004);
988    
989    Archive->Restore(0x0028,0x0006);
990    Archive->Restore(GetFile()->GetGrPixel(),GetFile()->GetNumPixel());
991
992    // For old ACR-NEMA (24 bits problem)
993    Archive->Restore(0x0028,0x0100);
994    Archive->Restore(0x0028,0x0101);
995    Archive->Restore(0x0028,0x0102);
996
997    // For the LUT
998    Archive->Restore(0x0028,0x1101);
999    Archive->Restore(0x0028,0x1102);
1000    Archive->Restore(0x0028,0x1103);
1001    Archive->Restore(0x0028,0x1201);
1002    Archive->Restore(0x0028,0x1202);
1003    Archive->Restore(0x0028,0x1203);
1004
1005    // For the Palette Color Lookup Table UID
1006    Archive->Restore(0x0028,0x1203); 
1007
1008    // group 0002 may be pushed out for ACR-NEMA writting purposes 
1009    Archive->Restore(0x0002,0x0000);
1010    Archive->Restore(0x0002,0x0001);
1011    Archive->Restore(0x0002,0x0002);
1012    Archive->Restore(0x0002,0x0003);
1013    Archive->Restore(0x0002,0x0010);
1014    Archive->Restore(0x0002,0x0012);
1015    Archive->Restore(0x0002,0x0013);
1016    Archive->Restore(0x0002,0x0016);
1017    Archive->Restore(0x0002,0x0100);
1018    Archive->Restore(0x0002,0x0102);
1019
1020 }
1021
1022 /**
1023  * \brief Pushes out the whole group 0002
1024  *        FIXME : better, set a flag to tell the writer not to write it ...
1025  *        FIXME : method should probably have an other name !
1026  *                SetWriteFileTypeToACR is NOT opposed to 
1027  *                SetWriteFileTypeToExplicitVR and SetWriteFileTypeToImplicitVR
1028  */ 
1029 void FileHelper::SetWriteFileTypeToACR()
1030 {
1031    Archive->Push(0x0002,0x0000);
1032    Archive->Push(0x0002,0x0001);
1033    Archive->Push(0x0002,0x0002);
1034    Archive->Push(0x0002,0x0003);
1035    Archive->Push(0x0002,0x0010);
1036    Archive->Push(0x0002,0x0012);
1037    Archive->Push(0x0002,0x0013);
1038    Archive->Push(0x0002,0x0016);
1039    Archive->Push(0x0002,0x0100);
1040    Archive->Push(0x0002,0x0102);
1041 }
1042
1043 /**
1044  * \brief Sets in the File the TransferSyntax to 'JPEG'
1045  */ 
1046 void FileHelper::SetWriteFileTypeToJPEG()
1047 {
1048    std::string ts = Util::DicomString( 
1049       Global::GetTS()->GetSpecialTransferSyntax(TS::JPEGBaselineProcess1) );
1050
1051    DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1052    tss->SetString(ts);
1053
1054    Archive->Push(tss);
1055    tss->Delete();
1056 }
1057
1058 /**
1059  * \brief Sets in the File the TransferSyntax to 'Explicit VR Little Endian"   
1060  */ 
1061 void FileHelper::SetWriteFileTypeToExplicitVR()
1062 {
1063    std::string ts = Util::DicomString( 
1064       Global::GetTS()->GetSpecialTransferSyntax(TS::ExplicitVRLittleEndian) );
1065
1066    DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1067    tss->SetString(ts);
1068    Archive->Push(tss);
1069    tss->Delete();
1070 }
1071
1072 /**
1073  * \brief Sets in the File the TransferSyntax to 'Implicit VR Little Endian"   
1074  */ 
1075 void FileHelper::SetWriteFileTypeToImplicitVR()
1076 {
1077    std::string ts = Util::DicomString(
1078       Global::GetTS()->GetSpecialTransferSyntax(TS::ImplicitVRLittleEndian) );
1079
1080    DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1081    tss->SetString(ts);
1082    Archive->Push(tss);
1083    tss->Delete();
1084 }
1085
1086
1087 /**
1088  * \brief Restore in the File the initial group 0002
1089  */ 
1090 void FileHelper::RestoreWriteFileType()
1091 {
1092 }
1093
1094 /**
1095  * \brief Set the Write not to Libido format
1096  */ 
1097 void FileHelper::SetWriteToLibido()
1098 {
1099    DataEntry *oldRow = FileInternal->GetDataEntry(0x0028, 0x0010);
1100    DataEntry *oldCol = FileInternal->GetDataEntry(0x0028, 0x0011);
1101    
1102    if ( oldRow && oldCol )
1103    {
1104       std::string rows, columns; 
1105
1106       //DataEntry *newRow=DataEntry::New(oldRow->GetDictEntry());
1107       //DataEntry *newCol=DataEntry::New(oldCol->GetDictEntry());
1108       
1109       DataEntry *newRow=DataEntry::New(0x0028, 0x0010, "US");
1110       DataEntry *newCol=DataEntry::New(0x0028, 0x0011, "US");
1111       
1112       newRow->Copy(oldCol);
1113       newCol->Copy(oldRow);
1114
1115       newRow->SetString(oldCol->GetString());
1116       newCol->SetString(oldRow->GetString());
1117
1118       Archive->Push(newRow);
1119       Archive->Push(newCol);
1120
1121       newRow->Delete();
1122       newCol->Delete();
1123    }
1124
1125    DataEntry *libidoCode = CopyDataEntry(0x0008,0x0010,"LO");
1126    libidoCode->SetString("ACRNEMA_LIBIDO_1.1");
1127    Archive->Push(libidoCode);
1128    libidoCode->Delete();
1129 }
1130
1131 /**
1132  * \brief Set the Write not to No Libido format
1133  */ 
1134 void FileHelper::SetWriteToNoLibido()
1135 {
1136    DataEntry *recCode = FileInternal->GetDataEntry(0x0008,0x0010);
1137    if ( recCode )
1138    {
1139       if ( recCode->GetString() == "ACRNEMA_LIBIDO_1.1" )
1140       {
1141          DataEntry *libidoCode = CopyDataEntry(0x0008,0x0010,"LO");
1142          libidoCode->SetString("");
1143          Archive->Push(libidoCode);
1144          libidoCode->Delete();
1145       }
1146    }
1147 }
1148
1149 /**
1150  * \brief Restore the Write format
1151  */ 
1152 void FileHelper::RestoreWriteOfLibido()
1153 {
1154    Archive->Restore(0x0028,0x0010);
1155    Archive->Restore(0x0028,0x0011);
1156    Archive->Restore(0x0008,0x0010);
1157
1158    // Restore 'LibIDO-special' entries, if any
1159    Archive->Restore(0x0028,0x0015);
1160    Archive->Restore(0x0028,0x0016);
1161    Archive->Restore(0x0028,0x0017);
1162    Archive->Restore(0x0028,0x00199);
1163 }
1164
1165 /**
1166  * \brief   Duplicates a DataEntry or creates it.
1167  * @param   group   Group number of the Entry 
1168  * @param   elem  Element number of the Entry
1169  * @param   vr  Value Representation of the Entry
1170  * \return  pointer to the new Bin Entry (NULL when creation failed).
1171  */ 
1172 DataEntry *FileHelper::CopyDataEntry(uint16_t group, uint16_t elem,
1173                                    const VRKey &vr)
1174 {
1175    DocEntry *oldE = FileInternal->GetDocEntry(group, elem);
1176    DataEntry *newE;
1177
1178    if ( oldE && vr != GDCM_VRUNKNOWN ) 
1179       if ( oldE->GetVR() != vr )
1180          oldE = NULL;
1181
1182    if ( oldE )
1183    {
1184       //newE = DataEntry::New(oldE->GetDictEntry());
1185       newE = DataEntry::New(group, elem, vr);
1186       newE->Copy(oldE);
1187    }
1188    else
1189    {
1190       newE = GetFile()->NewDataEntry(group, elem, vr);
1191    }
1192
1193    return newE;
1194 }
1195
1196 /**
1197  * \brief   This method is called automatically, just before writting
1198  *         in order to produce a 'True Dicom V3' image.
1199  *
1200  *         We cannot know *how* the user made the File :
1201  *         (reading an old ACR-NEMA file or a not very clean DICOM file ...) 
1202  *          Just before writting :
1203  *             - we check the Entries
1204  *             - we create the mandatory entries if they are missing
1205  *             - we modify the values if necessary
1206  *             - we push the sensitive entries to the Archive
1207  *          The writing process will restore the entries as they where before 
1208  *          entering FileHelper::CheckMandatoryElements, so the user will always
1209  *          see the entries just as they were before he decided to write.
1210  *
1211  * \note
1212  *       -  Entries whose type is 1 are mandatory, with a mandatory value
1213  *       -  Entries whose type is 1c are mandatory-inside-a-Sequence,
1214  *                             with a mandatory value
1215  *       -  Entries whose type is 2 are mandatory, with an optional value
1216  *       -  Entries whose type is 2c are mandatory-inside-a-Sequence,
1217  *                             with an optional value
1218  *       -  Entries whose type is 3 are optional
1219  * 
1220  * \todo 
1221  *         - warn the user if we had to add some entries :
1222  *         even if a mandatory entry is missing, we add it, with a default value
1223  *         (we don't want to give up the writting process if user forgot to
1224  *         specify Lena's Patient ID, for instance ...)
1225  *         - read the whole PS 3.3 Part of DICOM  (890 pages)
1226  *         and write a *full* checker (probably one method per Modality ...)
1227  *         Any contribution is welcome. 
1228  *         - write a user callable full checker, to allow post reading
1229  *         and/or pre writting image consistency check.           
1230  */ 
1231
1232 /* -------------------------------------------------------------------------------------
1233 To be moved to User's guide / WIKI  ?
1234
1235 We have to deal with 4 *very* different cases :
1236 -1) user created ex nihilo his own image and wants to write it as a Dicom image.
1237     USER_OWN_IMAGE
1238 -2) user modified the pixels of an existing image.
1239    FILTERED_IMAGE
1240 -3) user created a new image, using existing images (eg MIP, MPR, cartography image)
1241    CREATED_IMAGE
1242 -4) user modified/added some tags *without processing* the pixels (anonymization..
1243    UNMODIFIED_PIXELS_IMAGE
1244    
1245 gdcm::FileHelper::CheckMandatoryElements() deals automatically with these cases.
1246
1247 1)2)3)4)
1248 0008 0012 Instance Creation Date
1249 0008 0013 Instance Creation Time
1250 0008 0018 SOP Instance UID
1251 are *always* created with the current values; user has *no* possible intervention on
1252 them.
1253
1254 'Serie Instance UID'(0x0020,0x000e)
1255 'Study Instance UID'(0x0020,0x000d) are kept as is if already exist,
1256                                     created  if it doesn't.
1257  The user is allowed to create his own Series/Studies, 
1258      keeping the same 'Serie Instance UID' / 'Study Instance UID' for various images
1259  Warning :     
1260  The user shouldn't add any image to a 'Manufacturer Serie'
1261      but there is no way no to allow him to do that
1262      
1263  None of the 'shadow elements' are droped out.
1264      
1265
1266 1)
1267 'Conversion Type (0x0008,0x0064) is forced to 'SYN' (Synthetic Image).
1268  
1269 1)3)
1270 'Media Storage SOP Class UID' (0x0002,0x0002)
1271 'SOP Class UID'               (0x0008,0x0016) are set to 
1272                                                [Secondary Capture Image Storage]
1273 'Image Type'                  (0x0008,0x0008) is forced to  "DERIVED\PRIMARY"
1274 Conversion Type               (0x0008,0x0064) is forced to 'SYN' (Synthetic Image)
1275
1276 2)4)
1277 If 'SOP Class UID' exists in the native image  ('true DICOM' image)
1278     we create the 'Source Image Sequence' SeqEntry (0x0008, 0x2112)    
1279     --> 'Referenced SOP Class UID' (0x0008, 0x1150)
1280          whose value is the original 'SOP Class UID'
1281     --> 'Referenced SOP Instance UID' (0x0008, 0x1155)
1282          whose value is the original 'SOP Class UID'
1283
1284 3) TODO : find a trick to allow user to pass to the writter the list of the Dicom images 
1285           or the Series, (or the Study ?) he used to created his image 
1286           (MIP, MPR, cartography image, ...)
1287            These info should be stored (?)
1288           0008 1110 SQ 1 Referenced Study Sequence
1289           0008 1115 SQ 1 Referenced Series Sequence
1290           0008 1140 SQ 1 Referenced Image Sequence
1291        
1292 4) When user *knows* he didn't modified the pixels, we keep some informations unchanged :
1293 'Media Storage SOP Class UID' (0x0002,0x0002)
1294 'SOP Class UID'               (0x0008,0x0016)
1295 'Image Type'                  (0x0008,0x0008)
1296 'Conversion Type'             (0x0008,0x0064)
1297
1298
1299 Bellow follows the full description (hope so !) of the consistency checks performed 
1300 by gdcm::FileHelper::CheckMandatoryElements()
1301
1302
1303 -->'Media Storage SOP Class UID' (0x0002,0x0002)
1304 -->'SOP Class UID'               (0x0008,0x0016) are defaulted to 
1305                                                [Secondary Capture Image Storage]
1306 --> 'Image Type'  (0x0008,0x0008)
1307      is forced to  "DERIVED\PRIMARY"
1308      (The written image is no longer an 'ORIGINAL' one)
1309   Except if user knows he didn't modify the image (e.g. : he just anonymized the file)
1310    
1311  -->  Conversion Type (0x0008,0x0064)
1312      is defaulted to 'SYN' (Synthetic Image)
1313   when *he* knows he created his own image ex nihilo
1314             
1315 --> 'Modality' (0x0008,0x0060)   
1316     is defaulted to "OT" (other) if missing.   
1317     (a fully user created image belongs to *no* modality)
1318       
1319 --> 'Media Storage SOP Instance UID' (0x0002,0x0003)
1320 --> 'Implementation Class UID'       (0x0002,0x0012)
1321     are automatically generated; no user intervention possible
1322
1323 --> 'Serie Instance UID'(0x0020,0x000e)
1324 --> 'Study Instance UID'(0x0020,0x000d) are kept as is if already exist
1325                                              created  if it doesn't.
1326      The user is allowed to create his own Series/Studies, 
1327      keeping the same 'Serie Instance UID' / 'Study Instance UID' 
1328      for various images
1329      Warning :     
1330      The user shouldn't add any image to a 'Manufacturer Serie'
1331      but there is no way no to allowed him to do that 
1332              
1333 --> If 'SOP Class UID' exists in the native image  ('true DICOM' image)
1334     we create the 'Source Image Sequence' SeqEntry (0x0008, 0x2112)
1335     
1336     --> 'Referenced SOP Class UID' (0x0008, 0x1150)
1337          whose value is the original 'SOP Class UID'
1338     --> 'Referenced SOP Instance UID' (0x0008, 0x1155)
1339          whose value is the original 'SOP Class UID'
1340     
1341 --> Bits Stored, Bits Allocated, Hight Bit Position are checked for consistency
1342 --> Pixel Spacing     (0x0028,0x0030) is defaulted to "1.0\1.0"
1343 --> Samples Per Pixel (0x0028,0x0002) is defaulted to 1 (grayscale)
1344
1345 --> Imager Pixel Spacing (0x0018,0x1164) : defaulted to Pixel Spacing value
1346
1347 --> Instance Creation Date, Instance Creation Time are forced to current Date and Time
1348
1349 --> Study Date, Study Time are defaulted to current Date and Time
1350    (they remain unchanged if they exist)
1351
1352 --> Patient Orientation : (0x0020,0x0020), if not present, is deduced from 
1353     Image Orientation (Patient) : (0020|0037) or from
1354     Image Orientation (RET)     : (0020 0035)
1355    
1356 --> Study ID, Series Number, Instance Number, Patient Orientation (Type 2)
1357     are created, with empty value if there are missing.
1358
1359 --> Manufacturer, Institution Name, Patient's Name, (Type 2)
1360     are defaulted with a 'gdcm' value.
1361     
1362 --> Patient ID, Patient's Birth Date, Patient's Sex, (Type 2)
1363 --> Referring Physician's Name  (Type 2)
1364     are created, with empty value if there are missing.
1365
1366  -------------------------------------------------------------------------------------*/
1367
1368 void FileHelper::CheckMandatoryElements()
1369 {
1370    std::string sop =  Util::CreateUniqueUID();
1371
1372    // --------------------- For Meta Elements ---------------------
1373    // just to remember : 'official' 0002 group
1374    if ( WriteType != ACR && WriteType != ACR_LIBIDO )
1375    {
1376      // Group 000002 (Meta Elements) already pushed out
1377   
1378    //0002 0000 UL 1 Meta Group Length
1379    //0002 0001 OB 1 File Meta Information Version
1380    //0002 0002 UI 1 Media Stored SOP Class UID
1381    //0002 0003 UI 1 Media Stored SOP Instance UID
1382    //0002 0010 UI 1 Transfer Syntax UID
1383    //0002 0012 UI 1 Implementation Class UID
1384    //0002 0013 SH 1 Implementation Version Name
1385    //0002 0016 AE 1 Source Application Entity Title
1386    //0002 0100 UI 1 Private Information Creator
1387    //0002 0102 OB 1 Private Information
1388   
1389    // Create them if not found
1390    // Always modify the value
1391    // Push the entries to the archive.
1392       CopyMandatoryEntry(0x0002,0x0000,"0","UL");
1393
1394       DataEntry *e_0002_0001 = CopyDataEntry(0x0002,0x0001, "OB");
1395       e_0002_0001->SetBinArea((uint8_t*)Util::GetFileMetaInformationVersion(),
1396                                false);
1397       e_0002_0001->SetLength(2);
1398       Archive->Push(e_0002_0001);
1399       e_0002_0001->Delete(); 
1400
1401       if ( ContentType == FILTERED_IMAGE || ContentType == UNMODIFIED_PIXELS_IMAGE)
1402       {      
1403    // we keep the original 'Media Storage SOP Class UID', we default it if missing
1404          CheckMandatoryEntry(0x0002,0x0002,"1.2.840.10008.5.1.4.1.1.7","UI"); 
1405       }
1406       else
1407       {
1408    // It's *not* an image comming straight from a source. We force
1409    // 'Media Storage SOP Class UID'  --> [Secondary Capture Image Storage]
1410          CopyMandatoryEntry(0x0002,0x0002,"1.2.840.10008.5.1.4.1.1.7","UI");
1411       }
1412       
1413    // 'Media Storage SOP Instance UID'   
1414       CopyMandatoryEntry(0x0002,0x0003,sop,"UI");
1415       
1416    // 'Implementation Class UID'
1417    // FIXME : in all examples we have, 0x0002,0x0012 is not so long :
1418    //         seems to be Root UID + 4 digits (?)
1419       CopyMandatoryEntry(0x0002,0x0012,Util::CreateUniqueUID(),"UI");
1420
1421    // 'Implementation Version Name'
1422       std::string version = "GDCM ";
1423       version += Util::GetVersion();
1424       CopyMandatoryEntry(0x0002,0x0013,version,"SH");
1425    }
1426
1427    // --------------------- For DataSet ---------------------
1428
1429    if ( ContentType != USER_OWN_IMAGE) // when it's not a user made image
1430    { 
1431    
1432       gdcmDebugMacro( "USER_OWN_IMAGE (1)");
1433     // If 'SOP Class UID' exists ('true DICOM' image)
1434    // we create the 'Source Image Sequence' SeqEntry
1435    // to hold informations about the Source Image
1436   
1437       DataEntry *e_0008_0016 = FileInternal->GetDataEntry(0x0008, 0x0016);
1438       if ( e_0008_0016 )
1439       {
1440       // Create 'Source Image Sequence' SeqEntry
1441 //     SeqEntry *sis = SeqEntry::New (
1442 //            Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x2112) );
1443       SeqEntry *sis = SeqEntry::New (0x0008, 0x2112);
1444       SQItem *sqi = SQItem::New(1);
1445       // (we assume 'SOP Instance UID' exists too) 
1446       // create 'Referenced SOP Class UID'
1447 //     DataEntry *e_0008_1150 = DataEntry::New(
1448 //            Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x1150) );
1449       DataEntry *e_0008_1150 = DataEntry::New(0x0008, 0x1150, "UI");
1450       e_0008_1150->SetString( e_0008_0016->GetString());
1451       sqi->AddEntry(e_0008_1150);
1452       e_0008_1150->Delete();
1453       
1454       // create 'Referenced SOP Instance UID'
1455       DataEntry *e_0008_0018 = FileInternal->GetDataEntry(0x0008, 0x0018);
1456 //      DataEntry *e_0008_1155 = DataEntry::New(
1457 //            Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x1155) );
1458       DataEntry *e_0008_1155 = DataEntry::New(0x0008, 0x1155, "UI");
1459       e_0008_1155->SetString( e_0008_0018->GetString());
1460       sqi->AddEntry(e_0008_1155);
1461       e_0008_1155->Delete();
1462
1463       sis->AddSQItem(sqi,1);
1464       sqi->Delete();
1465
1466       // temporarily replaces any previous 'Source Image Sequence' 
1467       Archive->Push(sis);
1468       sis->Delete();
1469  
1470       // FIXME : is 'Image Type' *really* depending on the presence of 'SOP Class UID'?
1471        if ( ContentType == FILTERED_IMAGE)      
1472       // the user *knows* he just modified the pixels
1473       // the image is no longer an 'Original' one
1474          CopyMandatoryEntry(0x0008,0x0008,"DERIVED\\PRIMARY","CS");    
1475       }
1476    }
1477       
1478    if ( ContentType == FILTERED_IMAGE || ContentType == UNMODIFIED_PIXELS_IMAGE)
1479    {      
1480    // we keep the original 'Media Storage SOP Class UID', we default it if missing (it should be present !)
1481          CheckMandatoryEntry(0x0008,0x0016,"1.2.840.10008.5.1.4.1.1.7","UI");      
1482    }
1483    else
1484    {
1485    // It's *not* an image comming straight from a source. We force
1486    // 'Media Storage SOP Class UID'  --> [Secondary Capture Image Storage]
1487          CopyMandatoryEntry(0x0008,0x0016,"1.2.840.10008.5.1.4.1.1.7", "UI");      
1488    }   
1489
1490    // Push out 'LibIDO-special' entries, if any
1491    Archive->Push(0x0028,0x0015);
1492    Archive->Push(0x0028,0x0016);
1493    Archive->Push(0x0028,0x0017);
1494    Archive->Push(0x0028,0x00199);
1495
1496    // Deal with the pb of (Bits Stored = 12)
1497    // - we're gonna write the image as Bits Stored = 16
1498    if ( FileInternal->GetEntryString(0x0028,0x0100) ==  "12")
1499    {
1500       CopyMandatoryEntry(0x0028,0x0100,"16","US");
1501    }
1502
1503    // Check if user wasn't drunk ;-)
1504
1505    std::ostringstream s;
1506    // check 'Bits Allocated' vs decent values
1507    int nbBitsAllocated = FileInternal->GetBitsAllocated();
1508    if ( nbBitsAllocated == 0 || nbBitsAllocated > 32)
1509    {
1510       CopyMandatoryEntry(0x0028,0x0100,"16","US");
1511       gdcmWarningMacro("(0028,0100) changed from "
1512          << nbBitsAllocated << " to 16 for consistency purpose");
1513       nbBitsAllocated = 16; 
1514    }
1515    // check 'Bits Stored' vs 'Bits Allocated'   
1516    int nbBitsStored = FileInternal->GetBitsStored();
1517    if ( nbBitsStored == 0 || nbBitsStored > nbBitsAllocated )
1518    {
1519       s.str("");
1520       s << nbBitsAllocated;
1521       CopyMandatoryEntry(0x0028,0x0101,s.str(),"US");
1522       gdcmWarningMacro("(0028,0101) changed from "
1523                        << nbBitsStored << " to " << nbBitsAllocated
1524                        << " for consistency purpose" );
1525       nbBitsStored = nbBitsAllocated; 
1526     }
1527    // check 'Hight Bit Position' vs 'Bits Allocated' and 'Bits Stored'
1528    int highBitPosition = FileInternal->GetHighBitPosition();
1529    if ( highBitPosition == 0 || 
1530         highBitPosition > nbBitsAllocated-1 ||
1531         highBitPosition < nbBitsStored-1  )
1532    {
1533       s.str("");
1534       s << nbBitsStored - 1; 
1535       CopyMandatoryEntry(0x0028,0x0102,s.str(),"US");
1536       gdcmWarningMacro("(0028,0102) changed from "
1537                        << highBitPosition << " to " << nbBitsAllocated-1
1538                        << " for consistency purpose");
1539    }
1540
1541    std::string pixelSpacing = FileInternal->GetEntryString(0x0028,0x0030);
1542    if ( pixelSpacing == GDCM_UNFOUND )
1543    {
1544       pixelSpacing = "1.0\\1.0";
1545        // if missing, Pixel Spacing forced to "1.0\1.0"
1546       CopyMandatoryEntry(0x0028,0x0030,pixelSpacing,"DS");
1547    }
1548    
1549    // 'Imager Pixel Spacing' : defaulted to 'Pixel Spacing'
1550    // --> This one is the *legal* one !
1551    if ( ContentType != USER_OWN_IMAGE)
1552    //  we write it only when we are *sure* the image comes from
1553    //         an imager (see also 0008,0x0064)          
1554       CheckMandatoryEntry(0x0018,0x1164,pixelSpacing,"DS");
1555    
1556    // Samples Per Pixel (type 1) : default to grayscale 
1557    CheckMandatoryEntry(0x0028,0x0002,"1","US");
1558
1559    // --- Check UID-related Entries ---
1560  
1561    // At the end, not to overwrite the original ones,
1562    // needed by 'Referenced SOP Instance UID', 'Referenced SOP Class UID'   
1563    // 'SOP Instance UID'  
1564    CopyMandatoryEntry(0x0008,0x0018,sop,"UI");
1565
1566    if ( ContentType == USER_OWN_IMAGE)
1567    {
1568       gdcmDebugMacro( "USER_OWN_IMAGE (2)");   
1569        // Conversion Type.
1570        // Other possible values are :
1571        // See PS 3.3, Page 408
1572    
1573        // DV = Digitized Video
1574        // DI = Digital Interface   
1575        // DF = Digitized Film
1576        // WSD = Workstation
1577        // SD = Scanned Document
1578        // SI = Scanned Image
1579        // DRW = Drawing
1580        // SYN = Synthetic Image
1581            
1582       CheckMandatoryEntry(0x0008,0x0064,"SYN","CS"); // Why not?
1583    } 
1584 /*
1585    if ( ContentType == CREATED_IMAGE)
1586    {
1587    /// \todo : find a trick to pass the Media Storage SOP Instance UID of the images used to create the current image
1588    
1589    }
1590 */
1591            
1592    // ---- The user will never have to take any action on the following ----
1593
1594    // new value for 'SOP Instance UID'
1595    //SetMandatoryEntry(0x0008,0x0018,Util::CreateUniqueUID());
1596
1597    // Instance Creation Date
1598    const std::string &date = Util::GetCurrentDate();
1599    CopyMandatoryEntry(0x0008,0x0012,date,"DA");
1600  
1601    // Instance Creation Time
1602    const std::string &time = Util::GetCurrentTime();
1603    CopyMandatoryEntry(0x0008,0x0013,time,"TM");
1604
1605    // Study Date
1606    CheckMandatoryEntry(0x0008,0x0020,date,"DA");
1607    // Study Time
1608    CheckMandatoryEntry(0x0008,0x0030,time,"TM");
1609
1610    // Accession Number
1611    //CopyMandatoryEntry(0x0008,0x0050,"");
1612    CheckMandatoryEntry(0x0008,0x0050,"","SH");
1613    
1614
1615    // ----- Add Mandatory Entries if missing ---
1616    // Entries whose type is 1 are mandatory, with a mandatory value
1617    // Entries whose type is 1c are mandatory-inside-a-Sequence,
1618    //                          with a mandatory value
1619    // Entries whose type is 2 are mandatory, with an optional value
1620    // Entries whose type is 2c are mandatory-inside-a-Sequence,
1621    //                          with an optional value
1622    // Entries whose type is 3 are optional
1623
1624    // 'Study Instance UID'
1625    // Keep the value if exists
1626    // The user is allowed to create his own Study, 
1627    //          keeping the same 'Study Instance UID' for various images
1628    // The user may add images to a 'Manufacturer Study',
1629    //          adding new Series to an already existing Study 
1630    CheckMandatoryEntry(0x0020,0x000d,Util::CreateUniqueUID(),"UI");
1631
1632    // 'Serie Instance UID'
1633    // Keep the value if exists
1634    // The user is allowed to create his own Series, 
1635    // keeping the same 'Serie Instance UID' for various images
1636    // The user shouldn't add any image to a 'Manufacturer Serie'
1637    // but there is no way no to prevent him for doing that 
1638    CheckMandatoryEntry(0x0020,0x000e,Util::CreateUniqueUID(),"UI");
1639
1640    // Study ID
1641    CheckMandatoryEntry(0x0020,0x0010,"","SH");
1642
1643    // Series Number
1644    CheckMandatoryEntry(0x0020,0x0011,"","IS");
1645
1646    // Instance Number
1647    CheckMandatoryEntry(0x0020,0x0013,"","IS");
1648    
1649    // Patient Orientation
1650    // Can be computed from (0020|0037) :  Image Orientation (Patient)
1651    gdcm::Orientation *o = gdcm::Orientation::New();
1652    std::string ori = o->GetOrientation ( FileInternal );
1653    o->Delete();
1654    if (ori != "\\" && ori != GDCM_UNFOUND)
1655       CheckMandatoryEntry(0x0020,0x0020,ori,"CS");
1656    else   
1657       CheckMandatoryEntry(0x0020,0x0020,"","CS");
1658
1659    // Default Patient Position to HFS
1660    CheckMandatoryEntry(0x0018,0x5100,"HFS","CS");
1661
1662    // Modality : if missing we set it to 'OTher'
1663    CheckMandatoryEntry(0x0008,0x0060,"OT","CS");
1664
1665    // Manufacturer : if missing we set it to 'GDCM Factory'
1666    CheckMandatoryEntry(0x0008,0x0070,"GDCM Factory","LO");
1667
1668    // Institution Name : if missing we set it to 'GDCM Hospital'
1669    CheckMandatoryEntry(0x0008,0x0080,"GDCM Hospital","LO");
1670
1671    // Patient's Name : if missing, we set it to 'GDCM^Patient'
1672    CheckMandatoryEntry(0x0010,0x0010,"GDCM^Patient","PN");
1673
1674    // Patient ID
1675    CheckMandatoryEntry(0x0010,0x0020,"","LO");
1676
1677    // Patient's Birth Date : 'type 2' entry -> must exist, value not mandatory
1678    CheckMandatoryEntry(0x0010,0x0030,"","DA");
1679
1680    // Patient's Sex :'type 2' entry -> must exist, value not mandatory
1681    CheckMandatoryEntry(0x0010,0x0040,"","CS");
1682
1683    // Referring Physician's Name :'type 2' entry -> must exist, value not mandatory
1684    CheckMandatoryEntry(0x0008,0x0090,"","PN");
1685
1686  /*
1687    // Deal with element 0x0000 (group length) of each group.
1688    // First stage : get all the different Groups
1689    
1690   GroupHT grHT;
1691   DocEntry *d = FileInternal->GetFirstEntry();
1692   while(d)
1693   {
1694     grHT[d->GetGroup()] = 0;
1695     d=FileInternal->GetNextEntry();
1696   }
1697   // Second stage : add the missing ones (if any)
1698   for (GroupHT::iterator it = grHT.begin(); it != grHT.end(); ++it)  
1699   {
1700       CheckMandatoryEntry(it->first, 0x0000, "0"); 
1701   }    
1702   // Third stage : update all 'zero level' groups length
1703 */ 
1704
1705
1706
1707 void FileHelper::CheckMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr )
1708 {
1709    DataEntry *entry = FileInternal->GetDataEntry(group,elem);
1710    if ( !entry )
1711    {
1712       //entry = DataEntry::New(Global::GetDicts()->GetDefaultPubDict()->GetEntry(group,elem));
1713       entry = DataEntry::New(group,elem,vr);
1714       entry->SetString(value);
1715       Archive->Push(entry);
1716       entry->Delete();
1717    }    
1718 }
1719
1720 /// \todo : what is it used for ? (FileHelper::SetMandatoryEntry) 
1721 void FileHelper::SetMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr)
1722 {
1723    //DataEntry *entry = DataEntry::New(Global::GetDicts()->GetDefaultPubDict()->GetEntry(group,elem));
1724    DataEntry *entry = DataEntry::New(group,elem,vr);
1725    entry->SetString(value);
1726    Archive->Push(entry);
1727    entry->Delete();
1728 }
1729
1730 void FileHelper::CopyMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr)
1731 {
1732    DataEntry *entry = CopyDataEntry(group,elem,vr);
1733    entry->SetString(value);
1734    Archive->Push(entry);
1735    entry->Delete();
1736 }
1737
1738 /**
1739  * \brief Restore in the File the initial group 0002
1740  */
1741 void FileHelper::RestoreWriteMandatory()
1742 {
1743    // group 0002 may be pushed out for ACR-NEMA writting purposes 
1744    Archive->Restore(0x0002,0x0000);
1745    Archive->Restore(0x0002,0x0001);
1746    Archive->Restore(0x0002,0x0002);
1747    Archive->Restore(0x0002,0x0003);
1748    Archive->Restore(0x0002,0x0010);
1749    Archive->Restore(0x0002,0x0012);
1750    Archive->Restore(0x0002,0x0013);
1751    Archive->Restore(0x0002,0x0016);
1752    Archive->Restore(0x0002,0x0100);
1753    Archive->Restore(0x0002,0x0102);
1754
1755    // FIXME : Check if none is missing !
1756    
1757    Archive->Restore(0x0008,0x0012);
1758    Archive->Restore(0x0008,0x0013);
1759    Archive->Restore(0x0008,0x0016);
1760    Archive->Restore(0x0008,0x0018);
1761    Archive->Restore(0x0008,0x0060);
1762    Archive->Restore(0x0008,0x0070);
1763    Archive->Restore(0x0008,0x0080);
1764    Archive->Restore(0x0008,0x0090);
1765    Archive->Restore(0x0008,0x2112);
1766
1767    Archive->Restore(0x0010,0x0010);
1768    Archive->Restore(0x0010,0x0030);
1769    Archive->Restore(0x0010,0x0040);
1770
1771    Archive->Restore(0x0020,0x000d);
1772    Archive->Restore(0x0020,0x000e);
1773 }
1774
1775
1776 /**
1777  * \brief   CallStartMethod
1778  */
1779 void FileHelper::CallStartMethod()
1780 {
1781    Progress = 0.0f;
1782    Abort    = false;
1783    CommandManager::ExecuteCommand(this,CMD_STARTPROGRESS);
1784 }
1785
1786 /**
1787  * \brief   CallProgressMethod
1788  */
1789 void FileHelper::CallProgressMethod()
1790 {
1791    CommandManager::ExecuteCommand(this,CMD_PROGRESS);
1792 }
1793
1794 /**
1795  * \brief   CallEndMethod
1796  */
1797 void FileHelper::CallEndMethod()
1798 {
1799    Progress = 1.0f;
1800    CommandManager::ExecuteCommand(this,CMD_ENDPROGRESS);
1801 }
1802
1803 //-----------------------------------------------------------------------------
1804 // Private
1805 /**
1806  * \brief Factorization for various forms of constructors.
1807  */
1808 void FileHelper::Initialize()
1809 {
1810    UserFunction = 0;
1811    ContentType = USER_OWN_IMAGE;
1812    
1813    WriteMode = WMODE_RAW;
1814    WriteType = ExplicitVR;
1815
1816    PixelReadConverter  = new PixelReadConvert;
1817    PixelWriteConverter = new PixelWriteConvert;
1818    Archive = new DocEntryArchive( FileInternal );
1819 }
1820
1821 /**
1822  * \brief Reads/[decompresses] the pixels, 
1823  *        *without* making RGB from Palette Colors 
1824  * @return the pixels area, whatever its type 
1825  *         (uint8_t is just for prototyping : feel free to Cast it) 
1826  */ 
1827 uint8_t *FileHelper::GetRaw()
1828 {
1829    PixelReadConverter->SetUserFunction( UserFunction );
1830
1831    uint8_t *raw = PixelReadConverter->GetRaw();
1832    if ( ! raw )
1833    {
1834       // The Raw image migth not be loaded yet:
1835       std::ifstream *fp = FileInternal->OpenFile();
1836       PixelReadConverter->ReadAndDecompressPixelData( fp );
1837       if ( fp ) 
1838          FileInternal->CloseFile();
1839
1840       raw = PixelReadConverter->GetRaw();
1841       if ( ! raw )
1842       {
1843          gdcmWarningMacro( "Read/decompress of pixel data apparently went wrong.");
1844          return 0;
1845       }
1846    }
1847    return raw;
1848 }
1849
1850 //-----------------------------------------------------------------------------
1851 /**
1852  * \brief   Prints the common part of DataEntry, SeqEntry
1853  * @param   os ostream we want to print in
1854  * @param indent (unused)
1855  */
1856 void FileHelper::Print(std::ostream &os, std::string const &)
1857 {
1858    FileInternal->SetPrintLevel(PrintLevel);
1859    FileInternal->Print(os);
1860
1861    if ( FileInternal->IsReadable() )
1862    {
1863       PixelReadConverter->SetPrintLevel(PrintLevel);
1864       PixelReadConverter->Print(os);
1865    }
1866 }
1867
1868 //-----------------------------------------------------------------------------
1869 } // end namespace gdcm