changeset 2007:655489d9165d

DicomMap::ParseDicomMetaInformation()
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 09 Jun 2016 15:46:33 +0200
parents 6301bbcbcaed
children dc82c754dcaa
files Core/DicomFormat/DicomMap.cpp Core/DicomFormat/DicomMap.h Core/Enumerations.cpp Core/Enumerations.h OrthancServer/DatabaseWrapperBase.cpp OrthancServer/DicomProtocol/DicomUserConnection.cpp OrthancServer/FromDcmtkBridge.cpp OrthancServer/OrthancFindRequestHandler.cpp OrthancServer/OrthancRestApi/OrthancRestModalities.cpp OrthancServer/OrthancRestApi/OrthancRestResources.cpp OrthancServer/QueryRetrieveHandler.cpp OrthancServer/Search/HierarchicalMatcher.cpp OrthancServer/ToDcmtkBridge.cpp Plugins/Engine/OrthancPluginDatabase.cpp Plugins/Engine/OrthancPlugins.cpp Plugins/Engine/PluginsEnumerations.cpp UnitTestsSources/DicomMapTests.cpp UnitTestsSources/FromDcmtkTests.cpp UnitTestsSources/ImageProcessingTests.cpp UnitTestsSources/ServerIndexTests.cpp
diffstat 20 files changed, 455 insertions(+), 79 deletions(-) [+]
line wrap: on
line diff
--- a/Core/DicomFormat/DicomMap.cpp	Thu Jun 09 14:48:40 2016 +0200
+++ b/Core/DicomFormat/DicomMap.cpp	Thu Jun 09 15:46:33 2016 +0200
@@ -36,6 +36,7 @@
 #include <stdio.h>
 #include <memory>
 #include "DicomArray.h"
+#include "../Endianness.h"
 #include "../OrthancException.h"
 
 
@@ -292,7 +293,7 @@
 
     for (size_t i = 0; i < count; i++)
     {
-      result.SetValue(tags[i], "");
+      result.SetValue(tags[i], "", false);
     }
   }
 
@@ -304,8 +305,8 @@
   void DicomMap::SetupFindStudyTemplate(DicomMap& result)
   {
     SetupFindTemplate(result, studyTags, sizeof(studyTags) / sizeof(DicomTag));
-    result.SetValue(DICOM_TAG_ACCESSION_NUMBER, "");
-    result.SetValue(DICOM_TAG_PATIENT_ID, "");
+    result.SetValue(DICOM_TAG_ACCESSION_NUMBER, "", false);
+    result.SetValue(DICOM_TAG_PATIENT_ID, "", false);
 
     // These main DICOM tags are only indirectly related to the
     // General Study Module, remove them
@@ -317,9 +318,9 @@
   void DicomMap::SetupFindSeriesTemplate(DicomMap& result)
   {
     SetupFindTemplate(result, seriesTags, sizeof(seriesTags) / sizeof(DicomTag));
-    result.SetValue(DICOM_TAG_ACCESSION_NUMBER, "");
-    result.SetValue(DICOM_TAG_PATIENT_ID, "");
-    result.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, "");
+    result.SetValue(DICOM_TAG_ACCESSION_NUMBER, "", false);
+    result.SetValue(DICOM_TAG_PATIENT_ID, "", false);
+    result.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, "", false);
 
     // These tags are considered as "main" by Orthanc, but are not in the Series module
     result.Remove(DicomTag(0x0008, 0x0070));  // Manufacturer
@@ -339,10 +340,10 @@
   void DicomMap::SetupFindInstanceTemplate(DicomMap& result)
   {
     SetupFindTemplate(result, instanceTags, sizeof(instanceTags) / sizeof(DicomTag));
-    result.SetValue(DICOM_TAG_ACCESSION_NUMBER, "");
-    result.SetValue(DICOM_TAG_PATIENT_ID, "");
-    result.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, "");
-    result.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, "");
+    result.SetValue(DICOM_TAG_ACCESSION_NUMBER, "", false);
+    result.SetValue(DICOM_TAG_PATIENT_ID, "", false);
+    result.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, "", false);
+    result.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, "", false);
   }
 
 
@@ -479,4 +480,312 @@
       tags.insert(it->first);
     }
   }
