changeset 1744:b3de74dec2d5 db-changes

integration mainline->db-changes
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 26 Oct 2015 12:30:34 +0100
parents 54d78925cbb6 (current diff) 8fc1d096aa38 (diff)
children 38dda23c7d7d
files Core/DicomFormat/DicomNullValue.h Core/DicomFormat/DicomString.h OrthancServer/OrthancMoveRequestHandler.cpp OrthancServer/ServerIndex.cpp OrthancServer/ServerToolbox.cpp Plugins/Engine/OrthancPlugins.cpp UnitTestsSources/ServerIndexTests.cpp
diffstat 45 files changed, 1040 insertions(+), 345 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Tue Oct 20 17:39:58 2015 +0200
+++ b/CMakeLists.txt	Mon Oct 26 12:30:34 2015 +0100
@@ -96,6 +96,7 @@
   Core/DicomFormat/DicomImageInformation.cpp
   Core/DicomFormat/DicomIntegerPixelAccessor.cpp
   Core/DicomFormat/DicomInstanceHasher.cpp
+  Core/DicomFormat/DicomValue.cpp
   Core/Enumerations.cpp
   Core/FileStorage/FilesystemStorage.cpp
   Core/FileStorage/StorageAccessor.cpp
--- a/Core/DicomFormat/DicomArray.cpp	Tue Oct 20 17:39:58 2015 +0200
+++ b/Core/DicomFormat/DicomArray.cpp	Mon Oct 26 12:30:34 2015 +0100
@@ -63,7 +63,8 @@
     for (size_t  i = 0; i < elements_.size(); i++)
     {
       DicomTag t = elements_[i]->GetTag();
-      std::string s = elements_[i]->GetValue().AsString();
+      const DicomValue& v = elements_[i]->GetValue();
+      std::string s = v.IsNull() ? "(null)" : v.GetContent();
       printf("0x%04x 0x%04x [%s]\n", t.GetGroup(), t.GetElement(), s.c_str());
     }
   }
--- a/Core/DicomFormat/DicomImageInformation.cpp	Tue Oct 20 17:39:58 2015 +0200
+++ b/Core/DicomFormat/DicomImageInformation.cpp	Mon Oct 26 12:30:34 2015 +0100
@@ -54,7 +54,7 @@
 
     try
     {
-      std::string p = values.GetValue(DICOM_TAG_PHOTOMETRIC_INTERPRETATION).AsString();
+      std::string p = values.GetValue(DICOM_TAG_PHOTOMETRIC_INTERPRETATION).GetContent();
       Toolbox::ToUpperCase(p);
 
       if (p == "RGB")
@@ -114,13 +114,13 @@
         photometric_ = PhotometricInterpretation_Unknown;
       }
 
-      width_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_COLUMNS).AsString());
-      height_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_ROWS).AsString());
-      bitsAllocated_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_BITS_ALLOCATED).AsString());
+      width_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_COLUMNS).GetContent());
+      height_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_ROWS).GetContent());
+      bitsAllocated_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_BITS_ALLOCATED).GetContent());
 
       try
       {
-        samplesPerPixel_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_SAMPLES_PER_PIXEL).AsString());
+        samplesPerPixel_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_SAMPLES_PER_PIXEL).GetContent());
       }
       catch (OrthancException&)
       {
@@ -129,7 +129,7 @@
 
       try
       {
-        bitsStored_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_BITS_STORED).AsString());
+        bitsStored_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_BITS_STORED).GetContent());
       }
       catch (OrthancException&)
       {
@@ -138,7 +138,7 @@
 
       try
       {
-        highBit_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_HIGH_BIT).AsString());
+        highBit_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_HIGH_BIT).GetContent());
       }
       catch (OrthancException&)
       {
@@ -147,7 +147,7 @@
 
       try
       {
-        pixelRepresentation = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_PIXEL_REPRESENTATION).AsString());
+        pixelRepresentation = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_PIXEL_REPRESENTATION).GetContent());
       }
       catch (OrthancException&)
       {
@@ -160,7 +160,7 @@
         // https://www.dabsoft.ch/dicom/3/C.7.6.3.1.3/
         try
         {
-          planarConfiguration = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_PLANAR_CONFIGURATION).AsString());
+          planarConfiguration = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_PLANAR_CONFIGURATION).GetContent());
         }
         catch (OrthancException&)
         {
@@ -179,9 +179,9 @@
 
     try
     {
-      numberOfFrames_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_NUMBER_OF_FRAMES).AsString());
+      numberOfFrames_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_NUMBER_OF_FRAMES).GetContent());
     }
-    catch (OrthancException)
+    catch (OrthancException&)
     {
       // If the tag "NumberOfFrames" is absent, assume there is a single frame
       numberOfFrames_ = 1;
--- a/Core/DicomFormat/DicomInstanceHasher.cpp	Tue Oct 20 17:39:58 2015 +0200
+++ b/Core/DicomFormat/DicomInstanceHasher.cpp	Mon Oct 26 12:30:34 2015 +0100
@@ -60,10 +60,10 @@
   {
     const DicomValue* patientId = instance.TestAndGetValue(DICOM_TAG_PATIENT_ID);
 
-    Setup(patientId == NULL ? "" : patientId->AsString(),
-          instance.GetValue(DICOM_TAG_STUDY_INSTANCE_UID).AsString(),
-          instance.GetValue(DICOM_TAG_SERIES_INSTANCE_UID).AsString(),
-          instance.GetValue(DICOM_TAG_SOP_INSTANCE_UID).AsString());
+    Setup(patientId == NULL ? "" : patientId->GetContent(),
+          instance.GetValue(DICOM_TAG_STUDY_INSTANCE_UID).GetContent(),
+          instance.GetValue(DICOM_TAG_SERIES_INSTANCE_UID).GetContent(),
+          instance.GetValue(DICOM_TAG_SOP_INSTANCE_UID).GetContent());
   }
 
   const std::string& DicomInstanceHasher::HashPatient()
--- a/Core/DicomFormat/DicomMap.cpp	Tue Oct 20 17:39:58 2015 +0200
+++ b/Core/DicomFormat/DicomMap.cpp	Mon Oct 26 12:30:34 2015 +0100
@@ -35,7 +35,6 @@
 
 #include <stdio.h>
 #include <memory>
-#include "DicomString.h"
 #include "DicomArray.h"
 #include "../OrthancException.h"
 
--- a/Core/DicomFormat/DicomMap.h	Tue Oct 20 17:39:58 2015 +0200
+++ b/Core/DicomFormat/DicomMap.h	Mon Oct 26 12:30:34 2015 +0100
@@ -34,7 +34,6 @@
 
 #include "DicomTag.h"
 #include "DicomValue.h"
-#include "DicomString.h"
 #include "../Enumerations.h"
 
 #include <set>
@@ -105,14 +104,14 @@
     void SetValue(const DicomTag& tag,
                   const std::string& str)
     {
-      SetValue(tag, new DicomString(str));
+      SetValue(tag, new DicomValue(str, false));
     }
 
     void SetValue(uint16_t group, 
                   uint16_t element, 
                   const std::string& str)
     {
-      SetValue(group, element, new DicomString(str));
+      SetValue(group, element, new DicomValue(str, false));
     }
 
     bool HasTag(uint16_t group, uint16_t element) const
--- a/Core/DicomFormat/DicomNullValue.h	Tue Oct 20 17:39:58 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "DicomValue.h"
-
-namespace Orthanc
-{
-  class DicomNullValue : public DicomValue
-  {
-  public:
-    DicomNullValue()
-    {
-    }
-
-    virtual DicomValue* Clone() const 
-    {
-      return new DicomNullValue();
-    }
-
-    virtual std::string AsString() const
-    {
-      return "(null)";
-    }
-
-    virtual bool IsNull() const
-    {
-      return true;
-    }
-  };
-}
--- a/Core/DicomFormat/DicomString.h	Tue Oct 20 17:39:58 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,67 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "DicomValue.h"
-
-namespace Orthanc
-{
-  class DicomString : public DicomValue
-  {
-  private:
-    std::string value_;
-
-  public:
-    DicomString(const std::string& v) : value_(v)
-    {
-    }
-
-    DicomString(const char* v)
-    {
-      if (v)
-        value_ = v;
-      else
-        value_ = "";
-    }
-
-    virtual DicomValue* Clone() const 
-    {
-      return new DicomString(value_);
-    }
-
-    virtual std::string AsString() const
-    {
-      return value_;
-    }
-  };
-}
--- a/Core/DicomFormat/DicomTag.cpp	Tue Oct 20 17:39:58 2015 +0200
+++ b/Core/DicomFormat/DicomTag.cpp	Mon Oct 26 12:30:34 2015 +0100
@@ -114,6 +114,12 @@
     if (*this == DICOM_TAG_PATIENT_NAME)
       return "PatientName";
 
+    if (*this == DICOM_TAG_IMAGE_POSITION_PATIENT)
+      return "ImagePositionPatient";
+
+    if (*this == DICOM_TAG_IMAGE_ORIENTATION_PATIENT)
+      return "ImageOrientationPatient";
+
     return "";
   }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/DicomFormat/DicomValue.cpp	Mon Oct 26 12:30:34 2015 +0100
@@ -0,0 +1,90 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "../PrecompiledHeaders.h"
+#include "DicomValue.h"
+
+#include "../OrthancException.h"
+#include "../Toolbox.h"
+
+namespace Orthanc
+{
+  DicomValue::DicomValue(const DicomValue& other) : 
+    type_(other.type_),
+    content_(other.content_)
+  {
+  }
+
+
+  DicomValue::DicomValue(const std::string& content,
+                         bool isBinary) :
+    type_(isBinary ? Type_Binary : Type_String),
+    content_(content)
+  {
+  }
+  
+  
+  DicomValue::DicomValue(const char* data,
+                         size_t size,
+                         bool isBinary) :
+    type_(isBinary ? Type_Binary : Type_String)
+  {
+    content_.assign(data, size);
+  }
+    
+  
+  const std::string& DicomValue::GetContent() const
+  {
+    if (type_ == Type_Null)
+    {
+      throw OrthancException(ErrorCode_BadParameterType);
+    }
+    else
+    {
+      return content_;
+    }
+  }
+
+
+  DicomValue* DicomValue::Clone() const
+  {
+    return new DicomValue(*this);
+  }
+
+  
+  void DicomValue::FormatDataUriScheme(std::string& target,
+                                       const std::string& mime) const
+  {
+    Toolbox::EncodeBase64(target, GetContent());
+    target.insert(0, "data:" + mime + ";base64,");
+  }
+}
--- a/Core/DicomFormat/DicomValue.h	Tue Oct 20 17:39:58 2015 +0200
+++ b/Core/DicomFormat/DicomValue.h	Mon Oct 26 12:30:34 2015 +0100
@@ -32,22 +32,58 @@
 
 #pragma once
 
-#include "../IDynamicObject.h"
-
 #include <string>
+#include <boost/noncopyable.hpp>
 
 namespace Orthanc
 {
-  class DicomValue : public IDynamicObject
+  class DicomValue : public boost::noncopyable
   {
-  public:
-    virtual DicomValue* Clone() const = 0;
+  private:
+    enum Type
+    {
+      Type_Null,
+      Type_String,
+      Type_Binary
+    };
+
+    Type         type_;
+    std::string  content_;
+
+    DicomValue(const DicomValue& other);
 
-    virtual std::string AsString() const = 0;
+  public:
+    DicomValue() : type_(Type_Null)
+    {
+    }
+    
+    DicomValue(const std::string& content,
+               bool isBinary);
+    
+    DicomValue(const char* data,
+               size_t size,
+               bool isBinary);
+    
+    const std::string& GetContent() const;
 
-    virtual bool IsNull() const
+    bool IsNull() const
+    {
+      return type_ == Type_Null;
+    }
+
+    bool IsBinary() const
     {
-      return false;
+      return type_ == Type_Binary;
+    }
+    
+    DicomValue* Clone() const;
+
+    void FormatDataUriScheme(std::string& target,
+                             const std::string& mime) const;
+
+    void FormatDataUriScheme(std::string& target) const
+    {
+      FormatDataUriScheme(target, "application/octet-stream");
     }
   };
 }
--- a/Core/Logging.cpp	Tue Oct 20 17:39:58 2015 +0200
+++ b/Core/Logging.cpp	Mon Oct 26 12:30:34 2015 +0100
@@ -144,9 +144,7 @@
     std::ostream* warning_;
     std::ostream* info_;
 
-    std::auto_ptr<std::ofstream> errorFile_;
-    std::auto_ptr<std::ofstream> warningFile_;
-    std::auto_ptr<std::ofstream> infoFile_;
+    std::auto_ptr<std::ofstream> file_;
 
     LoggingState() : 
       infoEnabled_(false),
