]> Creatis software - gdcm.git/blob - src/gdcmFileHelper.cxx
'Acr-NEMA' data elements
[gdcm.git] / src / gdcmFileHelper.cxx
1 /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: gdcmFileHelper.cxx,v $
5   Language:  C++
6
7   Date:      $Date: 2006/07/04 09:51:11 $
8   Version:   $Revision: 1.107 $
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  * @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  
734    // --------------------------------------------------------------
735    // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
736    //
737    // if recognition code tells us we dealt with a LibIDO image
738    // we reproduce on disk the switch between lineNumber and columnNumber
739    // just before writting ...
740    /// \todo the best trick would be *change* the recognition code
741    ///       but pb expected if user deals with, e.g. COMPLEX images
742    
743    if ( WriteType == ACR_LIBIDO )
744    {
745       SetWriteToLibido();
746    }
747    else
748    {
749       SetWriteToNoLibido();
750    }
751    // ----------------- End of Special Patch ----------------
752   
753    switch(WriteMode)
754    {
755       case WMODE_RAW :
756          SetWriteToRaw(); // modifies and pushes to the archive, when necessary
757          break;
758       case WMODE_RGB :
759          SetWriteToRGB(); // modifies and pushes to the archive, when necessary
760          break;
761    }
762
763    bool check = CheckWriteIntegrity(); // verifies length
764    if (WriteType == JPEG ) check = true;
765    if (check)
766    {
767       check = FileInternal->Write(fileName,WriteType);
768    }
769
770    RestoreWrite(); 
771   // RestoreWriteFileType();
772   // RestoreWriteMandatory();
773    
774
775    // --------------------------------------------------------------
776    // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
777    // 
778    // ...and we restore the header to be Dicom Compliant again 
779    // just after writting
780    RestoreWriteOfLibido();
781    // ----------------- End of Special Patch ----------------
782
783    return check;
784 }
785
786 //-----------------------------------------------------------------------------
787 // Protected
788 /**
789  * \brief Checks the write integrity
790  *
791  * The tests made are :
792  *  - verify the size of the image to write with the possible write
793  *    when the user set an image data
794  * @return true if check is successfull
795  */
796 bool FileHelper::CheckWriteIntegrity()
797 {
798    if ( PixelWriteConverter->GetUserData() )
799    {
800       int numberBitsAllocated = FileInternal->GetBitsAllocated();
801       if ( numberBitsAllocated == 0 || numberBitsAllocated == 12 )
802       {
803          gdcmWarningMacro( "numberBitsAllocated changed from " 
804                           << numberBitsAllocated << " to 16 " 
805                           << " for consistency purpose" );
806          numberBitsAllocated = 16;
807       }
808
809       size_t decSize = FileInternal->GetXSize()
810                      * FileInternal->GetYSize() 
811                      * FileInternal->GetZSize()
812                      * FileInternal->GetTSize()     
813                      * FileInternal->GetSamplesPerPixel()
814                      * ( numberBitsAllocated / 8 );
815       size_t rgbSize = decSize;
816       if ( FileInternal->HasLUT() )
817          rgbSize = decSize * 3;
818
819       switch(WriteMode)
820       {
821          case WMODE_RAW :
822             if ( decSize!=PixelWriteConverter->GetUserDataSize() )
823             {
824                gdcmWarningMacro( "Data size (Raw) is incorrect. Should be " 
825                            << decSize << " / Found :" 
826                            << PixelWriteConverter->GetUserDataSize() );
827                return false;
828             }
829             break;
830          case WMODE_RGB :
831             if ( rgbSize!=PixelWriteConverter->GetUserDataSize() )
832             {
833                gdcmWarningMacro( "Data size (RGB) is incorrect. Should be " 
834                           << decSize << " / Found " 
835                           << PixelWriteConverter->GetUserDataSize() );
836                return false;
837             }
838             break;
839       }
840    }
841
842    return true;
843 }
844
845 /**
846  * \brief Updates the File to write RAW data (as opposed to RGB data)
847  *       (modifies, when necessary, photochromatic interpretation, 
848  *       bits allocated, Pixels element VR)
849  */ 
850 void FileHelper::SetWriteToRaw()
851 {
852    if ( FileInternal->GetNumberOfScalarComponents() == 3 
853     && !FileInternal->HasLUT() )
854    {
855       SetWriteToRGB();
856    } 
857    else
858    {
859       DataEntry *photInt = CopyDataEntry(0x0028,0x0004,"CS");
860       if (FileInternal->HasLUT() )
861       {
862          photInt->SetString("PALETTE COLOR ");
863       }
864       else
865       {
866          photInt->SetString("MONOCHROME2 ");
867       }
868
869       PixelWriteConverter->SetReadData(PixelReadConverter->GetRaw(),
870                                        PixelReadConverter->GetRawSize());
871
872       std::string vr = "OB";
873       if ( FileInternal->GetBitsAllocated()>8 )
874          vr = "OW";
875       if ( FileInternal->GetBitsAllocated()==24 ) // For RGB ACR files 
876          vr = "OB";
877        // For non RAW data. Mainly JPEG
878       if( WriteType == JPEG )
879       {
880          vr = "OW";
881       }
882       DataEntry *pixel = 
883          CopyDataEntry(GetFile()->GetGrPixel(),GetFile()->GetNumPixel(),vr);
884       pixel->SetFlag(DataEntry::FLAG_PIXELDATA);
885       pixel->SetBinArea(PixelWriteConverter->GetData(),false);
886       pixel->SetLength(PixelWriteConverter->GetDataSize());
887
888       Archive->Push(photInt);
889       Archive->Push(pixel);
890
891       photInt->Delete();
892       pixel->Delete();
893    }
894 }
895
896 /**
897  * \brief Updates the File to write RGB data (as opposed to RAW data)
898  *       (modifies, when necessary, photochromatic interpretation, 
899  *       samples per pixel, Planar configuration, 
900  *       bits allocated, bits stored, high bit -ACR 24 bits-
901  *       Pixels element VR, pushes out the LUT, )
902  */ 
903 void FileHelper::SetWriteToRGB()
904 {
905    if ( FileInternal->GetNumberOfScalarComponents()==3 )
906    {
907       PixelReadConverter->BuildRGBImage();
908       
909       DataEntry *spp = CopyDataEntry(0x0028,0x0002,"US");
910       spp->SetString("3 ");
911
912       DataEntry *planConfig = CopyDataEntry(0x0028,0x0006,"US");
913       planConfig->SetString("0 ");
914
915       DataEntry *photInt = CopyDataEntry(0x0028,0x0004,"CS");
916       photInt->SetString("RGB ");
917
918       if ( PixelReadConverter->GetRGB() )
919       {
920          PixelWriteConverter->SetReadData(PixelReadConverter->GetRGB(),
921                                           PixelReadConverter->GetRGBSize());
922       }
923       else // Raw data
924       {
925          PixelWriteConverter->SetReadData(PixelReadConverter->GetRaw(),
926                                           PixelReadConverter->GetRawSize());
927       }
928
929       std::string vr = "OB";
930       if ( FileInternal->GetBitsAllocated()>8 )
931          vr = "OW";
932       if ( FileInternal->GetBitsAllocated()==24 ) // For RGB ACR files 
933          vr = "OB";
934       DataEntry *pixel = 
935          CopyDataEntry(GetFile()->GetGrPixel(),GetFile()->GetNumPixel(),vr);
936       pixel->SetFlag(DataEntry::FLAG_PIXELDATA);
937       pixel->SetBinArea(PixelWriteConverter->GetData(),false);
938       pixel->SetLength(PixelWriteConverter->GetDataSize());
939
940       Archive->Push(spp);
941       Archive->Push(planConfig);
942       Archive->Push(photInt);
943       Archive->Push(pixel);
944
945       spp->Delete();
946       planConfig->Delete();
947       photInt->Delete();
948       pixel->Delete();
949
950       // Remove any LUT
951       Archive->Push(0x0028,0x1101);
952       Archive->Push(0x0028,0x1102);
953       Archive->Push(0x0028,0x1103);
954       Archive->Push(0x0028,0x1201);
955       Archive->Push(0x0028,0x1202);
956       Archive->Push(0x0028,0x1203);
957
958       // push out Palette Color Lookup Table UID, if any
959       Archive->Push(0x0028,0x1199);
960
961       // For old '24 Bits' ACR-NEMA
962       // Thus, we have a RGB image and the bits allocated = 24 and 
963       // samples per pixels = 1 (in the read file)
964       if ( FileInternal->GetBitsAllocated()==24 ) 
965       {
966          DataEntry *bitsAlloc = CopyDataEntry(0x0028,0x0100,"US");
967          bitsAlloc->SetString("8 ");
968
969          DataEntry *bitsStored = CopyDataEntry(0x0028,0x0101,"US");
970          bitsStored->SetString("8 ");
971
972          DataEntry *highBit = CopyDataEntry(0x0028,0x0102,"US");
973          highBit->SetString("7 ");
974
975          Archive->Push(bitsAlloc);
976          Archive->Push(bitsStored);
977          Archive->Push(highBit);
978
979          bitsAlloc->Delete();
980          bitsStored->Delete();
981          highBit->Delete();
982       }
983    }
984    else
985    {
986       SetWriteToRaw();
987    }
988 }
989
990 /**
991  * \brief Restore the File write mode  
992  */ 
993 void FileHelper::RestoreWrite()
994 {
995
996    Archive->Restore(0x0028,0x0002);
997    Archive->Restore(0x0028,0x0004);
998    
999    Archive->Restore(0x0028,0x0006);
1000    Archive->Restore(GetFile()->GetGrPixel(),GetFile()->GetNumPixel());
1001
1002    // For old ACR-NEMA (24 bits problem)
1003    Archive->Restore(0x0028,0x0100);
1004    Archive->Restore(0x0028,0x0101);
1005    Archive->Restore(0x0028,0x0102);
1006
1007    // For the LUT
1008    Archive->Restore(0x0028,0x1101);
1009    Archive->Restore(0x0028,0x1102);
1010    Archive->Restore(0x0028,0x1103);
1011    Archive->Restore(0x0028,0x1201);
1012    Archive->Restore(0x0028,0x1202);
1013    Archive->Restore(0x0028,0x1203);
1014
1015    // For the Palette Color Lookup Table UID
1016    Archive->Restore(0x0028,0x1203); 
1017
1018    // group 0002 may be pushed out for ACR-NEMA writting purposes 
1019    Archive->Restore(0x0002,0x0000);
1020    Archive->Restore(0x0002,0x0001);
1021    Archive->Restore(0x0002,0x0002);
1022    Archive->Restore(0x0002,0x0003);
1023    Archive->Restore(0x0002,0x0010);
1024    Archive->Restore(0x0002,0x0012);
1025    Archive->Restore(0x0002,0x0013);
1026    Archive->Restore(0x0002,0x0016);
1027    Archive->Restore(0x0002,0x0100);
1028    Archive->Restore(0x0002,0x0102);
1029
1030 }
1031
1032 /**
1033  * \brief Pushes out the whole group 0002
1034  *        FIXME : better, set a flag to tell the writer not to write it ...
1035  *        FIXME : method should probably have an other name !
1036  *                SetWriteFileTypeToACR is NOT opposed to 
1037  *                SetWriteFileTypeToExplicitVR and SetWriteFileTypeToImplicitVR
1038  */ 
1039 void FileHelper::SetWriteFileTypeToACR()
1040 {
1041    Archive->Push(0x0002,0x0000);
1042    Archive->Push(0x0002,0x0001);
1043    Archive->Push(0x0002,0x0002);
1044    Archive->Push(0x0002,0x0003);
1045    Archive->Push(0x0002,0x0010);
1046    Archive->Push(0x0002,0x0012);
1047    Archive->Push(0x0002,0x0013);
1048    Archive->Push(0x0002,0x0016);
1049    Archive->Push(0x0002,0x0100);
1050    Archive->Push(0x0002,0x0102);
1051 }
1052
1053 /**
1054  * \brief Sets in the File the TransferSyntax to 'JPEG'
1055  */ 
1056 void FileHelper::SetWriteFileTypeToJPEG()
1057 {
1058    std::string ts = Util::DicomString( 
1059       Global::GetTS()->GetSpecialTransferSyntax(TS::JPEGBaselineProcess1) );
1060
1061    DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1062    tss->SetString(ts);
1063
1064    Archive->Push(tss);
1065    tss->Delete();
1066 }
1067
1068 /**
1069  * \brief Sets in the File the TransferSyntax to 'Explicit VR Little Endian"   
1070  */ 
1071 void FileHelper::SetWriteFileTypeToExplicitVR()
1072 {
1073    std::string ts = Util::DicomString( 
1074       Global::GetTS()->GetSpecialTransferSyntax(TS::ExplicitVRLittleEndian) );
1075
1076    DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1077    tss->SetString(ts);
1078    Archive->Push(tss);
1079    tss->Delete();
1080 }
1081
1082 /**
1083  * \brief Sets in the File the TransferSyntax to 'Implicit VR Little Endian"   
1084  */ 
1085 void FileHelper::SetWriteFileTypeToImplicitVR()
1086 {
1087    std::string ts = Util::DicomString(
1088       Global::GetTS()->GetSpecialTransferSyntax(TS::ImplicitVRLittleEndian) );
1089
1090    DataEntry *tss = CopyDataEntry(0x0002,0x0010,"UI");
1091    tss->SetString(ts);
1092    Archive->Push(tss);
1093    tss->Delete();
1094 }
1095
1096
1097 /**
1098  * \brief Restore in the File the initial group 0002
1099  */ 
1100 void FileHelper::RestoreWriteFileType()
1101 {
1102 }
1103
1104 /**
1105  * \brief Set the Write not to Libido format
1106  */ 
1107 void FileHelper::SetWriteToLibido()
1108 {
1109    DataEntry *oldRow = FileInternal->GetDataEntry(0x0028, 0x0010);
1110    DataEntry *oldCol = FileInternal->GetDataEntry(0x0028, 0x0011);
1111    
1112    if ( oldRow && oldCol )
1113    {
1114       std::string rows, columns; 
1115
1116       //DataEntry *newRow=DataEntry::New(oldRow->GetDictEntry());
1117       //DataEntry *newCol=DataEntry::New(oldCol->GetDictEntry());
1118       
1119       DataEntry *newRow=DataEntry::New(0x0028, 0x0010, "US");
1120       DataEntry *newCol=DataEntry::New(0x0028, 0x0011, "US");
1121       
1122       newRow->Copy(oldCol);
1123       newCol->Copy(oldRow);
1124
1125       newRow->SetString(oldCol->GetString());
1126       newCol->SetString(oldRow->GetString());
1127
1128       Archive->Push(newRow);
1129       Archive->Push(newCol);
1130
1131       newRow->Delete();
1132       newCol->Delete();
1133    }
1134
1135    DataEntry *libidoCode = CopyDataEntry(0x0008,0x0010,"LO");
1136    libidoCode->SetString("ACRNEMA_LIBIDO_1.1");
1137    Archive->Push(libidoCode);
1138    libidoCode->Delete();
1139 }
1140
1141 /**
1142  * \brief Set the Write not to No Libido format
1143  */ 
1144 void FileHelper::SetWriteToNoLibido()
1145 {
1146    DataEntry *recCode = FileInternal->GetDataEntry(0x0008,0x0010);
1147    if ( recCode )
1148    {
1149       if ( recCode->GetString() == "ACRNEMA_LIBIDO_1.1" )
1150       {
1151          DataEntry *libidoCode = CopyDataEntry(0x0008,0x0010,"LO");
1152          libidoCode->SetString("");
1153          Archive->Push(libidoCode);
1154          libidoCode->Delete();
1155       }
1156    }
1157 }
1158
1159 /**
1160  * \brief Restore the Write format
1161  */ 
1162 void FileHelper::RestoreWriteOfLibido()
1163 {
1164    Archive->Restore(0x0028,0x0010);
1165    Archive->Restore(0x0028,0x0011);
1166    Archive->Restore(0x0008,0x0010);
1167
1168    // Restore 'LibIDO-special' entries, if any
1169    Archive->Restore(0x0028,0x0015);
1170    Archive->Restore(0x0028,0x0016);
1171    Archive->Restore(0x0028,0x0017);
1172    Archive->Restore(0x0028,0x00199);
1173 }
1174
1175 /**
1176  * \brief   Duplicates a DataEntry or creates it.
1177  * @param   group   Group number of the Entry 
1178  * @param   elem  Element number of the Entry
1179  * @param   vr  Value Representation of the Entry
1180  * \return  pointer to the new Bin Entry (NULL when creation failed).
1181  */ 
1182 DataEntry *FileHelper::CopyDataEntry(uint16_t group, uint16_t elem,
1183                                    const VRKey &vr)
1184 {
1185    DocEntry *oldE = FileInternal->GetDocEntry(group, elem);
1186    DataEntry *newE;
1187
1188    if ( oldE && vr != GDCM_VRUNKNOWN ) 
1189       if ( oldE->GetVR() != vr )
1190          oldE = NULL;
1191
1192    if ( oldE )
1193    {
1194       //newE = DataEntry::New(oldE->GetDictEntry());
1195       newE = DataEntry::New(group, elem, vr);
1196       newE->Copy(oldE);
1197    }
1198    else
1199    {
1200       newE = GetFile()->NewDataEntry(group, elem, vr);
1201    }
1202
1203    return newE;
1204 }
1205
1206 /**
1207  * \brief   This method is called automatically, just before writting
1208  *         in order to produce a 'True Dicom V3' image.
1209  *
1210  *         We cannot know *how* the user made the File :
1211  *         (reading an old ACR-NEMA file or a not very clean DICOM file ...) 
1212  *          Just before writting :
1213  *             - we check the Entries
1214  *             - we create the mandatory entries if they are missing
1215  *             - we modify the values if necessary
1216  *             - we push the sensitive entries to the Archive
1217  *          The writing process will restore the entries as they where before 
1218  *          entering FileHelper::CheckMandatoryElements, so the user will always
1219  *          see the entries just as they were before he decided to write.
1220  *
1221  * \note
1222  *       -  Entries whose type is 1 are mandatory, with a mandatory value
1223  *       -  Entries whose type is 1c are mandatory-inside-a-Sequence,
1224  *                             with a mandatory value
1225  *       -  Entries whose type is 2 are mandatory, with an optional value
1226  *       -  Entries whose type is 2c are mandatory-inside-a-Sequence,
1227  *                             with an optional value
1228  *       -  Entries whose type is 3 are optional
1229  * 
1230  * \todo 
1231  *         - warn the user if we had to add some entries :
1232  *         even if a mandatory entry is missing, we add it, with a default value
1233  *         (we don't want to give up the writting process if user forgot to
1234  *         specify Lena's Patient ID, for instance ...)
1235  *         - read the whole PS 3.3 Part of DICOM  (890 pages)
1236  *         and write a *full* checker (probably one method per Modality ...)
1237  *         Any contribution is welcome. 
1238  *         - write a user callable full checker, to allow post reading
1239  *         and/or pre writting image consistency check.           
1240  */ 
1241
1242 /* -------------------------------------------------------------------------------------
1243 To be moved to User's guide / WIKI  ?
1244
1245 We have to deal with 4 *very* different cases :
1246 -1) user created ex nihilo his own image and wants to write it as a Dicom image.
1247     USER_OWN_IMAGE
1248 -2) user modified the pixels of an existing image.
1249    FILTERED_IMAGE
1250 -3) user created a new image, using a set of existing images (eg MIP, MPR, cartography image)
1251    CREATED_IMAGE
1252 -4) user modified/added some tags *without processing* the pixels (anonymization..
1253    UNMODIFIED_PIXELS_IMAGE
1254 -Probabely some more to be added  
1255  
1256 gdcm::FileHelper::CheckMandatoryElements() deals automatically with these cases.
1257
1258 1)2)3)4)
1259 0008 0012 Instance Creation Date
1260 0008 0013 Instance Creation Time
1261 0008 0018 SOP Instance UID
1262 are *always* created with the current values; user has *no* possible intervention on
1263 them.
1264
1265 'Serie Instance UID'(0x0020,0x000e)
1266 'Study Instance UID'(0x0020,0x000d) are kept as is if already exist,
1267                                     created  if it doesn't.
1268  The user is allowed to create his own Series/Studies, 
1269      keeping the same 'Serie Instance UID' / 'Study Instance UID' for various images
1270  Warning :     
1271  The user shouldn't add any image to a 'Manufacturer Serie'
1272      but there is no way no to allow him to do that
1273      
1274  None of the 'shadow elements' are droped out.
1275      
1276
1277 1)
1278 'Conversion Type (0x0008,0x0064) is forced to 'SYN' (Synthetic Image).
1279  
1280 1)3)
1281 'Media Storage SOP Class UID' (0x0002,0x0002)
1282 'SOP Class UID'               (0x0008,0x0016) are set to 
1283                                                [Secondary Capture Image Storage]
1284 'Image Type'                  (0x0008,0x0008) is forced to  "DERIVED\PRIMARY"
1285 Conversion Type               (0x0008,0x0064) is forced to 'SYN' (Synthetic Image)
1286
1287 2)4)
1288 If 'SOP Class UID' exists in the native image  ('true DICOM' image)
1289     we create the 'Source Image Sequence' SeqEntry (0x0008, 0x2112)    
1290     --> 'Referenced SOP Class UID' (0x0008, 0x1150)
1291          whose value is the original 'SOP Class UID'
1292     --> 'Referenced SOP Instance UID' (0x0008, 0x1155)
1293          whose value is the original 'SOP Class UID'
1294
1295 3) TODO : find a trick to allow user to pass to the writter the list of the Dicom images 
1296           or the Series, (or the Study ?) he used to created his image 
1297           (MIP, MPR, cartography image, ...)
1298            These info should be stored (?)
1299           0008 1110 SQ 1 Referenced Study Sequence
1300           0008 1115 SQ 1 Referenced Series Sequence
1301           0008 1140 SQ 1 Referenced Image Sequence
1302        
1303 4) When user *knows* he didn't modified the pixels, we keep some informations unchanged :
1304 'Media Storage SOP Class UID' (0x0002,0x0002)
1305 'SOP Class UID'               (0x0008,0x0016)
1306 'Image Type'                  (0x0008,0x0008)
1307 'Conversion Type'             (0x0008,0x0064)
1308
1309
1310 Bellow follows the full description (hope so !) of the consistency checks performed 
1311 by gdcm::FileHelper::CheckMandatoryElements()
1312
1313
1314 -->'Media Storage SOP Class UID' (0x0002,0x0002)
1315 -->'SOP Class UID'               (0x0008,0x0016) are defaulted to 
1316                                                [Secondary Capture Image Storage]
1317 --> 'Image Type'  (0x0008,0x0008)
1318      is forced to  "DERIVED\PRIMARY"
1319      (The written image is no longer an 'ORIGINAL' one)
1320   Except if user knows he didn't modify the image (e.g. : he just anonymized the file)
1321    
1322  -->  Conversion Type (0x0008,0x0064)
1323      is defaulted to 'SYN' (Synthetic Image)
1324   when *he* knows he created his own image ex nihilo
1325             
1326 --> 'Modality' (0x0008,0x0060)   
1327     is defaulted to "OT" (other) if missing.   
1328     (a fully user created image belongs to *no* modality)
1329       
1330 --> 'Media Storage SOP Instance UID' (0x0002,0x0003)
1331 --> 'Implementation Class UID'       (0x0002,0x0012)
1332     are automatically generated; no user intervention possible
1333
1334 --> 'Serie Instance UID'(0x0020,0x000e)
1335 --> 'Study Instance UID'(0x0020,0x000d) are kept as is if already exist
1336                                              created  if it doesn't.
1337      The user is allowed to create his own Series/Studies, 
1338      keeping the same 'Serie Instance UID' / 'Study Instance UID' 
1339      for various images
1340      Warning :     
1341      The user shouldn't add any image to a 'Manufacturer Serie'
1342      but there is no way no to allowed him to do that 
1343              
1344 --> If 'SOP Class UID' exists in the native image  ('true DICOM' image)
1345     we create the 'Source Image Sequence' SeqEntry (0x0008, 0x2112)
1346     
1347     --> 'Referenced SOP Class UID' (0x0008, 0x1150)
1348          whose value is the original 'SOP Class UID'
1349     --> 'Referenced SOP Instance UID' (0x0008, 0x1155)
1350          whose value is the original 'SOP Class UID'
1351     
1352 --> Bits Stored, Bits Allocated, Hight Bit Position are checked for consistency
1353 --> Pixel Spacing     (0x0028,0x0030) is defaulted to "1.0\1.0"
1354 --> Samples Per Pixel (0x0028,0x0002) is defaulted to 1 (grayscale)
1355
1356 --> Imager Pixel Spacing (0x0018,0x1164) : defaulted to Pixel Spacing value
1357
1358 --> Instance Creation Date, Instance Creation Time are forced to current Date and Time
1359
1360 --> Study Date, Study Time are defaulted to current Date and Time
1361    (they remain unchanged if they exist)
1362
1363 --> Patient Orientation : (0x0020,0x0020), if not present, is deduced from 
1364     Image Orientation (Patient) : (0020|0037) or from
1365     Image Orientation (RET)     : (0020 0035)
1366    
1367 --> Study ID, Series Number, Instance Number, Patient Orientation (Type 2)
1368     are created, with empty value if there are missing.
1369
1370 --> Manufacturer, Institution Name, Patient's Name, (Type 2)
1371     are defaulted with a 'gdcm' value.
1372     
1373 --> Patient ID, Patient's Birth Date, Patient's Sex, (Type 2)
1374 --> Referring Physician's Name  (Type 2)
1375     are created, with empty value if there are missing.
1376
1377  -------------------------------------------------------------------------------------*/
1378
1379 void FileHelper::CheckMandatoryElements()
1380 {
1381    std::string sop =  Util::CreateUniqueUID();
1382
1383    // --------------------- For Meta Elements ---------------------
1384    // just to remember : 'official' 0002 group
1385    if ( WriteType != ACR && WriteType != ACR_LIBIDO )
1386    {
1387      // Group 000002 (Meta Elements) already pushed out
1388   
1389    //0002 0000 UL 1 Meta Group Length
1390    //0002 0001 OB 1 File Meta Information Version
1391    //0002 0002 UI 1 Media Stored SOP Class UID
1392    //0002 0003 UI 1 Media Stored SOP Instance UID
1393    //0002 0010 UI 1 Transfer Syntax UID
1394    //0002 0012 UI 1 Implementation Class UID
1395    //0002 0013 SH 1 Implementation Version Name
1396    //0002 0016 AE 1 Source Application Entity Title
1397    //0002 0100 UI 1 Private Information Creator
1398    //0002 0102 OB 1 Private Information
1399  
1400    // Push out 'ACR-NEMA-special' entries, if any
1401       Archive->Push(0x0008,0x0001); // Length to End
1402       Archive->Push(0x0008,0x0010); // Recognition Code
1403       Archive->Push(0x0028,0x0005); // Image Dimension  
1404   
1405    // Create them if not found
1406    // Always modify the value
1407    // Push the entries to the archive.
1408       CopyMandatoryEntry(0x0002,0x0000,"0","UL");
1409
1410       DataEntry *e_0002_0001 = CopyDataEntry(0x0002,0x0001, "OB");
1411       e_0002_0001->SetBinArea((uint8_t*)Util::GetFileMetaInformationVersion(),
1412                                false);
1413       e_0002_0001->SetLength(2);
1414       Archive->Push(e_0002_0001);
1415       e_0002_0001->Delete(); 
1416
1417       if ( ContentType == FILTERED_IMAGE || ContentType == UNMODIFIED_PIXELS_IMAGE)
1418       {      
1419    // we keep the original 'Media Storage SOP Class UID', we default it if missing
1420          CheckMandatoryEntry(0x0002,0x0002,"1.2.840.10008.5.1.4.1.1.7","UI"); 
1421       }
1422       else
1423       {
1424    // It's *not* an image comming straight from a source. We force
1425    // 'Media Storage SOP Class UID'  --> [Secondary Capture Image Storage]
1426          CopyMandatoryEntry(0x0002,0x0002,"1.2.840.10008.5.1.4.1.1.7","UI");
1427       }
1428       
1429    // 'Media Storage SOP Instance UID'   
1430       CopyMandatoryEntry(0x0002,0x0003,sop,"UI");
1431       
1432    // 'Implementation Class UID'
1433    // FIXME : in all examples we have, 0x0002,0x0012 is not so long :
1434    //         seems to be Root UID + 4 digits (?)
1435       CopyMandatoryEntry(0x0002,0x0012,Util::CreateUniqueUID(),"UI");
1436
1437    // 'Implementation Version Name'
1438       std::string version = "GDCM ";
1439       version += Util::GetVersion();
1440       CopyMandatoryEntry(0x0002,0x0013,version,"SH");
1441    }
1442
1443    // --------------------- For DataSet ---------------------
1444
1445    if ( ContentType != USER_OWN_IMAGE) // when it's not a user made image
1446    { 
1447    
1448       gdcmDebugMacro( "USER_OWN_IMAGE (1)");
1449     // If 'SOP Class UID' exists ('true DICOM' image)
1450    // we create the 'Source Image Sequence' SeqEntry
1451    // to hold informations about the Source Image
1452   
1453       DataEntry *e_0008_0016 = FileInternal->GetDataEntry(0x0008, 0x0016);
1454       if ( e_0008_0016 )
1455       {
1456       // Create 'Source Image Sequence' SeqEntry
1457 //     SeqEntry *sis = SeqEntry::New (
1458 //            Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x2112) );
1459       SeqEntry *sis = SeqEntry::New (0x0008, 0x2112);
1460       SQItem *sqi = SQItem::New(1);
1461       // (we assume 'SOP Instance UID' exists too) 
1462       // create 'Referenced SOP Class UID'
1463 //     DataEntry *e_0008_1150 = DataEntry::New(
1464 //            Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x1150) );
1465       DataEntry *e_0008_1150 = DataEntry::New(0x0008, 0x1150, "UI");
1466       e_0008_1150->SetString( e_0008_0016->GetString());
1467       sqi->AddEntry(e_0008_1150);
1468       e_0008_1150->Delete();
1469       
1470       // create 'Referenced SOP Instance UID'
1471       DataEntry *e_0008_0018 = FileInternal->GetDataEntry(0x0008, 0x0018);
1472 //      DataEntry *e_0008_1155 = DataEntry::New(
1473 //            Global::GetDicts()->GetDefaultPubDict()->GetEntry(0x0008, 0x1155) );
1474       DataEntry *e_0008_1155 = DataEntry::New(0x0008, 0x1155, "UI");
1475       e_0008_1155->SetString( e_0008_0018->GetString());
1476       sqi->AddEntry(e_0008_1155);
1477       e_0008_1155->Delete();
1478
1479       sis->AddSQItem(sqi,1);
1480       sqi->Delete();
1481
1482       // temporarily replaces any previous 'Source Image Sequence' 
1483       Archive->Push(sis);
1484       sis->Delete();
1485  
1486       // FIXME : is 'Image Type' *really* depending on the presence of 'SOP Class UID'?
1487        if ( ContentType == FILTERED_IMAGE)      
1488       // the user *knows* he just modified the pixels
1489       // the image is no longer an 'Original' one
1490          CopyMandatoryEntry(0x0008,0x0008,"DERIVED\\PRIMARY","CS");    
1491       }
1492    }
1493       
1494    if ( ContentType == FILTERED_IMAGE || ContentType == UNMODIFIED_PIXELS_IMAGE)
1495    {      
1496    // we keep the original 'Media Storage SOP Class UID', we default it if missing (it should be present !)
1497          CheckMandatoryEntry(0x0008,0x0016,"1.2.840.10008.5.1.4.1.1.7","UI");      
1498    }
1499    else
1500    {
1501    // It's *not* an image comming straight from a source. We force
1502    // 'Media Storage SOP Class UID'  --> [Secondary Capture Image Storage]
1503          CopyMandatoryEntry(0x0008,0x0016,"1.2.840.10008.5.1.4.1.1.7", "UI");      
1504    }
1505      
1506    Archive->Push(0x0028,0x005); // [Image Dimensions (RET)
1507    // Push out 'LibIDO-special' entries, if any
1508    Archive->Push(0x0028,0x0015);
1509    Archive->Push(0x0028,0x0016);
1510    Archive->Push(0x0028,0x0017);
1511    Archive->Push(0x0028,0x0198);  // very old versions
1512    Archive->Push(0x0028,0x0199);
1513  
1514    // Replace deprecated 0028 0012 US Planes   
1515    // by new             0028 0008 IS Number of Frames
1516    
1517   ///\todo : find if there is a rule!
1518    DataEntry *e_0028_0012 = FileInternal->GetDataEntry(0x0028, 0x0012);
1519    if ( e_0028_0012 )
1520    {
1521       CopyMandatoryEntry(0x0028, 0x0008,e_0028_0012->GetString(),"IS");
1522       Archive->Push(0x0028,0x0012);      
1523    }
1524
1525    // Deal with the pb of (Bits Stored = 12)
1526    // - we're gonna write the image as Bits Stored = 16
1527    if ( FileInternal->GetEntryString(0x0028,0x0100) ==  "12")
1528    {
1529       CopyMandatoryEntry(0x0028,0x0100,"16","US");
1530    }
1531
1532    // Check if user wasn't drunk ;-)
1533
1534    std::ostringstream s;
1535    // check 'Bits Allocated' vs decent values
1536    int nbBitsAllocated = FileInternal->GetBitsAllocated();
1537    if ( nbBitsAllocated == 0 || nbBitsAllocated > 32)
1538    {
1539       CopyMandatoryEntry(0x0028,0x0100,"16","US");
1540       gdcmWarningMacro("(0028,0100) changed from "
1541          << nbBitsAllocated << " to 16 for consistency purpose");
1542       nbBitsAllocated = 16; 
1543    }
1544    // check 'Bits Stored' vs 'Bits Allocated'   
1545    int nbBitsStored = FileInternal->GetBitsStored();
1546    if ( nbBitsStored == 0 || nbBitsStored > nbBitsAllocated )
1547    {
1548       s.str("");
1549       s << nbBitsAllocated;
1550       CopyMandatoryEntry(0x0028,0x0101,s.str(),"US");
1551       gdcmWarningMacro("(0028,0101) changed from "
1552                        << nbBitsStored << " to " << nbBitsAllocated
1553                        << " for consistency purpose" );
1554       nbBitsStored = nbBitsAllocated; 
1555     }
1556    // check 'Hight Bit Position' vs 'Bits Allocated' and 'Bits Stored'
1557    int highBitPosition = FileInternal->GetHighBitPosition();
1558    if ( highBitPosition == 0 || 
1559         highBitPosition > nbBitsAllocated-1 ||
1560         highBitPosition < nbBitsStored-1  )
1561    {
1562       s.str("");
1563       s << nbBitsStored - 1; 
1564       CopyMandatoryEntry(0x0028,0x0102,s.str(),"US");
1565       gdcmWarningMacro("(0028,0102) changed from "
1566                        << highBitPosition << " to " << nbBitsAllocated-1
1567                        << " for consistency purpose");
1568    }
1569
1570    std::string pixelSpacing = FileInternal->GetEntryString(0x0028,0x0030);
1571    if ( pixelSpacing == GDCM_UNFOUND )
1572    {
1573       pixelSpacing = "1.0\\1.0";
1574        // if missing, Pixel Spacing forced to "1.0\1.0"
1575       CopyMandatoryEntry(0x0028,0x0030,pixelSpacing,"DS");
1576    }
1577    
1578    // 'Imager Pixel Spacing' : defaulted to 'Pixel Spacing'
1579    // --> This one is the *legal* one !
1580    if ( ContentType != USER_OWN_IMAGE)
1581    //  we write it only when we are *sure* the image comes from
1582    //         an imager (see also 0008,0x0064)          
1583       CheckMandatoryEntry(0x0018,0x1164,pixelSpacing,"DS");
1584
1585
1586
1587 /*
1588 ///Exact meaning of RETired fields
1589
1590 // See page 73 of ACR-NEMA_300-1988.pdf !
1591
1592 // 0020,0020 : Patient Orientation : 
1593 Patient direction of the first row and
1594 column of the images. The first entry id the direction of the raws, given by the
1595 direction of the last pixel in the first row from the first pixel in tha row.
1596 the second entry is the direction of the columns, given by the direction of the
1597 last pixel in the first column from the first pixel in that column.
1598 L : Left, F : Feet, A : Anterior, P : Posterior.
1599 Up to 3 letters can be used in combination to indicate oblique planes.
1600
1601 //0020,0030 Image Position (RET)
1602 x,y,z coordinates im mm of the first pixel in the image
1603
1604 // 0020,0035 Image Orientation (RET)
1605 Direction cosines of the R axis of the image system with respect to the
1606 equipment coordinate axes x,y,z, followed by direction cosines of the C axis of
1607 the image system with respect to the same axes
1608
1609 //0020,0050 Location
1610 An image location reference, standard for the modality (such as CT bed
1611 position), used to indicate position. Calculation of position for other purposes
1612 is only from (0020,0030) and (0020,0035)
1613 */
1614  
1615 /*
1616 // if imagePositionPatient    not found, default it with imagePositionRet,    if any
1617 // if imageOrientationPatient not found, default it with imageOrientationRet, if any
1618
1619    std::string imagePositionRet        = FileInternal->GetEntryString(0x0020,0x0030);
1620    std::string imageOrientationRet     = FileInternal->GetEntryString(0x0020,0x0035);
1621    std::string imagePositionPatient    = FileInternal->GetEntryString(0x0020,0x0032);
1622    std::string imageOrientationPatient = FileInternal->GetEntryString(0x0020,0x0037);
1623    
1624    if(  imagePositionPatient == GDCM_UNFOUND && imageOrientationPatient == GDCM_UNFOUND
1625      && imagePositionRet     != GDCM_UNFOUND && imageOrientationRet     != GDCM_UNFOUND)
1626    {
1627       CopyMandatoryEntry(0x0020, 0x0032,imagePositionRet,"DS");
1628       Archive->Push(0x0020,0x0030); 
1629       CopyMandatoryEntry(0x0020, 0x0037,imageOrientationRet,"DS");
1630       Archive->Push(0x0020,0x0035);        
1631    }        
1632 */
1633     
1634    // Samples Per Pixel (type 1) : default to grayscale 
1635    CheckMandatoryEntry(0x0028,0x0002,"1","US");
1636
1637    // --- Check UID-related Entries ---
1638  
1639    // At the end, not to overwrite the original ones,
1640    // needed by 'Referenced SOP Instance UID', 'Referenced SOP Class UID'   
1641    // 'SOP Instance UID'  
1642    CopyMandatoryEntry(0x0008,0x0018,sop,"UI");
1643
1644    if ( ContentType == USER_OWN_IMAGE)
1645    {
1646       gdcmDebugMacro( "USER_OWN_IMAGE (2)");   
1647        // Conversion Type.
1648        // Other possible values are :
1649        // See PS 3.3, Page 408
1650    
1651        // DV = Digitized Video
1652        // DI = Digital Interface   
1653        // DF = Digitized Film
1654        // WSD = Workstation
1655        // SD = Scanned Document
1656        // SI = Scanned Image
1657        // DRW = Drawing
1658        // SYN = Synthetic Image
1659            
1660       CheckMandatoryEntry(0x0008,0x0064,"SYN","CS"); // Why not?
1661    } 
1662 /*
1663    if ( ContentType == CREATED_IMAGE)
1664    {
1665    /// \todo : find a trick to pass the Media Storage SOP Instance UID of the images used to create the current image
1666    
1667    }
1668 */
1669            
1670    // ---- The user will never have to take any action on the following ----
1671
1672    // new value for 'SOP Instance UID'
1673    //SetMandatoryEntry(0x0008,0x0018,Util::CreateUniqueUID());
1674
1675    // Instance Creation Date
1676    const std::string &date = Util::GetCurrentDate();
1677    CopyMandatoryEntry(0x0008,0x0012,date,"DA");
1678  
1679    // Instance Creation Time
1680    const std::string &time = Util::GetCurrentTime();
1681    CopyMandatoryEntry(0x0008,0x0013,time,"TM");
1682
1683    // Study Date
1684    CheckMandatoryEntry(0x0008,0x0020,date,"DA");
1685    // Study Time
1686    CheckMandatoryEntry(0x0008,0x0030,time,"TM");
1687
1688    // Accession Number
1689    //CopyMandatoryEntry(0x0008,0x0050,"");
1690    CheckMandatoryEntry(0x0008,0x0050,"","SH");
1691    
1692
1693    // ----- Add Mandatory Entries if missing ---
1694    // Entries whose type is 1 are mandatory, with a mandatory value
1695    // Entries whose type is 1c are mandatory-inside-a-Sequence,
1696    //                          with a mandatory value
1697    // Entries whose type is 2 are mandatory, with an optional value
1698    // Entries whose type is 2c are mandatory-inside-a-Sequence,
1699    //                          with an optional value
1700    // Entries whose type is 3 are optional
1701
1702    // 'Study Instance UID'
1703    // Keep the value if exists
1704    // The user is allowed to create his own Study, 
1705    //          keeping the same 'Study Instance UID' for various images
1706    // The user may add images to a 'Manufacturer Study',
1707    //          adding new Series to an already existing Study 
1708    CheckMandatoryEntry(0x0020,0x000d,Util::CreateUniqueUID(),"UI");
1709
1710    // 'Serie Instance UID'
1711    // Keep the value if exists
1712    // The user is allowed to create his own Series, 
1713    // keeping the same 'Serie Instance UID' for various images
1714    // The user shouldn't add any image to a 'Manufacturer Serie'
1715    // but there is no way no to prevent him for doing that 
1716    CheckMandatoryEntry(0x0020,0x000e,Util::CreateUniqueUID(),"UI");
1717
1718    // Study ID
1719    CheckMandatoryEntry(0x0020,0x0010,"","SH");
1720
1721    // Series Number
1722    CheckMandatoryEntry(0x0020,0x0011,"","IS");
1723
1724    // Instance Number
1725    CheckMandatoryEntry(0x0020,0x0013,"","IS");
1726    
1727    // Patient Orientation
1728    // Can be computed from (0020|0037) :  Image Orientation (Patient)
1729    gdcm::Orientation *o = gdcm::Orientation::New();
1730    std::string ori = o->GetOrientation ( FileInternal );
1731    o->Delete();
1732    if (ori != "\\" && ori != GDCM_UNFOUND)
1733       CheckMandatoryEntry(0x0020,0x0020,ori,"CS");
1734    else   
1735       CheckMandatoryEntry(0x0020,0x0020,"","CS");
1736
1737    // Default Patient Position to HFS
1738    CheckMandatoryEntry(0x0018,0x5100,"HFS","CS");
1739
1740    // Modality : if missing we set it to 'OTher'
1741    CheckMandatoryEntry(0x0008,0x0060,"OT","CS");
1742
1743    // Manufacturer : if missing we set it to 'GDCM Factory'
1744    CheckMandatoryEntry(0x0008,0x0070,"GDCM Factory","LO");
1745
1746    // Institution Name : if missing we set it to 'GDCM Hospital'
1747    CheckMandatoryEntry(0x0008,0x0080,"GDCM Hospital","LO");
1748
1749    // Patient's Name : if missing, we set it to 'GDCM^Patient'
1750    CheckMandatoryEntry(0x0010,0x0010,"GDCM^Patient","PN");
1751
1752    // Patient ID : some clinical softwares *demand* it although it's a 'type 2' entry.
1753    CheckMandatoryEntry(0x0010,0x0020,"gdcm ID","LO");
1754
1755    // Patient's Birth Date : 'type 2' entry -> must exist, value not mandatory
1756    CheckMandatoryEntry(0x0010,0x0030,"","DA");
1757
1758    // Patient's Sex :'type 2' entry -> must exist, value not mandatory
1759    CheckMandatoryEntry(0x0010,0x0040,"","CS");
1760
1761    // Referring Physician's Name :'type 2' entry -> must exist, value not mandatory
1762    CheckMandatoryEntry(0x0008,0x0090,"","PN");
1763
1764  /*
1765    // Deal with element 0x0000 (group length) of each group.
1766    // First stage : get all the different Groups
1767    
1768   GroupHT grHT;
1769   DocEntry *d = FileInternal->GetFirstEntry();
1770   while(d)
1771   {
1772     grHT[d->GetGroup()] = 0;
1773     d=FileInternal->GetNextEntry();
1774   }
1775   // Second stage : add the missing ones (if any)
1776   for (GroupHT::iterator it = grHT.begin(); it != grHT.end(); ++it)  
1777   {
1778       CheckMandatoryEntry(it->first, 0x0000, "0"); 
1779   }    
1780   // Third stage : update all 'zero level' groups length
1781 */ 
1782
1783
1784
1785 void FileHelper::CheckMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr )
1786 {
1787    DataEntry *entry = FileInternal->GetDataEntry(group,elem);
1788    if ( !entry )
1789    {
1790       //entry = DataEntry::New(Global::GetDicts()->GetDefaultPubDict()->GetEntry(group,elem));
1791       entry = DataEntry::New(group,elem,vr);
1792       entry->SetString(value);
1793       Archive->Push(entry);
1794       entry->Delete();
1795    }    
1796 }
1797
1798 /// \todo : what is it used for ? (FileHelper::SetMandatoryEntry) 
1799 void FileHelper::SetMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr)
1800 {
1801    //DataEntry *entry = DataEntry::New(Global::GetDicts()->GetDefaultPubDict()->GetEntry(group,elem));
1802    DataEntry *entry = DataEntry::New(group,elem,vr);
1803    entry->SetString(value);
1804    Archive->Push(entry);
1805    entry->Delete();
1806 }
1807
1808 void FileHelper::CopyMandatoryEntry(uint16_t group,uint16_t elem,std::string value,const VRKey &vr)
1809 {
1810    DataEntry *entry = CopyDataEntry(group,elem,vr);
1811    entry->SetString(value);
1812    Archive->Push(entry);
1813    entry->Delete();
1814 }
1815
1816 /**
1817  * \brief Restore in the File the initial group 0002
1818  */
1819 void FileHelper::RestoreWriteMandatory()
1820 {
1821    // group 0002 may be pushed out for ACR-NEMA writting purposes 
1822    Archive->Restore(0x0002,0x0000);
1823    Archive->Restore(0x0002,0x0001);
1824    Archive->Restore(0x0002,0x0002);
1825    Archive->Restore(0x0002,0x0003);
1826    Archive->Restore(0x0002,0x0010);
1827    Archive->Restore(0x0002,0x0012);
1828    Archive->Restore(0x0002,0x0013);
1829    Archive->Restore(0x0002,0x0016);
1830    Archive->Restore(0x0002,0x0100);
1831    Archive->Restore(0x0002,0x0102);
1832
1833    // FIXME : Check if none is missing !
1834    
1835    Archive->Restore(0x0008,0x0012);
1836    Archive->Restore(0x0008,0x0013);
1837    Archive->Restore(0x0008,0x0016);
1838    Archive->Restore(0x0008,0x0018);
1839    Archive->Restore(0x0008,0x0060);
1840    Archive->Restore(0x0008,0x0070);
1841    Archive->Restore(0x0008,0x0080);
1842    Archive->Restore(0x0008,0x0090);
1843    Archive->Restore(0x0008,0x2112);
1844
1845    Archive->Restore(0x0010,0x0010);
1846    Archive->Restore(0x0010,0x0030);
1847    Archive->Restore(0x0010,0x0040);
1848
1849    Archive->Restore(0x0020,0x000d);
1850    Archive->Restore(0x0020,0x000e);
1851 }
1852
1853
1854 /**
1855  * \brief   CallStartMethod
1856  */
1857 void FileHelper::CallStartMethod()
1858 {
1859    Progress = 0.0f;
1860    Abort    = false;
1861    CommandManager::ExecuteCommand(this,CMD_STARTPROGRESS);
1862 }
1863
1864 /**
1865  * \brief   CallProgressMethod
1866  */
1867 void FileHelper::CallProgressMethod()
1868 {
1869    CommandManager::ExecuteCommand(this,CMD_PROGRESS);
1870 }
1871
1872 /**
1873  * \brief   CallEndMethod
1874  */
1875 void FileHelper::CallEndMethod()
1876 {
1877    Progress = 1.0f;
1878    CommandManager::ExecuteCommand(this,CMD_ENDPROGRESS);
1879 }
1880
1881 //-----------------------------------------------------------------------------
1882 // Private
1883 /**
1884  * \brief Factorization for various forms of constructors.
1885  */
1886 void FileHelper::Initialize()
1887 {
1888    UserFunction = 0;
1889    ContentType = USER_OWN_IMAGE;
1890    
1891    WriteMode = WMODE_RAW;
1892    WriteType = ExplicitVR;
1893
1894    PixelReadConverter  = new PixelReadConvert;
1895    PixelWriteConverter = new PixelWriteConvert;
1896    Archive = new DocEntryArchive( FileInternal );
1897 }
1898
1899 /**
1900  * \brief Reads/[decompresses] the pixels, 
1901  *        *without* making RGB from Palette Colors 
1902  * @return the pixels area, whatever its type 
1903  *         (uint8_t is just for prototyping : feel free to Cast it) 
1904  */ 
1905 uint8_t *FileHelper::GetRaw()
1906 {
1907    PixelReadConverter->SetUserFunction( UserFunction );
1908
1909    uint8_t *raw = PixelReadConverter->GetRaw();
1910    if ( ! raw )
1911    {
1912       // The Raw image migth not be loaded yet:
1913       std::ifstream *fp = FileInternal->OpenFile();
1914       PixelReadConverter->ReadAndDecompressPixelData( fp );
1915       if ( fp ) 
1916          FileInternal->CloseFile();
1917
1918       raw = PixelReadConverter->GetRaw();
1919       if ( ! raw )
1920       {
1921          gdcmWarningMacro( "Read/decompress of pixel data apparently went wrong.");
1922          return 0;
1923       }
1924    }
1925    return raw;
1926 }
1927
1928 //-----------------------------------------------------------------------------
1929 /**
1930  * \brief   Prints the common part of DataEntry, SeqEntry
1931  * @param   os ostream we want to print in
1932  * @param indent (unused)
1933  */
1934 void FileHelper::Print(std::ostream &os, std::string const &)
1935 {
1936    FileInternal->SetPrintLevel(PrintLevel);
1937    FileInternal->Print(os);
1938
1939    if ( FileInternal->IsReadable() )
1940    {
1941       PixelReadConverter->SetPrintLevel(PrintLevel);
1942       PixelReadConverter->Print(os);
1943    }
1944 }
1945
1946 //-----------------------------------------------------------------------------
1947 } // end namespace gdcm
1948
1949
1950 /* Probabely something to be added to use Rescale Slope/Intercept
1951 Have a look ,at ITK code !
1952
1953 // Internal function to rescale pixel according to Rescale Slope/Intercept
1954 template<class TBuffer, class TSource>
1955 void RescaleFunction(TBuffer* buffer, TSource *source,
1956                      double slope, double intercept, size_t size)
1957 {
1958   size /= sizeof(TSource);
1959
1960   if (slope != 1.0 && intercept != 0.0)
1961     {
1962     // Duff's device.  Instead of this code:
1963     //
1964     //   for(unsigned int i=0; i<size; i++)
1965     //    {
1966     //    buffer[i] = (TBuffer)(source[i]*slope + intercept);
1967     //    }
1968     //
1969     // use Duff's device which exploits "fall through"
1970     register size_t n = (size + 7) / 8;
1971     switch ( size % 8)
1972       {
1973       case 0: do { *buffer++ = (TBuffer)((*source++)*slope + intercept);
1974       case 7:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
1975       case 6:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
1976       case 5:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
1977       case 4:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
1978       case 3:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
1979       case 2:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
1980       case 1:      *buffer++ = (TBuffer)((*source++)*slope + intercept);
1981                  }  while (--n > 0);
1982       }
1983     }
1984   else if (slope == 1.0 && intercept != 0.0)
1985     {
1986     // Duff's device.  Instead of this code:
1987     //
1988     //   for(unsigned int i=0; i<size; i++)
1989     //    {
1990     //    buffer[i] = (TBuffer)(source[i] + intercept);
1991     //    }
1992     //
1993     // use Duff's device which exploits "fall through"
1994     register size_t n = (size + 7) / 8;
1995     switch ( size % 8)
1996       {
1997       case 0: do { *buffer++ = (TBuffer)(*source++ + intercept);
1998       case 7:      *buffer++ = (TBuffer)(*source++ + intercept);
1999       case 6:      *buffer++ = (TBuffer)(*source++ + intercept);
2000       case 5:      *buffer++ = (TBuffer)(*source++ + intercept);
2001       case 4:      *buffer++ = (TBuffer)(*source++ + intercept);
2002       case 3:      *buffer++ = (TBuffer)(*source++ + intercept);
2003       case 2:      *buffer++ = (TBuffer)(*source++ + intercept);
2004       case 1:      *buffer++ = (TBuffer)(*source++ + intercept);
2005                  }  while (--n > 0);
2006       }
2007     }
2008   else if (slope != 1.0 && intercept == 0.0)
2009     {
2010     // Duff's device.  Instead of this code:
2011     //
2012     //   for(unsigned int i=0; i<size; i++)
2013     //    {
2014     //    buffer[i] = (TBuffer)(source[i]*slope);
2015     //    }
2016     //
2017     // use Duff's device which exploits "fall through"
2018     register size_t n = (size + 7) / 8;
2019     switch ( size % 8)
2020       {
2021       case 0: do { *buffer++ = (TBuffer)((*source++)*slope);
2022       case 7:      *buffer++ = (TBuffer)((*source++)*slope);
2023       case 6:      *buffer++ = (TBuffer)((*source++)*slope);
2024       case 5:      *buffer++ = (TBuffer)((*source++)*slope);
2025       case 4:      *buffer++ = (TBuffer)((*source++)*slope);
2026       case 3:      *buffer++ = (TBuffer)((*source++)*slope);
2027       case 2:      *buffer++ = (TBuffer)((*source++)*slope);
2028       case 1:      *buffer++ = (TBuffer)((*source++)*slope);
2029                  }  while (--n > 0);
2030       }
2031     }
2032   else
2033     {
2034     // Duff's device.  Instead of this code:
2035     //
2036     //   for(unsigned int i=0; i<size; i++)
2037     //    {
2038     //    buffer[i] = (TBuffer)(source[i]);
2039     //    }
2040     //
2041     // use Duff's device which exploits "fall through"
2042     register size_t n = (size + 7) / 8;
2043     switch ( size % 8)
2044       {
2045       case 0: do { *buffer++ = (TBuffer)(*source++);
2046       case 7:      *buffer++ = (TBuffer)(*source++);
2047       case 6:      *buffer++ = (TBuffer)(*source++);
2048       case 5:      *buffer++ = (TBuffer)(*source++);
2049       case 4:      *buffer++ = (TBuffer)(*source++);
2050       case 3:      *buffer++ = (TBuffer)(*source++);
2051       case 2:      *buffer++ = (TBuffer)(*source++);
2052       case 1:      *buffer++ = (TBuffer)(*source++);
2053                  }  while (--n > 0);
2054       }
2055     }
2056     
2057     
2058 }
2059
2060
2061 template<class TSource>
2062 void RescaleFunction(ImageIOBase::IOComponentType bufferType,
2063                      void* buffer, TSource *source,
2064                      double slope, double intercept, size_t size)
2065 {
2066   switch (bufferType)
2067     {
2068     case ImageIOBase::UCHAR:
2069       RescaleFunction( (unsigned char *)buffer, source, slope, intercept, size);
2070       break;
2071     case ImageIOBase::CHAR:
2072       RescaleFunction( (char *)buffer, source, slope, intercept, size);
2073       break;
2074     case ImageIOBase::USHORT:
2075       RescaleFunction( (unsigned short *)buffer, source, slope, intercept,size);
2076       break;
2077     case ImageIOBase::SHORT:
2078       RescaleFunction( (short *)buffer, source, slope, intercept, size);
2079       break;
2080     case ImageIOBase::UINT:
2081       RescaleFunction( (unsigned int *)buffer, source, slope, intercept, size);
2082       break;
2083     case ImageIOBase::INT:
2084       RescaleFunction( (int *)buffer, source, slope, intercept, size);
2085       break;
2086     case ImageIOBase::FLOAT:
2087       RescaleFunction( (float *)buffer, source, slope, intercept, size);
2088       break;
2089     case ImageIOBase::DOUBLE:
2090       RescaleFunction( (double *)buffer, source, slope, intercept, size);
2091       break;
2092     default:
2093       ::itk::OStringStream message;
2094       message << "itk::ERROR: GDCMImageIO: Unknown component type : " << bufferType;
2095       ::itk::ExceptionObject e(__FILE__, __LINE__, message.str().c_str(),ITK_LOCATION);
2096       throw e;
2097     }
2098 }
2099 */