+
+
+  static uint16_t ReadUnsignedInteger16(const char* dicom)
+  {
+    return le16toh(*reinterpret_cast<const uint16_t*>(dicom));
+  }
+
+
+  static uint32_t ReadUnsignedInteger32(const char* dicom)
+  {
+    return le32toh(*reinterpret_cast<const uint32_t*>(dicom));
+  }
+
+
+  static bool ValidateTag(const ValueRepresentation& vr,
+                          const std::string& value)
+  {
+    switch (vr)
+    {
+      case ValueRepresentation_ApplicationEntity:
+        return value.size() <= 16;
+
+      case ValueRepresentation_AgeString:
+        return (value.size() == 4 &&
+                isdigit(value[0]) &&
+                isdigit(value[1]) &&
+                isdigit(value[2]) &&
+                (value[3] == 'D' || value[3] == 'W' || value[3] == 'M' || value[3] == 'Y'));
+
+      case ValueRepresentation_AttributeTag:
+        return value.size() == 4;
+
+      case ValueRepresentation_CodeString:
+        return value.size() <= 16;
+
+      case ValueRepresentation_Date:
+        return value.size() <= 18;
+
+      case ValueRepresentation_DecimalString:
+        return value.size() <= 16;
+
+      case ValueRepresentation_DateTime:
+        return value.size() <= 54;
+
+      case ValueRepresentation_FloatingPointSingle:
+        return value.size() == 4;
+
+      case ValueRepresentation_FloatingPointDouble:
+        return value.size() == 8;
+
+      case ValueRepresentation_IntegerString:
+        return value.size() <= 12;
+
+      case ValueRepresentation_LongString:
+        return value.size() <= 64;
+
+      case ValueRepresentation_LongText:
+        return value.size() <= 10240;
+
+      case ValueRepresentation_OtherByte:
+        return true;
+      
+      case ValueRepresentation_OtherDouble:
+        return value.size() <= static_cast<size_t>((1llu << 32) - 8);
+
+      case ValueRepresentation_OtherFloat:
+        return value.size() <= static_cast<size_t>((1llu << 32) - 4);
+
+      case ValueRepresentation_OtherLong:
+        return true;
+
+      case ValueRepresentation_OtherWord:
+        return true;
+
+      case ValueRepresentation_PersonName:
+        return true;
+
+      case ValueRepresentation_ShortString:
+        return value.size() <= 16;
+
+      case ValueRepresentation_SignedLong:
+        return value.size() == 4;
+
+      case ValueRepresentation_Sequence:
+        return true;
+
+      case ValueRepresentation_SignedShort:
+        return value.size() == 2;
+
+      case ValueRepresentation_ShortText:
+        return value.size() <= 1024;
+
+      case ValueRepresentation_Time:
+        return value.size() <= 28;
+
+      case ValueRepresentation_UnlimitedCharacters:
+        return value.size() <= static_cast<size_t>((1llu << 32) - 2);
+
+      case ValueRepresentation_UniqueIdentifier:
+        return value.size() <= 64;
+
+      case ValueRepresentation_UnsignedLong:
+        return value.size() == 4;
+
+      case ValueRepresentation_Unknown:
+        return true;
+
+      case ValueRepresentation_UniversalResource:
+        return value.size() <= static_cast<size_t>((1llu << 32) - 2);
+
+      case ValueRepresentation_UnsignedShort:
+        return value.size() == 2;
+
+      case ValueRepresentation_UnlimitedText:
+        return value.size() <= static_cast<size_t>((1llu << 32) - 2);
+
+      default:
+        // Assume unsupported tags are OK
+        return true;
+    }
+  }
+
+
+  static void RemoveTagPadding(std::string& value,
+                               const ValueRepresentation& vr)
+  {
+    /**
+     * Remove padding from character strings, if need be. For the time
+     * being, only the UI VR is supported.
+     * http://dicom.nema.org/medical/dicom/current/output/chtml/part05/sect_6.2.html
+     **/
+
+    switch (vr)
+    {
+      case ValueRepresentation_UniqueIdentifier:
+      {
+        /**
+         * "Values with a VR of UI shall be padded with a single
+         * trailing NULL (00H) character when necessary to achieve even
+         * length."
+         **/
+
+        if (!value.empty() &&
+            value[value.size() - 1] == '\0')
+        {
+          value.resize(value.size() - 1);
+        }
+
+        break;
+      }
+
+      /**
+       * TODO implement other VR
+       **/
+
+      default:
+        // No padding is applicable to this VR
+        break;
+    }
+  }
+
+
+  static bool ReadNextTag(DicomTag& tag,
+                          ValueRepresentation& vr,
+                          std::string& value,
+                          const char* dicom,
+                          size_t size,
+                          size_t& position)
+  {
+    /**
+     * http://dicom.nema.org/medical/dicom/current/output/chtml/part05/chapter_7.html#sect_7.1.2
+     * This function reads a data element with Explicit VR encoded using Little-Endian.
+     **/
+
+    if (position + 6 > size)
+    {
+      return false;
+    }
+
+    tag = DicomTag(ReadUnsignedInteger16(dicom + position),
+                   ReadUnsignedInteger16(dicom + position + 2));
+
+    vr = StringToValueRepresentation(std::string(dicom + position + 4, 2), true);
+    if (vr == ValueRepresentation_NotSupported)
+    {
+      return false;
+    }
+
+    if (vr == ValueRepresentation_OtherByte ||
+        vr == ValueRepresentation_OtherDouble ||
+        vr == ValueRepresentation_OtherFloat ||
+        vr == ValueRepresentation_OtherLong ||
+        vr == ValueRepresentation_OtherWord ||
+        vr == ValueRepresentation_Sequence ||
+        vr == ValueRepresentation_UnlimitedCharacters ||
+        vr == ValueRepresentation_UniversalResource ||
+        vr == ValueRepresentation_UnlimitedText ||
+        vr == ValueRepresentation_Unknown)    // Note that "UN" should never appear in the Meta Information
+    {
+      if (position + 12 > size)
+      {
+        return false;
+      }
+
+      uint32_t length = ReadUnsignedInteger32(dicom + position + 8);
+
+      if (position + 12 + length > size)
+      {
+        return false;
+      }
+
+      value.assign(dicom + position + 12, length);
+      position += (12 + length);
+    }
+    else
+    {
+      if (position + 8 > size)
+      {
+        return false;
+      }
+
+      uint16_t length = ReadUnsignedInteger16(dicom + position + 6);
+
+      if (position + 8 + length > size)
+      {
+        return false;
+      }
+
+      value.assign(dicom + position + 8, length);
+      position += (8 + length);
+    }
+
+    if (!ValidateTag(vr, value))
+    {
+      return false;
+    }
+
+    RemoveTagPadding(value, vr);
+
+    return true;
+  }
+
+
+  bool DicomMap::ParseDicomMetaInformation(DicomMap& result,
+                                           const char* dicom,
+                                           size_t size)
+  {
+    /**
+     * http://dicom.nema.org/medical/dicom/current/output/chtml/part10/chapter_7.html
+     * According to Table 7.1-1, besides the "DICM" DICOM prefix, the
+     * file preamble (i.e. dicom[0..127]) should not be taken into
+     * account to determine whether the file is or is not a DICOM file.
+     **/
+
+    if (size < 132 ||
+        dicom[128] != 'D' ||
+        dicom[129] != 'I' ||
+        dicom[130] != 'C' ||
+        dicom[131] != 'M')
+    {
+      return false;
+    }
+
+
+    /**
+     * The DICOM File Meta Information must be encoded using the
+     * Explicit VR Little Endian Transfer Syntax
+     * (UID=1.2.840.10008.1.2.1).
+     **/
+
+    result.Clear();
+
+    // First, we read the "File Meta Information Group Length" tag
+    // (0002,0000) to know where to stop reading the meta header
+    size_t position = 132;
+
+    DicomTag tag(0x0000, 0x0000);  // Dummy initialization
+    ValueRepresentation vr;
+    std::string value;
+    if (!ReadNextTag(tag, vr, value, dicom, size, position) ||
+        tag.GetGroup() != 0x0002 ||
+        tag.GetElement() != 0x0000 ||
+        vr != ValueRepresentation_UnsignedLong ||
+        value.size() != 4)
+    {
+      return false;
+    }
+
+    size_t stopPosition = position + ReadUnsignedInteger32(value.c_str());
+    if (stopPosition > size)
+    {
+      return false;
+    }
+
+    while (position < stopPosition)
+    {
+      if (ReadNextTag(tag, vr, value, dicom, size, position))
+      {
+        result.SetValue(tag, value, IsBinaryValueRepresentation(vr));
+      }
+      else
+      {
+        return false;
+      }
+    }
+
+    return true;
+  }
 }