@@ -172,14 +170,14 @@
   {
     static void GetLogPath(boost::filesystem::path& log,
                            boost::filesystem::path& link,
-                           const char* level,
+                           const std::string& suffix,
                            const std::string& directory)
     {
       /**
          From Google Log documentation:
 
          Unless otherwise specified, logs will be written to the filename
-         "<program name>.<hostname>.<user name>.log.<severity level>.",
+         "<program name>.<hostname>.<user name>.log<suffix>.",
          followed by the date, time, and pid (you can't prevent the date,
          time, and pid from being in the filename).
 
@@ -208,21 +206,17 @@
 
       std::string programName = exe.filename().replace_extension("").string();
 
-      log = (root / (programName + ".log." +
-                     std::string(level) + "." +
-                     std::string(date)));
-
-      link = (root / (programName + "." + std::string(level)));
+      log = (root / (programName + ".log" + suffix + "." + std::string(date)));
+      link = (root / (programName + ".log" + suffix));
     }
 
 
-    static void PrepareLogFile(std::ostream*& stream,
-                               std::auto_ptr<std::ofstream>& file,
-                               const char* level,
+    static void PrepareLogFile(std::auto_ptr<std::ofstream>& file,
+                               const std::string& suffix,
                                const std::string& directory)
     {
       boost::filesystem::path log, link;
-      GetLogPath(log, link, level, directory);
+      GetLogPath(log, link, suffix, directory);
 
 #if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__)))
       boost::filesystem::remove(link);
@@ -230,7 +224,6 @@
 #endif
 
       file.reset(new std::ofstream(log.string().c_str()));
-      stream = file.get();
     }
 
 
@@ -273,9 +266,11 @@
       boost::mutex::scoped_lock lock(loggingMutex_);
       assert(loggingState_.get() != NULL);
 
-      PrepareLogFile(loggingState_->error_,   loggingState_->errorFile_,   "ERROR", path);
-      PrepareLogFile(loggingState_->warning_, loggingState_->warningFile_, "WARNING", path);
-      PrepareLogFile(loggingState_->info_,    loggingState_->infoFile_,    "INFO", path);
+      PrepareLogFile(loggingState_->file_, "" /* no suffix */, path);
+
+      loggingState_->warning_ = loggingState_->file_.get();
+      loggingState_->error_ = loggingState_->file_.get();
+      loggingState_->info_ = loggingState_->file_.get();
     }
 
     InternalLogger::InternalLogger(const char* level,
--- a/NEWS	Tue Oct 20 17:39:58 2015 +0200
+++ b/NEWS	Mon Oct 26 12:30:34 2015 +0100
@@ -6,6 +6,7 @@
 * "/tools/create-dicom": Support of hierarchical structures (creation of sequences)
 * "/modify" can insert/modify sequences
 * "/series/.../ordered-slices" to order the slices of a 2D+t or 3D image
+* New URI "/tools/shutdown" to stop Orthanc from the REST API
 * New URIs for attachments: ".../compress", ".../uncompress" and ".../is-compressed"
 
 Plugins
@@ -13,6 +14,7 @@
 
 * New function "OrthancPluginRegisterErrorCode()" to declare custom error codes
 * New function "OrthancPluginRegisterDictionaryTag()" to declare DICOM tags
+* New "OrthancStarted" and "OrthancStopped" events in change callbacks
 
 Lua
 ---
@@ -27,6 +29,9 @@
 * "/system" URI gives information about the plugins used for storage area and DB back-end
 * Plugin callbacks should now return explicit "OrthancPluginErrorCode" instead of integers
 * "/tools/create-dicom" can create tags with unknown VR
+* "--logdir" flag creates a single log file instead of 3 separate files for errors/warnings/infos
+* "--errors" flag lists the error codes that could be returned by Orthanc
+* Under Windows, the exit status of Orthanc corresponds to the encountered error code
 
 
 Version 0.9.4 (2015/09/16)
--- a/OrthancExplorer/explorer.js	Tue Oct 20 17:39:58 2015 +0200
+++ b/OrthancExplorer/explorer.js	Mon Oct 26 12:30:34 2015 +0100
@@ -236,8 +236,8 @@
           ).format
   (patient.MainDicomTags.PatientName,
    FormatMainDicomTags(patient.MainDicomTags, [ 
-     "PatientName", 
-     "OtherPatientIDs" 
+     "PatientName"
+     /*"OtherPatientIDs" */
    ]),
    patient.Studies.length
   );
@@ -288,7 +288,8 @@
      "SeriesTime", 
      "Manufacturer",
      "ImagesInAcquisition",
-     "SeriesDate"
+     "SeriesDate",
+     "ImageOrientationPatient"
    ]),
    c
   );
@@ -305,7 +306,8 @@
      "AcquisitionNumber", 
      "InstanceNumber", 
      "InstanceCreationDate", 
-     "InstanceCreationTime"
+     "InstanceCreationTime",
+     "ImagePositionPatient"
    ])
   );
 
--- a/OrthancServer/DicomFindQuery.cpp	Tue Oct 20 17:39:58 2015 +0200
+++ b/OrthancServer/DicomFindQuery.cpp	Mon Oct 26 12:30:34 2015 +0100
@@ -349,9 +349,15 @@
     for (std::set<DicomTag>::const_iterator
            it = tags.begin(); it != tags.end(); ++it)
     {
+      const DicomValue& value = mainTags.GetValue(*it);
+      if (value.IsBinary() || value.IsNull())
+      {
+        return false;
+      }
+
       Constraints::const_iterator constraint = constraints_.find(*it);
       if (constraint != constraints_.end() &&
-          !constraint->second->Apply(mainTags.GetValue(*it).AsString()))
+          !constraint->second->Apply(value.GetContent()))
       {
         return false;
       }
--- a/OrthancServer/DicomInstanceToStore.cpp	Tue Oct 20 17:39:58 2015 +0200
+++ b/OrthancServer/DicomInstanceToStore.cpp	Mon Oct 26 12:30:34 2015 +0100
@@ -110,7 +110,9 @@
     {
       json_.Allocate();
       FromDcmtkBridge::ToJson(json_.GetContent(), GetDataset(parsed_.GetContent()), 
-                              DicomToJsonFormat_Full, 256 /* max string length */);
+                              DicomToJsonFormat_Full, 
+                              DicomToJsonFlags_Default,
+                              256 /* max string length */);
     }
   }
 
--- a/OrthancServer/DicomProtocol/DicomUserConnection.cpp	Tue Oct 20 17:39:58 2015 +0200
+++ b/OrthancServer/DicomProtocol/DicomUserConnection.cpp	Mon Oct 26 12:30:34 2015 +0100
@@ -457,7 +457,8 @@
             const DicomValue* value = fix->TestAndGetValue(*it);
 
             if (value != NULL && 
-                value->AsString() == "*")
+                !value->IsNull() &&
+                value->GetContent() == "*")
             {
               fix->SetValue(*it, "");
             }
@@ -948,7 +949,7 @@
       throw OrthancException(ErrorCode_InternalError);
     }
 
-    const std::string tmp = findResult.GetValue(DICOM_TAG_QUERY_RETRIEVE_LEVEL).AsString();
+    const std::string tmp = findResult.GetValue(DICOM_TAG_QUERY_RETRIEVE_LEVEL).GetContent();
     ResourceType level = StringToResourceType(tmp.c_str());
 
     DicomMap move;
--- a/OrthancServer/FromDcmtkBridge.cpp	Tue Oct 20 17:39:58 2015 +0200
+++ b/OrthancServer/FromDcmtkBridge.cpp	Mon Oct 26 12:30:34 2015 +0100
@@ -47,8 +47,6 @@
 #include "../Core/OrthancException.h"
 #include "../Core/Images/PngWriter.h"
 #include "../Core/Uuid.h"
-#include "../Core/DicomFormat/DicomString.h"
-#include "../Core/DicomFormat/DicomNullValue.h"
 #include "../Core/DicomFormat/DicomIntegerPixelAccessor.h"
 
 #include <list>
@@ -332,7 +330,7 @@
       {
         target.SetValue(element->getTag().getGTag(),
                         element->getTag().getETag(),
-                        ConvertLeafElement(*element, encoding));
+                        ConvertLeafElement(*element, DicomToJsonFlags_Default, encoding));
       }
     }
   }
@@ -364,6 +362,7 @@
 
 
   DicomValue* FromDcmtkBridge::ConvertLeafElement(DcmElement& element,
+                                                  DicomToJsonFlags flags,
                                                   Encoding encoding)
   {
     if (!element.isLeaf())
@@ -379,18 +378,18 @@
       {
         if (c == NULL)  // This case corresponds to the empty string
         {
-          return new DicomString("");
+          return new DicomValue("", false);
         }
         else
         {
           std::string s(c);
           std::string utf8 = Toolbox::ConvertToUtf8(s, encoding);
-          return new DicomString(utf8);
+          return new DicomValue(utf8, false);
         }
       }
       else
       {
-        return new DicomNullValue;
+        return new DicomValue;
       }
     }
 
@@ -401,14 +400,26 @@
       {
 
         /**
-         * TODO.
+         * Deal with binary data (including PixelData).
          **/
 
         case EVR_OB:  // other byte
         case EVR_OF:  // other float
         case EVR_OW:  // other word
         case EVR_UN:  // unknown value representation
-          return new DicomNullValue;
+        case EVR_ox:  // OB or OW depending on context
+        {
+          if (!(flags & DicomToJsonFlags_ConvertBinaryToNull))
+          {
+            Uint8* data = NULL;
+            if (element.getUint8Array(data) == EC_Normal)
+            {
+              return new DicomValue(reinterpret_cast<const char*>(data), element.getLength(), true);
+            }
+          }
+
+          return new DicomValue;
+        }
     
           /**
            * String types, should never happen at this point because of
@@ -430,7 +441,7 @@
         case EVR_UT:  // unlimited text
         case EVR_PN:  // person name
         case EVR_UI:  // unique identifier
-          return new DicomNullValue;
+          return new DicomValue;
 
 
           /**
@@ -441,54 +452,54 @@
         {
           Sint32 f;
           if (dynamic_cast<DcmSignedLong&>(element).getSint32(f).good())
-            return new DicomString(boost::lexical_cast<std::string>(f));
+            return new DicomValue(boost::lexical_cast<std::string>(f), false);
           else
-            return new DicomNullValue;
+            return new DicomValue;
         }
 
         case EVR_SS:  // signed short
         {
           Sint16 f;
           if (dynamic_cast<DcmSignedShort&>(element).getSint16(f).good())
-            return new DicomString(boost::lexical_cast<std::string>(f));
+            return new DicomValue(boost::lexical_cast<std::string>(f), false);
           else
-            return new DicomNullValue;
+            return new DicomValue;
         }
 
         case EVR_UL:  // unsigned long
         {
           Uint32 f;
           if (dynamic_cast<DcmUnsignedLong&>(element).getUint32(f).good())
-            return new DicomString(boost::lexical_cast<std::string>(f));
+            return new DicomValue(boost::lexical_cast<std::string>(f), false);
           else
-            return new DicomNullValue;
+            return new DicomValue;
         }
 
         case EVR_US:  // unsigned short
         {
           Uint16 f;
           if (dynamic_cast<DcmUnsignedShort&>(element).getUint16(f).good())
-            return new DicomString(boost::lexical_cast<std::string>(f));
+            return new DicomValue(boost::lexical_cast<std::string>(f), false);
           else
-            return new DicomNullValue;
+            return new DicomValue;
         }
 
         case EVR_FL:  // float single-precision
         {
           Float32 f;
           if (dynamic_cast<DcmFloatingPointSingle&>(element).getFloat32(f).good())
-            return new DicomString(boost::lexical_cast<std::string>(f));
+            return new DicomValue(boost::lexical_cast<std::string>(f), false);
           else
-            return new DicomNullValue;
+            return new DicomValue;
         }
 
         case EVR_FD:  // float double-precision
         {
           Float64 f;
           if (dynamic_cast<DcmFloatingPointDouble&>(element).getFloat64(f).good())
-            return new DicomString(boost::lexical_cast<std::string>(f));
+            return new DicomValue(boost::lexical_cast<std::string>(f), false);
           else
-            return new DicomNullValue;
+            return new DicomValue;
         }
 
 
@@ -502,11 +513,11 @@
           if (dynamic_cast<DcmAttributeTag&>(element).getTagVal(tag, 0).good())
           {
             DicomTag t(tag.getGroup(), tag.getElement());
-            return new DicomString(t.Format());
+            return new DicomValue(t.Format(), false);
           }
           else
           {
-            return new DicomNullValue;
+            return new DicomValue;
           }
         }
 
@@ -517,14 +528,13 @@
          **/
 
         case EVR_SQ:  // sequence of items
-          return new DicomNullValue;
+          return new DicomValue;
 
 
           /**
            * Internal to DCMTK.
            **/ 
 
-        case EVR_ox:  // OB or OW depending on context
         case EVR_xs:  // SS or US depending on context
         case EVR_lt:  // US, SS or OW depending on context, used for LUT Data (thus the name)
         case EVR_na:  // na="not applicable", for data which has no VR
@@ -541,7 +551,7 @@
         case EVR_PixelData:  // used internally for uncompressed pixeld data
         case EVR_OverlayData:  // used internally for overlay data
         case EVR_UNKNOWN2B:  // used internally for elements with unknown VR with 2-byte length field in explicit VR
-          return new DicomNullValue;
+          return new DicomValue;
 
 
           /**
@@ -549,16 +559,16 @@
            **/ 
 
         default:
-          return new DicomNullValue;
+          return new DicomValue;
       }
     }
     catch (boost::bad_lexical_cast)
     {
-      return new DicomNullValue;
+      return new DicomValue;
     }
     catch (std::bad_cast)
     {
-      return new DicomNullValue;
+      return new DicomValue;
     }
   }
 
