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