--- a/Core/DicomFormat/DicomMap.h	Thu Jun 09 14:48:40 2016 +0200
+++ b/Core/DicomFormat/DicomMap.h	Thu Jun 09 15:46:33 2016 +0200
@@ -102,16 +102,18 @@
     }
 
     void SetValue(const DicomTag& tag,
-                  const std::string& str)
+                  const std::string& str,
+                  bool isBinary)
     {
-      SetValue(tag, new DicomValue(str, false));
+      SetValue(tag, new DicomValue(str, isBinary));
     }
 
     void SetValue(uint16_t group, 
                   uint16_t element, 
-                  const std::string& str)
+                  const std::string& str,
+                  bool isBinary)
     {
-      SetValue(group, element, new DicomValue(str, false));
+      SetValue(group, element, new DicomValue(str, isBinary));
     }
 
     bool HasTag(uint16_t group, uint16_t element) const
@@ -176,5 +178,9 @@
     static void LoadMainDicomTags(const DicomTag*& tags,
                                   size_t& size,
                                   ResourceType level);
+
+    static bool ParseDicomMetaInformation(DicomMap& result,
+                                          const char* dicom,
+                                          size_t size);
   };
 }
--- a/Core/Enumerations.cpp	Thu Jun 09 14:48:40 2016 +0200
+++ b/Core/Enumerations.cpp	Thu Jun 09 15:46:33 2016 +0200
@@ -974,7 +974,7 @@
     }
     else if (vr == "PN")
     {
-      return ValueRepresentation_PatientName;
+      return ValueRepresentation_PersonName;
     }
     else if (vr == "SH")
     {
@@ -1333,4 +1333,60 @@
     return (type >= FileContentType_StartUser &&
             type <= FileContentType_EndUser);
   }