@@ -622,9 +632,11 @@
   static void LeafValueToJson(Json::Value& target,
                               const DicomValue& value,
                               DicomToJsonFormat format,
+                              DicomToJsonFlags flags,
                               unsigned int maxStringLength)
   {
-    std::string content = value.AsString();
+    Json::Value* targetValue = NULL;
+    Json::Value* targetType = NULL;
 
     switch (format)
     {
@@ -632,52 +644,77 @@
       case DicomToJsonFormat_Simple:
       {
         assert(target.type() == Json::nullValue);
-
-        if (!value.IsNull() &&
-            (maxStringLength == 0 ||
-             content.size() <= maxStringLength))
-        {
-          target = content;
-        }
-
+        targetValue = &target;
         break;
       }      
 
       case DicomToJsonFormat_Full:
       {
         assert(target.type() == Json::objectValue);
-
-        if (value.IsNull())
-        {
-          target["Type"] = "Null";
-          target["Value"] = Json::nullValue;
-        }
-        else
-        {
-          if (maxStringLength == 0 ||
-              content.size() <= maxStringLength)
-          {
-            target["Type"] = "String";
-            target["Value"] = content;
-          }
-          else
-          {
-            target["Type"] = "TooLong";
-            target["Value"] = Json::nullValue;
-          }
-        }
+        target["Value"] = Json::nullValue;
+        target["Type"] = Json::nullValue;
+        targetType = &target["Type"];
+        targetValue = &target["Value"];
         break;
       }
 
       default:
         throw OrthancException(ErrorCode_ParameterOutOfRange);
     }
+
+    assert(targetValue != NULL);
+    assert(targetValue->type() == Json::nullValue);
+    assert(targetType == NULL || targetType->type() == Json::nullValue);
+
+    if (value.IsNull())
+    {
+      if (targetType != NULL)
+      {
+        *targetType = "Null";
+      }
+    }
+    else if (value.IsBinary())
+    {
+      if (flags & DicomToJsonFlags_ConvertBinaryToAscii)
+      {
+        *targetValue = Toolbox::ConvertToAscii(value.GetContent());
+      }
+      else
+      {
+        std::string s;
+        value.FormatDataUriScheme(s);
+        *targetValue = s;
+      }
+
+      if (targetType != NULL)
+      {
+        *targetType = "Binary";
+      }
+    }
+    else if (maxStringLength == 0 ||
+             value.GetContent().size() <= maxStringLength)
+    {
+      *targetValue = value.GetContent();
+
+      if (targetType != NULL)
+      {
+        *targetType = "String";
+      }
+    }
+    else
+    {
+      if (targetType != NULL)
+      {
+        *targetType = "TooLong";
+      }
+    }
   }                              
 
 
   static void DatasetToJson(Json::Value& parent,
                             DcmItem& item,
                             DicomToJsonFormat format,
+                            DicomToJsonFlags flags,
                             unsigned int maxStringLength,
                             Encoding encoding);
 
@@ -685,6 +722,7 @@
   void FromDcmtkBridge::ToJson(Json::Value& parent,
                                DcmElement& element,
                                DicomToJsonFormat format,
+                               DicomToJsonFlags flags,
                                unsigned int maxStringLength,
                                Encoding encoding)
   {
@@ -698,8 +736,8 @@
 
     if (element.isLeaf())
     {
-      std::auto_ptr<DicomValue> v(FromDcmtkBridge::ConvertLeafElement(element, encoding));
-      LeafValueToJson(target, *v, format, maxStringLength);
+      std::auto_ptr<DicomValue> v(FromDcmtkBridge::ConvertLeafElement(element, flags, encoding));
+      LeafValueToJson(target, *v, format, flags, maxStringLength);
     }
     else
     {
@@ -715,7 +753,7 @@
       {
         DcmItem* child = sequence.getItem(i);
         Json::Value& v = target.append(Json::objectValue);
-        DatasetToJson(v, *child, format, maxStringLength, encoding);
+        DatasetToJson(v, *child, format, flags, maxStringLength, encoding);
       }
     }
   }
@@ -724,6 +762,7 @@
   static void DatasetToJson(Json::Value& parent,
                             DcmItem& item,
                             DicomToJsonFormat format,
+                            DicomToJsonFlags flags,
                             unsigned int maxStringLength,
                             Encoding encoding)
   {
@@ -732,7 +771,44 @@
     for (unsigned long i = 0; i < item.card(); i++)
     {
       DcmElement* element = item.getElement(i);
-      FromDcmtkBridge::ToJson(parent, *element, format, maxStringLength, encoding);
+      if (element == NULL)
+      {
+        throw OrthancException(ErrorCode_InternalError);
+      }
+
+      if (!(flags & DicomToJsonFlags_IncludePrivateTags) &&
+          element->getTag().isPrivate())
+      {
+        continue;
+      }
+
+      if (!(flags & DicomToJsonFlags_IncludeUnknownTags))
+      {
+        DictionaryLocker locker;
+        if (locker->findEntry(element->getTag(), NULL) == NULL)
+        {
+          continue;
+        }
+      }
+
+      DcmEVR evr = element->getTag().getEVR();
+      if (evr == EVR_OB ||
+          evr == EVR_OF ||
+          evr == EVR_OW ||
+          evr == EVR_UN ||
+          evr == EVR_ox)
+      {
+        // This is a binary tag
+        DicomTag tag(FromDcmtkBridge::Convert(element->getTag()));
+
+        if ((tag == DICOM_TAG_PIXEL_DATA && !(flags & DicomToJsonFlags_IncludePixelData)) ||
+            (tag != DICOM_TAG_PIXEL_DATA && !(flags & DicomToJsonFlags_IncludeBinary)))
+        {
+          continue;
+        }
+      }
+
+      FromDcmtkBridge::ToJson(parent, *element, format, flags, maxStringLength, encoding);
     }
   }
 
@@ -740,10 +816,11 @@
   void FromDcmtkBridge::ToJson(Json::Value& target, 
                                DcmDataset& dataset,
                                DicomToJsonFormat format,
+                               DicomToJsonFlags flags,
                                unsigned int maxStringLength)
   {
     target = Json::objectValue;
-    DatasetToJson(target, dataset, format, maxStringLength, DetectEncoding(dataset));
+    DatasetToJson(target, dataset, format, flags, maxStringLength, DetectEncoding(dataset));
   }
 
 
@@ -841,18 +918,6 @@
   }
 
 
-  void FromDcmtkBridge::Print(FILE* fp, const DicomMap& m)
-  {
-    for (DicomMap::Map::const_iterator 
-           it = m.map_.begin(); it != m.map_.end(); ++it)
-    {
-      DicomTag t = it->first;
-      std::string s = it->second->AsString();
-      fprintf(fp, "0x%04x 0x%04x (%s) [%s]\n", t.GetGroup(), t.GetElement(), GetName(t).c_str(), s.c_str());
-    }
-  }
-
-
   void FromDcmtkBridge::ToJson(Json::Value& result,
                                const DicomMap& values,
                                bool simplify)
@@ -869,7 +934,15 @@
     {
       if (simplify)
       {
-        result[GetName(it->first)] = it->second->AsString();
+        if (it->second->IsNull())
+        {
+          result[GetName(it->first)] = Json::nullValue;
+        }
+        else
+        {
+          // TODO IsBinary
+          result[GetName(it->first)] = it->second->GetContent();
+        }
       }
       else
       {
@@ -884,8 +957,9 @@
         }
         else
         {
+          // TODO IsBinary
           value["Type"] = "String";
-          value["Value"] = it->second->AsString();
+          value["Value"] = it->second->GetContent();
         }
 
         result[it->first.Format()] = value;
@@ -1001,7 +1075,13 @@
 
   static bool IsBinaryTag(const DcmTag& key)
   {
-    return key.isPrivate() || key.isUnknownVR();
+    return (key.isPrivate() || 
+            key.isUnknownVR() || 
+            key.getEVR() == EVR_OB ||
+            key.getEVR() == EVR_OF ||
+            key.getEVR() == EVR_OW ||
+            key.getEVR() == EVR_UN ||
+            key.getEVR() == EVR_ox);
   }
 
 
@@ -1019,17 +1099,15 @@
       // http://support.dcmtk.org/docs/dcvr_8h-source.html
 
       /**
-       * TODO.
+       * Binary types, handled above
        **/
     
       case EVR_OB:  // other byte
       case EVR_OF:  // other float
       case EVR_OW:  // other word
-      case EVR_AT:  // attribute tag
-        throw OrthancException(ErrorCode_NotImplemented);
-
       case EVR_UN:  // unknown value representation
-        throw OrthancException(ErrorCode_ParameterOutOfRange);
+      case EVR_ox:  // OB or OW depending on context
+        throw OrthancException(ErrorCode_InternalError);
 
 
       /**
@@ -1115,10 +1193,17 @@
 
 
       /**
+       * TODO
+       **/
+
+      case EVR_AT:  // attribute tag
+        throw OrthancException(ErrorCode_NotImplemented);
+
+
+      /**
        * Internal to DCMTK.
        **/ 
 
-      case EVR_ox:  // OB or OW depending on context
       case EVR_xs:  // SS or US depending on context
       case EVR_lt:  // US, SS or OW depending on context, used for LUT Data (thus the name)
       case EVR_na:  // na="not applicable", for data which has no VR
--- a/OrthancServer/FromDcmtkBridge.h	Tue Oct 20 17:39:58 2015 +0200
+++ b/OrthancServer/FromDcmtkBridge.h	Mon Oct 26 12:30:34 2015 +0100
@@ -65,17 +65,20 @@
     static bool IsUnknownTag(const DicomTag& tag);
 
     static DicomValue* ConvertLeafElement(DcmElement& element,
+                                          DicomToJsonFlags flags,
                                           Encoding encoding);
 
     static void ToJson(Json::Value& parent,
                        DcmElement& element,
                        DicomToJsonFormat format,
+                       DicomToJsonFlags flags,
                        unsigned int maxStringLength,
-                       Encoding encoding);
+                       Encoding dicomEncoding);
 
     static void ToJson(Json::Value& target, 
                        DcmDataset& dataset,
                        DicomToJsonFormat format,
+                       DicomToJsonFlags flags,
                        unsigned int maxStringLength);
 
     static std::string GetName(const DicomTag& tag);
@@ -106,9 +109,6 @@
       target.SetValue(ParseTag(tagName), value);
     }
 
-    static void Print(FILE* fp, 
-                      const DicomMap& m);
-
     static void ToJson(Json::Value& result,
                        const DicomMap& values,
                        bool simplify);
--- a/OrthancServer/Internals/StoreScp.cpp	Tue Oct 20 17:39:58 2015 +0200
+++ b/OrthancServer/Internals/StoreScp.cpp	Mon Oct 26 12:30:34 2015 +0100
@@ -169,7 +169,9 @@
           {
             FromDcmtkBridge::Convert(summary, **imageDataSet);
             FromDcmtkBridge::ToJson(dicomJson, **imageDataSet,
-                                    DicomToJsonFormat_Full, 256 /* max string length */);
+                                    DicomToJsonFormat_Full, 
+                                    DicomToJsonFlags_Default, 
+                                    256 /* max string length */);
 
             if (!FromDcmtkBridge::SaveToMemoryBuffer(buffer, **imageDataSet))
             {
--- a/OrthancServer/OrthancFindRequestHandler.cpp	Tue Oct 20 17:39:58 2015 +0200
+++ b/OrthancServer/OrthancFindRequestHandler.cpp	Mon Oct 26 12:30:34 2015 +0100
@@ -240,12 +240,14 @@
      **/
 
     const DicomValue* levelTmp = input.TestAndGetValue(DICOM_TAG_QUERY_RETRIEVE_LEVEL);
-    if (levelTmp == NULL) 
+    if (levelTmp == NULL ||
+        levelTmp->IsNull() ||
+        levelTmp->IsBinary())
     {
       throw OrthancException(ErrorCode_BadRequest);
     }
 
-    ResourceType level = StringToResourceType(levelTmp->AsString().c_str());
+    ResourceType level = StringToResourceType(levelTmp->GetContent().c_str());
 
     if (level != ResourceType_Patient &&
         level != ResourceType_Study &&
@@ -265,7 +267,7 @@
       {
         LOG(INFO) << "  " << query.GetElement(i).GetTag()
                   << "  " << FromDcmtkBridge::GetName(query.GetElement(i).GetTag())
-                  << " = " << query.GetElement(i).GetValue().AsString();
+                  << " = " << query.GetElement(i).GetValue().GetContent();
       }
     }
 
@@ -288,7 +290,7 @@
         continue;
       }
 
