]> Creatis software - gdcm.git/blob - src/gdcmFile.cxx
3de485f7c2057e172f551cbfa691f022e85f109f
[gdcm.git] / src / gdcmFile.cxx
1   /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: gdcmFile.cxx,v $
5   Language:  C++
6   Date:      $Date: 2004/12/07 09:32:24 $
7   Version:   $Revision: 1.172 $
8                                                                                 
9   Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
10   l'Image). All rights reserved. See Doc/License.txt or
11   http://www.creatis.insa-lyon.fr/Public/Gdcm/License.html for details.
12                                                                                 
13      This software is distributed WITHOUT ANY WARRANTY; without even
14      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15      PURPOSE.  See the above copyright notices for more information.
16                                                                                 
17 =========================================================================*/
18
19 #include "gdcmFile.h"
20 #include "gdcmDocument.h"
21 #include "gdcmDebug.h"
22 #include "gdcmUtil.h"
23 #include "gdcmBinEntry.h"
24 #include "gdcmHeader.h"
25 #include "gdcmPixelReadConvert.h"
26 #include "gdcmPixelWriteConvert.h"
27 #include "gdcmDocEntryArchive.h"
28
29 #include <fstream>
30
31 namespace gdcm 
32 {
33 typedef std::pair<TagDocEntryHT::iterator,TagDocEntryHT::iterator> IterHT;
34
35 //-------------------------------------------------------------------------
36 // Constructor / Destructor
37 /**
38  * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3
39  *        file (Header only deals with the ... header)
40  *        Opens (in read only and when possible) an existing file and checks
41  *        for DICOM compliance. Returns NULL on failure.
42  *        It will be up to the user to load the pixels into memory
43  *        (see GetImageData, GetImageDataRaw)
44  * \note  the in-memory representation of all available tags found in
45  *        the DICOM header is post-poned to first header information access.
46  *        This avoid a double parsing of public part of the header when
47  *        user sets an a posteriori shadow dictionary (efficiency can be
48  *        seen as a side effect).   
49  * @param header already built Header
50  */
51 File::File(Header *header)
52 {
53    HeaderInternal = header;
54    SelfHeader = false;
55    Initialise();
56 }
57
58 /**
59  * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3
60  *        file (Header only deals with the ... header)
61  *        Opens (in read only and when possible) an existing file and checks
62  *        for DICOM compliance. Returns NULL on failure.
63  *        It will be up to the user to load the pixels into memory
64  *        (see GetImageData, GetImageDataRaw)
65  * \note  the in-memory representation of all available tags found in
66  *        the DICOM header is post-poned to first header information access.
67  *        This avoid a double parsing of public part of the header when
68  *        one sets an a posteriori shadow dictionary (efficiency can be
69  *        seen as a side effect).   
70  * @param filename file to be opened for parsing
71  */
72 File::File(std::string const & filename )
73 {
74    HeaderInternal = new Header( filename );
75    SelfHeader = true;
76    Initialise();
77 }
78
79 /**
80  * \brief Factorization for various forms of constructors.
81  */
82 void File::Initialise()
83 {
84    WriteMode = WMODE_DECOMPRESSED;
85    WriteType = ImplicitVR;
86
87    PixelReadConverter = new PixelReadConvert;
88    PixelWriteConverter = new PixelWriteConvert;
89    Archive = new DocEntryArchive( HeaderInternal );
90
91    if ( HeaderInternal->IsReadable() )
92    {
93       PixelReadConverter->GrabInformationsFromHeader( HeaderInternal );
94    }
95 }
96
97 /**
98  * \brief canonical destructor
99  * \note  If the Header was created by the File constructor,
100  *        it is destroyed by the File
101  */
102 File::~File()
103
104    if( PixelReadConverter )
105    {
106       delete PixelReadConverter;
107    }
108    if( PixelWriteConverter )
109    {
110       delete PixelWriteConverter;
111    }
112    if( Archive )
113    {
114       delete Archive;
115    }
116
117    if( SelfHeader )
118    {
119       delete HeaderInternal;
120    }
121    HeaderInternal = 0;
122 }
123
124 //-----------------------------------------------------------------------------
125 // Print
126
127 //-----------------------------------------------------------------------------
128 // Public
129 /**
130  * \brief   Get the size of the image data
131  * 
132  *          If the image can be RGB (with a lut or by default), the size 
133  *          corresponds to the RGB image
134  * @return  The image size
135  */
136 size_t File::GetImageDataSize()
137 {
138    return PixelReadConverter->GetRGBSize();
139 }
140
141 /**
142  * \brief   Get the size of the image data
143  * 
144  *          If the image can be RGB by transformation in a LUT, this
145  *          transformation isn't considered
146  * @return  The raw image size
147  */
148 size_t File::GetImageDataRawSize()
149 {
150    return PixelReadConverter->GetDecompressedSize();
151 }
152
153 /**
154  * \brief   - Allocates necessary memory, 
155  *          - Reads the pixels from disk (uncompress if necessary),
156  *          - Transforms YBR pixels, if any, into RGB pixels
157  *          - Transforms 3 planes R, G, B, if any, into a single RGB Plane
158  *          - Transforms single Grey plane + 3 Palettes into a RGB Plane
159  *          - Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
160  * @return  Pointer to newly allocated pixel data.
161  *          NULL if alloc fails 
162  */
163 uint8_t* File::GetImageData()
164 {
165    if ( ! GetDecompressed() )
166    {
167       // If the decompression failed nothing can be done.
168       return 0;
169    }
170
171    if ( HeaderInternal->HasLUT() && PixelReadConverter->BuildRGBImage() )
172    {
173       return PixelReadConverter->GetRGB();
174    }
175    else
176    {
177       // When no LUT or LUT conversion fails, return the decompressed
178       return PixelReadConverter->GetDecompressed();
179    }
180 }
181
182 /**
183  * \brief
184  *          Read the pixels from disk (uncompress if necessary),
185  *          Transforms YBR pixels, if any, into RGB pixels
186  *          Transforms 3 planes R, G, B, if any, into a single RGB Plane
187  *          Transforms single Grey plane + 3 Palettes into a RGB Plane   
188  *          Copies at most MaxSize bytes of pixel data to caller allocated
189  *          memory space.
190  * \warning This function allows people that want to build a volume
191  *          from an image stack *not to* have, first to get the image pixels, 
192  *          and then move them to the volume area.
193  *          It's absolutely useless for any VTK user since vtk chooses 
194  *          to invert the lines of an image, that is the last line comes first
195  *          (for some axis related reasons?). Hence he will have 
196  *          to load the image line by line, starting from the end.
197  *          VTK users have to call GetImageData
198  *     
199  * @param   destination Address (in caller's memory space) at which the
200  *          pixel data should be copied
201  * @param   maxSize Maximum number of bytes to be copied. When MaxSize
202  *          is not sufficient to hold the pixel data the copy is not
203  *          executed (i.e. no partial copy).
204  * @return  On success, the number of bytes actually copied. Zero on
205  *          failure e.g. MaxSize is lower than necessary.
206  */
207 size_t File::GetImageDataIntoVector (void* destination, size_t maxSize)
208 {
209    if ( ! GetDecompressed() )
210    {
211       // If the decompression failed nothing can be done.
212       return 0;
213    }
214
215    if ( HeaderInternal->HasLUT() && PixelReadConverter->BuildRGBImage() )
216    {
217       if ( PixelReadConverter->GetRGBSize() > maxSize )
218       {
219          dbg.Verbose(0, "File::GetImageDataIntoVector: pixel data bigger"
220                         "than caller's expected MaxSize");
221          return 0;
222       }
223       memcpy( destination,
224               (void*)PixelReadConverter->GetRGB(),
225               PixelReadConverter->GetRGBSize() );
226       return PixelReadConverter->GetRGBSize();
227    }
228
229    // Either no LUT conversion necessary or LUT conversion failed
230    if ( PixelReadConverter->GetDecompressedSize() > maxSize )
231    {
232       dbg.Verbose(0, "File::GetImageDataIntoVector: pixel data bigger"
233                      "than caller's expected MaxSize");
234       return 0;
235    }
236    memcpy( destination,
237            (void*)PixelReadConverter->GetDecompressed(),
238            PixelReadConverter->GetDecompressedSize() );
239    return PixelReadConverter->GetDecompressedSize();
240 }
241
242 /**
243  * \brief   Allocates necessary memory, 
244  *          Transforms YBR pixels (if any) into RGB pixels
245  *          Transforms 3 planes R, G, B  (if any) into a single RGB Plane
246  *          Copies the pixel data (image[s]/volume[s]) to newly allocated zone. 
247  *          DOES NOT transform Grey plane + 3 Palettes into a RGB Plane
248  * @return  Pointer to newly allocated pixel data.
249  * \        NULL if alloc fails 
250  */
251 uint8_t* File::GetImageDataRaw ()
252 {
253    return GetDecompressed();
254 }
255
256 uint8_t* File::GetDecompressed()
257 {
258    uint8_t* decompressed = PixelReadConverter->GetDecompressed();
259    if ( ! decompressed )
260    {
261       // The decompressed image migth not be loaded yet:
262       std::ifstream* fp = HeaderInternal->OpenFile();
263       PixelReadConverter->ReadAndDecompressPixelData( fp );
264       if(fp) 
265          HeaderInternal->CloseFile();
266
267       decompressed = PixelReadConverter->GetDecompressed();
268       if ( ! decompressed )
269       {
270          dbg.Verbose(0, "File::GetDecompressed: read/decompress of "
271                         "pixel data apparently went wrong.");
272          return 0;
273       }
274    }
275
276    return decompressed;
277 }
278
279 /**
280  * \brief   Points the internal pointer to the callers inData
281  *          image representation, BUT WITHOUT COPYING THE DATA.
282  *          'image' Pixels are presented as C-like 2D arrays : line per line.
283  *          'volume'Pixels are presented as C-like 3D arrays : plane per plane 
284  * \warning Since the pixels are not copied, it is the caller's responsability
285  *          not to deallocate it's data before gdcm uses them (e.g. with
286  *          the Write() method.
287  * @param inData user supplied pixel area
288  * @param expectedSize total image size, in Bytes
289  *
290  * @return boolean
291  */
292 bool File::SetImageData(uint8_t* inData, size_t expectedSize)
293 {
294    PixelWriteConverter->SetUserData(inData,expectedSize);
295
296    return true;
297 }
298
299 /**
300  * \brief Writes on disk A SINGLE Dicom file
301  *        NO test is performed on  processor "Endiannity".
302  *        It's up to the user to call his Reader properly
303  * @param fileName name of the file to be created
304  *                 (any already existing file is over written)
305  * @return false if write fails
306  */
307
308 bool File::WriteRawData(std::string const & fileName)
309 {
310   std::ofstream fp1(fileName.c_str(), std::ios::out | std::ios::binary );
311    if (!fp1)
312    {
313       dbg.Verbose(2, "Fail to open (write) file:", fileName.c_str());
314       return false;
315    }
316
317    if(PixelWriteConverter->GetUserData())
318       fp1.write((char*)PixelWriteConverter->GetUserData(), PixelWriteConverter->GetUserDataSize());
319    else if(PixelReadConverter->GetRGB())
320       fp1.write((char*)PixelReadConverter->GetRGB(), PixelReadConverter->GetRGBSize());
321    else if(PixelReadConverter->GetDecompressed())
322       fp1.write((char*)PixelReadConverter->GetDecompressed(), PixelReadConverter->GetDecompressedSize());
323
324    fp1.close();
325
326    return true;
327 }
328
329 /**
330  * \brief Writes on disk A SINGLE Dicom file, 
331  *        using the Implicit Value Representation convention
332  *        NO test is performed on  processor "Endiannity".
333  * @param fileName name of the file to be created
334  *                 (any already existing file is overwritten)
335  * @return false if write fails
336  */
337
338 bool File::WriteDcmImplVR (std::string const & fileName)
339 {
340    SetWriteTypeToDcmImplVR();
341    return Write(fileName);
342 }
343
344 /**
345 * \brief Writes on disk A SINGLE Dicom file, 
346  *        using the Explicit Value Representation convention
347  *        NO test is performed on  processor "Endiannity". * @param fileName name of the file to be created
348  *                 (any already existing file is overwritten)
349  * @return false if write fails
350  */
351
352 bool File::WriteDcmExplVR (std::string const & fileName)
353 {
354    SetWriteTypeToDcmExplVR();
355    return Write(fileName);
356 }
357
358 /**
359  * \brief Writes on disk A SINGLE Dicom file, 
360  *        using the ACR-NEMA convention
361  *        NO test is performed on  processor "Endiannity".
362  *        (a l'attention des logiciels cliniques 
363  *        qui ne prennent en entrée QUE des images ACR ...
364  * \warning if a DICOM_V3 header is supplied,
365  *         groups < 0x0008 and shadow groups are ignored
366  * \warning NO TEST is performed on processor "Endiannity".
367  * @param fileName name of the file to be created
368  *                 (any already existing file is overwritten)
369  * @return false if write fails
370  */
371
372 bool File::WriteAcr (std::string const & fileName)
373 {
374    SetWriteTypeToAcr();
375    return Write(fileName);
376 }
377
378 bool File::Write(std::string const& fileName)
379 {
380    return WriteBase(fileName);
381 }
382
383 bool File::SetEntryByNumber(std::string const& content,
384                             uint16_t group, uint16_t element)
385
386    HeaderInternal->SetEntryByNumber(content,group,element);
387    return true;
388 }
389
390 /**
391  * \brief Access to the underlying \ref PixelReadConverter RGBA LUT
392  */
393 uint8_t* File::GetLutRGBA()
394 {
395    return PixelReadConverter->GetLutRGBA();
396 }
397
398 //-----------------------------------------------------------------------------
399 // Protected
400 /**
401  * \brief NOT a end user inteded function
402  *        (used by WriteDcmExplVR, WriteDcmImplVR, WriteAcr, etc)
403  * @param fileName name of the file to be created
404  *                 (any already existing file is overwritten)
405  * @param  type file type (ExplicitVR, ImplicitVR, ...)
406  * @return false if write fails
407  */
408 bool File::WriteBase (std::string const & fileName)
409 {
410    switch(WriteType)
411    {
412       case ImplicitVR:
413          SetWriteFileTypeToImplicitVR();
414          break;
415       case ExplicitVR:
416          SetWriteFileTypeToExplicitVR();
417          break;
418       case ACR:
419       case ACR_LIBIDO:
420          SetWriteFileTypeToACR();
421          break;
422       default:
423          SetWriteFileTypeToExplicitVR();
424    }
425
426    // --------------------------------------------------------------
427    // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
428    //
429    // if recognition code tells us we dealt with a LibIDO image
430    // we reproduce on disk the switch between lineNumber and columnNumber
431    // just before writting ...
432    /// \todo the best trick would be *change* the recognition code
433    ///       but pb expected if user deals with, e.g. COMPLEX images
434    if( WriteType == ACR_LIBIDO )
435    {
436       SetWriteToLibido();
437    }
438    else
439    {
440       SetWriteToNoLibido();
441    }
442    // ----------------- End of Special Patch ----------------
443   
444    switch(WriteMode)
445    {
446       case WMODE_DECOMPRESSED :
447          SetWriteToDecompressed();
448          break;
449       case WMODE_RGB :
450          SetWriteToRGB();
451          break;
452    }
453
454    bool check = CheckWriteIntegrity();
455    if(check)
456    {
457       check = HeaderInternal->Write(fileName,WriteType);
458    }
459
460    RestoreWrite();
461    RestoreWriteFileType();
462
463    // --------------------------------------------------------------
464    // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
465    // 
466    // ...and we restore the Header to be Dicom Compliant again 
467    // just after writting
468    RestoreWriteOfLibido();
469    // ----------------- End of Special Patch ----------------
470
471    return check;
472 }
473
474 /**
475  * \brief Check the write integrity
476  *
477  * The tests made are :
478  *  - verify the size of the image to write with the possible write
479  *    when the user set an image data
480  * @return true if the check successfulls
481  */
482 bool File::CheckWriteIntegrity()
483 {
484    if(PixelWriteConverter->GetUserData())
485    {
486       int numberBitsAllocated = HeaderInternal->GetBitsAllocated();
487       if ( numberBitsAllocated == 0 || numberBitsAllocated == 12 )
488       {
489          numberBitsAllocated = 16;
490       }
491
492       size_t decSize = HeaderInternal->GetXSize()
493                     * HeaderInternal->GetYSize() 
494                     * HeaderInternal->GetZSize()
495                     * ( numberBitsAllocated / 8 )
496                     * HeaderInternal->GetSamplesPerPixel();
497       size_t rgbSize = decSize;
498       if( HeaderInternal->HasLUT() )
499          rgbSize = decSize * 3;
500
501       switch(WriteMode)
502       {
503          case WMODE_DECOMPRESSED :
504             if( decSize!=PixelWriteConverter->GetUserDataSize() )
505             {
506                dbg.Verbose(0, "File::CheckWriteIntegrity: Data size is incorrect");
507                return false;
508             }
509             break;
510          case WMODE_RGB :
511             if( rgbSize!=PixelWriteConverter->GetUserDataSize() )
512             {
513                dbg.Verbose(0, "File::CheckWriteIntegrity: Data size is incorrect");
514                return false;
515             }
516             break;
517       }
518    }
519    
520    return true;
521 }
522
523 void File::SetWriteToDecompressed()
524 {
525    if(HeaderInternal->GetNumberOfScalarComponents()==3 && !HeaderInternal->HasLUT())
526    {
527       SetWriteToRGB();
528    } 
529    else
530    {
531       ValEntry* photInt = CopyValEntry(0x0028,0x0004);
532       if(HeaderInternal->HasLUT())
533       {
534          photInt->SetValue("PALETTE COLOR ");
535          photInt->SetLength(14);
536       }
537       else
538       {
539          photInt->SetValue("MONOCHROME1 ");
540          photInt->SetLength(12);
541       }
542
543       PixelWriteConverter->SetReadData(PixelReadConverter->GetDecompressed(),
544                                        PixelReadConverter->GetDecompressedSize());
545
546       BinEntry* pixel = CopyBinEntry(GetHeader()->GetGrPixel(),GetHeader()->GetNumPixel());
547       pixel->SetValue(GDCM_BINLOADED);
548       pixel->SetBinArea(PixelWriteConverter->GetData(),false);
549       pixel->SetLength(PixelWriteConverter->GetDataSize());
550
551       Archive->Push(photInt);
552       Archive->Push(pixel);
553    }
554 }
555
556 void File::SetWriteToRGB()
557 {
558    if(HeaderInternal->GetNumberOfScalarComponents()==3)
559    {
560       PixelReadConverter->BuildRGBImage();
561       
562       ValEntry* spp = CopyValEntry(0x0028,0x0002);
563       spp->SetValue("3 ");
564       spp->SetLength(2);
565
566       ValEntry* planConfig = CopyValEntry(0x0028,0x0006);
567       planConfig->SetValue("0 ");
568       planConfig->SetLength(2);
569
570       ValEntry* photInt = CopyValEntry(0x0028,0x0004);
571       photInt->SetValue("RGB ");
572       photInt->SetLength(4);
573
574       if(PixelReadConverter->GetRGB())
575       {
576          PixelWriteConverter->SetReadData(PixelReadConverter->GetRGB(),
577                                           PixelReadConverter->GetRGBSize());
578       }
579       else // Decompressed data
580       {
581          PixelWriteConverter->SetReadData(PixelReadConverter->GetDecompressed(),
582                                           PixelReadConverter->GetDecompressedSize());
583       }
584
585       BinEntry* pixel = CopyBinEntry(GetHeader()->GetGrPixel(),GetHeader()->GetNumPixel());
586       pixel->SetValue(GDCM_BINLOADED);
587       pixel->SetBinArea(PixelWriteConverter->GetData(),false);
588       pixel->SetLength(PixelWriteConverter->GetDataSize());
589
590       Archive->Push(spp);
591       Archive->Push(planConfig);
592       Archive->Push(photInt);
593       Archive->Push(pixel);
594
595       // Remove any LUT
596       Archive->Push(0x0028,0x1101);
597       Archive->Push(0x0028,0x1102);
598       Archive->Push(0x0028,0x1103);
599       Archive->Push(0x0028,0x1201);
600       Archive->Push(0x0028,0x1202);
601       Archive->Push(0x0028,0x1203);
602
603       // For old ACR-NEMA
604       // Thus, we have a RGB image and the bits allocated = 24 and 
605       // samples per pixels = 1 (in the read file)
606       if(HeaderInternal->GetBitsAllocated()==24) 
607       {
608          ValEntry* bitsAlloc = CopyValEntry(0x0028,0x0100);
609          bitsAlloc->SetValue("8 ");
610          bitsAlloc->SetLength(2);
611
612          ValEntry* bitsStored = CopyValEntry(0x0028,0x0101);
613          bitsStored->SetValue("8 ");
614          bitsStored->SetLength(2);
615
616          ValEntry* highBit = CopyValEntry(0x0028,0x0102);
617          highBit->SetValue("7 ");
618          highBit->SetLength(2);
619
620          Archive->Push(bitsAlloc);
621          Archive->Push(bitsStored);
622          Archive->Push(highBit);
623       }
624    }
625    else
626    {
627       SetWriteToDecompressed();
628    }
629 }
630
631 void File::RestoreWrite()
632 {
633    Archive->Restore(0x0028,0x0002);
634    Archive->Restore(0x0028,0x0004);
635    Archive->Restore(0x0028,0x0006);
636    Archive->Restore(GetHeader()->GetGrPixel(),GetHeader()->GetNumPixel());
637
638    // For old ACR-NEMA (24 bits problem)
639    Archive->Restore(0x0028,0x0100);
640    Archive->Restore(0x0028,0x0101);
641    Archive->Restore(0x0028,0x0102);
642
643    // For the LUT
644    Archive->Restore(0x0028,0x1101);
645    Archive->Restore(0x0028,0x1102);
646    Archive->Restore(0x0028,0x1103);
647    Archive->Restore(0x0028,0x1201);
648    Archive->Restore(0x0028,0x1202);
649    Archive->Restore(0x0028,0x1203);
650 }
651
652 void File::SetWriteFileTypeToACR()
653 {
654    Archive->Push(0x0002,0x0010);
655 }
656
657 void File::SetWriteFileTypeToExplicitVR()
658 {
659    std::string ts = Util::DicomString( 
660       Document::GetTransferSyntaxValue(ExplicitVRLittleEndian).c_str() );
661
662    ValEntry* tss = CopyValEntry(0x0002,0x0010);
663    tss->SetValue(ts);
664    tss->SetLength(ts.length());
665
666    Archive->Push(tss);
667 }
668
669 void File::SetWriteFileTypeToImplicitVR()
670 {
671    std::string ts = Util::DicomString(
672       Document::GetTransferSyntaxValue(ImplicitVRLittleEndian).c_str() );
673
674    ValEntry* tss = CopyValEntry(0x0002,0x0010);
675    tss->SetValue(ts);
676    tss->SetLength(ts.length());
677 }
678
679 void File::RestoreWriteFileType()
680 {
681    Archive->Restore(0x0002,0x0010);
682 }
683
684 void File::SetWriteToLibido()
685 {
686    ValEntry *oldRow = dynamic_cast<ValEntry *>(HeaderInternal->GetDocEntryByNumber(0x0028, 0x0010));
687    ValEntry *oldCol = dynamic_cast<ValEntry *>(HeaderInternal->GetDocEntryByNumber(0x0028, 0x0011));
688    
689    if( oldRow && oldCol )
690    {
691       std::string rows, columns; 
692
693       ValEntry *newRow=new ValEntry(oldRow->GetDictEntry());
694       ValEntry *newCol=new ValEntry(oldCol->GetDictEntry());
695
696       newRow->Copy(oldCol);
697       newCol->Copy(oldRow);
698
699       newRow->SetValue(oldCol->GetValue());
700       newCol->SetValue(oldRow->GetValue());
701
702       Archive->Push(newRow);
703       Archive->Push(newCol);
704    }
705
706    ValEntry *libidoCode = CopyValEntry(0x0008,0x0010);
707    libidoCode->SetValue("ACRNEMA_LIBIDO_1.1");
708    libidoCode->SetLength(10);
709    Archive->Push(libidoCode);
710 }
711
712 void File::SetWriteToNoLibido()
713 {
714    ValEntry *recCode = dynamic_cast<ValEntry *>(HeaderInternal->GetDocEntryByNumber(0x0008,0x0010));
715    if( recCode )
716    {
717       if( recCode->GetValue() == "ACRNEMA_LIBIDO_1.1" )
718       {
719          ValEntry *libidoCode = CopyValEntry(0x0008,0x0010);
720          libidoCode->SetValue("");
721          libidoCode->SetLength(0);
722          Archive->Push(libidoCode);
723       }
724    }
725 }
726
727 void File::RestoreWriteOfLibido()
728 {
729    Archive->Restore(0x0028,0x0010);
730    Archive->Restore(0x0028,0x0011);
731    Archive->Restore(0x0008,0x0010);
732 }
733
734 ValEntry* File::CopyValEntry(uint16_t group,uint16_t element)
735 {
736    DocEntry* oldE = HeaderInternal->GetDocEntryByNumber(group, element);
737    ValEntry* newE;
738
739    if(oldE)
740    {
741       newE = new ValEntry(oldE->GetDictEntry());
742       newE->Copy(oldE);
743    }
744    else
745    {
746       newE = GetHeader()->NewValEntryByNumber(group,element);
747    }
748
749    return(newE);
750 }
751
752 BinEntry* File::CopyBinEntry(uint16_t group,uint16_t element)
753 {
754    DocEntry* oldE = HeaderInternal->GetDocEntryByNumber(group, element);
755    BinEntry* newE;
756
757    if(oldE)
758    {
759       newE = new BinEntry(oldE->GetDictEntry());
760       newE->Copy(oldE);
761    }
762    else
763    {
764       newE = GetHeader()->NewBinEntryByNumber(group,element);
765    }
766
767    return(newE);
768 }
769
770 //-----------------------------------------------------------------------------
771 // Private
772
773 //-----------------------------------------------------------------------------
774 } // end namespace gdcm
775