+
+
+  bool IsBinaryValueRepresentation(ValueRepresentation vr)
+  {
+    // http://dicom.nema.org/medical/dicom/current/output/chtml/part05/sect_6.2.html
+
+    switch (vr)
+    {
+      case ValueRepresentation_ApplicationEntity:     // AE
+      case ValueRepresentation_AgeString:             // AS
+      case ValueRepresentation_CodeString:            // CS
+      case ValueRepresentation_Date:                  // DA
+      case ValueRepresentation_DecimalString:         // DS
+      case ValueRepresentation_DateTime:              // DT
+      case ValueRepresentation_IntegerString:         // IS
+      case ValueRepresentation_LongString:            // LO
+      case ValueRepresentation_LongText:              // LT
+      case ValueRepresentation_PersonName:            // PN
+      case ValueRepresentation_ShortString:           // SH
+      case ValueRepresentation_ShortText:             // ST
+      case ValueRepresentation_Time:                  // TM
+      case ValueRepresentation_UnlimitedCharacters:   // UC
+      case ValueRepresentation_UniqueIdentifier:      // UI (UID)
+      case ValueRepresentation_UniversalResource:     // UR (URI or URL)
+      case ValueRepresentation_UnlimitedText:         // UT
+      {
+        return false;
+      }
+
+      /**
+       * Below are all the VR whose character repertoire is tagged as
+       * "not applicable"
+       **/
+      case ValueRepresentation_AttributeTag:          // AT (2 x uint16_t)
+      case ValueRepresentation_FloatingPointSingle:   // FL (float)
+      case ValueRepresentation_FloatingPointDouble:   // FD (double)
+      case ValueRepresentation_OtherByte:             // OB
+      case ValueRepresentation_OtherDouble:           // OD
+      case ValueRepresentation_OtherFloat:            // OF
+      case ValueRepresentation_OtherLong:             // OL
+      case ValueRepresentation_OtherWord:             // OW
+      case ValueRepresentation_SignedLong:            // SL (int32_t)
+      case ValueRepresentation_Sequence:              // SQ
+      case ValueRepresentation_SignedShort:           // SS (int16_t)
+      case ValueRepresentation_UnsignedLong:          // UL (uint32_t)
+      case ValueRepresentation_Unknown:               // UN
+      case ValueRepresentation_UnsignedShort:         // US (uint16_t)
+      {
+        return true;
+      }
+
+      case ValueRepresentation_NotSupported:
+      default:
+        throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+  }
 }
--- a/Core/Enumerations.h	Thu Jun 09 14:48:40 2016 +0200
+++ b/Core/Enumerations.h	Thu Jun 09 15:46:33 2016 +0200
@@ -410,7 +410,7 @@
     ValueRepresentation_OtherFloat = 15,           // OF
     ValueRepresentation_OtherLong = 16,            // OL
     ValueRepresentation_OtherWord = 17,            // OW
-    ValueRepresentation_PatientName = 18,          // PN
+    ValueRepresentation_PersonName = 18,           // PN
     ValueRepresentation_ShortString = 19,          // SH
     ValueRepresentation_SignedLong = 20,           // SL (int32_t)
     ValueRepresentation_Sequence = 21,             // SQ
@@ -524,4 +524,6 @@
   HttpStatus ConvertErrorCodeToHttpStatus(ErrorCode error);
 
   bool IsUserContentType(FileContentType type);
+
+  bool IsBinaryValueRepresentation(ValueRepresentation vr);
 }
--- a/OrthancServer/DatabaseWrapperBase.cpp	Thu Jun 09 14:48:40 2016 +0200
+++ b/OrthancServer/DatabaseWrapperBase.cpp	Thu Jun 09 15:46:33 2016 +0200
@@ -350,7 +350,7 @@
     {
       map.SetValue(s.ColumnInt(1),
                    s.ColumnInt(2),
-                   s.ColumnString(3));
+                   s.ColumnString(3), false);
     }
   }
 
--- a/OrthancServer/DicomProtocol/DicomUserConnection.cpp	Thu Jun 09 14:48:40 2016 +0200
+++ b/OrthancServer/DicomProtocol/DicomUserConnection.cpp	Thu Jun 09 15:46:33 2016 +0200
@@ -401,7 +401,7 @@
 
         if (!m.HasTag(DICOM_TAG_QUERY_RETRIEVE_LEVEL))
         {
-          m.SetValue(DICOM_TAG_QUERY_RETRIEVE_LEVEL, payload.level);
+          m.SetValue(DICOM_TAG_QUERY_RETRIEVE_LEVEL, payload.level, false);
         }
 
         payload.answers->Add(m);
@@ -507,7 +507,7 @@
                 !value->IsNull() &&
                 value->GetContent() == "*")
             {
-              fix->SetValue(*it, "");
+              fix->SetValue(*it, "", false);
             }
           }
         }
@@ -1065,7 +1065,7 @@
                                         const std::string& patientId)
   {
     DicomMap query;
-    query.SetValue(DICOM_TAG_PATIENT_ID, patientId);
+    query.SetValue(DICOM_TAG_PATIENT_ID, patientId, false);
     MoveInternal(targetAet, ResourceType_Patient, query);
   }
 
@@ -1073,7 +1073,7 @@
                                       const std::string& studyUid)
   {
     DicomMap query;
-    query.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, studyUid);
+    query.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, studyUid, false);
     MoveInternal(targetAet, ResourceType_Study, query);
   }
 