-      std::string value = query.GetElement(i).GetValue().AsString();
+      std::string value = query.GetElement(i).GetValue().GetContent();
       if (value.size() == 0)
       {
         // An empty string corresponds to a "*" wildcard constraint, so we ignore it
--- a/OrthancServer/OrthancMoveRequestHandler.cpp	Tue Oct 20 17:39:58 2015 +0200
+++ b/OrthancServer/OrthancMoveRequestHandler.cpp	Mon Oct 26 12:30:34 2015 +0100
@@ -138,10 +138,17 @@
       return false;
     }
 
-    std::string value = input.GetValue(tag).AsString();
+    const DicomValue& value = input.GetValue(tag);
+    if (value.IsNull() ||
+        value.IsBinary())
+    {
+      return false;
+    }
+
+    const std::string& content = value.GetContent();
 
     std::list<std::string> ids;
-    context_.GetIndex().LookupIdentifierExact(ids, level, tag, value);
+    context_.GetIndex().LookupIdentifierExact(ids, level, tag, content);
 
     if (ids.size() != 1)
     {
@@ -170,7 +177,7 @@
         {
           LOG(INFO) << "  " << query.GetElement(i).GetTag()
                     << "  " << FromDcmtkBridge::GetName(query.GetElement(i).GetTag())
-                    << " = " << query.GetElement(i).GetValue().AsString();
+                    << " = " << query.GetElement(i).GetValue().GetContent();
         }
       }
     }
@@ -183,7 +190,9 @@
 
     const DicomValue* levelTmp = input.TestAndGetValue(DICOM_TAG_QUERY_RETRIEVE_LEVEL);
 
-    if (levelTmp == NULL) 
+    if (levelTmp == NULL ||
+        levelTmp->IsNull() ||
+        levelTmp->IsBinary())
     {
       // The query level is not present in the C-Move request, which
       // does not follow the DICOM standard. This is for instance the
@@ -208,7 +217,7 @@
     }
 
     assert(levelTmp != NULL);
