]> Creatis software - gdcm.git/blob - src/gdcmFileHelper.cxx
70034af2efb06763e4c44b8e6ca5952325c0e5a9
[gdcm.git] / src / gdcmFileHelper.cxx
1 /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: gdcmFileHelper.cxx,v $
5   Language:  C++
6
7   Date:      $Date: 2007/04/12 13:22:49 $
8   Version:   $Revision: 1.111 $
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 its (group, element) 
234  *          and modifies its 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 its (group, element) 
249  *          and modifies its 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                                          VRKey const &vr )
274 {
275    return FileInternal->InsertEntryString(content, group, elem, vr);
276 }
277
278 /**
279  * \brief   Modifies the value of a given DataEntry when it exists.
280  *          Creates it with the given value when unexistant.
281  *          A copy of the binArea is made to be kept in the Document.
282  * @param   binArea (binary)value to be set
283  * @param   lgth new value length
284  * @param   group   Group number of the Entry 
285  * @param   elem  Element number of the Entry
286  * @param   vr  Value Represenation of the DataElement to be inserted 
287  * \return  pointer to the modified/created DataEntry (NULL when creation
288  *          failed).
289  */
290 DataEntry *FileHelper::InsertEntryBinArea(uint8_t *binArea, int lgth,
291                                           uint16_t group, uint16_t elem,
292                                           VRKey const &vr )
293 {
294    return FileInternal->InsertEntryBinArea(binArea, lgth, group, elem, vr);
295 }
296
297 /**
298  * \brief   Adds an empty SeqEntry 
299  *          (remove any existing entry with same group,elem)
300  * @param   group   Group number of the Entry 
301  * @param   elem  Element number of the Entry
302  * \return  pointer to the created SeqEntry (NULL when creation
303  *          failed).
304  */
305 SeqEntry *FileHelper::InsertSeqEntry(uint16_t group, uint16_t elem)
306 {
307    return FileInternal->InsertSeqEntry(group, elem);
308 }
309
310 /**
311  * \brief   Get the size of the image data
312  *          If the image can be RGB (with a lut or by default), the size 
313  *          corresponds to the RGB image
314  *         (use GetImageDataRawSize if you want to be sure to get *only*
315  *          the size of the pixels)
316  * @return  The image size
317  */
318 size_t FileHelper::GetImageDataSize()
319 {
320    if ( PixelWriteConverter->GetUserData() )
321    {
322       return PixelWriteConverter->GetUserDataSize();
323    }
324    return PixelReadConverter->GetRGBSize();
325 }
326
327 /**
328  * \brief   Get the size of the image data.
329  *          If the image could be converted to RGB using a LUT, 
330  *          this transformation is not taken into account by GetImageDataRawSize
331  *          (use GetImageDataSize if you wish)
332  * @return  The raw image size
333  */
334 size_t FileHelper::GetImageDataRawSize()
335 {
336    if ( PixelWriteConverter->GetUserData() )
337    {
338       return PixelWriteConverter->GetUserDataSize();
339    }
340    return PixelReadConverter->GetRawSize();
341 }
342
343 /**
344  * \brief brings pixels into memory :  
345  *          - Allocates necessary memory,
346  *          - Reads the pixels from disk (uncompress if necessary),
347  *          - Transforms YBR pixels, if any, into RGB pixels,
348  *          - Transforms 3 planes R, G, B, if any, into a single RGB Plane
349  *          - Transforms single Grey plane + 3 Palettes into a RGB Plane
350  *          - Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
351  * @return  Pointer to newly allocated pixel data.
352  *          (uint8_t is just for prototyping. feel free to cast)
353  *          NULL if alloc fails 
354  */
355 uint8_t *FileHelper::GetImageData()
356 {
357    if ( PixelWriteConverter->GetUserData() )
358    {
359       return PixelWriteConverter->GetUserData();
360    }
361
362    if ( ! GetRaw() )
363    {
364       // If the decompression failed nothing can be done.
365       return 0;
366    }
367
368    if ( FileInternal->HasLUT() && PixelReadConverter->BuildRGBImage() )
369    {
370       return PixelReadConverter->GetRGB();
371    }
372    else
373    {
374       // When no LUT or LUT conversion fails, return the Raw
375       return PixelReadConverter->GetRaw();
376    }
377 }
378
379 /**
380  * \brief brings pixels into memory :  
381  *          - Allocates necessary memory, 
382  *          - Transforms YBR pixels (if any) into RGB pixels
383  *          - Transforms 3 planes R, G, B  (if any) into a single RGB Plane
384  *          - Copies the pixel data (image[s]/volume[s]) to newly allocated zone. 
385  *          - DOES NOT transform Grey plane + 3 Palettes into a RGB Plane
386  * @return  Pointer to newly allocated pixel data.
387  *          (uint8_t is just for prototyping. feel free to cast)
388  *          NULL if alloc fails
389  */
390 uint8_t *FileHelper::GetImageDataRaw ()
391 {
392    return GetRaw();
393 }
394
395 //#ifndef GDCM_LEGACY_REMOVE
396 /*
397  * \brief   Useless function, since PixelReadConverter forces us 
398  *          copy the Pixels anyway.  
399  *          Reads the pixels from disk (uncompress if necessary),
400  *          Transforms YBR pixels, if any, into RGB pixels
401  *          Transforms 3 planes R, G, B, if any, into a single RGB Plane
402  *          Transforms single Grey plane + 3 Palettes into a RGB Plane   
403  *          Copies at most MaxSize bytes of pixel data to caller allocated
404  *          memory space.
405  * \warning This function allows people that want to build a volume
406  *          from an image stack *not to* have, first to get the image pixels, 
407  *          and then move them to the volume area.
408  *          It's absolutely useless for any VTK user since vtk chooses 
409  *          to invert the lines of an image, that is the last line comes first
410  *          (for some axis related reasons?). Hence he will have 
411  *          to load the image line by line, starting from the end.
412  *          VTK users have to call GetImageData
413  *     
414  * @param   destination Address (in caller's memory space) at which the
415  *          pixel data should be copied
416  * @param   maxSize Maximum number of bytes to be copied. When MaxSize
417  *          is not sufficient to hold the pixel data the copy is not
418  *          executed (i.e. no partial copy).
419  * @return  On success, the number of bytes actually copied. Zero on
420  *          failure e.g. MaxSize is lower than necessary.
421  */
422  /*
423 size_t FileHelper::GetImageDataIntoVector (void *destination, size_t maxSize)
424 {
425    if ( ! GetRaw() )
426    {
427       // If the decompression failed nothing can be done.
428       return 0;
429    }
430
431    if ( FileInternal->HasLUT() && PixelReadConverter->BuildRGBImage() )
432    {
433       if ( PixelReadConverter->GetRGBSize() > maxSize )
434       {
435          gdcmWarningMacro( "Pixel data bigger than caller's expected MaxSize");
436          return 0;
437       }
438       memcpy( destination,
439               (void*)PixelReadConverter->GetRGB(),
440               PixelReadConverter->GetRGBSize() );
441       return PixelReadConverter->GetRGBSize();
442    }
443
444    // Either no LUT conversion necessary or LUT conversion failed
445    if ( PixelReadConverter->GetRawSize() > maxSize )
446    {
447       gdcmWarningMacro( "Pixel data bigger than caller's expected MaxSize");
448       return 0;
449    }
450    memcpy( destination,
451            (void *)PixelReadConverter->GetRaw(),
452            PixelReadConverter->GetRawSize() );
453    return PixelReadConverter->GetRawSize();
454 }
455 */
456 //#endif
457
458 /**
459  * \brief   Points the internal pointer to the callers inData
460  *          image representation, BUT WITHOUT COPYING THE DATA.
461  *          'image' Pixels are presented as C-like 2D arrays : line per line.
462  *          'volume'Pixels are presented as C-like 3D arrays : plane per plane 
463  * \warning Since the pixels are not copied, it is the caller's responsability
464  *          not to deallocate its data before gdcm uses them (e.g. with
465  *          the Write() method )
466  * @param inData user supplied pixel area (uint8_t* is just for the compiler.
467  *               user is allowed to pass any kind of pixelsn since the size is
468  *               given in bytes) 
469  * @param expectedSize total image size, *in Bytes*
470  */
471 void FileHelper::SetImageData(uint8_t *inData, size_t expectedSize)
472 {
473    SetUserData(inData, expectedSize);
474 }
475
476 /**
477  * \brief   Set the image data defined by the user
478  * \warning When writting the file, this data are get as default data to write
479  * @param inData user supplied pixel area (uint8_t* is just for the compiler.
480  *               user is allowed to pass any kind of pixels since the size is
481  *               given in bytes) 
482  * @param expectedSize total image size, *in Bytes* 
483  */
484 void FileHelper::SetUserData(uint8_t *inData, size_t expectedSize)
485 {
486    PixelWriteConverter->SetUserData(inData, expectedSize);
487 }
488
489 /**
490  * \brief   Get the image data defined by the user
491  * \warning When writting the file, this data are get as default data to write
492  */
493 uint8_t *FileHelper::GetUserData()
494 {
495    return PixelWriteConverter->GetUserData();
496 }
497
498 /**
499  * \brief   Get the image data size defined by the user
500  * \warning When writting the file, this data are get as default data to write
501  */
502 size_t FileHelper::GetUserDataSize()
503 {
504    return PixelWriteConverter->GetUserDataSize();
505 }
506
507 /**
508  * \brief   Get the image data from the file.
509  *          If a LUT is found, the data are expanded to be RGB
510  */
511 uint8_t *FileHelper::GetRGBData()
512 {
513    return PixelReadConverter->GetRGB();
514 }
515
516 /**
517  * \brief   Get the image data size from the file.
518  *          If a LUT is found, the data are expanded to be RGB
519  */
520 size_t FileHelper::GetRGBDataSize()
521 {
522    return PixelReadConverter->GetRGBSize();
523 }
524
525 /**
526  * \brief   Get the image data from the file.
527  *          Even when a LUT is found, the data are not expanded to RGB!
528  */
529 uint8_t *FileHelper::GetRawData()
530 {
531    return PixelReadConverter->GetRaw();
532 }
533
534 /**
535  * \brief   Get the image data size from the file.
536  *          Even when a LUT is found, the data are not expanded to RGB!
537  */
538 size_t FileHelper::GetRawDataSize()
539 {
540    return PixelReadConverter->GetRawSize();
541 }
542
543 /**
544  * \brief Access to the underlying \ref PixelReadConverter RGBA LUT
545  */
546 uint8_t* FileHelper::GetLutRGBA()
547 {
548    if ( PixelReadConverter->GetLutRGBA() ==0 )
549       PixelReadConverter->BuildLUTRGBA();
550    return PixelReadConverter->GetLutRGBA();
551 }
552
553 /**
554  * \brief Access to the underlying \ref PixelReadConverter RGBA LUT Item Number
555  */
556 int FileHelper::GetLutItemNumber()
557 {
558    return PixelReadConverter->GetLutItemNumber();
559 }
560
561 /**
562  * \brief Access to the underlying \ref PixelReadConverter RGBA LUT Item Size
563  */
564 int FileHelper::GetLutItemSize()
565 {
566    return PixelReadConverter->GetLutItemSize();
567 }
568
569 /**
570  * \brief Writes on disk A SINGLE Dicom file
571  *        NO test is performed on  processor "Endiannity".
572  *        It's up to the user to call his Reader properly
573  * @param fileName name of the file to be created
574  *                 (any already existing file is over written)
575  * @return false if write fails
576  */
577 bool FileHelper::WriteRawData(std::string const &fileName)
578 {
579    std::ofstream fp1(fileName.c_str(), std::ios::out | std::ios::binary );
580    if (!fp1)
581    {
582       gdcmWarningMacro( "Fail to open (write) file:" << fileName.c_str());
583       return false;
584    }
585
586    if ( PixelWriteConverter->GetUserData() )
587    {
588       fp1.write( (char *)PixelWriteConverter->GetUserData(), 
589                  PixelWriteConverter->GetUserDataSize() );
590    }
591    else if ( PixelReadConverter->GetRGB() )
592    {
593       fp1.write( (char *)PixelReadConverter->GetRGB(), 
594                  PixelReadConverter->GetRGBSize());
595    }
596    else if ( PixelReadConverter->GetRaw() )
597    {
598       fp1.write( (char *)PixelReadConverter->GetRaw(), 
599                  PixelReadConverter->GetRawSize());
600    }
601    else
602    {
603       gdcmErrorMacro( "Nothing written." );
604    }
605
606    fp1.close();
607
608    return true;
609 }
610
611 /**
612  * \brief Writes on disk A SINGLE Dicom file, 
613  *        using the Implicit Value Representation convention
614  *        NO test is performed on  processor "Endianity".
615  * @param fileName name of the file to be created
616  *                 (any already existing file is overwritten)
617  * @return false if write fails
618  */
619
620 bool FileHelper::WriteDcmImplVR (std::string const &fileName)
621 {
622    SetWriteTypeToDcmImplVR();
623    return Write(fileName);
624 }
625
626 /**
627 * \brief Writes on disk A SINGLE Dicom file, 
628  *        using the Explicit Value Representation convention
629  *        NO test is performed on  processor "Endiannity". 
630  * @param fileName name of the file to be created
631  *                 (any already existing file is overwritten)
632  * @return false if write fails
633  */
634
635 bool FileHelper::WriteDcmExplVR (std::string const &fileName)
636 {
637    SetWriteTypeToDcmExplVR();
638    return Write(fileName);
639 }
640
641 /**
642  * \brief Writes on disk A SINGLE Dicom file, 
643  *        using the ACR-NEMA convention
644  *        NO test is performed on  processor "Endiannity".
645  *        (a l'attention des logiciels cliniques 
646  *        qui ne prennent en entrée QUE des images ACR ...
647  * \warning if a DICOM_V3 header is supplied,
648  *         groups < 0x0008 and shadow groups are ignored
649  * \warning NO TEST is performed on processor "Endiannity".
650  * @param fileName name of the file to be created
651  *                 (any already existing file is overwritten)
652  * @return false if write fails
653  */
654
655 bool FileHelper::WriteAcr (std::string const &fileName)
656 {
657    SetWriteTypeToAcr();
658    return Write(fileName);
659 }
660
661 /**
662  * \brief Writes on disk A SINGLE Dicom file, 
663  * @param fileName name of the file to be created
664  *                 (any already existing file is overwritten)
665  * @return false if write fails
666  */
667 bool FileHelper::Write(std::string const &fileName)
668 {
669
670    CheckMandatoryElements(); //called once, here !
671    
672    bool flag = false;
673    DocEntry *e;   
674    switch(WriteType)
675    {
676       case ImplicitVR:
677          SetWriteFileTypeToImplicitVR();
678          break;
679  
680       case Unknown:  // should never happen; ExplicitVR is the default value
681       case ExplicitVR:
682
683    // User should ask gdcm to write an image in Explicit VR mode
684    // only when he is sure *all* the VR of *all* the DataElements is known.
685    // i.e : when there are *only* Public Groups
686    // or *all* the Shadow Groups are fully described in the relevant Shadow
687    // Dictionnary
688    // Let's just *dream* about it; *never* trust a user !
689    // We turn to Implicit VR if at least the VR of one element is unknown.
690    
691  
692          e = FileInternal->GetFirstEntry();
693          while (e != 0)
694          {
695             if (e->GetVR() == "  ")  
696             {
697                SetWriteTypeToDcmImplVR();
698                SetWriteFileTypeToImplicitVR();
699                flag = true;
700                break;         
701             } 
702             e = FileInternal->GetNextEntry();
703          }        
704
705          if (!flag)
706          {
707             SetWriteFileTypeToExplicitVR();
708          }
709          break;
710
711          SetWriteFileTypeToExplicitVR(); // to see JPRx
712   break;
713       case ACR:
714       case ACR_LIBIDO:
715       // NOTHING is done here just for LibIDO.
716       // Just to avoid further trouble if user creates a file ex-nihilo,
717       // wants to write it as an ACR-NEMA file,
718       // and forgets to create any Entry belonging to group 0008
719       // (shame on him !)
720       // We add Recognition Code (RET)
721         if ( ! FileInternal->GetDataEntry(0x0008, 0x0010) )
722             FileInternal->InsertEntryString("ACR-NEMA V1.0 ", 
723                                              0x0008, 0x0010, "LO");
724          SetWriteFileTypeToACR();
725         // SetWriteFileTypeToImplicitVR(); // ACR IS implicit VR !
726          break;
727  
728       /// \todo FIXME : JPEG may be either ExplicitVR or ImplicitVR
729       case JPEG:
730          SetWriteFileTypeToJPEG();
731          break;
732
733        case JPEG2000:
734          SetWriteFileTypeToJPEG2000();
735          break;
736    }
737
738    // --------------------------------------------------------------
739    // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
740    //
741    // if recognition code tells us we dealt with a LibIDO image
742    // we reproduce on disk the switch between lineNumber and columnNumber
743    // just before writting ...
744    /// \todo the best trick would be *change* the recognition code
745    ///       but pb expected if user deals with, e.g. COMPLEX images
746    
747    if ( WriteType == ACR_LIBIDO )
748    {
749       SetWriteToLibido();
750    }
751    else
752    {
753       SetWriteToNoLibido();
754    }
755    // ----------------- End of Special Patch ----------------
756   
757    switch(WriteMode)
758    {
759       case WMODE_RAW :
760          SetWriteToRaw(); // modifies and pushes to the archive, when necessary
761          break;
762       case WMODE_RGB :
763          SetWriteToRGB(); // modifies and pushes to the archive, when necessary
764          break;
765    }
766
767    bool check = CheckWriteIntegrity(); // verifies length
768    if (WriteType == JPEG || WriteType == JPEG2000) check = true;
769    if (check)
770    {
771       check = FileInternal->Write(fileName,WriteType);
772    }
773
774    RestoreWrite(); 
775   // RestoreWriteFileType();
776   // RestoreWriteMandatory();
777    
778
779    // --------------------------------------------------------------
780    // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
781    // 
782    // ...and we restore the header to be Dicom Compliant again 
783    // just after writting
784    RestoreWriteOfLibido();
785    // ----------------- End of Special Patch ----------------
786
787    return check;
788 }
789
790 //-----------------------------------------------------------------------------
791 // Protected
792 /**
793  * \brief Checks the write integrity
794  *
795  * The tests made are :
796  *  - verify the size of the image to write with the possible write
797  *    when the user set an image data
798  * @return true if check is successfull
799  */
800 bool FileHelper::CheckWriteIntegrity()
801 {
802    if ( PixelWriteConverter->GetUserData() )
803    {
804       int numberBitsAllocated = FileInternal->GetBitsAllocated();
805       if ( numberBitsAllocated == 0 || numberBitsAllocated == 12 )
806       {
807          gdcmWarningMacro( "numberBitsAllocated changed from " 
808                           << numberBitsAllocated << " to 16 " 
809                           << " for consistency purpose" );
810          numberBitsAllocated = 16;
811       }
812
813       size_t decSize = FileInternal->GetXSize()
814                      * FileInternal->GetYSize() 
815                      * FileInternal->GetZSize()
816                      * FileInternal->GetTSize()     
817                      * FileInternal->GetSamplesPerPixel()
818                      * ( numberBitsAllocated / 8 );
819       size_t rgbSize = decSize;
820       if ( FileInternal->HasLUT() )
821          rgbSize = decSize * 3;
822
823       switch(WriteMode)
824       {
825          case WMODE_RAW :
826             if ( decSize!=PixelWriteConverter->GetUserDataSize() )
827             {
828                gdcmWarningMacro( "Data size (Raw) is incorrect. Should be " 
829                            << decSize << " / Found :" 
830                            << PixelWriteConverter->GetUserDataSize() );
831                return false;
832             }
833             break;
834          case WMODE_RGB :
835             if ( rgbSize!=PixelWriteConverter->GetUserDataSize() )
836             {
837                gdcmWarningMacro( "Data size (RGB) is incorrect. Should be " 
838                           << decSize << " / Found " 
839                           << PixelWriteConverter->GetUserDataSize() );
840                return false;
841             }
842             break;
843       }
844    }
845
846    return true;
847 }
848
849 /**
850  * \brief Updates the File to write RAW data (as opposed to RGB data)
851  *       (modifies, when necessary, photochromatic interpretation, 
852  *       bits allocated, Pixels element VR)
853  */ 
854 void FileHelper::SetWriteToRaw()
855 {
856    if ( FileInternal->GetNumberOfScalarComponents() == 3 
857     && !FileInternal->HasLUT() )
858    {
859       SetWriteToRGB();
860    } 
861    else
862    {
863       DataEntry *photInt = CopyDataEntry(0x0028,0x0004,"CS");
864       if (FileInternal->HasLUT() )
865       {
866          photInt->SetString("PALETTE COLOR ");
867       }
868       else
869       {
870          photInt->SetString("MONOCHROME2 ");
871       }
872
873       PixelWriteConverter->SetReadData(PixelReadConverter->GetRaw(),
874                                        PixelReadConverter->GetRawSize());
875
876       std::string vr = "OB";
877       if ( FileInternal->GetBitsAllocated()>8 )
878          vr = "OW";
879       if ( FileInternal->GetBitsAllocated()==24 ) // For RGB ACR files 
880          vr = "OB";
881        // For non RAW data. Mainly JPEG
882       if( WriteType == JPEG || WriteType == JPEG2000)
883       {
884          vr = "OW";
885       }
886       DataEntry *pixel = 
887          CopyDataEntry(GetFile()->GetGrPixel(),GetFile()->GetNumPixel(),vr);
888       pixel->SetFlag(DataEntry::FLAG_PIXELDATA);
889       pixel->SetBinArea(PixelWriteConverter->GetData(),false);
890       pixel->SetLength(PixelWriteConverter->GetDataSize());
891
892       Archive->Push(photInt);
893       Archive->Push(pixel);
894
895       photInt->Delete();
896       pixel->Delete();
897    }
898 }
899
900 /**
901  * \brief Updates the File to write RGB data (as opposed to RAW data)
902  *       (modifies, when necessary, photochromatic interpretation, 
903  *       samples per pixel, Planar configuration, 
904  *       bits allocated, bits stored, high bit -ACR 24 bits-
905  *       Pixels element VR, pushes out the LUT, )
906  */ 
907 void FileHelper::SetWriteToRGB()
908 {
909    if ( FileInternal->GetNumberOfScalarComponents()==3 )
910    {
911       PixelReadConverter->BuildRGBImage();
912       
913       DataEntry *spp = CopyDataEntry(0x0028,0x0002,"US");
914       spp->SetString("3 ");
915
916       DataEntry *planConfig = CopyDataEntry(0x0028,0x0006,"US");
917       planConfig->SetString("0 ");
918
919       DataEntry *photInt = CopyDataEntry(0x0028,0x0004,"CS");
920       photInt->SetString("RGB ");
921
922       if ( PixelReadConverter->GetRGB() )
923       {
924          PixelWriteConverter->SetReadData(PixelReadConverter->GetRGB(),
925                                           PixelReadConverter->GetRGBSize());
926       }
927       else // Raw data
928       {
929          PixelWriteConverter->SetReadData(PixelReadConverter->GetRaw(),
930                                           PixelReadConverter->GetRawSize());
931       }
932
933       std::string vr = "OB";
934       if ( FileInternal->GetBitsAllocated()>8 )
935          vr = "OW";
936       if ( FileInternal->GetBitsAllocated()==24 ) // For RGB ACR files 
937          vr = "OB";
938       DataEntry *pixel = 
939          CopyDataEntry(GetFile()->GetGrPixel(),GetFile()->GetNumPixel(),vr);
940       pixel->SetFlag(DataEntry::FLAG_PIXELDATA);
941       pixel->SetBinArea(PixelWriteConverter->GetData(),false);
942       pixel->SetLength(PixelWriteConverter->GetDataSize());
943
944       Archive->Push(spp);
945       Archive->Push(planConfig);
946       Archive->Push(photInt);
947       Archive->Push(pixel);
948
949       spp->Delete();
950       planConfig->Delete();
951       photInt->Delete();
952       pixel->Delete();
953
954       // Remove any LUT
955       Archive->Push(0x0028,0x1101);
956       Archive->Push(0x0028,0x1102);
957       Archive->Push(0x0028,0x1103);
958       Archive->Push(0x0028,0x1201);
959       Archive->Push(0x0028,0x1202);
960       Archive->Push(0x0028,0x1203);
961
962       // push out Palette Color Lookup Table UID, if any
963       Archive->Push(0x0028,0x1199);
964
965       // For old '24 Bits' ACR-NEMA
966       // Thus, we have a RGB image and the bits allocated = 24 and 
967       // samples per pixels = 1 (in the read file)
968       if ( FileInternal->GetBitsAllocated()==24 ) 
969       {
970          DataEntry *bitsAlloc = CopyDataEntry(0x0028,0x0100,"US");
971          bitsAlloc->SetString("8 ");
972
973          DataEntry *bitsStored = CopyDataEntry(0x0028,0x0101,"US");
974          bitsStored->SetString("8 ");
975
976          DataEntry *highBit = CopyDataEntry(0x0028,0x0102,"US");
977          highBit->SetString("7 ");
978
979          Archive->Push(bitsAlloc);
980          Archive->Push(bitsStored);
981          Archive->Push(highBit);
982
983          bitsAlloc->Delete();
984          bitsStored->Delete();
985          highBit->Delete();
986       }
987    }
988    else
989    {
990       SetWriteToRaw();
991    }
992 }
993
994 /**
995  * \brief Restore the File write mode  
996  */ 
997 void FileHelper::RestoreWrite()
998 {
999
1000    Archive->Restore(0x0028,0x0002);
1001    Archive->Restore(0x0028,0x0004);
1002    
1003    Archive->Restore(0x0028,0x0006);
1004    Archive->Restore(GetFile()->GetGrPixel(),GetFile()->GetNumPixel());
1005
1006    // For old ACR-NEMA (24 bits problem)
1007    Archive->Restore(0x0028,0x0100);
1008    Archive->Restore(0x0028,0x0101);
1009    Archive->Restore(0x0028,0x0102);
1010
1011    // For the LUT
1012    Archive->Restore(0x0028,0x1101);
1013    Archive->Restore(0x0028,0x1102);
1014    Archive->Restore(0x0028,0x1103);
1015    Archive->Restore(0x0028,0x1201);
1016    Archive->Restore(0x0028,0x1202);
1017    Archive->Restore(0x0028,0x1203);
1018
1019    // For the Palette Color Lookup Table UID
1020    Archive->Restore(0x0028,0x1203); 
1021
1022    // group 0002 may be pushed out for ACR-NEMA writting purposes 
1023    Archive->Restore(0x0002,0x0000);
1024    Archive->Restore(0x0002,0x0001);
1025    Archive->Restore(0x0002,0x0002);
1026    Archive->Restore(0x0002,0x0003);
1027    Archive->Restore(0x0002,0x0010);
1028    Archive->Restore(0x0002,0x0012);
1029    Archive->Restore(0x0002,0x0013);
1030    Archive->Restore(0x0002,0x0016);
1031    Archive->Restore(0x0002,0x0100);
1032    Archive->Restore(0x0002,0x0102);
1033
1034 }
1035
1036 /**
1037  * \brief Pushes out the whole group 0002
1038  *        FIXME : better, set a flag to tell the writer not to write it ...
1039  *        FIXME : method should probably have an other name !
1040  *                SetWriteFileTypeToACR is NOT opposed to 
1041  *                SetWriteFileTypeToExplicitVR and SetWriteFileTypeToImplicitVR
1042  */ 
1043 void FileHelper::SetWriteFileTypeToACR()
1044 {
1045    Archive->Push(0x0002,0x0000);
1046    Archive->Push(0x0002,0x0001);
1047    Archive->Push(0x0002,0x0002);
1048    Archive->Push(0x0002,0x0003);
1049    Archive->Push(0x0002,0x0010);
1050    Archive->Push(0x0002,0x0012);
1051    Archive->Push(0x0002,0x0013);
1052    Archive->Push(0x0002,0x0016);
1053    Archive->Push(0x0002,0x0100);
1054    Archive->Push(0x0002,0x0102);
1055 }
1056
1057  /**
1058   * \brief Sets in the File the TransferSyntax to 'JPEG2000'
1059   */
1060 void FileHelper::SetWriteFileTypeToJPEG2000()
1061 {
1062    std::string ts = Util::DicomString(
1063    Global::GetTS()->GetSpecialTransferSyntax(TS::JPEG2000Lossless) );
1064    
1065    DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1066    tss->SetString(ts);
1067
1068    Archive->Push(tss);
1069    tss->Delete();   
1070 }
1071
1072 /**
1073  * \brief Sets in the File the TransferSyntax to 'JPEG'
1074  */ 
1075 void FileHelper::SetWriteFileTypeToJPEG()
1076 {
1077    std::string ts = Util::DicomString( 
1078       Global::GetTS()->GetSpecialTransferSyntax(TS::JPEGBaselineProcess1) );
1079
1080    DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1081    tss->SetString(ts);
1082
1083    Archive->Push(tss);
1084    tss->Delete();
1085 }
1086
1087 /**
1088  * \brief Sets in the File the TransferSyntax to 'Explicit VR Little Endian"   
1089  */ 
1090 void FileHelper::SetWriteFileTypeToExplicitVR()
1091 {
1092    std::string ts = Util::DicomString( 
1093       Global::GetTS()->GetSpecialTransferSyntax(TS::ExplicitVRLittleEndian) );
1094
1095    DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1096    tss->SetString(ts);
1097    Archive->Push(tss);
1098    tss->Delete();
1099 }
1100
1101 /**
1102  * \brief Sets in the File the TransferSyntax to 'Implicit VR Little Endian"   
1103  */ 
1104 void FileHelper::SetWriteFileTypeToImplicitVR()
1105 {
1106    std::string ts = Util::DicomString(
1107       Global::GetTS()->GetSpecialTransferSyntax(TS::ImplicitVRLittleEndian) );
1108
1109    DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1110    tss->SetString(ts);
1111    Archive->Push(tss);
1112    tss->Delete();
1113 }
1114
1115
1116 /**
1117  * \brief Restore in the File the initial group 0002
1118  */ 
1119 void FileHelper::RestoreWriteFileType()
1120 {
1121 }
1122
1123 /**
1124  * \brief Set the Write not to Libido format
1125  */ 
1126 void FileHelper::SetWriteToLibido()
1127 {
1128    DataEntry *oldRow = FileInternal->GetDataEntry(0x0028, 0x0010);
1129    DataEntry *oldCol = FileInternal->GetDataEntry(0x0028, 0x0011);
1130    
1131    if ( oldRow && oldCol )
1132    {
1133       std::string rows, columns; 
1134
1135       //DataEntry *newRow=DataEntry::New(oldRow->GetDictEntry());
1136       //DataEntry *newCol=DataEntry::New(oldCol->GetDictEntry());
1137       
1138       DataEntry *newRow=DataEntry::New(0x0028, 0x0010, "US");
1139       DataEntry *newCol=DataEntry::New(0x0028, 0x0011, "US");
1140       
1141       newRow->Copy(oldCol);
1142       newCol->Copy(oldRow);
1143
1144       newRow->SetString(oldCol->GetString());
1145       newCol->SetString(oldRow->GetString());
1146
1147       Archive->Push(newRow);
1148       Archive->Push(newCol);
1149
1150       newRow->Delete();
1151       newCol->Delete();
1152    }
1153
1154    DataEntry *libidoCode = CopyDataEntry(0x0008,0x0010,"LO");
1155    libidoCode->SetString("ACRNEMA_LIBIDO_1.1");
1156    Archive->Push(libidoCode);
1157    libidoCode->Delete();
1158 }
1159
1160 /**
1161  * \brief Set the Write not to No Libido format
1162  */ 
1163 void FileHelper::SetWriteToNoLibido()
1164 {
1165    DataEntry *recCode = FileInternal->GetDataEntry(0x0008,0x0010);
1166    if ( recCode )
1167    {
1168       if ( recCode->GetString() == "ACRNEMA_LIBIDO_1.1" )
1169       {
1170          DataEntry *libidoCode = CopyDataEntry(0x0008,0x0010,"LO");
1171          libidoCode->SetString("");
1172          Archive->Push(libidoCode);
1173          libidoCode->Delete();
1174       }
1175    }
1176 }
1177
1178 /**
1179  * \brief Restore the Write format
1180  */ 
1181 void FileHelper::RestoreWriteOfLibido()
1182 {
1183    Archive->Restore(0x0028,0x0010);
1184    Archive->Restore(0x0028,0x0011);
1185    Archive->Restore(0x0008,0x0010);
1186
1187    // Restore 'LibIDO-special' entries, if any
1188    Archive->Restore(0x0028,0x0015);
1189    Archive->Restore(0x0028,0x0016);
1190    Archive->Restore(0x0028,0x0017);
1191    Archive->Restore(0x0028,0x00199);
1192 }
1193
1194 /**
1195  * \brief   Duplicates a DataEntry or creates it.
1196  * @param   group   Group number of the Entry 
1197  * @param   elem  Element number of the Entry
1198  * @param   vr  Value Representation of the Entry
1199  * \return  pointer to the new Bin Entry (NULL when creation failed).
1200  */ 
1201 DataEntry *FileHelper::CopyDataEntry(uint16_t group, uint16_t elem,
1202                                    const VRKey &vr)
1203 {
1204    DocEntry *oldE = FileInternal->GetDocEntry(group, elem);
1205    DataEntry *newE;
1206
1207    if ( oldE && vr != GDCM_VRUNKNOWN ) 
1208       if ( oldE->GetVR() != vr )
1209          oldE = NULL;
1210
1211    if ( oldE )
1212    {
1213       //newE = DataEntry::New(oldE->GetDictEntry());
1214       newE = DataEntry::New(group, elem, vr);
1215       newE->Copy(oldE);
1216    }
1217    else
1218    {
1219       newE = GetFile()->NewDataEntry(group, elem, vr);
1220    }
1221
1222    return newE;
1223 }
1224
1225 /**
1226  * \brief   This method is called automatically, just before writting
1227  *         in order to produce a 'True Dicom V3' image.
1228  *
1229  *         We cannot know *how* the user made the File :
1230  *         (reading an old ACR-NEMA file or a not very clean DICOM file ...) 
1231  *          Just before writting :
1232  *             - we check the Entries
1233  *             - we create the mandatory entries if they are missing
1234  *             - we modify the values if necessary
1235  *             - we push the sensitive entries to the Archive
1236  *          The writing process will restore the entries as they where before 
1237  *          entering FileHelper::CheckMandatoryElements, so the user will always
1238  *          see the entries just as they were before he decided to write.
1239  *
1240  * \note
1241  *       -  Entries whose type is 1 are mandatory, with a mandatory value
1242  *       -  Entries whose type is 1c are mandatory-inside-a-Sequence,
1243  *                             with a mandatory value
1244  *       -  Entries whose type is 2 are mandatory, with an optional value
1245  *       -  Entries whose type is 2c are mandatory-inside-a-Sequence,
1246  *                             with an optional value
1247  *       -  Entries whose type is 3 are optional
1248  * 
1249  * \todo 
1250  *         - warn the user if we had to add some entries :
1251  *         even if a mandatory entry is missing, we add it, with a default value
1252  *         (we don't want to give up the writting process if user forgot to
1253  *         specify Lena's Patient ID, for instance ...)
1254  *         - read the whole PS 3.3 Part of DICOM  (890 pages)
1255  *         and write a *full* checker (probably one method per Modality ...)
1256  *         Any contribution is welcome. 
1257  *         - write a user callable full checker, to allow post reading
1258  *         and/or pre writting image consistency check.           
1259  */ 
1260
1261 /* -------------------------------------------------------------------------------------
1262 To be moved to User's guide / WIKI  ?
1263
1264 We have to deal with 4 *very* different cases :
1265 -1) user created ex nihilo his own image and wants to write it as a Dicom image.
1266     USER_OWN_IMAGE
1267 -2) user modified the pixels of an existing image.
1268    FILTERED_IMAGE
1269 -3) user created a new image, using a set of existing images (eg MIP, MPR, cartography image)
1270    CREATED_IMAGE
1271 -4) user modified/added some tags *without processing* the pixels (anonymization..
1272    UNMODIFIED_PIXELS_IMAGE
1273 -Probabely some more to be added  
1274  
1275 gdcm::FileHelper::CheckMandatoryElements() deals automatically with these cases.
1276
1277 1)2)3)4)
1278 0008 0012 Instance Creation Date
1279 0008 0013 Instance Creation Time
1280 0008 0018 SOP Instance UID
1281 are *always* created with the current values; user has *no* possible intervention on
1282 them.
1283
1284 'Serie Instance UID'(0x0020,0x000e)
1285 'Study Instance UID'(0x0020,0x000d) are kept as is if already exist,
1286                                     created  if it doesn't.
1287  The user is allowed to create his own Series/Studies, 
1288      keeping the same 'Serie Instance UID' / 'Study Instance UID' for various images
1289  Warning :     
1290  The user shouldn't add any image to a 'Manufacturer Serie'
1291      but there is no way no to allow him to do that
1292      
1293  None of the 'shadow elements' are droped out.
1294      
1295
1296 1)
1297 'Conversion Type (0x0008,0x0064) is forced to 'SYN' (Synthetic Image).
1298  
1299 1)3)
1300 'Media Storage SOP Class UID' (0x0002,0x0002)
1301 'SOP Class UID'               (0x0008,0x0016) are set to 
1302                                                [Secondary Capture Image Storage]
1303 'Image Type'                  (0x0008,0x0008) is forced to  "DERIVED\PRIMARY"
1304 Conversion Type               (0x0008,0x0064) is forced to 'SYN' (Synthetic Image)
1305
1306 2)4)
1307 If 'SOP Class UID' exists in the native image  ('true DICOM' image)
1308     we create the 'Source Image Sequence' SeqEntry (0x0008, 0x2112)    
1309     --> 'Referenced SOP Class UID' (0x0008, 0x1150)
1310          whose value is the original 'SOP Class UID'
1311     --> 'Referenced SOP Instance UID' (0x0008, 0x1155)
1312          whose value is the original 'SOP Class UID'
1313
1314 3) TODO : find a trick to allow user to pass to the writter the list of the Dicom images 
1315           or the Series, (or the Study ?) he used to created his image 
1316           (MIP, MPR, cartography image, ...)
1317            These info should be stored (?)
1318           0008 1110 SQ 1 Referenced Study Sequence
1319           0008 1115 SQ 1 Referenced Series Sequence
1320           0008 1140 SQ 1 Referenced Image Sequence
1321        
1322 4) When user *knows* he didn't modified the pixels, we keep some informations unchanged :
1323 'Media Storage SOP Class UID' (0x0002,0x0002)
1324 'SOP Class UID'               (0x0008,0x0016)
1325 'Image Type'                  (0x0008,0x0008)
1326 'Conversion Type'             (0x0008,0x0064)
1327
1328
1329 Bellow follows the full description (hope so !) of the consistency checks performed 
1330 by gdcm::FileHelper::CheckMandatoryElements()
1331
1332
1333 -->'Media Storage SOP Class UID' (0x0002,0x0002)
1334 -->'SOP Class UID'               (0x0008,0x0016) are defaulted to 
1335                                                [Secondary Capture Image Storage]
1336 --> 'Image Type'  (0x0008,0x0008)
1337      is forced to  "DERIVED\PRIMARY"
1338      (The written image is no longer an 'ORIGINAL' one)
1339   Except if user knows he didn't modify the image (e.g. : he just anonymized the file)
1340    
1341  -->  Conversion Type (0x0008,0x0064)
1342      is defaulted to 'SYN' (Synthetic Image)
1343   when *he* knows he created his own image ex nihilo
1344             
1345 --> 'Modality' (0x0008,0x0060)   
1346     is defaulted to "OT" (other) if missing.   
1347     (a fully user created image belongs to *no* modality)
1348       
1349 --> 'Media Storage SOP Instance UID' (0x0002,0x0003)
1350 --> 'Implementation Class UID'       (0x0002,0x0012)
1351     are automatically generated; no user intervention possible
1352
1353 --> 'Serie Instance UID'(0x0020,0x000e)
1354 --> 'Study Instance UID'(0x0020,0x000d) are kept as is if already exist
1355                                              created  if it doesn't.
1356      The user is allowed to create his own Series/Studies, 
1357      keeping the same 'Serie Instance UID' / 'Study Instance UID' 
1358      for various images
1359      Warning :     
1360      The user shouldn't add any image to a 'Manufacturer Serie'
1361      but there is no way no to allowed him to do that 
1362              
1363 --> If 'SOP Class UID' exists in the native image  ('true DICOM' image)
1364     we create the 'Source Image Sequence' SeqEntry (0x0008, 0x2112)
1365     
1366     --> 'Referenced SOP Class UID' (0x0008, 0x1150)
1367          whose value is the original 'SOP Class UID'
1368     --> 'Referenced SOP Instance UID' (0x0008, 0x1155)
1369          whose value is the original 'SOP Class UID'
1370     
1371 --> Bits Stored, Bits Allocated, Hight Bit Position are checked for consistency
1372 --> Pixel Spacing     (0x0028,0x0030) is defaulted to "1.0\1.0"
1373 --> Samples Per Pixel (0x0028,0x0002) is defaulted to 1 (grayscale)
1374
1375 --> Imager Pixel Spacing (0x0018,0x1164) : defaulted to Pixel Spacing value
1376
1377 --> Instance Creation Date, Instance Creation Time are forced to current Date and Time
1378
1379 --> Study Date, Study Time are defaulted to current Date and Time
1380    (they remain unchanged if they exist)
1381
1382 --> Patient Orientation : (0x0020,0x0020), if not present, is deduced from 
1383     Image Orientation (Patient) : (0020|0037) or from
1384     Image Orientation (RET)     : (0020 0035)
1385    
1386 --> Study ID, Series Number, Instance Number, Patient Orientation (Type 2)
1387     are created, with empty value if there are missing.
1388
1389 --> Manufacturer, Institution Name, Patient's Name, (Type 2)
1390     are defaulted with a 'gdcm' value.
1391     
1392 --> Patient ID, Patient's Birth Date, Patient's Sex, (Type 2)
1393 --> Referring Physician's Name  (Type 2)
1394     are created, with empty value if there are missing.
1395
1396  -------------------------------------------------------------------------------------*/
1397
1398 void FileHelper::CheckMandatoryElements()
1399 {
1400    std::string sop =  Util::CreateUniqueUID();
1401
1402    // --------------------- For Meta Elements ---------------------
1403    // just to remember : 'official' 0002 group
1404    if ( WriteType != ACR && WriteType != ACR_LIBIDO )
1405    {
1406      // Group 000002 (Meta Elements) already pushed out
1407   
1408    //0002 0000 UL 1 Meta Group Length
1409    //0002 0001 OB 1 File Meta Information Version
1410    //0002 0002 UI 1 Media Stored SOP Class UID
1411    //0002 0003 UI 1 Media Stored SOP Instance UID
1412    //0002 0010 UI 1 Transfer Syntax UID
1413    //0002 0012 UI 1 Implementation Class UID
1414    //0002 0013 SH 1 Implementation Version Name
1415    //0002 0016 AE 1 Source Application Entity Title
1416    //0002 0100 UI 1 Private Information Creator
1417    //0002 0102 OB 1 Private Information
1418  
1419    // Push out 'ACR-NEMA-special' entries, if any
1420       Archive->Push(0x0008,0x0001); // Length to End
1421       Archive->Push(0x0008,0x0010); // Recognition Code
1422       Archive->Push(0x0028,0x0005); // Image Dimension  
1423   
1424    // Create them if not found
1425    // Always modify the value
1426    // Push the entries to the archive.
1427       CopyMandatoryEntry(0x0002,0x0000,"0","UL");
1428
1429       DataEntry *e_0002_0001 = CopyDataEntry(0x0002,0x0001, "OB");
1430       e_0002_0001->SetBinArea((uint8_t*)Util::GetFileMetaInformationVersion(),
1431                                false);
1432       e_0002_0001->SetLength(2);
1433       Archive->Push(e_0002_0001);
1434       e_0002_0001->Delete(); 
1435
1436       if ( ContentType == FILTERED_IMAGE || ContentType == UNMODIFIED_PIXELS_IMAGE)
1437       {      
1438    // we keep the original 'Media Storage SOP Class UID', we default it if missing
1439          CheckMandatoryEntry(0x0002,0x0002,"1.2.840.10008.5.1.4.1.1.7","UI"); 
1440       }
1441       else
1442       {
1443    // It's *not* an image comming straight from a source. We force
1444    // 'Media Storage SOP Class UID'  --> [Secondary Capture Image Storage]
1445          CopyMandatoryEntry(0x0002,0x0002,"1.2.840.10008.5.1.4.1.1.7","UI");
1446       }
1447       
1448    // 'Media Storage SOP Instance UID'   
1449       CopyMandatoryEntry(0x0002,0x0003,sop,"UI");
1450       
1451    // 'Implementation Class UID'
1452    // FIXME : in all examples we have, 0x0002,0x0012 is not so long :
1453    //         seems to be Root UID + 4 digits (?)
1454       CopyMandatoryEntry(0x0002,0x0012,Util::CreateUniqueUID(),"UI");
1455
1456    // 'Implementation Version Name'
1457       std::string version = "GDCM ";
1458       version += Util::GetVersion();
1459       CopyMandatoryEntry(0x0002,0x0013,version,"SH");
1460    }
1461
1462    // --------------------- For DataSet ---------------------
1463
1464    if ( ContentType != USER_OWN_IMAGE) // when it's not a user made image
1465    { 
1466    
1467       gdcmDebugMacro( "USER_OWN_IMAGE (1)");
1468     // If 'SOP Class UID' exists ('true DICOM' image)
1469    // we create the 'Source Image Sequence' SeqEntry
1470    // to hold informations about the Source Image
1471   
1472       DataEntry *e_0008_0016 = FileInternal->GetDataEntry(0x0008, 0x0016);
1473       if ( e_0008_0016 )
1474       {
1475       // Create 'Source Image Sequence' SeqEntry
1476 //     SeqEntry *sis = SeqEntry::New (
1477 //            Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x2112) );
1478       SeqEntry *sis = SeqEntry::New (0x0008, 0x2112);
1479       SQItem *sqi = SQItem::New(1);
1480       // (we assume 'SOP Instance UID' exists too) 
1481       // create 'Referenced SOP Class UID'
1482 //     DataEntry *e_0008_1150 = DataEntry::New(
1483 //            Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x1150) );
1484       DataEntry *e_0008_1150 = DataEntry::New(0x0008, 0x1150, "UI");
1485       e_0008_1150->SetString( e_0008_0016->GetString());
1486       sqi->AddEntry(e_0008_1150);
1487       e_0008_1150->Delete();
1488       
1489       // create 'Referenced SOP Instance UID'
1490       DataEntry *e_0008_0018 = FileInternal->GetDataEntry(0x0008, 0x0018);
1491 //      DataEntry *e_0008_1155 = DataEntry::New(
1492 //            Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x1155) );
1493       DataEntry *e_0008_1155 = DataEntry::New(0x0008, 0x1155, "UI");
1494       e_0008_1155->SetString( e_0008_0018->GetString());
1495       sqi->AddEntry(e_0008_1155);
1496       e_0008_1155->Delete();
1497
1498       sis->AddSQItem(sqi,1);
1499       sqi->Delete();
1500
1501       // temporarily replaces any previous 'Source Image Sequence' 
1502       Archive->Push(sis);
1503       sis->Delete();
1504  
1505       // FIXME : is 'Image Type' *really* depending on the presence of 'SOP Class UID'?
1506        if ( ContentType == FILTERED_IMAGE)      
1507       // the user *knows* he just modified the pixels
1508       // the image is no longer an 'Original' one
1509          CopyMandatoryEntry(0x0008,0x0008,"DERIVED\\PRIMARY","CS");    
1510       }
1511    }
1512       
1513    if ( ContentType == FILTERED_IMAGE || ContentType == UNMODIFIED_PIXELS_IMAGE)
1514    {      
1515    // we keep the original 'Media Storage SOP Class UID', we default it if missing (it should be present !)
1516          CheckMandatoryEntry(0x0008,0x0016,"1.2.840.10008.5.1.4.1.1.7","UI");      
1517    }
1518    else
1519    {
1520    // It's *not* an image comming straight from a source. We force
1521    // 'Media Storage SOP Class UID'  --> [Secondary Capture Image Storage]
1522          CopyMandatoryEntry(0x0008,0x0016,"1.2.840.10008.5.1.4.1.1.7", "UI");      
1523    }
1524      
1525    Archive->Push(0x0028,0x005); // [Image Dimensions (RET)
1526    // Push out 'LibIDO-special' entries, if any
1527    Archive->Push(0x0028,0x0015);
1528    Archive->Push(0x0028,0x0016);
1529    Archive->Push(0x0028,0x0017);
1530    Archive->Push(0x0028,0x0198);  // very old versions
1531    Archive->Push(0x0028,0x0199);
1532  
1533    // Replace deprecated 0028 0012 US Planes   
1534    // by new             0028 0008 IS Number of Frames
1535    
1536   ///\todo : find if there is a rule!
1537    DataEntry *e_0028_0012 = FileInternal->GetDataEntry(0x0028, 0x0012);
1538    if ( e_0028_0012 )
1539    {
1540       CopyMandatoryEntry(0x0028, 0x0008,e_0028_0012->GetString(),"IS");
1541       Archive->Push(0x0028,0x0012);      
1542    }
1543
1544    // Deal with the pb of (Bits Stored = 12)
1545    // - we're gonna write the image as Bits Stored = 16
1546    if ( FileInternal->GetEntryString(0x0028,0x0100) ==  "12")
1547    {
1548       CopyMandatoryEntry(0x0028,0x0100,"16","US");
1549    }
1550
1551    // Check if user wasn't drunk ;-)
1552
1553    std::ostringstream s;
1554    // check 'Bits Allocated' vs decent values
1555    int nbBitsAllocated = FileInternal->GetBitsAllocated();
1556    if ( nbBitsAllocated == 0 || nbBitsAllocated > 32 
1557       || ( nbBitsAllocated > 8 && nbBitsAllocated <16) )
1558    {
1559       CopyMandatoryEntry(0x0028,0x0100,"16","US");
1560       gdcmWarningMacro("(0028,0100) changed from "
1561          << nbBitsAllocated << " to 16 for consistency purpose");
1562       nbBitsAllocated = 16; 
1563    }
1564    // check 'Bits Stored' vs 'Bits Allocated'   
1565    int nbBitsStored = FileInternal->GetBitsStored();
1566    if ( nbBitsStored == 0 || nbBitsStored > nbBitsAllocated )
1567    {
1568       s.str("");
1569       s << nbBitsAllocated;
1570       CopyMandatoryEntry(0x0028,0x0101,s.str(),"US");
1571       gdcmWarningMacro("(0028,0101) changed from "
1572                        << nbBitsStored << " to " << nbBitsAllocated
1573                        << " for consistency purpose" );
1574       nbBitsStored = nbBitsAllocated; 
1575     }
1576    // check 'Hight Bit Position' vs 'Bits Allocated' and 'Bits Stored'
1577    int highBitPosition = FileInternal->GetHighBitPosition();
1578    if ( highBitPosition == 0 || 
1579         highBitPosition > nbBitsAllocated-1 ||
1580         highBitPosition < nbBitsStored-1  )
1581    {
1582       s.str("");
1583       s << nbBitsStored - 1; 
1584       CopyMandatoryEntry(0x0028,0x0102,s.str(),"US");
1585       gdcmWarningMacro("(0028,0102) changed from "
1586                        << highBitPosition << " to " << nbBitsAllocated-1
1587                        << " for consistency purpose");
1588    }
1589
1590    // check Pixel Representation (default it as 0 -unsigned-)
1591    
1592    DataEntry *e_0028_0103 = FileInternal->GetDataEntry(0x0028, 0x0103);
1593    if ( !e_0028_0103 )
1594    {
1595       gdcmWarningMacro("PixelRepresentation (0028,0103) is supposed to be mandatory");
1596       CopyMandatoryEntry(0x0028, 0x0103,"0","US"); 
1597    }
1598    else
1599    {
1600       int sign = (int)e_0028_0103->GetValue(0);
1601       if (sign !=1 && sign !=0)
1602       {
1603          gdcmWarningMacro("PixelRepresentation (0028,0103) is supposed to be =1 or =0");
1604          CopyMandatoryEntry(0x0028, 0x0103,"0","US");
1605       }
1606    }
1607
1608    std::string pixelSpacing = FileInternal->GetEntryString(0x0028,0x0030);
1609    if ( pixelSpacing == GDCM_UNFOUND )
1610    {
1611       pixelSpacing = "1.0\\1.0";
1612        // if missing, Pixel Spacing forced to "1.0\1.0"
1613       CopyMandatoryEntry(0x0028,0x0030,pixelSpacing,"DS");
1614    }
1615    
1616    // 'Imager Pixel Spacing' : defaulted to 'Pixel Spacing'
1617    // --> This one is the *legal* one !
1618    if ( ContentType != USER_OWN_IMAGE)
1619    //  we write it only when we are *sure* the image comes from
1620    //         an imager (see also 0008,0x0064)          
1621       CheckMandatoryEntry(0x0018,0x1164,pixelSpacing,"DS");
1622
1623
1624
1625 /*
1626 ///Exact meaning of RETired fields
1627
1628 // See page 73 of ACR-NEMA_300-1988.pdf !
1629
1630 // 0020,0020 : Patient Orientation : 
1631 Patient direction of the first row and
1632 column of the images. The first entry id the direction of the raws, given by the
1633 direction of the last pixel in the first row from the first pixel in tha row.
1634 the second entry is the direction of the columns, given by the direction of the
1635 last pixel in the first column from the first pixel in that column.
1636 L : Left, F : Feet, A : Anterior, P : Posterior.
1637 Up to 3 letters can be used in combination to indicate oblique planes.
1638
1639 //0020,0030 Image Position (RET)
1640 x,y,z coordinates im mm of the first pixel in the image
1641
1642 // 0020,0035 Image Orientation (RET)
1643 Direction cosines of the R axis of the image system with respect to the
1644 equipment coordinate axes x,y,z, followed by direction cosines of the C axis of
1645 the image system with respect to the same axes
1646
1647 //0020,0050 Location
1648 An image location reference, standard for the modality (such as CT bed
1649 position), used to indicate position. Calculation of position for other purposes
1650 is only from (0020,0030) and (0020,0035)
1651 */
1652  
1653 /*
1654 // if imagePositionPatient    not found, default it with imagePositionRet,    if any
1655 // if imageOrientationPatient not found, default it with imageOrientationRet, if any
1656
1657    std::string imagePositionRet        = FileInternal->GetEntryString(0x0020,0x0030);
1658    std::string imageOrientationRet     = FileInternal->GetEntryString(0x0020,0x0035);
1659    std::string imagePositionPatient    = FileInternal->GetEntryString(0x0020,0x0032);
1660    std::string imageOrientationPatient = FileInternal->GetEntryString(0x0020,0x0037);
1661    
1662    if(  imagePositionPatient == GDCM_UNFOUND && imageOrientationPatient == GDCM_UNFOUND
1663      && imagePositionRet     != GDCM_UNFOUND && imageOrientationRet     != GDCM_UNFOUND)
1664    {
1665       CopyMandatoryEntry(0x0020, 0x0032,imagePositionRet,"DS");
1666       Archive->Push(0x0020,0x0030); 
1667       CopyMandatoryEntry(0x0020, 0x0037,imageOrientationRet,"DS");
1668       Archive->Push(0x0020,0x0035);        
1669    }        
1670 */
1671     
1672    // Samples Per Pixel (type 1) : default to grayscale 
1673    CheckMandatoryEntry(0x0028,0x0002,"1","US");
1674
1675    // --- Check UID-related Entries ---
1676  
1677    // At the end, not to overwrite the original ones,
1678    // needed by 'Referenced SOP Instance UID', 'Referenced SOP Class UID'   
1679    // 'SOP Instance UID'  
1680    CopyMandatoryEntry(0x0008,0x0018,sop,"UI");
1681
1682    if ( ContentType == USER_OWN_IMAGE)
1683    {
1684       gdcmDebugMacro( "USER_OWN_IMAGE (2)");   
1685        // Conversion Type.
1686        // Other possible values are :
1687        // See PS 3.3, Page 408
1688    
1689        // DV = Digitized Video
1690        // DI = Digital Interface   
1691        // DF = Digitized Film
1692        // WSD = Workstation
1693        // SD = Scanned Document
1694        // SI = Scanned Image
1695        // DRW = Drawing
1696        // SYN = Synthetic Image
1697            
1698       CheckMandatoryEntry(0x0008,0x0064,"SYN","CS"); // Why not?
1699    } 
1700 /*
1701    if ( ContentType == CREATED_IMAGE)
1702    {
1703    /// \todo : find a trick to pass the Media Storage SOP Instance UID of the images used to create the current image
1704    
1705    }
1706 */
1707            
1708    // ---- The user will never have to take any action on the following ----
1709
1710    // new value for 'SOP Instance UID'
1711    //SetMandatoryEntry(0x0008,0x0018,Util::CreateUniqueUID());
1712
1713    // Instance Creation Date
1714    const std::string &date = Util::GetCurrentDate();
1715    CopyMandatoryEntry(0x0008,0x0012,date,"DA");
1716  
1717    // Instance Creation Time
1718    const std::string &time = Util::GetCurrentTime();
1719    CopyMandatoryEntry(0x0008,0x0013,time,"TM");
1720
1721    // Study Date
1722    CheckMandatoryEntry(0x0008,0x0020,date,"DA");
1723    // Study Time
1724    CheckMandatoryEntry(0x0008,0x0030,time,"TM");
1725
1726    // Accession Number
1727    //CopyMandatoryEntry(0x0008,0x0050,"");
1728    CheckMandatoryEntry(0x0008,0x0050,"","SH");
1729    
1730
1731    // ----- Add Mandatory Entries if missing ---
1732    // Entries whose type is 1 are mandatory, with a mandatory value
1733    // Entries whose type is 1c are mandatory-inside-a-Sequence,
1734    //                          with a mandatory value
1735    // Entries whose type is 2 are mandatory, with an optional value
1736    // Entries whose type is 2c are mandatory-inside-a-Sequence,
1737    //                          with an optional value
1738    // Entries whose type is 3 are optional
1739
1740    // 'Study Instance UID'
1741    // Keep the value if exists
1742    // The user is allowed to create his own Study, 
1743    //          keeping the same 'Study Instance UID' for various images
1744    // The user may add images to a 'Manufacturer Study',
1745    //          adding new Series to an already existing Study 
1746    CheckMandatoryEntry(0x0020,0x000d,Util::CreateUniqueUID(),"UI");
1747
1748    // 'Serie Instance UID'
1749    // Keep the value if exists
1750    // The user is allowed to create his own Series, 
1751    // keeping the same 'Serie Instance UID' for various images
1752    // The user shouldn't add any image to a 'Manufacturer Serie'
1753    // but there is no way no to prevent him for doing that 
1754    CheckMandatoryEntry(0x0020,0x000e,Util::CreateUniqueUID(),"UI");
1755
1756    // Study ID
1757    CheckMandatoryEntry(0x0020,0x0010,"","SH");
1758
1759    // Series Number
1760    CheckMandatoryEntry(0x0020,0x0011,"","IS");
1761
1762    // Instance Number
1763    CheckMandatoryEntry(0x0020,0x0013,"","IS");
1764    
1765    // Patient Orientation
1766    // Can be computed from (0020|0037) :  Image Orientation (Patient)
1767    gdcm::Orientation *o = gdcm::Orientation::New();
1768    std::string ori = o->GetOrientation ( FileInternal );
1769    o->Delete();
1770    if (ori != "\\" && ori != GDCM_UNFOUND)
1771       CheckMandatoryEntry(0x0020,0x0020,ori,"CS");
1772    else   
1773       CheckMandatoryEntry(0x0020,0x0020,"","CS");
1774
1775    // Default Patient Position to HFS
1776    CheckMandatoryEntry(0x0018,0x5100,"HFS","CS");
1777
1778    // Modality : if missing we set it to 'OTher'
1779    CheckMandatoryEntry(0x0008,0x0060,"OT","CS");
1780
1781    // Manufacturer : if missing we set it to 'GDCM Factory'
1782    CheckMandatoryEntry(0x0008,0x0070,"GDCM Factory","LO");
1783
1784    // Institution Name : if missing we set it to 'GDCM Hospital'
1785    CheckMandatoryEntry(0x0008,0x0080,"GDCM Hospital","LO");
1786
1787    // Patient's Name : if missing, we set it to 'GDCM^Patient'
1788    CheckMandatoryEntry(0x0010,0x0010,"GDCM^Patient","PN");
1789
1790    // Patient ID : some clinical softwares *demand* it although it's a 'type 2' entry.
1791    CheckMandatoryEntry(0x0010,0x0020,"gdcm ID","LO");
1792
1793    // Patient's Birth Date : 'type 2' entry -> must exist, value not mandatory
1794    CheckMandatoryEntry(0x0010,0x0030,"","DA");
1795
1796    // Patient's Sex :'type 2' entry -> must exist, value not mandatory
1797    CheckMandatoryEntry(0x0010,0x0040,"","CS");
1798
1799    // Referring Physician's Name :'type 2' entry -> must exist, value not mandatory
1800    CheckMandatoryEntry(0x0008,0x0090,"","PN");
1801
1802  /*
1803    // Deal with element 0x0000 (group length) of each group.
1804    // First stage : get all the different Groups
1805    
1806   GroupHT grHT;
1807   DocEntry *d = FileInternal->GetFirstEntry();
1808   while(d)
1809   {
1810     grHT[d->GetGroup()] = 0;
1811     d=FileInternal->GetNextEntry();
1812   }
1813   // Second stage : add the missing ones (if any)
1814   for (GroupHT::iterator it = grHT.begin(); it != grHT.end(); ++it)  
1815   {
1816       CheckMandatoryEntry(it->first, 0x0000, "0"); 
1817   }    
1818   // Third stage : update all 'zero level' groups length
1819 */ 
1820
1821
1822
1823 void FileHelper::CheckMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr )
1824 {
1825    DataEntry *entry = FileInternal->GetDataEntry(group,elem);
1826    if ( !entry )
1827    {
1828       //entry = DataEntry::New(Global::GetDicts()->GetDefaultPubDict()->GetEntry(group,elem));
1829       entry = DataEntry::New(group,elem,vr);
1830       entry->SetString(value);
1831       Archive->Push(entry);
1832       entry->Delete();
1833    }    
1834 }
1835
1836 /// \todo : what is it used for ? (FileHelper::SetMandatoryEntry) 
1837 void FileHelper::SetMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr)
1838 {
1839    //DataEntry *entry = DataEntry::New(Global::GetDicts()->GetDefaultPubDict()->GetEntry(group,elem));
1840    DataEntry *entry = DataEntry::New(group,elem,vr);
1841    entry->SetString(value);
1842    Archive->Push(entry);
1843    entry->Delete();
1844 }
1845
1846 void FileHelper::CopyMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr)
1847 {
1848    DataEntry *entry = CopyDataEntry(group,elem,vr);
1849    entry->SetString(value);
1850    Archive->Push(entry);
1851    entry->Delete();
1852 }
1853
1854 /**
1855  * \brief Restore in the File the initial group 0002
1856  */
1857 void FileHelper::RestoreWriteMandatory()
1858 {
1859    // group 0002 may be pushed out for ACR-NEMA writting purposes 
1860    Archive->Restore(0x0002,0x0000);
1861    Archive->Restore(0x0002,0x0001);
1862    Archive->Restore(0x0002,0x0002);
1863    Archive->Restore(0x0002,0x0003);
1864    Archive->Restore(0x0002,0x0010);
1865    Archive->Restore(0x0002,0x0012);
1866    Archive->Restore(0x0002,0x0013);
1867    Archive->Restore(0x0002,0x0016);
1868    Archive->Restore(0x0002,0x0100);
1869    Archive->Restore(0x0002,0x0102);
1870
1871    // FIXME : Check if none is missing !
1872    
1873    Archive->Restore(0x0008,0x0012);
1874    Archive->Restore(0x0008,0x0013);
1875    Archive->Restore(0x0008,0x0016);
1876    Archive->Restore(0x0008,0x0018);
1877    Archive->Restore(0x0008,0x0060);
1878    Archive->Restore(0x0008,0x0070);
1879    Archive->Restore(0x0008,0x0080);
1880    Archive->Restore(0x0008,0x0090);
1881    Archive->Restore(0x0008,0x2112);
1882
1883    Archive->Restore(0x0010,0x0010);
1884    Archive->Restore(0x0010,0x0030);
1885    Archive->Restore(0x0010,0x0040);
1886
1887    Archive->Restore(0x0020,0x000d);
1888    Archive->Restore(0x0020,0x000e);
1889 }
1890
1891
1892 /**
1893  * \brief   CallStartMethod
1894  */
1895 void FileHelper::CallStartMethod()
1896 {
1897    Progress = 0.0f;
1898    Abort    = false;
1899    CommandManager::ExecuteCommand(this,CMD_STARTPROGRESS);
1900 }
1901
1902 /**
1903  * \brief   CallProgressMethod
1904  */
1905 void FileHelper::CallProgressMethod()
1906 {
1907    CommandManager::ExecuteCommand(this,CMD_PROGRESS);
1908 }
1909
1910 /**
1911  * \brief   CallEndMethod
1912  */
1913 void FileHelper::CallEndMethod()
1914 {
1915    Progress = 1.0f;
1916    CommandManager::ExecuteCommand(this,CMD_ENDPROGRESS);
1917 }
1918
1919 //-----------------------------------------------------------------------------
1920 // Private
1921 /**
1922  * \brief Factorization for various forms of constructors.
1923  */
1924 void FileHelper::Initialize()
1925 {
1926    UserFunction = 0;
1927    ContentType = USER_OWN_IMAGE;
1928    
1929    WriteMode = WMODE_RAW;
1930    WriteType = ExplicitVR;
1931
1932    PixelReadConverter  = new PixelReadConvert;
1933    PixelWriteConverter = new PixelWriteConvert;
1934    Archive = new DocEntryArchive( FileInternal );
1935 }
1936
1937 /**
1938  * \brief Reads/[decompresses] the pixels, 
1939  *        *without* making RGB from Palette Colors 
1940  * @return the pixels area, whatever its type 
1941  *         (uint8_t is just for prototyping : feel free to Cast it) 
1942  */ 
1943 uint8_t *FileHelper::GetRaw()
1944 {
1945    PixelReadConverter->SetUserFunction( UserFunction );
1946
1947    uint8_t *raw = PixelReadConverter->GetRaw();
1948    if ( ! raw )
1949    {
1950       // The Raw image migth not be loaded yet:
1951       std::ifstream *fp = FileInternal->OpenFile();
1952       PixelReadConverter->ReadAndDecompressPixelData( fp );
1953       if ( fp ) 
1954          FileInternal->CloseFile();
1955
1956       raw = PixelReadConverter->GetRaw();
1957       if ( ! raw )
1958       {
1959          gdcmWarningMacro( "Read/decompress of pixel data apparently went wrong.");
1960          return 0;
1961       }
1962    }
1963    return raw;
1964 }
1965
1966 //-----------------------------------------------------------------------------
1967 /**
1968  * \brief   Prints the common part of DataEntry, SeqEntry
1969  * @param   os ostream we want to print in
1970  * @param indent (unused)
1971  */
1972 void FileHelper::Print(std::ostream &os, std::string const &)
1973 {
1974    FileInternal->SetPrintLevel(PrintLevel);
1975    FileInternal->Print(os);
1976
1977    if ( FileInternal->IsReadable() )
1978    {
1979       PixelReadConverter->SetPrintLevel(PrintLevel);
1980       PixelReadConverter->Print(os);
1981    }
1982 }
1983
1984 //-----------------------------------------------------------------------------
1985 } // end namespace gdcm
1986
1987
1988 /* Probabely something to be added to use Rescale Slope/Intercept
1989 Have a look ,at ITK code !
1990
1991 // Internal function to rescale pixel according to Rescale Slope/Intercept
1992 template<class TBuffer, class TSource>
1993 void RescaleFunction(TBuffer* buffer, TSource *source,
1994                      double slope, double intercept, size_t size)
1995 {
1996   size /= sizeof(TSource);
1997
1998   if (slope != 1.0 && intercept != 0.0)
1999     {
2000     // Duff's device.  Instead of this code:
2001     //
2002     //   for(unsigned int i=0; i<size; i++)
2003     //    {
2004     //    buffer[i] = (TBuffer)(source[i]*slope + intercept);
2005     //    }
2006     //
2007     // use Duff's device which exploits "fall through"
2008     register size_t n = (size + 7) / 8;
2009     switch ( size % 8)
2010       {
2011       case 0: do { *buffer++ = (TBuffer)((*source++)*slope + intercept);
2012       case 7:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
2013       case 6:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
2014       case 5:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
2015       case 4:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
2016       case 3:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
2017       case 2:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
2018       case 1:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
2019                  }  while (--n > 0);
2020       }
2021     }
2022   else if (slope == 1.0 && intercept != 0.0)
2023     {
2024     // Duff's device.  Instead of this code:
2025     //
2026     //   for(unsigned int i=0; i<size; i++)
2027     //    {
2028     //    buffer[i] = (TBuffer)(source[i] + intercept);
2029     //    }
2030     //
2031     // use Duff's device which exploits "fall through"
2032     register size_t n = (size + 7) / 8;
2033     switch ( size % 8)
2034       {
2035       case 0: do { *buffer++ = (TBuffer)(*source++ + intercept);
2036       case 7:      *buffer++ = (TBuffer)(*source++ + intercept);
2037       case 6:      *buffer++ = (TBuffer)(*source++ + intercept);
2038       case 5:      *buffer++ = (TBuffer)(*source++ + intercept);
2039       case 4:      *buffer++ = (TBuffer)(*source++ + intercept);
2040       case 3:      *buffer++ = (TBuffer)(*source++ + intercept);
2041       case 2:      *buffer++ = (TBuffer)(*source++ + intercept);
2042       case 1:      *buffer++ = (TBuffer)(*source++ + intercept);
2043                  }  while (--n > 0);
2044       }
2045     }
2046   else if (slope != 1.0 && intercept == 0.0)
2047     {
2048     // Duff's device.  Instead of this code:
2049     //
2050     //   for(unsigned int i=0; i<size; i++)
2051     //    {
2052     //    buffer[i] = (TBuffer)(source[i]*slope);
2053     //    }
2054     //
2055     // use Duff's device which exploits "fall through"
2056     register size_t n = (size + 7) / 8;
2057     switch ( size % 8)
2058       {
2059       case 0: do { *buffer++ = (TBuffer)((*source++)*slope);
2060       case 7:      *buffer++ = (TBuffer)((*source++)*slope);
2061       case 6:      *buffer++ = (TBuffer)((*source++)*slope);
2062       case 5:      *buffer++ = (TBuffer)((*source++)*slope);
2063       case 4:      *buffer++ = (TBuffer)((*source++)*slope);
2064       case 3:      *buffer++ = (TBuffer)((*source++)*slope);
2065       case 2:      *buffer++ = (TBuffer)((*source++)*slope);
2066       case 1:      *buffer++ = (TBuffer)((*source++)*slope);
2067                  }  while (--n > 0);
2068       }
2069     }
2070   else
2071     {
2072     // Duff's device.  Instead of this code:
2073     //
2074     //   for(unsigned int i=0; i<size; i++)
2075     //    {
2076     //    buffer[i] = (TBuffer)(source[i]);
2077     //    }
2078     //
2079     // use Duff's device which exploits "fall through"
2080     register size_t n = (size + 7) / 8;
2081     switch ( size % 8)
2082       {
2083       case 0: do { *buffer++ = (TBuffer)(*source++);
2084       case 7:      *buffer++ = (TBuffer)(*source++);
2085       case 6:      *buffer++ = (TBuffer)(*source++);
2086       case 5:      *buffer++ = (TBuffer)(*source++);
2087       case 4:      *buffer++ = (TBuffer)(*source++);
2088       case 3:      *buffer++ = (TBuffer)(*source++);
2089       case 2:      *buffer++ = (TBuffer)(*source++);
2090       case 1:      *buffer++ = (TBuffer)(*source++);
2091                  }  while (--n > 0);
2092       }
2093     }
2094     
2095     
2096 }
2097
2098
2099 template<class TSource>
2100 void RescaleFunction(ImageIOBase::IOComponentType bufferType,
2101                      void* buffer, TSource *source,
2102                      double slope, double intercept, size_t size)
2103 {
2104   switch (bufferType)
2105     {
2106     case ImageIOBase::UCHAR:
2107       RescaleFunction( (unsigned char *)buffer, source, slope, intercept, size);
2108       break;
2109     case ImageIOBase::CHAR:
2110       RescaleFunction( (char *)buffer, source, slope, intercept, size);
2111       break;
2112     case ImageIOBase::USHORT:
2113       RescaleFunction( (unsigned short *)buffer, source, slope, intercept,size);
2114       break;
2115     case ImageIOBase::SHORT:
2116       RescaleFunction( (short *)buffer, source, slope, intercept, size);
2117       break;
2118     case ImageIOBase::UINT:
2119       RescaleFunction( (unsigned int *)buffer, source, slope, intercept, size);
2120       break;
2121     case ImageIOBase::INT:
2122       RescaleFunction( (int *)buffer, source, slope, intercept, size);
2123       break;
2124     case ImageIOBase::FLOAT:
2125       RescaleFunction( (float *)buffer, source, slope, intercept, size);
2126       break;
2127     case ImageIOBase::DOUBLE:
2128       RescaleFunction( (double *)buffer, source, slope, intercept, size);
2129       break;
2130     default:
2131       ::itk::OStringStream message;
2132       message << "itk::ERROR: GDCMImageIO: Unknown component type : " << bufferType;
2133       ::itk::ExceptionObject e(__FILE__, __LINE__, message.str().c_str(),ITK_LOCATION);
2134       throw e;
2135     }
2136 }
2137 */