@@ -1082,8 +1082,8 @@
                                        const std::string& seriesUid)
   {
     DicomMap query;
-    query.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, studyUid);
-    query.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, seriesUid);
+    query.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, studyUid, false);
+    query.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, seriesUid, false);
     MoveInternal(targetAet, ResourceType_Series, query);
   }
 
@@ -1093,9 +1093,9 @@
                                          const std::string& instanceUid)
   {
     DicomMap query;
-    query.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, studyUid);
-    query.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, seriesUid);
-    query.SetValue(DICOM_TAG_SOP_INSTANCE_UID, instanceUid);
+    query.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, studyUid, false);
+    query.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, seriesUid, false);
+    query.SetValue(DICOM_TAG_SOP_INSTANCE_UID, instanceUid, false);
     MoveInternal(targetAet, ResourceType_Instance, query);
   }
 
--- a/OrthancServer/FromDcmtkBridge.cpp	Thu Jun 09 14:48:40 2016 +0200
+++ b/OrthancServer/FromDcmtkBridge.cpp	Thu Jun 09 15:46:33 2016 +0200
@@ -1136,7 +1136,7 @@
         return ValueRepresentation_OtherWord;
 
       case EVR_PN:
-        return ValueRepresentation_PatientName;
+        return ValueRepresentation_PersonName;
 
       case EVR_SH:
         return ValueRepresentation_ShortString;
@@ -1761,7 +1761,7 @@
         throw OrthancException(ErrorCode_BadFileFormat);
       }
       
-      target.SetValue(ParseTag(members[i]), value.asString());
+      target.SetValue(ParseTag(members[i]), value.asString(), false);
     }
   }
 }
--- a/OrthancServer/OrthancFindRequestHandler.cpp	Thu Jun 09 14:48:40 2016 +0200
+++ b/OrthancServer/OrthancFindRequestHandler.cpp	Thu Jun 09 15:46:33 2016 +0200
@@ -84,7 +84,7 @@
       s += *it;
     }
 
-    result.SetValue(tag, s);
+    result.SetValue(tag, s, false);
   }
 
 
@@ -148,7 +148,7 @@
     if (query.HasTag(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_STUDIES))
     {
       result.SetValue(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_STUDIES,
-                      boost::lexical_cast<std::string>(studies.size()));
+                      boost::lexical_cast<std::string>(studies.size()), false);
     }
 
     if (!query.HasTag(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_SERIES) &&
@@ -164,7 +164,7 @@
     if (query.HasTag(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_SERIES))
     {
       result.SetValue(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_SERIES,
-                      boost::lexical_cast<std::string>(series.size()));
+                      boost::lexical_cast<std::string>(series.size()), false);
     }
 
     if (!query.HasTag(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_INSTANCES))
@@ -178,7 +178,7 @@
     if (query.HasTag(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_INSTANCES))
     {
       result.SetValue(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_INSTANCES,
-                      boost::lexical_cast<std::string>(instances.size()));
+                      boost::lexical_cast<std::string>(instances.size()), false);
     }
   }
 
@@ -196,7 +196,7 @@
     if (query.HasTag(DICOM_TAG_NUMBER_OF_STUDY_RELATED_SERIES))
     {
       result.SetValue(DICOM_TAG_NUMBER_OF_STUDY_RELATED_SERIES,
-                      boost::lexical_cast<std::string>(series.size()));
+                      boost::lexical_cast<std::string>(series.size()), false);
     }
 
     if (query.HasTag(DICOM_TAG_MODALITIES_IN_STUDY))
@@ -218,7 +218,7 @@
     if (query.HasTag(DICOM_TAG_NUMBER_OF_STUDY_RELATED_INSTANCES))
     {
       result.SetValue(DICOM_TAG_NUMBER_OF_STUDY_RELATED_INSTANCES,
-                      boost::lexical_cast<std::string>(instances.size()));
+                      boost::lexical_cast<std::string>(instances.size()), false);
     }
 
     if (query.HasTag(DICOM_TAG_SOP_CLASSES_IN_STUDY))
@@ -241,7 +241,7 @@
     if (query.HasTag(DICOM_TAG_NUMBER_OF_SERIES_RELATED_INSTANCES))
     {
       result.SetValue(DICOM_TAG_NUMBER_OF_SERIES_RELATED_INSTANCES,
-                      boost::lexical_cast<std::string>(instances.size()));
+                      boost::lexical_cast<std::string>(instances.size()), false);
     }
   }
 
@@ -342,11 +342,11 @@
         if (resource.isMember(tag))
         {
           value = resource.get(tag, Json::arrayValue).get("Value", "").asString();
-          result.SetValue(query.GetElement(i).GetTag(), value);
+          result.SetValue(query.GetElement(i).GetTag(), value, false);
         }
         else
         {
-          result.SetValue(query.GetElement(i).GetTag(), "");
+          result.SetValue(query.GetElement(i).GetTag(), "", false);
         }
       }
     }
@@ -356,7 +356,7 @@
       DicomArray tmp(*counters);
       for (size_t i = 0; i < tmp.GetSize(); i++)
       {
-        result.SetValue(tmp.GetElement(i).GetTag(), tmp.GetElement(i).GetValue().GetContent());
+        result.SetValue(tmp.GetElement(i).GetTag(), tmp.GetElement(i).GetValue().GetContent(), false);
       }
     }
 
