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