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