@@ -496,7 +496,7 @@
       }
 
       DicomTag tag(FromDcmtkBridge::ParseTag(members[i]));
-      target.SetValue(tag, output[members[i]].asString());
+      target.SetValue(tag, output[members[i]].asString(), false);
     }
 
     return true;
@@ -615,7 +615,7 @@
         // DICOM specifies that searches must be case sensitive, except
         // for tags with a PN value representation
         bool sensitive = true;
-        if (vr == ValueRepresentation_PatientName)
+        if (vr == ValueRepresentation_PersonName)
         {
           sensitive = caseSensitivePN;
         }
--- a/OrthancServer/OrthancRestApi/OrthancRestModalities.cpp	Thu Jun 09 14:48:40 2016 +0200
+++ b/OrthancServer/OrthancRestApi/OrthancRestModalities.cpp	Thu Jun 09 15:46:33 2016 +0200
@@ -97,7 +97,7 @@
     for (size_t i = 0; i < members.size(); i++)
     {
       DicomTag t = FromDcmtkBridge::ParseTag(members[i]);
-      result.SetValue(t, query[members[i]].asString());
+      result.SetValue(t, query[members[i]].asString(), false);
     }
 
     return true;
@@ -287,7 +287,7 @@
     std::string tmp;
     if (source.GetTagValue(tmp, tag))
     {
-      target.SetValue(tag, tmp);
+      target.SetValue(tag, tmp, false);
     }
   }
 
--- a/OrthancServer/OrthancRestApi/OrthancRestResources.cpp	Thu Jun 09 14:48:40 2016 +0200
+++ b/OrthancServer/OrthancRestApi/OrthancRestResources.cpp	Thu Jun 09 15:46:33 2016 +0200
@@ -1289,6 +1289,9 @@
     std::string dicomContent;
     context.ReadFile(dicomContent, publicId, FileContentType_Dicom);
 
+    // TODO Consider using "DicomMap::ParseDicomMetaInformation()" to
+    // speed up things here
+
     ParsedDicomFile dicom(dicomContent);
 
     Json::Value header;
--- a/OrthancServer/QueryRetrieveHandler.cpp	Thu Jun 09 14:48:40 2016 +0200
+++ b/OrthancServer/QueryRetrieveHandler.cpp	Thu Jun 09 15:46:33 2016 +0200
@@ -84,7 +84,7 @@
                                       const std::string& value)
   {
     Invalidate();
-    query_.SetValue(tag, value);
+    query_.SetValue(tag, value, false);
   }
 
 
--- a/OrthancServer/Search/HierarchicalMatcher.cpp	Thu Jun 09 14:48:40 2016 +0200
+++ b/OrthancServer/Search/HierarchicalMatcher.cpp	Thu Jun 09 15:46:33 2016 +0200
@@ -147,7 +147,7 @@
           // DICOM specifies that searches must be case sensitive, except
           // for tags with a PN value representation
           bool sensitive = true;
-          if (vr == ValueRepresentation_PatientName)
+          if (vr == ValueRepresentation_PersonName)
           {
             sensitive = caseSensitivePN;
           }
--- a/OrthancServer/ToDcmtkBridge.cpp	Thu Jun 09 14:48:40 2016 +0200
+++ b/OrthancServer/ToDcmtkBridge.cpp	Thu Jun 09 15:46:33 2016 +0200
@@ -116,7 +116,7 @@
       case ValueRepresentation_OtherWord:
         return EVR_OW;
 
-      case ValueRepresentation_PatientName:
+      case ValueRepresentation_PersonName:
         return EVR_PN;
 
       case ValueRepresentation_ShortString:
--- a/Plugins/Engine/OrthancPluginDatabase.cpp	Thu Jun 09 14:48:40 2016 +0200
+++ b/Plugins/Engine/OrthancPluginDatabase.cpp	Thu Jun 09 15:46:33 2016 +0200
@@ -986,7 +986,7 @@
       {
         const OrthancPluginDicomTag& tag = *reinterpret_cast<const OrthancPluginDicomTag*>(answer.valueGeneric);
         assert(answerDicomMap_ != NULL);
-        answerDicomMap_->SetValue(tag.group, tag.element, std::string(tag.value));
+        answerDicomMap_->SetValue(tag.group, tag.element, std::string(tag.value), false);
         break;
       }
 
--- a/Plugins/Engine/OrthancPlugins.cpp	Thu Jun 09 14:48:40 2016 +0200
+++ b/Plugins/Engine/OrthancPlugins.cpp	Thu Jun 09 15:46:33 2016 +0200
@@ -511,7 +511,7 @@
       {
         if (!input.HasTag(*it))
         {
-          tmp.SetValue(*it, "");
+          tmp.SetValue(*it, "", false);
         }
       }      
 