-    ResourceType level = StringToResourceType(levelTmp->AsString().c_str());      
+    ResourceType level = StringToResourceType(levelTmp->GetContent().c_str());      
 
 
     /**
--- a/OrthancServer/OrthancRestApi/OrthancRestApi.cpp	Tue Oct 20 17:39:58 2015 +0200
+++ b/OrthancServer/OrthancRestApi/OrthancRestApi.cpp	Mon Oct 26 12:30:34 2015 +0100
@@ -59,11 +59,20 @@
 
   void OrthancRestApi::ResetOrthanc(RestApiPostCall& call)
   {
+    OrthancRestApi::GetApi(call).leaveBarrier_ = true;
     OrthancRestApi::GetApi(call).resetRequestReceived_ = true;
     call.GetOutput().AnswerBuffer("{}", "application/json");
   }
 
 
+  void OrthancRestApi::ShutdownOrthanc(RestApiPostCall& call)
+  {
+    OrthancRestApi::GetApi(call).leaveBarrier_ = true;
+    call.GetOutput().AnswerBuffer("{}", "application/json");
+    LOG(WARNING) << "Shutdown request received";
+  }
+
+
 
 
 
@@ -99,6 +108,7 @@
 
   OrthancRestApi::OrthancRestApi(ServerContext& context) : 
     context_(context),
+    leaveBarrier_(false),
     resetRequestReceived_(false)
   {
     RegisterSystem();
@@ -114,6 +124,7 @@
     // Auto-generated directories
     Register("/tools", RestApi::AutoListChildren);
     Register("/tools/reset", ResetOrthanc);
+    Register("/tools/shutdown", ShutdownOrthanc);
     Register("/instances/{id}/frames/{frame}", RestApi::AutoListChildren);
   }
 
--- a/OrthancServer/OrthancRestApi/OrthancRestApi.h	Tue Oct 20 17:39:58 2015 +0200
+++ b/OrthancServer/OrthancRestApi/OrthancRestApi.h	Mon Oct 26 12:30:34 2015 +0100
@@ -49,6 +49,7 @@
 
   private:
     ServerContext& context_;
+    bool leaveBarrier_;
     bool resetRequestReceived_;
 
     void RegisterSystem();
@@ -65,10 +66,17 @@
 
     static void ResetOrthanc(RestApiPostCall& call);
 
+    static void ShutdownOrthanc(RestApiPostCall& call);
+
   public:
     OrthancRestApi(ServerContext& context);
 
-    const bool& ResetRequestReceivedFlag() const
+    const bool& LeaveBarrierFlag() const
+    {
+      return leaveBarrier_;
+    }
+
+    bool IsResetRequestReceived() const
     {
       return resetRequestReceived_;
     }
--- a/OrthancServer/OrthancRestApi/OrthancRestModalities.cpp	Tue Oct 20 17:39:58 2015 +0200
+++ b/OrthancServer/OrthancRestApi/OrthancRestModalities.cpp	Mon Oct 26 12:30:34 2015 +0100
@@ -198,8 +198,8 @@
       return;
     }
 
-    if (fields.GetValue(DICOM_TAG_ACCESSION_NUMBER).AsString().size() <= 2 &&
-        fields.GetValue(DICOM_TAG_PATIENT_ID).AsString().size() <= 2)
+    if (fields.GetValue(DICOM_TAG_ACCESSION_NUMBER).GetContent().size() <= 2 &&
+        fields.GetValue(DICOM_TAG_PATIENT_ID).GetContent().size() <= 2)
     {
       return;
     }        
@@ -228,9 +228,9 @@
       return;
     }
 
-    if ((fields.GetValue(DICOM_TAG_ACCESSION_NUMBER).AsString().size() <= 2 &&
-         fields.GetValue(DICOM_TAG_PATIENT_ID).AsString().size() <= 2) ||
-        fields.GetValue(DICOM_TAG_STUDY_INSTANCE_UID).AsString().size() <= 2)
+    if ((fields.GetValue(DICOM_TAG_ACCESSION_NUMBER).GetContent().size() <= 2 &&
+         fields.GetValue(DICOM_TAG_PATIENT_ID).GetContent().size() <= 2) ||
+        fields.GetValue(DICOM_TAG_STUDY_INSTANCE_UID).GetContent().size() <= 2)
     {
       return;
     }        
@@ -259,10 +259,10 @@
       return;
     }
 
-    if ((fields.GetValue(DICOM_TAG_ACCESSION_NUMBER).AsString().size() <= 2 &&
-         fields.GetValue(DICOM_TAG_PATIENT_ID).AsString().size() <= 2) ||
-        fields.GetValue(DICOM_TAG_STUDY_INSTANCE_UID).AsString().size() <= 2 ||
-        fields.GetValue(DICOM_TAG_SERIES_INSTANCE_UID).AsString().size() <= 2)
+    if ((fields.GetValue(DICOM_TAG_ACCESSION_NUMBER).GetContent().size() <= 2 &&
+         fields.GetValue(DICOM_TAG_PATIENT_ID).GetContent().size() <= 2) ||
+        fields.GetValue(DICOM_TAG_STUDY_INSTANCE_UID).GetContent().size() <= 2 ||
+        fields.GetValue(DICOM_TAG_SERIES_INSTANCE_UID).GetContent().size() <= 2)
     {
       return;
     }        
--- a/OrthancServer/ParsedDicomFile.cpp	Tue Oct 20 17:39:58 2015 +0200
+++ b/OrthancServer/ParsedDicomFile.cpp	Mon Oct 26 12:30:34 2015 +0100
@@ -90,8 +90,6 @@
 #include "../Core/Images/ImageBuffer.h"
 #include "../Core/Images/PngWriter.h"
 #include "../Core/Uuid.h"
-#include "../Core/DicomFormat/DicomString.h"
-#include "../Core/DicomFormat/DicomNullValue.h"
 #include "../Core/DicomFormat/DicomIntegerPixelAccessor.h"
 #include "../Core/Images/PngReader.h"
 
@@ -763,15 +761,18 @@
         return false;
       }
 
-      std::auto_ptr<DicomValue> v(FromDcmtkBridge::ConvertLeafElement(*element, GetEncoding()));
+      std::auto_ptr<DicomValue> v(FromDcmtkBridge::ConvertLeafElement
+                                  (*element, DicomToJsonFlags_Default, GetEncoding()));
       
-      if (v.get() == NULL)
+      if (v.get() == NULL ||
+          v->IsNull())
       {
         value = "";
       }
       else
       {
-        value = v->AsString();
+        // TODO v->IsBinary()
+        value = v->GetContent();
       }
       
       return true;
@@ -1120,9 +1121,10 @@
 
   void ParsedDicomFile::ToJson(Json::Value& target, 
                                DicomToJsonFormat format,
+                               DicomToJsonFlags flags,
                                unsigned int maxStringLength)
   {
-    FromDcmtkBridge::ToJson(target, *pimpl_->file_->getDataset(), format, maxStringLength);
+    FromDcmtkBridge::ToJson(target, *pimpl_->file_->getDataset(), format, flags, maxStringLength);
   }
 
 
--- a/OrthancServer/ParsedDicomFile.h	Tue Oct 20 17:39:58 2015 +0200
+++ b/OrthancServer/ParsedDicomFile.h	Mon Oct 26 12:30:34 2015 +0100
@@ -37,6 +37,7 @@
 #include "ServerEnumerations.h"
 #include "../Core/Images/ImageAccessor.h"
 #include "../Core/Images/ImageBuffer.h"
+#include "../Core/IDynamicObject.h"
 
 namespace Orthanc
 {
@@ -134,6 +135,7 @@
 
     void ToJson(Json::Value& target, 
                 DicomToJsonFormat format,
+                DicomToJsonFlags flags,
                 unsigned int maxStringLength);
 
     bool HasTag(const DicomTag& tag) const;
--- a/OrthancServer/ServerContext.cpp	Tue Oct 20 17:39:58 2015 +0200
+++ b/OrthancServer/ServerContext.cpp	Mon Oct 26 12:30:34 2015 +0100
@@ -520,6 +520,18 @@
     }
   }
 
+  OrthancPlugins& ServerContext::GetPlugins()
+  {
+    if (HasPlugins())
+    {
+      return *plugins_;
+    }
+    else
+    {
+      throw OrthancException(ErrorCode_InternalError);
+    }
+  }
+
 #endif
 
 
--- a/OrthancServer/ServerContext.h	Tue Oct 20 17:39:58 2015 +0200
+++ b/OrthancServer/ServerContext.h	Mon Oct 26 12:30:34 2015 +0100
@@ -256,6 +256,8 @@
     void ResetPlugins();
 
     const OrthancPlugins& GetPlugins() const;
+
+    OrthancPlugins& GetPlugins();
 #endif
 
     bool HasPlugins() const;
--- a/OrthancServer/ServerEnumerations.h	Tue Oct 20 17:39:58 2015 +0200
+++ b/OrthancServer/ServerEnumerations.h	Mon Oct 26 12:30:34 2015 +0100
@@ -108,6 +108,23 @@
     DicomToJsonFormat_Simple
   };
 
+  enum DicomToJsonFlags
+  {
+    DicomToJsonFlags_IncludeBinary         = (1 << 0),
+    DicomToJsonFlags_IncludePrivateTags    = (1 << 1),
+    DicomToJsonFlags_IncludeUnknownTags    = (1 << 2),
+    DicomToJsonFlags_IncludePixelData      = (1 << 3),
+    DicomToJsonFlags_ConvertBinaryToAscii  = (1 << 4),
+    DicomToJsonFlags_ConvertBinaryToNull   = (1 << 5),
+
+    // Some predefined combinations
+    DicomToJsonFlags_None     = 0,
+    DicomToJsonFlags_Default  = (DicomToJsonFlags_IncludePrivateTags | 
+                                 DicomToJsonFlags_IncludeUnknownTags | 
+                                 DicomToJsonFlags_IncludePixelData | 
+                                 DicomToJsonFlags_ConvertBinaryToNull)
+  };
+
 
   /**
    * WARNING: Do not change the explicit values in the enumerations
--- a/OrthancServer/ServerIndex.cpp	Tue Oct 20 17:39:58 2015 +0200
+++ b/OrthancServer/ServerIndex.cpp	Mon Oct 26 12:30:34 2015 +0100
@@ -394,8 +394,8 @@
           (value2 = dicomSummary.TestAndGetValue(DICOM_TAG_NUMBER_OF_TEMPORAL_POSITIONS)) != NULL)
       {
         // Patch for series with temporal positions thanks to Will Ryder
-        int64_t imagesInAcquisition = boost::lexical_cast<int64_t>(value->AsString());
-        int64_t countTemporalPositions = boost::lexical_cast<int64_t>(value2->AsString());
+        int64_t imagesInAcquisition = boost::lexical_cast<int64_t>(value->GetContent());
+        int64_t countTemporalPositions = boost::lexical_cast<int64_t>(value2->GetContent());
         std::string expected = boost::lexical_cast<std::string>(imagesInAcquisition * countTemporalPositions);
         db.SetMetadata(series, MetadataType_Series_ExpectedNumberOfInstances, expected);
       }
@@ -404,18 +404,21 @@
                (value2 = dicomSummary.TestAndGetValue(DICOM_TAG_NUMBER_OF_TIME_SLICES)) != NULL)
       {
         // Support of Cardio-PET images
-        int64_t numberOfSlices = boost::lexical_cast<int64_t>(value->AsString());
-        int64_t numberOfTimeSlices = boost::lexical_cast<int64_t>(value2->AsString());
+        int64_t numberOfSlices = boost::lexical_cast<int64_t>(value->GetContent());
+        int64_t numberOfTimeSlices = boost::lexical_cast<int64_t>(value2->GetContent());
         std::string expected = boost::lexical_cast<std::string>(numberOfSlices * numberOfTimeSlices);
         db.SetMetadata(series, MetadataType_Series_ExpectedNumberOfInstances, expected);
       }
 
       else if ((value = dicomSummary.TestAndGetValue(DICOM_TAG_CARDIAC_NUMBER_OF_IMAGES)) != NULL)
       {
-        db.SetMetadata(series, MetadataType_Series_ExpectedNumberOfInstances, value->AsString());
+        db.SetMetadata(series, MetadataType_Series_ExpectedNumberOfInstances, value->GetContent());
       }
     }
-    catch (boost::bad_lexical_cast)
+    catch (OrthancException&)
+    {
+    }
+    catch (boost::bad_lexical_cast&)
     {
     }
   }
@@ -768,8 +771,12 @@
       if ((value = dicomSummary.TestAndGetValue(DICOM_TAG_INSTANCE_NUMBER)) != NULL ||
           (value = dicomSummary.TestAndGetValue(DICOM_TAG_IMAGE_INDEX)) != NULL)
       {
-        db_.SetMetadata(instance, MetadataType_Instance_IndexInSeries, value->AsString());
-        instanceMetadata[MetadataType_Instance_IndexInSeries] = value->AsString();
+        if (!value->IsNull() && 
+            !value->IsBinary())
+        {
+          db_.SetMetadata(instance, MetadataType_Instance_IndexInSeries, value->GetContent());
+          instanceMetadata[MetadataType_Instance_IndexInSeries] = value->GetContent();
+        }
       }
 
       // Check whether the series of this new instance is now completed
@@ -1197,22 +1204,22 @@
       switch (currentType)
       {
         case ResourceType_Patient:
-          patientId = map.GetValue(DICOM_TAG_PATIENT_ID).AsString();
+          patientId = map.GetValue(DICOM_TAG_PATIENT_ID).GetContent();
           done = true;
           break;
 
         case ResourceType_Study:
-          studyInstanceUid = map.GetValue(DICOM_TAG_STUDY_INSTANCE_UID).AsString();
+          studyInstanceUid = map.GetValue(DICOM_TAG_STUDY_INSTANCE_UID).GetContent();
           currentType = ResourceType_Patient;
           break;
 
         case ResourceType_Series:
-          seriesInstanceUid = map.GetValue(DICOM_TAG_SERIES_INSTANCE_UID).AsString();
+          seriesInstanceUid = map.GetValue(DICOM_TAG_SERIES_INSTANCE_UID).GetContent();
           currentType = ResourceType_Study;
           break;
 
         case ResourceType_Instance:
-          sopInstanceUid = map.GetValue(DICOM_TAG_SOP_INSTANCE_UID).AsString();
+          sopInstanceUid = map.GetValue(DICOM_TAG_SOP_INSTANCE_UID).GetContent();
           currentType = ResourceType_Series;
           break;
 
--- a/OrthancServer/ServerIndexChange.h	Tue Oct 20 17:39:58 2015 +0200
+++ b/OrthancServer/ServerIndexChange.h	Mon Oct 26 12:30:34 2015 +0100
@@ -33,6 +33,7 @@
 #pragma once
 
 #include "ServerEnumerations.h"
+#include "../Core/IDynamicObject.h"
 #include "../Core/Toolbox.h"
 
 #include <string>
--- a/OrthancServer/ServerToolbox.cpp	Tue Oct 20 17:39:58 2015 +0200
+++ b/OrthancServer/ServerToolbox.cpp	Mon Oct 26 12:30:34 2015 +0100
@@ -91,6 +91,21 @@
     }
 
 
+    static std::string ValueAsString(const DicomMap& summary,
+                                     const DicomTag& tag)
+    {
+      const DicomValue& value = summary.GetValue(tag);
+      if (value.IsNull())
+      {
+        return "(null)";
+      }
+      else
+      {
+        return value.GetContent();
+      }
+    }
+
+
     void LogMissingRequiredTag(const DicomMap& summary)
     {
       std::string s, t;
@@ -99,7 +114,7 @@
       {
         if (t.size() > 0)
           t += ", ";
-        t += "PatientID=" + summary.GetValue(DICOM_TAG_PATIENT_ID).AsString();
+        t += "PatientID=" + ValueAsString(summary, DICOM_TAG_PATIENT_ID);
       }
       else
       {
@@ -112,7 +127,7 @@
       {
         if (t.size() > 0)
           t += ", ";
-        t += "StudyInstanceUID=" + summary.GetValue(DICOM_TAG_STUDY_INSTANCE_UID).AsString();
+        t += "StudyInstanceUID=" + ValueAsString(summary, DICOM_TAG_STUDY_INSTANCE_UID);
       }
       else
       {
@@ -125,7 +140,7 @@
       {
         if (t.size() > 0)
           t += ", ";
-        t += "SeriesInstanceUID=" + summary.GetValue(DICOM_TAG_SERIES_INSTANCE_UID).AsString();
+        t += "SeriesInstanceUID=" + ValueAsString(summary, DICOM_TAG_SERIES_INSTANCE_UID);
       }
       else
       {
@@ -138,7 +153,7 @@
       {
         if (t.size() > 0)
           t += ", ";
-        t += "SOPInstanceUID=" + summary.GetValue(DICOM_TAG_SOP_INSTANCE_UID).AsString();
+        t += "SOPInstanceUID=" + ValueAsString(summary, DICOM_TAG_SOP_INSTANCE_UID);
       }
       else
       {
@@ -168,7 +183,12 @@
       {
         const DicomElement& element = flattened.GetElement(i);
         const DicomTag& tag = element.GetTag();
-        database.SetMainDicomTag(resource, tag, element.GetValue().AsString());
+        const DicomValue& value = element.GetValue();
+        if (!value.IsNull() && 
+            !value.IsBinary())
+        {
+          database.SetMainDicomTag(resource, tag, element.GetValue().GetContent());
+        }
       }
     }
 
@@ -180,9 +200,10 @@
     {
       const DicomValue* value = tags.TestAndGetValue(tag);
       if (value != NULL &&
-          !value->IsNull())
+          !value->IsNull() &&
+          !value->IsBinary())
       {
-        std::string s = value->AsString();
+        std::string s = value->GetContent();
 
         if (tag != DICOM_TAG_PATIENT_ID &&
             tag != DICOM_TAG_STUDY_INSTANCE_UID &&
--- a/OrthancServer/SliceOrdering.cpp	Tue Oct 20 17:39:58 2015 +0200
+++ b/OrthancServer/SliceOrdering.cpp	Mon Oct 26 12:30:34 2015 +0100
@@ -82,13 +82,14 @@
     const DicomValue* value = map.TestAndGetValue(tag);
 
     if (value == NULL ||
-        value->IsNull())
+        value->IsNull() ||
+        value->IsBinary())
     {
       return false;
     }
     else
     {
-      return TokenizeVector(result, value->AsString(), expectedSize);
+      return TokenizeVector(result, value->GetContent(), expectedSize);
     }
   }
 
@@ -117,11 +118,12 @@
 
       const DicomValue* frames = instance.TestAndGetValue(DICOM_TAG_NUMBER_OF_FRAMES);
       if (frames != NULL &&
-          !frames->IsNull())
+          !frames->IsNull() &&
+          !frames->IsBinary())
       {
         try
         {
-          framesCount_ = boost::lexical_cast<unsigned int>(frames->AsString());
+          framesCount_ = boost::lexical_cast<unsigned int>(frames->GetContent());
         }
         catch (boost::bad_lexical_cast&)
         {
--- a/OrthancServer/ToDcmtkBridge.cpp	Tue Oct 20 17:39:58 2015 +0200
+++ b/OrthancServer/ToDcmtkBridge.cpp	Mon Oct 26 12:30:34 2015 +0100
@@ -52,8 +52,11 @@
     for (DicomMap::Map::const_iterator 
            it = map.map_.begin(); it != map.map_.end(); ++it)
     {
-      std::string s = it->second->AsString();
-      DU_putStringDOElement(result.get(), Convert(it->first), s.c_str());
+      if (!it->second->IsNull())
+      {
+        std::string s = it->second->GetContent();
+        DU_putStringDOElement(result.get(), Convert(it->first), s.c_str());
+      }
     }
 
     return result.release();
--- a/OrthancServer/main.cpp	Tue Oct 20 17:39:58 2015 +0200
+++ b/OrthancServer/main.cpp	Mon Oct 26 12:30:34 2015 +0100
@@ -387,7 +387,7 @@
 
 
 
-static void PrintHelp(char* path)
+static void PrintHelp(const char* path)
 {
   std::cout 
     << "Usage: " << path << " [OPTION]... [CONFIGURATION]" << std::endl
@@ -404,6 +404,7 @@
     << "  --logdir=[dir]\tdirectory where to store the log files" << std::endl
     << "\t\t\t(if not used, the logs are dumped to stderr)" << std::endl
     << "  --config=[file]\tcreate a sample configuration file and exit" << std::endl
+    << "  --errors\t\tprint the supported error codes and exit" << std::endl
     << "  --verbose\t\tbe verbose in logs" << std::endl
     << "  --trace\t\thighest verbosity in logs (for debug)" << std::endl
     << "  --upgrade\t\tallow Orthanc to upgrade the version of the" << std::endl
@@ -413,12 +414,16 @@
     << std::endl
     << "Exit status:" << std::endl
     << "   0 if success," << std::endl
+#if defined(_WIN32)
+    << "!= 0 if error (use the --errors option to get the list of possible errors)." << std::endl
+#else
     << "  -1 if error (have a look at the logs)." << std::endl
+#endif
     << std::endl;
 }
 
 
-static void PrintVersion(char* path)
+static void PrintVersion(const char* path)
 {
   std::cout
     << path << " " << ORTHANC_VERSION << std::endl
@@ -431,6 +436,124 @@
 }
 
 
+static void PrintErrorCode(ErrorCode code, const char* description)
+{
+  std::cout 
+    << std::right << std::setw(16) 
+    << static_cast<int>(code)
+    << "   " << description << std::endl;
+}
+
+
+static void PrintErrors(const char* path)
+{
+  std::cout
+    << path << " " << ORTHANC_VERSION << std::endl
+    << "Orthanc, lightweight, RESTful DICOM server for healthcare and medical research." 
+    << std::endl << std::endl
+    << "List of error codes that could be returned by Orthanc:" 
+    << std::endl << std::endl;
+
+  // The content of the following brackets is automatically generated
+  // by the "GenerateErrorCodes.py" script
+  {
+    PrintErrorCode(ErrorCode_InternalError, "Internal error");
+    PrintErrorCode(ErrorCode_Success, "Success");
+    PrintErrorCode(ErrorCode_Plugin, "Error encountered within the plugin engine");
+    PrintErrorCode(ErrorCode_NotImplemented, "Not implemented yet");
+    PrintErrorCode(ErrorCode_ParameterOutOfRange, "Parameter out of range");
+    PrintErrorCode(ErrorCode_NotEnoughMemory, "Not enough memory");
+    PrintErrorCode(ErrorCode_BadParameterType, "Bad type for a parameter");
+    PrintErrorCode(ErrorCode_BadSequenceOfCalls, "Bad sequence of calls");
+    PrintErrorCode(ErrorCode_InexistentItem, "Accessing an inexistent item");
+    PrintErrorCode(ErrorCode_BadRequest, "Bad request");
+    PrintErrorCode(ErrorCode_NetworkProtocol, "Error in the network protocol");
+    PrintErrorCode(ErrorCode_SystemCommand, "Error while calling a system command");
+    PrintErrorCode(ErrorCode_Database, "Error with the database engine");
+    PrintErrorCode(ErrorCode_UriSyntax, "Badly formatted URI");
+    PrintErrorCode(ErrorCode_InexistentFile, "Inexistent file");
+    PrintErrorCode(ErrorCode_CannotWriteFile, "Cannot write to file");
+    PrintErrorCode(ErrorCode_BadFileFormat, "Bad file format");
+    PrintErrorCode(ErrorCode_Timeout, "Timeout");
+    PrintErrorCode(ErrorCode_UnknownResource, "Unknown resource");
+    PrintErrorCode(ErrorCode_IncompatibleDatabaseVersion, "Incompatible version of the database");
+    PrintErrorCode(ErrorCode_FullStorage, "The file storage is full");
+    PrintErrorCode(ErrorCode_CorruptedFile, "Corrupted file (e.g. inconsistent MD5 hash)");
+    PrintErrorCode(ErrorCode_InexistentTag, "Inexistent tag");
+    PrintErrorCode(ErrorCode_ReadOnly, "Cannot modify a read-only data structure");
+    PrintErrorCode(ErrorCode_IncompatibleImageFormat, "Incompatible format of the images");
+    PrintErrorCode(ErrorCode_IncompatibleImageSize, "Incompatible size of the images");
+    PrintErrorCode(ErrorCode_SharedLibrary, "Error while using a shared library (plugin)");
+    PrintErrorCode(ErrorCode_UnknownPluginService, "Plugin invoking an unknown service");
+    PrintErrorCode(ErrorCode_UnknownDicomTag, "Unknown DICOM tag");
+    PrintErrorCode(ErrorCode_BadJson, "Cannot parse a JSON document");
+    PrintErrorCode(ErrorCode_Unauthorized, "Bad credentials were provided to an HTTP request");
+    PrintErrorCode(ErrorCode_BadFont, "Badly formatted font file");
+    PrintErrorCode(ErrorCode_DatabasePlugin, "The plugin implementing a custom database back-end does not fulfill the proper interface");
+    PrintErrorCode(ErrorCode_StorageAreaPlugin, "Error in the plugin implementing a custom storage area");
+    PrintErrorCode(ErrorCode_SQLiteNotOpened, "SQLite: The database is not opened");
+    PrintErrorCode(ErrorCode_SQLiteAlreadyOpened, "SQLite: Connection is already open");
+    PrintErrorCode(ErrorCode_SQLiteCannotOpen, "SQLite: Unable to open the database");
+    PrintErrorCode(ErrorCode_SQLiteStatementAlreadyUsed, "SQLite: This cached statement is already being referred to");
+    PrintErrorCode(ErrorCode_SQLiteExecute, "SQLite: Cannot execute a command");
+    PrintErrorCode(ErrorCode_SQLiteRollbackWithoutTransaction, "SQLite: Rolling back a nonexistent transaction (have you called Begin()?)");
+    PrintErrorCode(ErrorCode_SQLiteCommitWithoutTransaction, "SQLite: Committing a nonexistent transaction");
+    PrintErrorCode(ErrorCode_SQLiteRegisterFunction, "SQLite: Unable to register a function");
+    PrintErrorCode(ErrorCode_SQLiteFlush, "SQLite: Unable to flush the database");
+    PrintErrorCode(ErrorCode_SQLiteCannotRun, "SQLite: Cannot run a cached statement");
+    PrintErrorCode(ErrorCode_SQLiteCannotStep, "SQLite: Cannot step over a cached statement");
+    PrintErrorCode(ErrorCode_SQLiteBindOutOfRange, "SQLite: Bing a value while out of range (serious error)");
+    PrintErrorCode(ErrorCode_SQLitePrepareStatement, "SQLite: Cannot prepare a cached statement");
+    PrintErrorCode(ErrorCode_SQLiteTransactionAlreadyStarted, "SQLite: Beginning the same transaction twice");
+    PrintErrorCode(ErrorCode_SQLiteTransactionCommit, "SQLite: Failure when committing the transaction");
+    PrintErrorCode(ErrorCode_SQLiteTransactionBegin, "SQLite: Cannot start a transaction");
+    PrintErrorCode(ErrorCode_DirectoryOverFile, "The directory to be created is already occupied by a regular file");
+    PrintErrorCode(ErrorCode_FileStorageCannotWrite, "Unable to create a subdirectory or a file in the file storage");
+    PrintErrorCode(ErrorCode_DirectoryExpected, "The specified path does not point to a directory");
+    PrintErrorCode(ErrorCode_HttpPortInUse, "The TCP port of the HTTP server is already in use");
+    PrintErrorCode(ErrorCode_DicomPortInUse, "The TCP port of the DICOM server is already in use");
+    PrintErrorCode(ErrorCode_BadHttpStatusInRest, "This HTTP status is not allowed in a REST API");
+    PrintErrorCode(ErrorCode_RegularFileExpected, "The specified path does not point to a regular file");
+    PrintErrorCode(ErrorCode_PathToExecutable, "Unable to get the path to the executable");
+    PrintErrorCode(ErrorCode_MakeDirectory, "Cannot create a directory");
+    PrintErrorCode(ErrorCode_BadApplicationEntityTitle, "An application entity title (AET) cannot be empty or be longer than 16 characters");
+    PrintErrorCode(ErrorCode_NoCFindHandler, "No request handler factory for DICOM C-FIND SCP");
+    PrintErrorCode(ErrorCode_NoCMoveHandler, "No request handler factory for DICOM C-MOVE SCP");
+    PrintErrorCode(ErrorCode_NoCStoreHandler, "No request handler factory for DICOM C-STORE SCP");
+    PrintErrorCode(ErrorCode_NoApplicationEntityFilter, "No application entity filter");
+    PrintErrorCode(ErrorCode_NoSopClassOrInstance, "DicomUserConnection: Unable to find the SOP class and instance");
+    PrintErrorCode(ErrorCode_NoPresentationContext, "DicomUserConnection: No acceptable presentation context for modality");
+    PrintErrorCode(ErrorCode_DicomFindUnavailable, "DicomUserConnection: The C-FIND command is not supported by the remote SCP");
+    PrintErrorCode(ErrorCode_DicomMoveUnavailable, "DicomUserConnection: The C-MOVE command is not supported by the remote SCP");
+    PrintErrorCode(ErrorCode_CannotStoreInstance, "Cannot store an instance");
+    PrintErrorCode(ErrorCode_CreateDicomNotString, "Only string values are supported when creating DICOM instances");
+    PrintErrorCode(ErrorCode_CreateDicomOverrideTag, "Trying to override a value inherited from a parent module");
+    PrintErrorCode(ErrorCode_CreateDicomUseContent, "Use \"Content\" to inject an image into a new DICOM instance");
+    PrintErrorCode(ErrorCode_CreateDicomNoPayload, "No payload is present for one instance in the series");
+    PrintErrorCode(ErrorCode_CreateDicomUseDataUriScheme, "The payload of the DICOM instance must be specified according to Data URI scheme");
+    PrintErrorCode(ErrorCode_CreateDicomBadParent, "Trying to attach a new DICOM instance to an inexistent resource");
+    PrintErrorCode(ErrorCode_CreateDicomParentIsInstance, "Trying to attach a new DICOM instance to an instance (must be a series, study or patient)");
+    PrintErrorCode(ErrorCode_CreateDicomParentEncoding, "Unable to get the encoding of the parent resource");
+    PrintErrorCode(ErrorCode_UnknownModality, "Unknown modality");
+    PrintErrorCode(ErrorCode_BadJobOrdering, "Bad ordering of filters in a job");
+    PrintErrorCode(ErrorCode_JsonToLuaTable, "Cannot convert the given JSON object to a Lua table");
+    PrintErrorCode(ErrorCode_CannotCreateLua, "Cannot create the Lua context");
+    PrintErrorCode(ErrorCode_CannotExecuteLua, "Cannot execute a Lua command");
+    PrintErrorCode(ErrorCode_LuaAlreadyExecuted, "Arguments cannot be pushed after the Lua function is executed");
+    PrintErrorCode(ErrorCode_LuaBadOutput, "The Lua function does not give the expected number of outputs");
+    PrintErrorCode(ErrorCode_NotLuaPredicate, "The Lua function is not a predicate (only true/false outputs allowed)");
+    PrintErrorCode(ErrorCode_LuaReturnsNoString, "The Lua function does not return a string");
+    PrintErrorCode(ErrorCode_StorageAreaAlreadyRegistered, "Another plugin has already registered a custom storage area");
+    PrintErrorCode(ErrorCode_DatabaseBackendAlreadyRegistered, "Another plugin has already registered a custom database back-end");
+    PrintErrorCode(ErrorCode_DatabaseNotInitialized, "Plugin trying to call the database during its initialization");
+    PrintErrorCode(ErrorCode_SslDisabled, "Orthanc has been built without SSL support");
+    PrintErrorCode(ErrorCode_CannotOrderSlices, "Unable to order the slices of the series");
+  }
+
+  std::cout << std::endl;
+}
+
+
 
 static void LoadLuaScripts(ServerContext& context)
 {
@@ -474,13 +597,27 @@
 {
   LOG(WARNING) << "Orthanc has started";
 
+#if ORTHANC_PLUGINS_ENABLED == 1
+  if (context.HasPlugins())
+  {
+    context.GetPlugins().SignalOrthancStarted();
+  }
+#endif
+
   context.GetLua().Execute("Initialize");
 
-  Toolbox::ServerBarrier(restApi.ResetRequestReceivedFlag());
-  bool restart = restApi.ResetRequestReceivedFlag();
+  Toolbox::ServerBarrier(restApi.LeaveBarrierFlag());
+  bool restart = restApi.IsResetRequestReceived();
 
   context.GetLua().Execute("Finalize");
 
+#if ORTHANC_PLUGINS_ENABLED == 1
+  if (context.HasPlugins())
+  {
+    context.GetPlugins().SignalOrthancStopped();
+  }
+#endif
+
   if (restart)
   {
     LOG(WARNING) << "Reset request received, restarting Orthanc";
@@ -853,6 +990,11 @@
         configurationFile = argv[i];
       }
     }
+    else if (argument == "--errors")
+    {
+      PrintErrors(argv[0]);
+      return 0;
+    }
     else if (argument == "--help")
     {
       PrintHelp(argv[0]);
@@ -937,8 +1079,20 @@
   }
   catch (const OrthancException& e)
   {
-    LOG(ERROR) << "Uncaught exception, stopping now: [" << e.What() << "]";
+    LOG(ERROR) << "Uncaught exception, stopping now: [" << e.What() << "] (code " << e.GetErrorCode() << ")";
+#if defined(_WIN32)
+    if (e.GetErrorCode() >= ErrorCode_START_PLUGINS)
+    {
+      status = static_cast<int>(ErrorCode_Plugin);
+    }
+    else
+    {
+      status = static_cast<int>(e.GetErrorCode());
+    }
+
+#else
     status = -1;
+#endif
   }
   catch (const std::exception& e) 
   {
--- a/Plugins/Engine/OrthancPlugins.cpp	Tue Oct 20 17:39:58 2015 +0200
+++ b/Plugins/Engine/OrthancPlugins.cpp	Mon Oct 26 12:30:34 2015 +0100
@@ -299,7 +299,15 @@
         sizeof(int32_t) != sizeof(OrthancPluginImageFormat) ||
         sizeof(int32_t) != sizeof(OrthancPluginCompressionType) ||
         sizeof(int32_t) != sizeof(OrthancPluginValueRepresentation) ||
-        sizeof(int32_t) != sizeof(_OrthancPluginDatabaseAnswerType))
+        sizeof(int32_t) != sizeof(OrthancPluginDicomToJsonFlags) ||
+        sizeof(int32_t) != sizeof(OrthancPluginDicomToJsonFormat) ||
+        sizeof(int32_t) != sizeof(_OrthancPluginDatabaseAnswerType) ||
+        static_cast<int>(OrthancPluginDicomToJsonFlags_IncludeBinary) != static_cast<int>(DicomToJsonFlags_IncludeBinary) ||
+        static_cast<int>(OrthancPluginDicomToJsonFlags_IncludePrivateTags) != static_cast<int>(DicomToJsonFlags_IncludePrivateTags) ||
+        static_cast<int>(OrthancPluginDicomToJsonFlags_IncludeUnknownTags) != static_cast<int>(DicomToJsonFlags_IncludeUnknownTags) ||
+        static_cast<int>(OrthancPluginDicomToJsonFlags_IncludePixelData) != static_cast<int>(DicomToJsonFlags_IncludePixelData) ||
+        static_cast<int>(OrthancPluginDicomToJsonFlags_ConvertBinaryToNull) != static_cast<int>(DicomToJsonFlags_ConvertBinaryToNull) ||
+        static_cast<int>(OrthancPluginDicomToJsonFlags_ConvertBinaryToAscii) != static_cast<int>(DicomToJsonFlags_ConvertBinaryToAscii))
     {
       /* Sanity check of the compiler */
       throw OrthancException(ErrorCode_Plugin);
