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