--- a/Plugins/Engine/PluginsEnumerations.cpp	Thu Jun 09 14:48:40 2016 +0200
+++ b/Plugins/Engine/PluginsEnumerations.cpp	Thu Jun 09 15:46:33 2016 +0200
@@ -376,7 +376,7 @@
           return ValueRepresentation_OtherWord;
 
         case OrthancPluginValueRepresentation_PN:
-          return ValueRepresentation_PatientName;
+          return ValueRepresentation_PersonName;
 
         case OrthancPluginValueRepresentation_SH:
           return ValueRepresentation_ShortString;
@@ -474,7 +474,7 @@
         case ValueRepresentation_OtherWord:
           return OrthancPluginValueRepresentation_OW;
 
-        case ValueRepresentation_PatientName:
+        case ValueRepresentation_PersonName:
           return OrthancPluginValueRepresentation_PN;
 
         case ValueRepresentation_ShortString:
--- a/UnitTestsSources/DicomMapTests.cpp	Thu Jun 09 14:48:40 2016 +0200
+++ b/UnitTestsSources/DicomMapTests.cpp	Thu Jun 09 15:46:33 2016 +0200
@@ -90,7 +90,7 @@
 
   ASSERT_FALSE(m.HasTag(DICOM_TAG_PATIENT_NAME));
   ASSERT_FALSE(m.HasTag(0x0010, 0x0010));
-  m.SetValue(0x0010, 0x0010, "PatientName");
+  m.SetValue(0x0010, 0x0010, "PatientName", false);
   ASSERT_TRUE(m.HasTag(DICOM_TAG_PATIENT_NAME));
   ASSERT_TRUE(m.HasTag(0x0010, 0x0010));
 
@@ -99,9 +99,9 @@
   ASSERT_EQ(DICOM_TAG_PATIENT_NAME, *s.begin());
 
   ASSERT_FALSE(m.HasTag(DICOM_TAG_PATIENT_ID));
-  m.SetValue(DICOM_TAG_PATIENT_ID, "PatientID");
+  m.SetValue(DICOM_TAG_PATIENT_ID, "PatientID", false);
   ASSERT_TRUE(m.HasTag(0x0010, 0x0020));
-  m.SetValue(DICOM_TAG_PATIENT_ID, "PatientID2");
+  m.SetValue(DICOM_TAG_PATIENT_ID, "PatientID2", false);
   ASSERT_EQ("PatientID2", m.GetValue(0x0010, 0x0020).GetContent());
 
   m.GetTags(s);
@@ -117,7 +117,7 @@
   std::auto_ptr<DicomMap> mm(m.Clone());
   ASSERT_EQ("PatientName", mm->GetValue(DICOM_TAG_PATIENT_NAME).GetContent());  
 
-  m.SetValue(DICOM_TAG_PATIENT_ID, "Hello");
+  m.SetValue(DICOM_TAG_PATIENT_ID, "Hello", false);
   ASSERT_THROW(mm->GetValue(DICOM_TAG_PATIENT_ID), OrthancException);
   mm->CopyTagIfExists(m, DICOM_TAG_PATIENT_ID);
   ASSERT_EQ("Hello", mm->GetValue(DICOM_TAG_PATIENT_ID).GetContent());  
--- a/UnitTestsSources/FromDcmtkTests.cpp	Thu Jun 09 14:48:40 2016 +0200
+++ b/UnitTestsSources/FromDcmtkTests.cpp	Thu Jun 09 15:46:33 2016 +0200
@@ -301,7 +301,7 @@
 
 TEST(FromDcmtkBridge, ValueRepresentation)
 {
-  ASSERT_EQ(ValueRepresentation_PatientName, 
+  ASSERT_EQ(ValueRepresentation_PersonName, 
             FromDcmtkBridge::LookupValueRepresentation(DICOM_TAG_PATIENT_NAME));
   ASSERT_EQ(ValueRepresentation_Date, 
             FromDcmtkBridge::LookupValueRepresentation(DicomTag(0x0008, 0x0020) /* StudyDate */));
@@ -576,8 +576,8 @@
 
 TEST(ParsedDicomFile, ToJsonFlags1)
 {
-  FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7053, 0x1000), ValueRepresentation_PatientName, "MyPrivateTag", 1, 1);
-  FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7050, 0x1000), ValueRepresentation_PatientName, "Declared public tag", 1, 1);
+  FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7053, 0x1000), ValueRepresentation_PersonName, "MyPrivateTag", 1, 1);
+  FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7050, 0x1000), ValueRepresentation_PersonName, "Declared public tag", 1, 1);
 
   ParsedDicomFile f(true);
   f.Insert(DicomTag(0x7050, 0x1000), "Some public tag", false);  // Even group => public tag
@@ -691,7 +691,7 @@
 
   {
     DicomMap m;
-    m.SetValue(DICOM_TAG_PATIENT_ID, "hello");
+    m.SetValue(DICOM_TAG_PATIENT_ID, "hello", false);
     a.Add(m);
   }
 
@@ -703,7 +703,7 @@
 
   {
     DicomMap m;
-    m.SetValue(DICOM_TAG_PATIENT_ID, "world");
+    m.SetValue(DICOM_TAG_PATIENT_ID, "world", false);
     a.Add(m);
   }
 