@@ -506,7 +514,9 @@
 
 
 
-  void OrthancPlugins::SignalChange(const ServerIndexChange& change)
+  void OrthancPlugins::SignalChangeInternal(OrthancPluginChangeType changeType,
+                                            OrthancPluginResourceType resourceType,
+                                            const char* resource)
   {
     boost::recursive_mutex::scoped_lock lock(pimpl_->changeCallbackMutex_);
 
@@ -514,10 +524,7 @@
            callback = pimpl_->onChangeCallbacks_.begin(); 
          callback != pimpl_->onChangeCallbacks_.end(); ++callback)
     {
-      OrthancPluginErrorCode error = (*callback)
-        (Plugins::Convert(change.GetChangeType()),
-         Plugins::Convert(change.GetResourceType()),
-         change.GetPublicId().c_str());
+      OrthancPluginErrorCode error = (*callback) (changeType, resourceType, resource);
 
       if (error != OrthancPluginErrorCode_Success)
       {
@@ -529,6 +536,15 @@
 
 
 
+  void OrthancPlugins::SignalChange(const ServerIndexChange& change)
+  {
+    SignalChangeInternal(Plugins::Convert(change.GetChangeType()),
+                         Plugins::Convert(change.GetResourceType()),
+                         change.GetPublicId().c_str());
+  }
+
+
+
   static void CopyToMemoryBuffer(OrthancPluginMemoryBuffer& target,
                                  const void* data,
                                  size_t size)
@@ -1229,6 +1245,39 @@
 
     font.Draw(target, p.utf8Text, p.x, p.y, p.r, p.g, p.b);
   }
+
+
+  void OrthancPlugins::ApplyDicomToJson(_OrthancPluginService service,
+                                        const void* parameters)
+  {
+    const _OrthancPluginDicomToJson& p =
+      *reinterpret_cast<const _OrthancPluginDicomToJson*>(parameters);
+
+    std::auto_ptr<ParsedDicomFile> dicom;
+
+    if (service == _OrthancPluginService_DicomBufferToJson)
+    {
+      dicom.reset(new ParsedDicomFile(p.buffer, p.size));
+    }
+    else
+    {
+      if (p.instanceId == NULL)
+      {
+        throw OrthancException(ErrorCode_ParameterOutOfRange);
+      }
+
+      std::string content;
+      pimpl_->context_->ReadFile(content, p.instanceId, FileContentType_Dicom);
+      dicom.reset(new ParsedDicomFile(content));
+    }
+
+    Json::Value json;
+    dicom->ToJson(json, Plugins::Convert(p.format), 
+                  static_cast<DicomToJsonFlags>(p.flags), p.maxStringLength);
+
+    Json::FastWriter writer;
+    *p.result = CopyString(writer.write(json));
+  }
         
 
   bool OrthancPlugins::InvokeService(SharedLibrary& plugin,
@@ -1726,6 +1775,11 @@
         return true;
       }
 
+      case _OrthancPluginService_DicomBufferToJson:
+      case _OrthancPluginService_DicomInstanceToJson:
+        ApplyDicomToJson(service, parameters);
+        return true;
+
       default:
       {
         // This service is unknown to the Orthanc plugin engine
--- a/Plugins/Engine/OrthancPlugins.h	Tue Oct 20 17:39:58 2015 +0200
+++ b/Plugins/Engine/OrthancPlugins.h	Mon Oct 26 12:30:34 2015 +0100
@@ -127,6 +127,13 @@
 
     void DrawText(const void* parameters);
 
+    void ApplyDicomToJson(_OrthancPluginService service,
+                          const void* parameters);
+
+    void SignalChangeInternal(OrthancPluginChangeType changeType,
+                              OrthancPluginResourceType resourceType,
+                              const char* resource);
+
   public:
     OrthancPlugins();
 
@@ -183,6 +190,16 @@
     const PluginsManager& GetManager() const;
 
     PluginsErrorDictionary& GetErrorDictionary();
+
+    void SignalOrthancStarted()
+    {
+      SignalChangeInternal(OrthancPluginChangeType_OrthancStarted, OrthancPluginResourceType_None, NULL);
+    }
+
+    void SignalOrthancStopped()
+    {
+      SignalChangeInternal(OrthancPluginChangeType_OrthancStopped, OrthancPluginResourceType_None, NULL);
+    }
   };
 }
 
