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