+ return( BitsAlloc + Signed);
+}
+
+/**
+ * \ingroup gdcmHeader
+ * \brief Recover the offset (from the beginning of the file) of the pixels.
+ */
+size_t gdcmHeader::GetPixelOffset(void) {
+ // If this file complies with the norm we should encounter the
+ // "Image Location" tag (0x0028, 0x0200). This tag contains the
+ // the group that contains the pixel data (hence the "Pixel Data"
+ // is found by indirection through the "Image Location").
+ // Inside the group pointed by "Image Location" the searched element
+ // is conventionally the element 0x0010 (when the norm is respected).
+ // When the "Image Location" is absent we default to group 0x7fe0.
+ guint16 grPixel;
+ guint16 numPixel;
+ std::string ImageLocation = GetEntryByNumber(0x0028, 0x0200);
+
+ if ( ImageLocation == GDCM_UNFOUND ) { // Image Location
+ grPixel = 0x7fe0;
+ } else {
+ grPixel = (guint16) atoi( ImageLocation.c_str() );
+ }
+ if (grPixel != 0x7fe0)
+ // This is a kludge for old dirty Philips imager.
+ numPixel = 0x1010;
+ else
+ numPixel = 0x0010;
+
+ gdcmHeaderEntry* PixelElement = GetHeaderEntryByNumber(grPixel,numPixel);
+ if (PixelElement)
+ return PixelElement->GetOffset();
+ else
+ return 0;
+}
+
+/**
+ * \ingroup gdcmHeader
+ * \brief Recover the pixel area length (in Bytes)
+ * @return 0 by default. NOT USABLE file. The caller has to check.
+ */
+size_t gdcmHeader::GetPixelAreaLength(void) {
+ // If this file complies with the norm we should encounter the
+ // "Image Location" tag (0x0028, 0x0200). This tag contains the
+ // the group that contains the pixel data (hence the "Pixel Data"
+ // is found by indirection through the "Image Location").
+ // Inside the group pointed by "Image Location" the searched element
+ // is conventionally the element 0x0010 (when the norm is respected).
+ // When the "Image Location" is absent we default to group 0x7fe0.
+ guint16 grPixel;
+ guint16 numPixel;
+ std::string ImageLocation = GetEntryByNumber(0x0028, 0x0200);
+ if ( ImageLocation == GDCM_UNFOUND ) {
+ grPixel = 0x7fe0;
+ } else {
+ grPixel = (guint16) atoi( ImageLocation.c_str() );
+ }
+ if (grPixel != 0x7fe0)
+ // This is a kludge for old dirty Philips imager.
+ numPixel = 0x1010;
+ else
+ numPixel = 0x0010;
+
+ gdcmHeaderEntry* PixelElement = GetHeaderEntryByNumber(grPixel,numPixel);
+ if (PixelElement)
+ return PixelElement->GetLength();
+ else
+ return 0;
+}
+
+/**
+ * \ingroup gdcmHeader
+ * \brief tells us if LUT are used
+ * \warning Right now, 'Segmented xxx Palette Color Lookup Table Data'
+ * \ are NOT considered as LUT, since nobody knows
+ * \ how to deal with them
+ * @return a Boolean
+ */
+bool gdcmHeader::HasLUT(void) {
+
+ // Check the presence of the LUT Descriptors
+ if ( !GetHeaderEntryByNumber(0x0028,0x1101) )
+ return false;
+ // LutDescriptorGreen
+ if ( !GetHeaderEntryByNumber(0x0028,0x1102) )
+ return false;
+ // LutDescriptorBlue
+ if ( !GetHeaderEntryByNumber(0x0028,0x1103) )
+ return false;
+ // It is not enough
+ // we check also
+ if ( !GetHeaderEntryByNumber(0x0028,0x1201) )
+ return false;
+ if ( !GetHeaderEntryByNumber(0x0028,0x1202) )
+ return false;
+ if ( !GetHeaderEntryByNumber(0x0028,0x1203) )
+ return false;
+ return true;
+}
+
+/**
+ * \ingroup gdcmHeader
+ * \brief gets the info from 0028,1101 : Lookup Table Desc-Red
+ * \ else 0
+ * @return Lookup Table number of Bits , 0 by default
+ * \ when (0028,0004),Photometric Interpretation = [PALETTE COLOR ]
+ */
+int gdcmHeader::GetLUTNbits(void) {
+ std::vector<std::string> tokens;
+ //int LutLength;
+ //int LutDepth;
+ int LutNbits;
+ //Just hope Lookup Table Desc-Red = Lookup Table Desc-Red = Lookup Table Desc-Blue
+ // Consistency already checked in GetLUTLength
+ std::string LutDescription = GetEntryByNumber(0x0028,0x1101);
+ if (LutDescription == GDCM_UNFOUND)
+ return 0;
+ tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
+ Tokenize (LutDescription, tokens, "\\");
+ //LutLength=atoi(tokens[0].c_str());
+ //LutDepth=atoi(tokens[1].c_str());
+ LutNbits=atoi(tokens[2].c_str());
+ tokens.clear();
+ return LutNbits;
+}
+
+/**
+ * \ingroup gdcmHeader
+ * \brief builts Red/Green/Blue/Alpha LUT from Header
+ * \ when (0028,0004),Photometric Interpretation = [PALETTE COLOR ]
+ * \ and (0028,1101),(0028,1102),(0028,1102)
+ * \ - xxx Palette Color Lookup Table Descriptor - are found
+ * \ and (0028,1201),(0028,1202),(0028,1202)
+ * \ - xxx Palette Color Lookup Table Data - are found
+ * \warning does NOT deal with :
+ * \ 0028 1100 Gray Lookup Table Descriptor (Retired)
+ * \ 0028 1221 Segmented Red Palette Color Lookup Table Data
+ * \ 0028 1222 Segmented Green Palette Color Lookup Table Data
+ * \ 0028 1223 Segmented Blue Palette Color Lookup Table Data
+ * \ no known Dicom reader deals with them :-(
+ * @return a RGBA Lookup Table
+ */
+unsigned char * gdcmHeader::GetLUTRGBA(void) {
+// Not so easy : see
+// http://www.barre.nom.fr/medical/dicom2/limitations.html#Color%20Lookup%20Tables
+
+// if Photometric Interpretation # PALETTE COLOR, no LUT to be done
+ if (gdcmHeader::GetEntryByNumber(0x0028,0x0004) != "PALETTE COLOR ") {
+ return NULL;
+ }
+ int lengthR, debR, nbitsR;
+ int lengthG, debG, nbitsG;
+ int lengthB, debB, nbitsB;
+
+// Get info from Lut Descriptors
+// (the 3 LUT descriptors may be different)
+ std::string LutDescriptionR = GetEntryByNumber(0x0028,0x1101);
+ if (LutDescriptionR == GDCM_UNFOUND)
+ return NULL;
+ std::string LutDescriptionG = GetEntryByNumber(0x0028,0x1102);
+ if (LutDescriptionG == GDCM_UNFOUND)
+ return NULL;
+ std::string LutDescriptionB = GetEntryByNumber(0x0028,0x1103);
+ if (LutDescriptionB == GDCM_UNFOUND)
+ return NULL;
+
+ std::vector<std::string> tokens;
+
+ tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
+ Tokenize (LutDescriptionR, tokens, "\\");
+ lengthR=atoi(tokens[0].c_str()); // Red LUT length in Bytes
+ debR =atoi(tokens[1].c_str()); // subscript of the first Lut Value
+ nbitsR =atoi(tokens[2].c_str()); // Lut item size (in Bits)
+ tokens.clear();
+
+ tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
+ Tokenize (LutDescriptionG, tokens, "\\");
+ lengthG=atoi(tokens[0].c_str()); // Green LUT length in Bytes
+ debG =atoi(tokens[1].c_str());
+ nbitsG =atoi(tokens[2].c_str());
+ tokens.clear();
+
+ tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
+ Tokenize (LutDescriptionB, tokens, "\\");
+ lengthB=atoi(tokens[0].c_str()); // Blue LUT length in Bytes
+ debB =atoi(tokens[1].c_str());
+ nbitsB =atoi(tokens[2].c_str());
+ tokens.clear();
+
+// Load LUTs into memory, (as they were stored on disk)
+ unsigned char *lutR = (unsigned char *)
+ GetPubEntryVoidAreaByNumber(0x0028,0x1201);
+ unsigned char *lutG = (unsigned char *)
+ GetPubEntryVoidAreaByNumber(0x0028,0x1202);
+ unsigned char *lutB = (unsigned char *)
+ GetPubEntryVoidAreaByNumber(0x0028,0x1203);
+
+ if (!lutR || !lutG || !lutB ) {
+ return NULL;
+ }
+ // forge the 4 * 8 Bits Red/Green/Blue/Alpha LUT
+
+ unsigned char *LUTRGBA = (unsigned char *)calloc(1024,1); // 256 * 4 (R, G, B, Alpha)
+ if (!LUTRGBA) {
+ return NULL;
+ }
+ memset(LUTRGBA, 0, 1024);
+ // Bits Allocated
+ int nb;
+ std::string str_nb = GetEntryByNumber(0x0028,0x0100);
+ if (str_nb == GDCM_UNFOUND ) {
+ nb = 16;
+ } else {
+ nb = atoi(str_nb.c_str() );
+ }
+ int mult;
+
+ if (nbitsR==16 && nb==8) // when LUT item size is different than pixel size
+ mult=2; // high byte must be = low byte
+ else // See PS 3.3-2003 C.11.1.1.2 p 619
+ mult=1;
+
+ // if we get a black image, let's just remove the '+1'
+ // from 'i*mult+1' and check again
+ // if it works, we shall have to check the 3 Palettes
+ // to see which byte is ==0 (first one, or second one)
+ // and fix the code
+ // We give up the checking to avoid some overhead
+ unsigned char *a;
+ int i;
+
+ a = LUTRGBA+0;
+ for(i=0;i<lengthR;i++) {
+ *a = lutR[i*mult+1];
+ a+=4;
+ }
+ a = LUTRGBA+1;
+ for(i=0;i<lengthG;i++) {
+ *a = lutG[i*mult+1];
+ a+=4;
+ }
+ a = LUTRGBA+2;
+ for(i=0;i<lengthB;i++) {
+ *a = lutB[i*mult+1];
+ a+=4;
+ }
+ a = LUTRGBA+3;
+ for(i=0;i<256;i++) {
+ *a = 1; // Alpha component
+ a+=4;
+ }
+
+//How to free the now useless LUTs?
+
+//free(LutR); free(LutB); free(LutG); // Seg Fault when used
+ return(LUTRGBA);
+}
+
+/**
+ * \ingroup gdcmHeader
+ * \brief gets the info from 0002,0010 : Transfert Syntax
+ * \ else 1.
+ * @return Transfert Syntax Name (as oposite to Transfert Syntax UID)
+ */
+std::string gdcmHeader::GetTransfertSyntaxName(void) {
+ // use the gdcmTS (TS : Transfert Syntax)
+ std::string TransfertSyntax = GetEntryByNumber(0x0002,0x0010);
+ if (TransfertSyntax == GDCM_UNFOUND) {
+ dbg.Verbose(0, "gdcmHeader::GetTransfertSyntaxName: unfound Transfert Syntax (0002,0010)");
+ return "Uncompressed ACR-NEMA";
+ }
+ // we do it only when we need it
+ gdcmTS * ts = gdcmGlobal::GetTS();
+ std::string tsName=ts->GetValue(TransfertSyntax);
+ //delete ts; // Seg Fault when deleted ?!
+ return tsName;
+}
+
+/**
+ * \ingroup gdcmHeader
+ * \brief Searches within the file Header for element value of
+ * a given tag.
+ * @param tagName name of the searched element.
+ * @return Corresponding element value when it exists, and the string
+ * GDCM_UNFOUND ("gdcm::Unfound") otherwise.
+ */
+std::string gdcmHeader::GetPubEntryByName(std::string tagName) {
+ gdcmDictEntry *dictEntry = RefPubDict->GetTagByName(tagName);
+ if( dictEntry == NULL)
+ return GDCM_UNFOUND;
+
+ return(GetEntryByNumber(dictEntry->GetGroup(),dictEntry->GetElement()));
+}
+
+/**
+ * \ingroup gdcmHeader
+ * \brief Searches within the elements parsed with the file Header for
+ * the element value representation of a given tag.
+ *
+ * Obtaining the VR (Value Representation) might be needed by caller
+ * to convert the string typed content to caller's native type
+ * (think of C++ vs Python). The VR is actually of a higher level
+ * of semantics than just the native C++ type.
+ * @param tagName name of the searched element.
+ * @return Corresponding element value representation when it exists,
+ * and the string GDCM_UNFOUND ("gdcm::Unfound") otherwise.
+ */
+std::string gdcmHeader::GetPubEntryVRByName(std::string tagName) {
+ gdcmDictEntry *dictEntry = RefPubDict->GetTagByName(tagName);
+ if( dictEntry == NULL)
+ return GDCM_UNFOUND;
+
+ gdcmHeaderEntry* elem = GetHeaderEntryByNumber(dictEntry->GetGroup(),
+ dictEntry->GetElement());
+ return elem->GetVR();
+}
+
+/**
+ * \ingroup gdcmHeader
+ * \brief Searches within the public dictionary for element value of
+ * a given tag.
+ * @param group Group of the researched tag.
+ * @param element Element of the researched tag.
+ * @return Corresponding element value when it exists, and the string
+ * GDCM_UNFOUND ("gdcm::Unfound") otherwise.
+ */
+std::string gdcmHeader::GetPubEntryByNumber(guint16 group, guint16 element) {
+ return PubEntrySet.GetEntryByNumber(group, element);
+}
+
+/**
+ * \ingroup gdcmHeader
+ * \brief Searches within the public dictionary for element value
+ * representation of a given tag.
+ *
+ * Obtaining the VR (Value Representation) might be needed by caller
+ * to convert the string typed content to caller's native type
+ * (think of C++ vs Python). The VR is actually of a higher level
+ * of semantics than just the native C++ type.
+ * @param group Group of the researched tag.
+ * @param element Element of the researched tag.
+ * @return Corresponding element value representation when it exists,
+ * and the string GDCM_UNFOUND ("gdcm::Unfound") otherwise.
+ */
+std::string gdcmHeader::GetPubEntryVRByNumber(guint16 group, guint16 element) {
+ gdcmHeaderEntry* elem = GetHeaderEntryByNumber(group, element);
+ if ( !elem )
+ return GDCM_UNFOUND;
+ return elem->GetVR();
+}
+
+/**
+ * \ingroup gdcmHeader
+ * \brief Accesses an existing gdcmHeaderEntry (i.e. a Dicom Element)
+ * in the PubHeaderEntrySet of this instance
+ * through tag name and modifies it's content with the given value.
+ * @param content new value to substitute with
+ * @param tagName name of the Header Entry (Dicom Element) to be modified
+ */
+bool gdcmHeader::SetPubEntryByName(std::string content, std::string tagName) {
+ //return ( PubHeaderEntrySet.SetHeaderEntryByName (content, tagName) );
+ gdcmDictEntry *dictEntry = RefPubDict->GetTagByName(tagName);
+ if( dictEntry == NULL)
+ return false;
+ return(PubEntrySet.SetEntryByNumber(content,
+ dictEntry->GetGroup(),
+ dictEntry->GetElement()));
+}
+
+/**
+ * \ingroup gdcmHeader
+ * \brief Accesses an existing gdcmHeaderEntry (i.e. a Dicom Element)
+ * in the PubHeaderEntrySet of this instance
+ * through it's (group, element) and modifies it's content with
+ * the given value.
+ * @param content new value to substitute with
+ * @param group group of the Dicom Element to modify
+ * @param element element of the Dicom Element to modify
+ */
+bool gdcmHeader::SetPubEntryByNumber(std::string content, guint16 group,
+ guint16 element)
+
+//TODO : homogeneiser les noms : SetPubElValByNumber
+// qui appelle PubHeaderEntrySet.SetHeaderEntryByNumber
+// pourquoi pas SetPubHeaderEntryByNumber ??
+{
+ return ( PubEntrySet.SetEntryByNumber (content, group, element) );
+}
+
+/**
+ * \ingroup gdcmHeader
+ * \brief Accesses an existing gdcmHeaderEntry (i.e. a Dicom Element)
+ * in the PubHeaderEntrySet of this instance
+ * through it's (group, element) and modifies it's length with
+ * the given value.
+ * \warning Use with extreme caution.
+ * @param length new length to substitute with
+ * @param group group of the ElVal to modify
+ * @param element element of the ElVal to modify
+ * @return 1 on success, 0 otherwise.
+ */
+
+bool gdcmHeader::SetPubEntryLengthByNumber(guint32 length, guint16 group,
+ guint16 element) {
+ return ( PubEntrySet.SetEntryLengthByNumber (length, group, element) );
+}
+
+/**
+ * \ingroup gdcmHeader
+ * \brief Searches within Header Entries (Dicom Elements) parsed with
+ * the public and private dictionaries
+ * for the element value of a given tag.
+ * @param tagName name of the searched element.
+ * @return Corresponding element value when it exists,
+ * and the string GDCM_UNFOUND ("gdcm::Unfound") otherwise.
+ */
+std::string gdcmHeader::GetEntryByName(std::string tagName) {
+ return GetPubEntryByName(tagName);
+}
+
+/**
+ * \ingroup gdcmHeader
+ * \brief Searches within Header Entries (Dicom Elements) parsed with
+ * the public and private dictionaries
+ * for the element value representation of a given tag.
+ *
+ * Obtaining the VR (Value Representation) might be needed by caller
+ * to convert the string typed content to caller's native type
+ * (think of C++ vs Python). The VR is actually of a higher level
+ * of semantics than just the native C++ type.
+ * @param tagName name of the searched element.
+ * @return Corresponding element value representation when it exists,
+ * and the string GDCM_UNFOUND ("gdcm::Unfound") otherwise.
+ */
+std::string gdcmHeader::GetEntryVRByName(std::string tagName) {
+ return GetPubEntryVRByName(tagName);
+}
+
+/**
+ * \ingroup gdcmHeader
+ * \brief Searches within Header Entries (Dicom Elements) parsed with
+ * the public and private dictionaries
+ * for the element value representation of a given tag.
+ * @param group Group of the searched tag.
+ * @param element Element of the searched tag.
+ * @return Corresponding element value representation when it exists,
+ * and the string GDCM_UNFOUND ("gdcm::Unfound") otherwise.
+ */
+std::string gdcmHeader::GetEntryByNumber(guint16 group, guint16 element) {
+ return GetPubEntryByNumber(group, element);
+}
+
+/**
+ * \ingroup gdcmHeader
+ * \brief Searches within Header Entries (Dicom Elements) parsed with
+ * the public and private dictionaries
+ * for the element value representation of a given tag..
+ *
+ * Obtaining the VR (Value Representation) might be needed by caller
+ * to convert the string typed content to caller's native type
+ * (think of C++ vs Python). The VR is actually of a higher level
+ * of semantics than just the native C++ type.
+ * @param group Group of the searched tag.
+ * @param element Element of the searched tag.
+ * @return Corresponding element value representation when it exists,
+ * and the string GDCM_UNFOUND ("gdcm::Unfound") otherwise.
+ */
+std::string gdcmHeader::GetEntryVRByNumber(guint16 group, guint16 element) {
+ return GetPubEntryVRByNumber(group, element);
+}
+
+/**
+ * \ingroup gdcmHeader
+ * \brief Sets the value (string) of the Header Entry (Dicom Element)
+ * @param content string value of the Dicom Element
+ * @param tagName name of the searched Dicom Element.
+ * @return true when found
+ */
+bool gdcmHeader::SetEntryByName(std::string content,std::string tagName) {
+ gdcmDictEntry *dictEntry = RefPubDict->GetTagByName(tagName);
+ if( dictEntry == NULL)
+ return false;
+
+
+ TagKey key = gdcmDictEntry::TranslateToKey(dictEntry->GetGroup(),
+ dictEntry->GetElement());
+ if ( PubEntrySet.GetTagHT().count(key) == 0 )
+ return false;
+ int l = content.length();
+ if(l%2) { // Odd length are padded with a space (020H).
+ l++;
+ content = content + '\0';
+ }
+
+ //tagHt[key]->SetValue(content);
+ gdcmHeaderEntry * a;
+ IterHT p;
+ TagHeaderEntryHT::iterator p2;
+ // DO NOT remove the following lines : they explain how the stuff works
+ //p= tagHt.equal_range(key); // get a pair of iterators first-last synonym
+ //p2=p.first; // iterator on the first synonym
+ //a=p2->second; // H Table target column (2-nd col)
+ // or, easier :
+ a = ((PubEntrySet.GetTagHT().equal_range(key)).first)->second;
+ a-> SetValue(content);
+ std::string vr = a->GetVR();
+
+ guint32 lgr;
+ if( (vr == "US") || (vr == "SS") )
+ lgr = 2;
+ else if( (vr == "UL") || (vr == "SL") )
+ lgr = 4;
+ else
+ lgr = l;
+ a->SetLength(lgr);
+ return true;
+}
+
+/**
+ * \ingroup gdcmHeader
+ * \brief opens the file
+ * @param exception_on_error
+ * @return
+ */
+FILE *gdcmHeader::OpenFile(bool exception_on_error)
+ throw(gdcmFileError) {
+ fp=fopen(filename.c_str(),"rb");
+ if(exception_on_error) {
+ if(!fp)
+ throw gdcmFileError("gdcmHeader::gdcmHeader(const char *, bool)");
+ }
+
+ if ( fp ) {
+ guint16 zero;
+ fread(&zero, (size_t)2, (size_t)1, fp);
+
+ //ACR -- or DICOM with no Preamble --
+ if( zero == 0x0008 || zero == 0x0800 || zero == 0x0002 || zero == 0x0200)
+ return(fp);
+
+ //DICOM
+ fseek(fp, 126L, SEEK_CUR);
+ char dicm[4];
+ fread(dicm, (size_t)4, (size_t)1, fp);
+ if( memcmp(dicm, "DICM", 4) == 0 )
+ return(fp);
+
+ fclose(fp);
+ dbg.Verbose(0, "gdcmHeader::gdcmHeader not DICOM/ACR", filename.c_str());
+ }
+ else {
+ dbg.Verbose(0, "gdcmHeader::gdcmHeader cannot open file", filename.c_str());
+ }
+ return(NULL);
+}
+
+/**
+ * \ingroup gdcmHeader
+ * \brief closes the file
+ * @return TRUE if the close was successfull
+ */
+bool gdcmHeader::CloseFile(void) {
+ int closed = fclose(fp);
+ fp = (FILE *)0;
+ if (! closed)
+ return false;
+ return true;
+}
+
+/**
+ * \ingroup gdcmHeader
+ * \brief Parses the header of the file but WITHOUT loading element values.
+ */
+void gdcmHeader::ParseHeader(bool exception_on_error) throw(gdcmFormatError) {
+ gdcmHeaderEntry * newHeaderEntry = (gdcmHeaderEntry *)0;
+
+ rewind(fp);
+ CheckSwap();
+ while ( (newHeaderEntry = ReadNextHeaderEntry()) ) {
+ SkipHeaderEntry(newHeaderEntry);
+ PubEntrySet.Add(newHeaderEntry);
+ }
+}
+
+/**
+ * \ingroup gdcmHeader
+ * \brief
+ * @param fp file pointer on an already open file
+ * @param type file type ( ImplicitVR, ExplicitVR, ...)
+ * @return Boolean
+ */
+bool gdcmHeader::Write(FILE * fp, FileType type) {
+
+ // TODO : move the following lines (and a lot of others, to be written)
+ // to a future function CheckAndCorrectHeader
+
+ if (type == ImplicitVR) {
+ std::string implicitVRTransfertSyntax = "1.2.840.10008.1.2";
+ ReplaceOrCreateByNumber(implicitVRTransfertSyntax,0x0002, 0x0010);
+
+ //FIXME Refer to standards on page 21, chapter 6.2 "Value representation":
+ // values with a VR of UI shall be padded with a single trailing null
+ // Dans le cas suivant on doit pader manuellement avec un 0
+
+ PubEntrySet.SetEntryLengthByNumber(18, 0x0002, 0x0010);
+ }
+
+ if (type == ExplicitVR) {
+ std::string explicitVRTransfertSyntax = "1.2.840.10008.1.2.1";
+ ReplaceOrCreateByNumber(explicitVRTransfertSyntax,0x0002, 0x0010);
+
+ //FIXME Refer to standards on page 21, chapter 6.2 "Value representation":
+ // values with a VR of UI shall be padded with a single trailing null
+ // Dans le cas suivant on doit pader manuellement avec un 0
+
+ PubEntrySet.SetEntryLengthByNumber(20, 0x0002, 0x0010);
+ }
+
+ return PubEntrySet.Write(fp, type);
+}
+
+/**
+ * \ingroup gdcmFile
+ * \brief Sets the Pixel Area size in the Header
+ * --> not-for-rats function
+ *
+ * \warning WARNING doit-etre etre publique ?
+ * TODO : y aurait il un inconvenient à fusionner ces 2 fonctions
+ *
+ * @param ImageDataSize new Pixel Area Size
+ * warning : nothing else is checked
+ */
+void gdcmHeader::SetImageDataSize(size_t ImageDataSize) {
+ std::string content1;
+ char car[20];
+ // Assumes HeaderEntry (0x7fe0, 0x0010) exists ...
+ sprintf(car,"%d",ImageDataSize);
+
+ gdcmHeaderEntry *a = GetHeaderEntryByNumber(0x7fe0, 0x0010);
+ a->SetLength(ImageDataSize);
+
+ ImageDataSize+=8;
+ sprintf(car,"%d",ImageDataSize);
+ content1=car;
+ SetPubEntryByNumber(content1, 0x7fe0, 0x0000);
+}
+
+/**
+ * \ingroup gdcmHeader
+ * \brief Modifies the value of a given Header Entry (Dicom Element)
+ * if it exists; Creates it with the given value if it doesn't
+ * @param Value passed as a std::string
+ * @param Group
+ * @param Elem
+ * \return boolean
+ */
+bool gdcmHeader::ReplaceOrCreateByNumber(std::string Value,
+ guint16 Group, guint16 Elem ) {
+
+ if (CheckIfExistByNumber(Group, Elem) == 0) {
+ gdcmHeaderEntry* a =NewHeaderEntryByNumber(Group, Elem);
+ if (a == NULL)
+ return false;
+ PubEntrySet.Add(a);
+ }
+ PubEntrySet.SetEntryByNumber(Value, Group, Elem);
+ return(true);
+}
+
+/**
+ * \ingroup gdcmHeader
+ * \brief Modifies the value of a given Header Entry (Dicom Element)
+ * if it exists; Creates it with the given value if it doesn't
+ * @param Value passed as a char*
+ * @param Group
+ * @param Elem
+ * \return boolean
+ *
+ */
+bool gdcmHeader::ReplaceOrCreateByNumber(char* Value, guint16 Group, guint16 Elem ) {
+
+ gdcmHeaderEntry* nvHeaderEntry=NewHeaderEntryByNumber(Group, Elem);
+ // TODO : check if fails
+ PubEntrySet.Add(nvHeaderEntry);
+ std::string v = Value;
+ PubEntrySet.SetEntryByNumber(v, Group, Elem);
+ return(true);
+}
+
+/**
+ * \ingroup gdcmHeader
+ * \brief Set a new value if the invoked element exists
+ * Seems to be useless !!!
+ * @param Value
+ * @param Group
+ * @param Elem
+ * \return integer acts as a boolean
+ */
+bool gdcmHeader::ReplaceIfExistByNumber(char* Value, guint16 Group, guint16 Elem ) {
+
+ std::string v = Value;
+ PubEntrySet.SetEntryByNumber(v, Group, Elem);
+ return true;
+}
+
+/**
+ * \ingroup gdcmHeader
+ * \brief Swaps back the bytes of 4-byte long integer accordingly to
+ * processor order.
+ * @return The properly swaped 32 bits integer.
+ */
+guint32 gdcmHeader::SwapLong(guint32 a) {
+ switch (sw) {
+ case 0 :
+ break;
+ case 4321 :
+ a=( ((a<<24) & 0xff000000) | ((a<<8) & 0x00ff0000) |
+ ((a>>8) & 0x0000ff00) | ((a>>24) & 0x000000ff) );
+ break;
+
+ case 3412 :
+ a=( ((a<<16) & 0xffff0000) | ((a>>16) & 0x0000ffff) );
+ break;
+
+ case 2143 :
+ a=( ((a<<8) & 0xff00ff00) | ((a>>8) & 0x00ff00ff) );
+ break;
+ default :
+ dbg.Error(" gdcmHeader::SwapLong : unset swap code");
+ a=0;
+ }
+ return(a);
+}
+
+/**
+ * \ingroup gdcmHeader
+ * \brief Swaps the bytes so they agree with the processor order
+ * @return The properly swaped 16 bits integer.
+ */
+guint16 gdcmHeader::SwapShort(guint16 a) {
+ if ( (sw==4321) || (sw==2143) )
+ a =(((a<<8) & 0x0ff00) | ((a>>8)&0x00ff));
+ return (a);
+}
+
+//-----------------------------------------------------------------------------
+// Protected
+/**
+ * \ingroup gdcmHeader
+ * \brief
+ *
+ * @return
+ */
+gdcmHeaderEntry *gdcmHeader::GetHeaderEntryByNumber(guint16 Group, guint16 Elem) {
+ gdcmHeaderEntry *HeaderEntry = PubEntrySet.GetHeaderEntryByNumber(Group, Elem);
+ if (!HeaderEntry) {
+ dbg.Verbose(1, "gdcmHeader::GetHeaderEntryByNumber",
+ "failed to Locate gdcmHeaderEntry");
+ return NULL;
+ }
+ return HeaderEntry;
+}
+
+/**
+ * \ingroup gdcmHeader
+ * \brief Searches within the Header Entries for a Dicom Element of
+ * a given tag.
+ * @param tagName name of the searched Dicom Element.
+ * @return Corresponding Dicom Element when it exists, and NULL
+ * otherwise.
+ */
+ gdcmHeaderEntry *gdcmHeader::GetHeaderEntryByName(std::string tagName) {
+ gdcmDictEntry *dictEntry = RefPubDict->GetTagByName(tagName);
+ if( dictEntry == NULL)
+ return NULL;
+
+ return(GetHeaderEntryByNumber(dictEntry->GetGroup(),dictEntry->GetElement()));
+}
+
+/**
+ * \ingroup gdcmHeader
+ * \brief Checks if a given HeaderEntry (group,number)
+ * \ exists in the Public HeaderEntrySet
+ * @param Group
+ * @param Elem
+ * @return boolean
+ */
+bool gdcmHeader::CheckIfExistByNumber(guint16 Group, guint16 Elem ) {
+ return (PubEntrySet.CheckIfExistByNumber(Group, Elem)>0);
+}
+
+/**
+ * \ingroup gdcmHeader
+ * \brief Gets (from Header) the offset of a 'non string' element value
+ * \ (LoadElementValues has already be executed)
+ * @param Group
+ * @param Elem
+ * @return File Offset of the Element Value
+ */
+size_t gdcmHeader::GetPubEntryOffsetByNumber(guint16 Group, guint16 Elem) {
+ gdcmHeaderEntry* Entry = GetHeaderEntryByNumber(Group, Elem);
+ if (!Entry) {
+ dbg.Verbose(1, "gdcmHeader::GetHeaderEntryByNumber",
+ "failed to Locate gdcmHeaderEntry");
+ return (size_t)0;
+ }
+ return Entry->GetOffset();
+}
+
+/**
+ * \ingroup gdcmHeader
+ * \brief Gets (from Header) a 'non string' element value
+ * \ (LoadElementValues has already be executed)
+ * @param Group
+ * @param Elem
+ * @return Pointer to the 'non string' area
+ */
+void * gdcmHeader::GetPubEntryVoidAreaByNumber(guint16 Group, guint16 Elem) {
+ gdcmHeaderEntry* Entry = GetHeaderEntryByNumber(Group, Elem);
+ if (!Entry) {
+ dbg.Verbose(1, "gdcmHeader::GetHeaderEntryByNumber",
+ "failed to Locate gdcmHeaderEntry");
+ return (NULL);
+ }
+ return Entry->GetVoidArea();
+}
+
+/**
+ * \ingroup gdcmHeader
+ * \brief Loads (from disk) the element content
+ * when a string is not suitable
+ */
+void * gdcmHeader::LoadEntryVoidArea(guint16 Group, guint16 Elem) {
+ gdcmHeaderEntry * Element= GetHeaderEntryByNumber(Group, Elem);
+ if ( !Element )
+ return NULL;
+ size_t o =(size_t)Element->GetOffset();
+ fseek(fp, o, SEEK_SET);
+ int l=Element->GetLength();
+ void * a = malloc(l);
+ if(!a) {
+ return NULL;
+ }
+
+ PubEntrySet.SetVoidAreaByNumber(a, Group, Elem);
+ // TODO check the result
+ size_t l2 = fread(a, 1, l ,fp);
+ if(l != l2) {
+ free(a);
+ return NULL;
+ }
+ return a;
+}
+
+//-----------------------------------------------------------------------------
+// Private
+/**
+ * \ingroup gdcmHeader
+ * \brief Loads the element values of all the Header Entries pointed in the
+ * public Chained List.
+ */
+void gdcmHeader::LoadHeaderEntries(void) {
+ rewind(fp);
+ for (ListTag::iterator i = GetPubListEntry().begin();
+ i != GetPubListEntry().end();
+ ++i){
+ LoadHeaderEntry(*i);
+ }
+
+ rewind(fp);
+
+ // Load 'non string' values
+ std::string PhotometricInterpretation = GetPubEntryByNumber(0x0028,0x0004);
+ if( PhotometricInterpretation == "PALETTE COLOR " ){
+ LoadEntryVoidArea(0x0028,0x1200); // gray LUT
+ LoadEntryVoidArea(0x0028,0x1201); // R LUT
+ LoadEntryVoidArea(0x0028,0x1202); // G LUT
+ LoadEntryVoidArea(0x0028,0x1203); // B LUT
+
+ LoadEntryVoidArea(0x0028,0x1221); // Segmented Red Palette Color LUT Data
+ LoadEntryVoidArea(0x0028,0x1222); // Segmented Green Palette Color LUT Data
+ LoadEntryVoidArea(0x0028,0x1223); // Segmented Blue Palette Color LUT Data
+ }
+
+ // --------------------------------------------------------------
+ // Special Patch to allow gdcm to read ACR-LibIDO formated images
+ //
+ // if recognition code tells us we deal with a LibIDO image
+ // we switch lineNumber and columnNumber
+ //
+ std::string RecCode;
+ RecCode = GetPubEntryByNumber(0x0008, 0x0010); // recognition code
+ if (RecCode == "ACRNEMA_LIBIDO_1.1" ||
+ RecCode == "CANRME_AILIBOD1_1." ) {
+ filetype = ACR_LIBIDO;
+ std::string rows = GetPubEntryByNumber(0x0028, 0x0010);
+ std::string columns = GetPubEntryByNumber(0x0028, 0x0011);
+ SetPubEntryByNumber(columns, 0x0028, 0x0010);
+ SetPubEntryByNumber(rows , 0x0028, 0x0011);
+ }
+ // ----------------- End of Special Patch ----------------
+}
+
+/**
+ * \ingroup gdcmHeader
+ * \brief Loads the element content if it's length is not bigger
+ * than the value specified with
+ * gdcmHeader::SetMaxSizeLoadElementValue()
+ * @param ElVal Header Entry (Dicom Element) to be dealt with
+ */
+void gdcmHeader::LoadHeaderEntry(gdcmHeaderEntry * ElVal) {
+ size_t item_read;
+ guint16 group = ElVal->GetGroup();
+ std::string vr= ElVal->GetVR();
+ guint32 length = ElVal->GetLength();
+ bool SkipLoad = false;
+
+ fseek(fp, (long)ElVal->GetOffset(), SEEK_SET);
+
+ // the test was commented out to 'go inside' the SeQuences
+ // we don't any longer skip them !
+
+ // if( vr == "SQ" ) // (DO NOT remove this comment)
+ // SkipLoad = true;
+
+ // A SeQuence "contains" a set of Elements.
+ // (fffe e000) tells us an Element is beginning
+ // (fffe e00d) tells us an Element just ended
+ // (fffe e0dd) tells us the current SeQuence just ended
+ if( group == 0xfffe )
+ SkipLoad = true;
+
+ if ( SkipLoad ) {
+ ElVal->SetLength(0);
+ ElVal->SetValue("gdcm::Skipped");
+ return;
+ }
+
+ // When the length is zero things are easy:
+ if ( length == 0 ) {
+ ElVal->SetValue("");
+ return;
+ }
+
+ // The elements whose length is bigger than the specified upper bound
+ // are not loaded. Instead we leave a short notice of the offset of
+ // the element content and it's length.
+ if (length > MaxSizeLoadElementValue) {
+ std::ostringstream s;
+ s << "gdcm::NotLoaded.";
+ s << " Address:" << (long)ElVal->GetOffset();
+ s << " Length:" << ElVal->GetLength();
+ s << " x(" << std::hex << ElVal->GetLength() << ")";
+ ElVal->SetValue(s.str());