--- a/Plugins/Engine/PluginsEnumerations.cpp	Tue Oct 20 17:39:58 2015 +0200
+++ b/Plugins/Engine/PluginsEnumerations.cpp	Mon Oct 26 12:30:34 2015 +0100
@@ -210,6 +210,25 @@
     }
 
 
+    DicomToJsonFormat Convert(OrthancPluginDicomToJsonFormat format)
+    {
+      switch (format)
+      {
+        case OrthancPluginDicomToJsonFormat_Full:
+          return DicomToJsonFormat_Full;
+
+        case OrthancPluginDicomToJsonFormat_Short:
+          return DicomToJsonFormat_Short;
+
+        case OrthancPluginDicomToJsonFormat_Simple:
+          return DicomToJsonFormat_Simple;
+
+        default:
+          throw OrthancException(ErrorCode_ParameterOutOfRange);
+      }
+    }
+
+
 #if !defined(ORTHANC_ENABLE_DCMTK) || ORTHANC_ENABLE_DCMTK != 0
     DcmEVR Convert(OrthancPluginValueRepresentation vr)
     {
--- a/Plugins/Engine/PluginsEnumerations.h	Tue Oct 20 17:39:58 2015 +0200
+++ b/Plugins/Engine/PluginsEnumerations.h	Mon Oct 26 12:30:34 2015 +0100
@@ -59,6 +59,8 @@
 
     FileContentType Convert(OrthancPluginContentType type);
 
+    DicomToJsonFormat Convert(OrthancPluginDicomToJsonFormat format);
+
 #if !defined(ORTHANC_ENABLE_DCMTK) || ORTHANC_ENABLE_DCMTK != 0
     DcmEVR Convert(OrthancPluginValueRepresentation vr);
 #endif
--- a/Plugins/Include/orthanc/OrthancCPlugin.h	Tue Oct 20 17:39:58 2015 +0200
+++ b/Plugins/Include/orthanc/OrthancCPlugin.h	Mon Oct 26 12:30:34 2015 +0100
@@ -387,6 +387,8 @@
     _OrthancPluginService_CallHttpClient = 18,
     _OrthancPluginService_RegisterErrorCode = 19,
     _OrthancPluginService_RegisterDictionaryTag = 20,
+    _OrthancPluginService_DicomBufferToJson = 21,
+    _OrthancPluginService_DicomInstanceToJson = 22,
 
     /* Registration of callbacks */
     _OrthancPluginService_RegisterRestCallback = 1000,
@@ -548,6 +550,7 @@
     OrthancPluginResourceType_Study = 1,       /*!< Study */
     OrthancPluginResourceType_Series = 2,      /*!< Series */
     OrthancPluginResourceType_Instance = 3,    /*!< Instance */
+    OrthancPluginResourceType_None = 4,        /*!< Unavailable resource type */
 
     _OrthancPluginResourceType_INTERNAL = 0x7fffffff
   } OrthancPluginResourceType;
@@ -570,6 +573,8 @@
     OrthancPluginChangeType_StablePatient = 7,      /*!< Timeout: No new instance in this patient */
     OrthancPluginChangeType_StableSeries = 8,       /*!< Timeout: No new instance in this series */
     OrthancPluginChangeType_StableStudy = 9,        /*!< Timeout: No new instance in this study */
+    OrthancPluginChangeType_OrthancStarted = 10,    /*!< Orthanc has started */
+    OrthancPluginChangeType_OrthancStopped = 11,    /*!< Orthanc is stopping */
 
     _OrthancPluginChangeType_INTERNAL = 0x7fffffff
   } OrthancPluginChangeType;
@@ -642,6 +647,38 @@
 
 
   /**
+   * The possible output formats for a DICOM-to-JSON conversion.
+   * @ingroup Toolbox
+   **/
+  typedef enum
+  {
+    OrthancPluginDicomToJsonFormat_Full = 1,    /*!< Full output, with most details */
+    OrthancPluginDicomToJsonFormat_Short = 2,   /*!< Tags output as hexadecimal numbers */
+    OrthancPluginDicomToJsonFormat_Simple = 3,  /*!< Human-readable JSON */
+
+    _OrthancPluginDicomToJsonFormat_INTERNAL = 0x7fffffff
+  } OrthancPluginDicomToJsonFormat;
+
+
+  /**
+   * Flags to customize a DICOM-to-JSON conversion. By default, binary
+   * tags are formatted using Data URI scheme.
+   * @ingroup Toolbox
+   **/
+  typedef enum
+  {
+    OrthancPluginDicomToJsonFlags_IncludeBinary         = (1 << 0),  /*!< Include the binary tags */
+    OrthancPluginDicomToJsonFlags_IncludePrivateTags    = (1 << 1),  /*!< Include the private tags */
+    OrthancPluginDicomToJsonFlags_IncludeUnknownTags    = (1 << 2),  /*!< Include the tags unknown by the dictionary */
+    OrthancPluginDicomToJsonFlags_IncludePixelData      = (1 << 3),  /*!< Include the pixel data */
+    OrthancPluginDicomToJsonFlags_ConvertBinaryToAscii  = (1 << 4),  /*!< Output binary tags as-is, dropping non-ASCII */
+    OrthancPluginDicomToJsonFlags_ConvertBinaryToNull   = (1 << 5),  /*!< Signal binary tags as null values */
+
+    _OrthancPluginDicomToJsonFlags_INTERNAL = 0x7fffffff
+  } OrthancPluginDicomToJsonFlags;
+
+
+  /**
    * @brief A memory buffer allocated by the core system of Orthanc.
    *
    * A memory buffer allocated by the core system of Orthanc. When the
@@ -852,7 +889,9 @@
         sizeof(int32_t) != sizeof(OrthancPluginChangeType) ||
         sizeof(int32_t) != sizeof(OrthancPluginCompressionType) ||
         sizeof(int32_t) != sizeof(OrthancPluginImageFormat) ||
-        sizeof(int32_t) != sizeof(OrthancPluginValueRepresentation))
+        sizeof(int32_t) != sizeof(OrthancPluginValueRepresentation) ||
+        sizeof(int32_t) != sizeof(OrthancPluginDicomToJsonFormat) ||
+        sizeof(int32_t) != sizeof(OrthancPluginDicomToJsonFlags))
     {
       /* Mismatch in the size of the enumerations */
       return 0;
@@ -3806,6 +3845,115 @@
   }
 
 
+  typedef struct
+  {
+    char**                          result;
+    const char*                     instanceId;
+    const char*                     buffer;
+    uint32_t                        size;
+    OrthancPluginDicomToJsonFormat  format;
+    OrthancPluginDicomToJsonFlags   flags;
+    uint32_t                        maxStringLength;
+  } _OrthancPluginDicomToJson;
+
+
+  /**
+   * @brief Format a DICOM memory buffer as a JSON string.
+   *
+   * This function takes as input a memory buffer containing a DICOM
+   * file, and outputs a JSON string representing the tags of this
+   * DICOM file.
+   *
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param buffer The memory buffer containing the DICOM file.
+   * @param size The size of the memory buffer.
+   * @param format The output format.
+   * @param flags The output flags.
+   * @param maxStringLength The maximum length of a field. Too long fields will
+   * be output as "null". The 0 value means no maximum length.
+   * @return The NULL value if the case of an error, or the JSON
+   * string. This string must be freed by OrthancPluginFreeString().
+   * @ingroup Toolbox
+   * @see OrthancPluginDicomInstanceToJson
+   **/
+  ORTHANC_PLUGIN_INLINE char* OrthancPluginDicomBufferToJson(
+    OrthancPluginContext*           context,
+    const char*                     buffer,
+    uint32_t                        size,
+    OrthancPluginDicomToJsonFormat  format,
+    OrthancPluginDicomToJsonFlags   flags, 
+    uint32_t                        maxStringLength)
+  {
+    char* result;
+
+    _OrthancPluginDicomToJson params;
+    memset(&params, 0, sizeof(params));
+    params.result = &result;
+    params.buffer = buffer;
+    params.size = size;
+    params.format = format;
+    params.flags = flags;
+    params.maxStringLength = maxStringLength;
+
+    if (context->InvokeService(context, _OrthancPluginService_DicomBufferToJson, &params) != OrthancPluginErrorCode_Success)
+    {
+      /* Error */
+      return NULL;
+    }
+    else
+    {
+      return result;
+    }
+  }
+
+
+  /**
+   * @brief Format a DICOM instance as a JSON string.
+   *
+   * This function formats a DICOM instance that is stored in Orthanc,
+   * and outputs a JSON string representing the tags of this DICOM
+   * instance.
+   *
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param instanceId The Orthanc identifier of the instance.
+   * @param format The output format.
+   * @param flags The output flags.
+   * @param maxStringLength The maximum length of a field. Too long fields will
+   * be output as "null". The 0 value means no maximum length.
+   * @return The NULL value if the case of an error, or the JSON
+   * string. This string must be freed by OrthancPluginFreeString().
+   * @ingroup Toolbox
+   * @see OrthancPluginDicomInstanceToJson
+   **/
+  ORTHANC_PLUGIN_INLINE char* OrthancPluginDicomInstanceToJson(
+    OrthancPluginContext*           context,
+    const char*                     instanceId,
+    OrthancPluginDicomToJsonFormat  format,
+    OrthancPluginDicomToJsonFlags   flags, 
+    uint32_t                        maxStringLength)
+  {
+    char* result;
+
+    _OrthancPluginDicomToJson params;
+    memset(&params, 0, sizeof(params));
+    params.result = &result;
+    params.instanceId = instanceId;
+    params.format = format;
+    params.flags = flags;
+    params.maxStringLength = maxStringLength;
+
+    if (context->InvokeService(context, _OrthancPluginService_DicomInstanceToJson, &params) != OrthancPluginErrorCode_Success)
+    {
+      /* Error */
+      return NULL;
+    }
+    else
+    {
+      return result;
+    }
+  }
+
+
 #ifdef  __cplusplus
 }
 #endif