@@ -719,7 +719,7 @@
 {
   FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7057, 0x1000), ValueRepresentation_OtherByte, "MyPrivateTag", 1, 1);
   FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7059, 0x1000), ValueRepresentation_OtherByte, "MyPrivateTag", 1, 1);
-  FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7050, 0x1000), ValueRepresentation_PatientName, "Declared public tag", 1, 1);
+  FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7050, 0x1000), ValueRepresentation_PersonName, "Declared public tag", 1, 1);
 
   Json::Value v;
   const std::string sopClassUid = "1.2.840.10008.5.1.4.1.1.1";  // CR Image Storage:
--- a/UnitTestsSources/ImageProcessingTests.cpp	Thu Jun 09 14:48:40 2016 +0200
+++ b/UnitTestsSources/ImageProcessingTests.cpp	Thu Jun 09 15:46:33 2016 +0200
@@ -44,14 +44,14 @@
 {
   // Cardiac/MR*
   DicomMap m;
-  m.SetValue(DICOM_TAG_ROWS, "24");
-  m.SetValue(DICOM_TAG_COLUMNS, "16");
-  m.SetValue(DICOM_TAG_BITS_ALLOCATED, "16");
-  m.SetValue(DICOM_TAG_SAMPLES_PER_PIXEL, "1");
-  m.SetValue(DICOM_TAG_BITS_STORED, "12");
-  m.SetValue(DICOM_TAG_HIGH_BIT, "11");
-  m.SetValue(DICOM_TAG_PIXEL_REPRESENTATION, "0");
-  m.SetValue(DICOM_TAG_PHOTOMETRIC_INTERPRETATION, "MONOCHROME2");
+  m.SetValue(DICOM_TAG_ROWS, "24", false);
+  m.SetValue(DICOM_TAG_COLUMNS, "16", false);
+  m.SetValue(DICOM_TAG_BITS_ALLOCATED, "16", false);
+  m.SetValue(DICOM_TAG_SAMPLES_PER_PIXEL, "1", false);
+  m.SetValue(DICOM_TAG_BITS_STORED, "12", false);
+  m.SetValue(DICOM_TAG_HIGH_BIT, "11", false);
+  m.SetValue(DICOM_TAG_PIXEL_REPRESENTATION, "0", false);
+  m.SetValue(DICOM_TAG_PHOTOMETRIC_INTERPRETATION, "MONOCHROME2", false);
 
   DicomImageInformation info(m);
   PixelFormat format;
@@ -64,14 +64,14 @@
 {
   // Delphine CT
   DicomMap m;
-  m.SetValue(DICOM_TAG_ROWS, "24");
-  m.SetValue(DICOM_TAG_COLUMNS, "16");
-  m.SetValue(DICOM_TAG_BITS_ALLOCATED, "16");
-  m.SetValue(DICOM_TAG_SAMPLES_PER_PIXEL, "1");
-  m.SetValue(DICOM_TAG_BITS_STORED, "16");
-  m.SetValue(DICOM_TAG_HIGH_BIT, "15");
-  m.SetValue(DICOM_TAG_PIXEL_REPRESENTATION, "1");
-  m.SetValue(DICOM_TAG_PHOTOMETRIC_INTERPRETATION, "MONOCHROME2");
+  m.SetValue(DICOM_TAG_ROWS, "24", false);
+  m.SetValue(DICOM_TAG_COLUMNS, "16", false);
+  m.SetValue(DICOM_TAG_BITS_ALLOCATED, "16", false);
+  m.SetValue(DICOM_TAG_SAMPLES_PER_PIXEL, "1", false);
+  m.SetValue(DICOM_TAG_BITS_STORED, "16", false);
+  m.SetValue(DICOM_TAG_HIGH_BIT, "15", false);
+  m.SetValue(DICOM_TAG_PIXEL_REPRESENTATION, "1", false);
+  m.SetValue(DICOM_TAG_PHOTOMETRIC_INTERPRETATION, "MONOCHROME2", false);
 
   DicomImageInformation info(m);
   PixelFormat format;
--- a/UnitTestsSources/ServerIndexTests.cpp	Thu Jun 09 14:48:40 2016 +0200
+++ b/UnitTestsSources/ServerIndexTests.cpp	Thu Jun 09 15:46:33 2016 +0200
@@ -790,10 +790,10 @@
   {
     std::string id = boost::lexical_cast<std::string>(i);
     DicomMap instance;
-    instance.SetValue(DICOM_TAG_PATIENT_ID, "patient-" + id);
-    instance.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, "study-" + id);
-    instance.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, "series-" + id);
-    instance.SetValue(DICOM_TAG_SOP_INSTANCE_UID, "instance-" + id);
+    instance.SetValue(DICOM_TAG_PATIENT_ID, "patient-" + id, false);
+    instance.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, "study-" + id, false);
+    instance.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, "series-" + id, false);
+    instance.SetValue(DICOM_TAG_SOP_INSTANCE_UID, "instance-" + id, false);
 
     std::map<MetadataType, std::string> instanceMetadata;
     DicomInstanceToStore toStore;