--- a/Resources/GenerateErrorCodes.py	Tue Oct 20 17:39:58 2015 +0200
+++ b/Resources/GenerateErrorCodes.py	Mon Oct 26 12:30:34 2015 +0100
@@ -141,3 +141,19 @@
 
 with open(path, 'w') as f:
     f.write(a)
+
+
+
+##
+## Generate the "PrintErrors" function in "main.cpp"
+##
+
+path = os.path.join(BASE, 'OrthancServer', 'main.cpp')
+with open(path, 'r') as f:
+    a = f.read()
+
+s = '\n'.join(map(lambda x: '    PrintErrorCode(ErrorCode_%s, "%s");' % (x['Name'], x['Description']), ERRORS))
+a = re.sub('(static void PrintErrors[^{}]*?{[^{}]*?{)([^}]*?)}', r'\1\n%s\n  }' % s, a, re.DOTALL)
+
+with open(path, 'w') as f:
+    f.write(a)
--- a/UnitTestsSources/DicomMapTests.cpp	Tue Oct 20 17:39:58 2015 +0200
+++ b/UnitTestsSources/DicomMapTests.cpp	Mon Oct 26 12:30:34 2015 +0100
@@ -36,7 +36,6 @@
 #include "../Core/Uuid.h"
 #include "../Core/OrthancException.h"
 #include "../Core/DicomFormat/DicomMap.h"
-#include "../Core/DicomFormat/DicomNullValue.h"
 #include "../OrthancServer/FromDcmtkBridge.h"
 
 #include <memory>
@@ -103,7 +102,7 @@
   m.SetValue(DICOM_TAG_PATIENT_ID, "PatientID");
   ASSERT_TRUE(m.HasTag(0x0010, 0x0020));
   m.SetValue(DICOM_TAG_PATIENT_ID, "PatientID2");
-  ASSERT_EQ("PatientID2", m.GetValue(0x0010, 0x0020).AsString());
+  ASSERT_EQ("PatientID2", m.GetValue(0x0010, 0x0020).GetContent());
 
   m.GetTags(s);
   ASSERT_EQ(2u, s.size());
@@ -116,14 +115,14 @@
   ASSERT_EQ(DICOM_TAG_PATIENT_NAME, *s.begin());
 
   std::auto_ptr<DicomMap> mm(m.Clone());
-  ASSERT_EQ("PatientName", mm->GetValue(DICOM_TAG_PATIENT_NAME).AsString());  
+  ASSERT_EQ("PatientName", mm->GetValue(DICOM_TAG_PATIENT_NAME).GetContent());  
 
   m.SetValue(DICOM_TAG_PATIENT_ID, "Hello");
   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).AsString());  
+  ASSERT_EQ("Hello", mm->GetValue(DICOM_TAG_PATIENT_ID).GetContent());  
 
-  DicomNullValue v;
+  DicomValue v;
   ASSERT_TRUE(v.IsNull());
 }
 
--- a/UnitTestsSources/FromDcmtkTests.cpp	Tue Oct 20 17:39:58 2015 +0200
+++ b/UnitTestsSources/FromDcmtkTests.cpp	Mon Oct 26 12:30:34 2015 +0100
@@ -339,7 +339,7 @@
     element.reset(FromDcmtkBridge::FromJson(DICOM_TAG_PATIENT_NAME, a, false, Encoding_Utf8));
 
     Json::Value b;
-    FromDcmtkBridge::ToJson(b, *element, DicomToJsonFormat_Short, 0, Encoding_Ascii);
+    FromDcmtkBridge::ToJson(b, *element, DicomToJsonFormat_Short, DicomToJsonFlags_Default, 0, Encoding_Ascii);
     ASSERT_EQ("Hello", b["0010,0010"].asString());
   }
 
@@ -363,7 +363,7 @@
     element.reset(FromDcmtkBridge::FromJson(DICOM_TAG_PATIENT_NAME, a, true, Encoding_Utf8));
 
     Json::Value b;
-    FromDcmtkBridge::ToJson(b, *element, DicomToJsonFormat_Short, 0, Encoding_Ascii);
+    FromDcmtkBridge::ToJson(b, *element, DicomToJsonFormat_Short, DicomToJsonFlags_Default, 0, Encoding_Ascii);
     ASSERT_EQ("Hello", b["0010,0010"].asString());
   }
 
@@ -374,7 +374,7 @@
 
     {
       Json::Value b;
-      FromDcmtkBridge::ToJson(b, *element, DicomToJsonFormat_Short, 0, Encoding_Ascii);
+      FromDcmtkBridge::ToJson(b, *element, DicomToJsonFormat_Short, DicomToJsonFlags_Default, 0, Encoding_Ascii);
       ASSERT_EQ(Json::arrayValue, b["0008,1110"].type());
       ASSERT_EQ(2, b["0008,1110"].size());
       
@@ -391,7 +391,7 @@
 
     {
       Json::Value b;
-      FromDcmtkBridge::ToJson(b, *element, DicomToJsonFormat_Full, 0, Encoding_Ascii);
+      FromDcmtkBridge::ToJson(b, *element, DicomToJsonFormat_Full, DicomToJsonFlags_Default, 0, Encoding_Ascii);
 
       Json::Value c;
       Toolbox::SimplifyTags(c, b);
@@ -470,7 +470,7 @@
 
   {
     Json::Value b;
-    f.ToJson(b, DicomToJsonFormat_Full, 0);
+    f.ToJson(b, DicomToJsonFormat_Full, DicomToJsonFlags_Default, 0);
 
     Json::Value c;
     Toolbox::SimplifyTags(c, b);
@@ -515,8 +515,94 @@
       f.Replace(DICOM_TAG_PATIENT_NAME, s, false);
 
       Json::Value v;
-      f.ToJson(v, DicomToJsonFormat_Simple, 0);
+      f.ToJson(v, DicomToJsonFormat_Simple, DicomToJsonFlags_Default, 0);
       ASSERT_EQ(v["PatientName"].asString(), std::string(testEncodingsExpected[i]));
     }
   }
 }
+
+
+TEST(ParsedDicomFile, ToJsonFlags1)
+{
+  FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7053, 0x1000), EVR_PN, "MyPrivateTag", 1, 1);
+  FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7050, 0x1000), EVR_PN, "Declared public tag", 1, 1);
+
+  ParsedDicomFile f;
+  f.Insert(DicomTag(0x7050, 0x1000), "Some public tag", false);  // Even group => public tag
+  f.Insert(DicomTag(0x7052, 0x1000), "Some unknown tag", false);  // Even group => public, unknown tag
+  f.Insert(DicomTag(0x7053, 0x1000), "Some private tag", false);  // Odd group => private tag
+
+  Json::Value v;
+  f.ToJson(v, DicomToJsonFormat_Short, DicomToJsonFlags_None, 0);
+  ASSERT_EQ(Json::objectValue, v.type());
+  ASSERT_EQ(6, v.getMemberNames().size());
+  ASSERT_FALSE(v.isMember("7052,1000"));
+  ASSERT_FALSE(v.isMember("7053,1000"));
+  ASSERT_TRUE(v.isMember("7050,1000"));
+  ASSERT_EQ(Json::stringValue, v["7050,1000"].type());
+  ASSERT_EQ("Some public tag", v["7050,1000"].asString());
+
+  f.ToJson(v, DicomToJsonFormat_Short, DicomToJsonFlags_IncludePrivateTags, 0);
+  ASSERT_EQ(Json::objectValue, v.type());
+  ASSERT_EQ(7, v.getMemberNames().size());
+  ASSERT_FALSE(v.isMember("7052,1000"));
+  ASSERT_TRUE(v.isMember("7050,1000"));
+  ASSERT_TRUE(v.isMember("7053,1000"));
+  ASSERT_EQ("Some public tag", v["7050,1000"].asString());
+  ASSERT_EQ(Json::nullValue, v["7053,1000"].type());  // TODO SHOULD BE STRING
+
+  f.ToJson(v, DicomToJsonFormat_Short, DicomToJsonFlags_IncludeUnknownTags, 0);
+  ASSERT_EQ(Json::objectValue, v.type());
+  ASSERT_EQ(7, v.getMemberNames().size());
+  ASSERT_TRUE(v.isMember("7050,1000"));
+  ASSERT_TRUE(v.isMember("7052,1000"));
+  ASSERT_FALSE(v.isMember("7053,1000"));
+  ASSERT_EQ("Some public tag", v["7050,1000"].asString());
+  ASSERT_EQ(Json::nullValue, v["7052,1000"].type());  // TODO SHOULD BE STRING
+
+  f.ToJson(v, DicomToJsonFormat_Short, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludeUnknownTags | DicomToJsonFlags_IncludePrivateTags), 0);
+  ASSERT_EQ(Json::objectValue, v.type());
+  ASSERT_EQ(8, v.getMemberNames().size());
+  ASSERT_TRUE(v.isMember("7050,1000"));
+  ASSERT_TRUE(v.isMember("7052,1000"));
+  ASSERT_TRUE(v.isMember("7053,1000"));
+  ASSERT_EQ("Some public tag", v["7050,1000"].asString());
+  ASSERT_EQ(Json::nullValue, v["7052,1000"].type());  // TODO SHOULD BE STRING
+  ASSERT_EQ(Json::nullValue, v["7053,1000"].type());  // TODO SHOULD BE STRING
+}
+
+
+TEST(ParsedDicomFile, ToJsonFlags2)
+{
+  ParsedDicomFile f;
+  f.Insert(DICOM_TAG_PIXEL_DATA, "Pixels", false);
+
+  Json::Value v;
+  f.ToJson(v, DicomToJsonFormat_Short, DicomToJsonFlags_None, 0);
+  ASSERT_EQ(Json::objectValue, v.type());
+  ASSERT_EQ(5, v.getMemberNames().size());
+  ASSERT_FALSE(v.isMember("7fe0,0010"));  
+
+  f.ToJson(v, DicomToJsonFormat_Short, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludePixelData | DicomToJsonFlags_ConvertBinaryToNull), 0);
+  ASSERT_EQ(Json::objectValue, v.type());
+  ASSERT_EQ(6, v.getMemberNames().size());
+  ASSERT_TRUE(v.isMember("7fe0,0010"));  
+  ASSERT_EQ(Json::nullValue, v["7fe0,0010"].type());  
+
+  f.ToJson(v, DicomToJsonFormat_Short, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludePixelData | DicomToJsonFlags_ConvertBinaryToAscii), 0);
+  ASSERT_EQ(Json::objectValue, v.type());
+  ASSERT_EQ(6, v.getMemberNames().size());
+  ASSERT_TRUE(v.isMember("7fe0,0010"));  
+  ASSERT_EQ(Json::stringValue, v["7fe0,0010"].type());  
+  ASSERT_EQ("Pixels", v["7fe0,0010"].asString());  
+
+  f.ToJson(v, DicomToJsonFormat_Short, DicomToJsonFlags_IncludePixelData, 0);
+  ASSERT_EQ(Json::objectValue, v.type());
+  ASSERT_EQ(6, v.getMemberNames().size());
+  ASSERT_TRUE(v.isMember("7fe0,0010"));  
+  ASSERT_EQ(Json::stringValue, v["7fe0,0010"].type());
+  std::string mime, content;
+  Toolbox::DecodeDataUriScheme(mime, content, v["7fe0,0010"].asString());
+  ASSERT_EQ("application/octet-stream", mime);
+  ASSERT_EQ("Pixels", content);
+}
--- a/UnitTestsSources/ServerIndexTests.cpp	Tue Oct 20 17:39:58 2015 +0200
+++ b/UnitTestsSources/ServerIndexTests.cpp	Mon Oct 26 12:30:34 2015 +0100
@@ -33,7 +33,6 @@
 #include "PrecompiledHeadersUnitTests.h"
 #include "gtest/gtest.h"
 
-#include "../Core/DicomFormat/DicomNullValue.h"
 #include "../Core/FileStorage/FilesystemStorage.h"
 #include "../Core/Logging.h"
 #include "../Core/Uuid.h"