changeset 434:9a0457a3ca19 transcoding

integration mainline->transcoding
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 26 May 2020 11:06:13 +0200
parents d575f05723eb (diff) ad671caa2dcf (current diff)
children d9f2b8280f86
files CMakeLists.txt Resources/Orthanc/DownloadOrthancFramework.cmake
diffstat 15 files changed, 171 insertions(+), 1347 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Tue May 26 11:05:10 2020 +0200
+++ b/CMakeLists.txt	Tue May 26 11:06:13 2020 +0200
@@ -24,8 +24,7 @@
 set(ORTHANC_DICOM_WEB_VERSION "mainline")
 
 if (ORTHANC_DICOM_WEB_VERSION STREQUAL "mainline")
-  # TODO - Switch to "mainline" after "transcoding" is made the new "default"
-  set(ORTHANC_FRAMEWORK_DEFAULT_VERSION "1.6.1")
+  set(ORTHANC_FRAMEWORK_DEFAULT_VERSION "mainline")
   set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "hg")
 else()
   set(ORTHANC_FRAMEWORK_DEFAULT_VERSION "1.6.1")
@@ -42,7 +41,6 @@
 set(ORTHANC_FRAMEWORK_ROOT "" CACHE STRING "Path to the Orthanc source directory, if ORTHANC_FRAMEWORK_SOURCE is \"path\"")
 
 # Advanced parameters to fine-tune linking against system libraries
-set(USE_SYSTEM_GDCM ON CACHE BOOL "Use the system version of Grassroot DICOM (GDCM)")
 set(USE_SYSTEM_ORTHANC_SDK ON CACHE BOOL "Use the system version of the Orthanc plugin SDK")
 set(ORTHANC_SDK_VERSION "1.5.7" CACHE STRING "Version of the Orthanc plugin SDK to use, if not using the system version (can be \"1.5.4\", \"1.5.7\", or \"framework\")")
 
@@ -67,7 +65,6 @@
 include(${ORTHANC_ROOT}/Resources/CMake/OrthancFrameworkConfiguration.cmake)
 include_directories(${ORTHANC_ROOT})
 
-include(${CMAKE_SOURCE_DIR}/Resources/CMake/GdcmConfiguration.cmake)
 include(${CMAKE_SOURCE_DIR}/Resources/CMake/JavaScriptLibraries.cmake)
 
 
@@ -147,7 +144,6 @@
 
 set(CORE_SOURCES
   Plugin/Configuration.cpp
-  Plugin/GdcmParsedDicomFile.cpp
 
   ${ORTHANC_ROOT}/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp
   ${ORTHANC_CORE_SOURCES}
@@ -167,8 +163,6 @@
   ${AUTOGENERATED_SOURCES}
   )
 
-target_link_libraries(OrthancDicomWeb ${GDCM_LIBRARIES})
-
 message("Setting the version of the library to ${ORTHANC_DICOM_WEB_VERSION}")
 
 add_definitions(-DORTHANC_DICOM_WEB_VERSION="${ORTHANC_DICOM_WEB_VERSION}")
@@ -192,11 +186,5 @@
   )
 
 target_link_libraries(UnitTests
-  ${GDCM_LIBRARIES}
   ${GOOGLE_TEST_LIBRARIES}
   )
-
-if (STATIC_BUILD OR NOT USE_SYSTEM_GDCM)
-  add_dependencies(OrthancDicomWeb GDCM)
-  add_dependencies(UnitTests GDCM)
-endif()
--- a/NEWS	Tue May 26 11:05:10 2020 +0200
+++ b/NEWS	Tue May 26 11:06:13 2020 +0200
@@ -2,6 +2,11 @@
 ===============================
 
 
+=> Minimum SDK version: 1.7.0 <=
+
+
+* Removed dependency on GDCM
+
 Maintenance
 -----------
 
--- a/Plugin/DicomWebFormatter.cpp	Tue May 26 11:05:10 2020 +0200
+++ b/Plugin/DicomWebFormatter.cpp	Tue May 26 11:06:13 2020 +0200
@@ -47,9 +47,10 @@
                                    const uint32_t *levelIndex,
                                    uint16_t tagGroup,
                                    uint16_t tagElement,
-                                   OrthancPluginValueRepresentation vr)
+                                   OrthancPluginValueRepresentation vr,
+                                   void* payload)
   {
-    const DicomWebFormatter& that = GetSingleton();
+    const DicomWebFormatter& that = *reinterpret_cast<const DicomWebFormatter*>(payload);
 
     switch (that.mode_)
     {
@@ -60,7 +61,7 @@
 
       case OrthancPluginDicomWebBinaryMode_BulkDataUri:
       {
-        std::string uri = GetSingleton().bulkRoot_;
+        std::string uri = that.bulkRoot_;
 
         for (size_t i = 0; i < levelDepth; i++)
         {
@@ -77,31 +78,25 @@
   }
 
 
-  DicomWebFormatter::Locker::Locker(OrthancPluginDicomWebBinaryMode mode,
-                                    const std::string& bulkRoot) :
-    that_(GetSingleton()),
-    lock_(that_.mutex_)
+  void DicomWebFormatter::Apply(std::string& target,
+                                OrthancPluginContext* context,
+                                const void* data,
+                                size_t size,
+                                bool xml,
+                                OrthancPluginDicomWebBinaryMode mode,
+                                const std::string& bulkRoot)
   {
-    that_.mode_ = mode;
-    that_.bulkRoot_ = bulkRoot;
-  }
-
-
-  void DicomWebFormatter::Locker::Apply(std::string& target,
-                                        OrthancPluginContext* context,
-                                        const void* data,
-                                        size_t size,
-                                        bool xml)
-  {
+    DicomWebFormatter payload(mode, bulkRoot);
+    
     OrthancString s;
 
     if (xml)
     {
-      s.Assign(OrthancPluginEncodeDicomWebXml(context, data, size, Callback));
+      s.Assign(OrthancPluginEncodeDicomWebXml2(context, data, size, Callback, &payload));
     }
     else
     {
-      s.Assign(OrthancPluginEncodeDicomWebJson(context, data, size, Callback));
+      s.Assign(OrthancPluginEncodeDicomWebJson2(context, data, size, Callback, &payload));
     }
 
     if (s.GetContent() == NULL)
@@ -116,14 +111,16 @@
   }
 
 
-  void DicomWebFormatter::Locker::Apply(std::string& target,
-                                        OrthancPluginContext* context,
-                                        const Json::Value& value,
-                                        bool xml)
+  void DicomWebFormatter::Apply(std::string& target,
+                                OrthancPluginContext* context,
+                                const Json::Value& value,
+                                bool xml,
+                                OrthancPluginDicomWebBinaryMode mode,
+                                const std::string& bulkRoot)
   {
     MemoryBuffer dicom;
     dicom.CreateDicom(value, OrthancPluginCreateDicomFlags_None);
-    Apply(target, context, dicom.GetData(), dicom.GetSize(), xml);
+    Apply(target, context, dicom.GetData(), dicom.GetSize(), xml, mode, bulkRoot);
   }
 
 
@@ -166,11 +163,8 @@
 
     std::string item;
 
-    {
-      // TODO - Avoid a global mutex => Need to change Orthanc SDK
-      OrthancPlugins::DicomWebFormatter::Locker locker(mode, bulkRoot);
-      locker.Apply(item, context_, dicom, size, isXml_);
-    }
+    OrthancPlugins::DicomWebFormatter::Apply(
+      item, context_, dicom, size, isXml_, mode, bulkRoot);
    
     if (isXml_)
     {
--- a/Plugin/DicomWebFormatter.h	Tue May 26 11:05:10 2020 +0200
+++ b/Plugin/DicomWebFormatter.h	Tue May 26 11:06:13 2020 +0200
@@ -29,7 +29,6 @@
 #include <json/value.h>
 
 #include <boost/noncopyable.hpp>
-#include <boost/thread/mutex.hpp>
 
 
 namespace OrthancPlugins
@@ -37,16 +36,9 @@
   class DicomWebFormatter : public boost::noncopyable
   {
   private:
-    boost::mutex                     mutex_;
     OrthancPluginDicomWebBinaryMode  mode_;
     std::string                      bulkRoot_;
 
-    static DicomWebFormatter& GetSingleton()
-    {
-      static DicomWebFormatter formatter;
-      return formatter;
-    }
-
     static void Callback(OrthancPluginDicomWebNode *node,
                          OrthancPluginDicomWebSetBinaryNode setter,
                          uint32_t levelDepth,
@@ -55,30 +47,31 @@
                          const uint32_t *levelIndex,
                          uint16_t tagGroup,
                          uint16_t tagElement,
-                         OrthancPluginValueRepresentation vr);
-
-  public:
-    class Locker : public boost::noncopyable
-    {
-    private:
-      DicomWebFormatter&         that_;
-      boost::mutex::scoped_lock  lock_;
+                         OrthancPluginValueRepresentation vr,
+                         void* payload);
 
-    public:
-      Locker(OrthancPluginDicomWebBinaryMode mode,
-             const std::string& bulkRoot);
+    DicomWebFormatter(OrthancPluginDicomWebBinaryMode mode,
+                      const std::string& bulkRoot) :
+      mode_(mode),
+      bulkRoot_(bulkRoot)
+    {
+    }
+    
+  public:
+    static void Apply(std::string& target,
+                      OrthancPluginContext* context,
+                      const void* data,
+                      size_t size,
+                      bool xml,
+                      OrthancPluginDicomWebBinaryMode mode,
+                      const std::string& bulkRoot);
 
-      void Apply(std::string& target,
-                 OrthancPluginContext* context,
-                 const void* data,
-                 size_t size,
-                 bool xml);
-
-      void Apply(std::string& target,
-                 OrthancPluginContext* context,
-                 const Json::Value& value,
-                 bool xml);
-    };
+    static void Apply(std::string& target,
+                      OrthancPluginContext* context,
+                      const Json::Value& value,
+                      bool xml,
+                      OrthancPluginDicomWebBinaryMode mode,
+                      const std::string& bulkRoot);
 
     class HttpWriter : public boost::noncopyable
     {
--- a/Plugin/GdcmParsedDicomFile.cpp	Tue May 26 11:05:10 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,438 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * 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
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "GdcmParsedDicomFile.h"
-
-#include "ChunkedBuffer.h"
-
-#include <Core/Toolbox.h>
-
-#include <gdcmDict.h>
-#include <gdcmDictEntry.h>
-#include <gdcmDicts.h>
-#include <gdcmGlobal.h>
-#include <gdcmStringFilter.h>
-
-#include <boost/lexical_cast.hpp>
-#include <json/writer.h>
-
-
-namespace OrthancPlugins
-{
-  static const gdcm::Dict* dictionary_ = NULL;
-
-  
-  void GdcmParsedDicomFile::Initialize()
-  {
-    if (dictionary_ != NULL)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-    }
-    else
-    {
-      dictionary_ = &gdcm::Global::GetInstance().GetDicts().GetPublicDict();
-
-      if (dictionary_ == NULL)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError,
-                                        "Cannot initialize the DICOM dictionary of GDCM");
-      }
-    }
-  }
-  
-
-  static std::string MyStripSpaces(const std::string& source)
-  {
-    size_t first = 0;
-
-    while (first < source.length() &&
-           (isspace(source[first]) || 
-            source[first] == '\0'))
-    {
-      first++;
-    }
-
-    if (first == source.length())
-    {
-      // String containing only spaces
-      return "";
-    }
-
-    size_t last = source.length();
-    while (last > first &&
-           (isspace(source[last - 1]) ||
-            source[last - 1] == '\0'))
-    {
-      last--;
-    }          
-    
-    assert(first <= last);
-    return source.substr(first, last - first);
-  }
-
-
-  static const char* GetVRName(bool& isSequence,
-                               const gdcm::Tag& tag,
-                               gdcm::VR vr)
-  {
-    if (dictionary_ == NULL)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls,
-                                      "GDCM has not been initialized");
-    }
-    
-    if (vr == gdcm::VR::INVALID)
-    {
-      const gdcm::DictEntry &entry = dictionary_->GetDictEntry(tag);
-      vr = entry.GetVR();
-
-      if (vr == gdcm::VR::OB_OW)
-      {
-        vr = gdcm::VR::OB;
-      }
-    }
-
-    isSequence = (vr == gdcm::VR::SQ);
-
-    const char* str = gdcm::VR::GetVRString(vr);
-    if (isSequence)
-    {
-      return str;
-    }
-
-    if (str == NULL ||
-        strlen(str) != 2 ||
-        !(str[0] >= 'A' && str[0] <= 'Z') ||
-        !(str[1] >= 'A' && str[1] <= 'Z'))
-    {
-      return "UN";
-    }
-    else
-    {
-      return str;
-    }
-  }
-
-
-  static const char* GetVRName(bool& isSequence,
-                               const gdcm::DataElement& element)
-  {
-    return GetVRName(isSequence, element.GetTag(), element.GetVR());
-  }
-
-
-  template <int T>
-  static void ConvertNumberTag(std::string& target,
-                               const gdcm::DataElement& source)
-  {
-    if (source.IsEmpty())
-    {
-      target.clear();
-    }
-    else
-    {
-      typename gdcm::Element<T, gdcm::VM::VM1_n> element;
-
-      element.Set(source.GetValue());
-
-      for (unsigned int i = 0; i < element.GetLength(); i++)
-      {
-        if (i != 0)
-        {
-          target += "\\";
-        }
-      
-        target = boost::lexical_cast<std::string>(element.GetValue());
-      }
-    }
-  }
-
-
-  static bool ConvertDicomStringToUtf8(std::string& result,
-                                       const gdcm::DataElement& element,
-                                       const Orthanc::Encoding sourceEncoding)
-  {
-    const gdcm::ByteValue* data = element.GetByteValue();
-    if (!data)
-    {
-      return false;
-    }
-
-    bool isSequence;
-    std::string vr = GetVRName(isSequence, element);
-
-    if (!isSequence)
-    {
-      if (vr == "FL")
-      {
-        ConvertNumberTag<gdcm::VR::FL>(result, element);
-        return true;
-      }
-      else if (vr == "FD")
-      {
-        ConvertNumberTag<gdcm::VR::FD>(result, element);
-        return true;
-      }
-      else if (vr == "SL")
-      {
-        ConvertNumberTag<gdcm::VR::SL>(result, element);
-        return true;
-      }
-      else if (vr == "SS")
-      {
-        ConvertNumberTag<gdcm::VR::SS>(result, element);
-        return true;
-      }
-      else if (vr == "UL")
-      {
-        ConvertNumberTag<gdcm::VR::UL>(result, element);
-        return true;
-      }
-      else if (vr == "US")
-      {
-        ConvertNumberTag<gdcm::VR::US>(result, element);
-        return true;
-      }
-    }
-
-    if (sourceEncoding == Orthanc::Encoding_Utf8)
-    {
-      result.assign(data->GetPointer(), data->GetLength());
-    }
-    else
-    {
-      std::string tmp(data->GetPointer(), data->GetLength());
-      result = Orthanc::Toolbox::ConvertToUtf8(tmp, sourceEncoding, false);
-    }
-
-    result = MyStripSpaces(result);
-    return true;
-  }
-
-
-
-  void GdcmParsedDicomFile::Setup(const std::string& dicom)
-  {
-    // Prepare a memory stream over the DICOM instance
-    std::stringstream stream(dicom);
-
-    // Parse the DICOM instance using GDCM
-    reader_.SetStream(stream);
-
-    if (!reader_.Read())
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat,
-                                      "GDCM cannot decode this DICOM instance of length " +
-                                      boost::lexical_cast<std::string>(dicom.size()));
-    }
-  }
-
-
-  GdcmParsedDicomFile::GdcmParsedDicomFile(const OrthancPlugins::MemoryBuffer& buffer)
-  {
-    // TODO Avoid this unnecessary memcpy by defining a stream over the MemoryBuffer
-    std::string dicom(buffer.GetData(), buffer.GetData() + buffer.GetSize());
-    Setup(dicom);
-  }
-
-
-  static bool GetRawTag(std::string& result,
-                        const gdcm::DataSet& dataset,
-                        const gdcm::Tag& tag,
-                        bool stripSpaces)
-  {
-    if (dataset.FindDataElement(tag))
-    {
-      const gdcm::ByteValue* value = dataset.GetDataElement(tag).GetByteValue();
-      if (value)
-      {
-        result.assign(value->GetPointer(), value->GetLength());
-
-        if (stripSpaces)
-        {
-          result = MyStripSpaces(result);
-        }
-
-        return true;
-      }
-    }
-
-    return false;
-  }
-
-
-  bool GdcmParsedDicomFile::GetRawTag(std::string& result,
-                                      const gdcm::Tag& tag,
-                                      bool stripSpaces) const
-  {
-    return OrthancPlugins::GetRawTag(result, GetDataSet(), tag, stripSpaces);
-  }
-
-
-  std::string GdcmParsedDicomFile::GetRawTagWithDefault(const gdcm::Tag& tag,
-                                                        const std::string& defaultValue,
-                                                        bool stripSpaces) const
-  {
-    std::string result;
-    if (!GetRawTag(result, tag, stripSpaces))
-    {
-      return defaultValue;
-    }
-    else
-    {
-      return result;
-    }
-  }
-
-
-  std::string GdcmParsedDicomFile::GetRawTagWithDefault(const Orthanc::DicomTag& tag,
-                                                        const std::string& defaultValue,
-                                                        bool stripSpaces) const
-  {
-    gdcm::Tag t(tag.GetGroup(), tag.GetElement());
-    return GetRawTagWithDefault(t, defaultValue, stripSpaces);
-  }
-
-
-  bool GdcmParsedDicomFile::GetStringTag(std::string& result,
-                                         const gdcm::Tag& tag,
-                                         bool stripSpaces) const
-  {
-    if (!GetDataSet().FindDataElement(tag))
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_InexistentTag);
-    }
-
-    const gdcm::DataElement& element = GetDataSet().GetDataElement(tag);
-
-    if (!ConvertDicomStringToUtf8(result, element, GetEncoding()))
-    {
-      return false;
-    }
-
-    if (stripSpaces)
-    {
-      result = MyStripSpaces(result);
-    }
-
-    return true;
-  }
-
-
-  bool GdcmParsedDicomFile::GetIntegerTag(int& result,
-                                          const gdcm::Tag& tag) const
-  {
-    std::string tmp;
-    if (!GetStringTag(tmp, tag, true))
-    {
-      return false;
-    }
-
-    try
-    {
-      result = boost::lexical_cast<int>(tmp);
-      return true;
-    }
-    catch (boost::bad_lexical_cast&)
-    {
-      return false;
-    }
-  }
-
-
-  std::string FormatTag(const gdcm::Tag& tag)
-  {
-    char tmp[16];
-    sprintf(tmp, "%04X%04X", tag.GetGroup(), tag.GetElement());
-    return std::string(tmp);
-  }
-
-
-  static std::string GetWadoUrl(const std::string& wadoBase,
-                                const gdcm::DataSet& dicom)
-  {
-    static const gdcm::Tag DICOM_TAG_STUDY_INSTANCE_UID(0x0020, 0x000d);
-    static const gdcm::Tag DICOM_TAG_SERIES_INSTANCE_UID(0x0020, 0x000e);
-    static const gdcm::Tag DICOM_TAG_SOP_INSTANCE_UID(0x0008, 0x0018);
-
-    std::string study, series, instance;
-
-    if (!GetRawTag(study, dicom, DICOM_TAG_STUDY_INSTANCE_UID, true) ||
-        !GetRawTag(series, dicom, DICOM_TAG_SERIES_INSTANCE_UID, true) ||
-        !GetRawTag(instance, dicom, DICOM_TAG_SOP_INSTANCE_UID, true))
-    {
-      return "";
-    }
-    else
-    {
-      return Configuration::GetWadoUrl(wadoBase, study, series, instance);
-    }
-  }
-
-
-  static Orthanc::Encoding DetectEncoding(const gdcm::DataSet& dicom)
-  {
-    static const gdcm::Tag DICOM_TAG_SPECIFIC_CHARACTER_SET(0x0008, 0x0005);
-
-    if (!dicom.FindDataElement(DICOM_TAG_SPECIFIC_CHARACTER_SET))
-    {
-      return Orthanc::Encoding_Ascii;
-    }
-
-    const gdcm::DataElement& element = 
-      dicom.GetDataElement(DICOM_TAG_SPECIFIC_CHARACTER_SET);
-
-    const gdcm::ByteValue* data = element.GetByteValue();
-    if (!data)
-    {
-      return Configuration::GetDefaultEncoding();
-    }
-
-    std::string tmp(data->GetPointer(), data->GetLength());
-    tmp = MyStripSpaces(tmp);
-
-    Orthanc::Encoding encoding;
-    if (Orthanc::GetDicomEncoding(encoding, tmp.c_str()))
-    {
-      return encoding;
-    }
-    else
-    {
-      return Configuration::GetDefaultEncoding();
-    }
-  }
-
-
-  Orthanc::Encoding  GdcmParsedDicomFile::GetEncoding() const
-  {
-    return DetectEncoding(GetDataSet());
-  }
-  
-
-  std::string GdcmParsedDicomFile::GetWadoUrl(const OrthancPluginHttpRequest* request) const
-  {
-    const std::string base = OrthancPlugins::Configuration::GetBaseUrl(request);
-    return OrthancPlugins::GetWadoUrl(base, GetDataSet());
-  }
-}
-
-
-#include "./GdcmParsedDicomFile_TransferSyntaxes.impl.h"
--- a/Plugin/GdcmParsedDicomFile.h	Tue May 26 11:05:10 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,98 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * 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
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "Configuration.h"
-
-#include <Core/ChunkedBuffer.h>
-#include <Core/Enumerations.h>
-#include <Core/DicomFormat/DicomTag.h>
-#include <Plugins/Samples/Common/OrthancPluginCppWrapper.h>
-
-#include <gdcmReader.h>
-#include <gdcmDataSet.h>
-#include <pugixml.hpp>
-#include <list>
-
-
-namespace OrthancPlugins
-{
-  class GdcmParsedDicomFile : public boost::noncopyable
-  {
-  private:
-    gdcm::Reader reader_;
-
-    void Setup(const std::string& dicom);
-
-    Orthanc::Encoding  GetEncoding() const;
-
-  public:
-    static void Initialize();
-    
-    explicit GdcmParsedDicomFile(const OrthancPlugins::MemoryBuffer& item);
-
-    explicit GdcmParsedDicomFile(const std::string& dicom)
-    {
-      Setup(dicom);
-    }
-
-    const gdcm::File& GetFile() const
-    {
-      return reader_.GetFile();
-    }
-
-    const gdcm::DataSet& GetDataSet() const
-    {
-      return reader_.GetFile().GetDataSet();
-    }
-
-    bool GetRawTag(std::string& result,
-                   const gdcm::Tag& tag,
-                   bool stripSpaces) const;
-
-    std::string GetRawTagWithDefault(const gdcm::Tag& tag,
-                                     const std::string& defaultValue,
-                                     bool stripSpaces) const;
-
-    std::string GetRawTagWithDefault(const Orthanc::DicomTag& tag,
-                                     const std::string& defaultValue,
-                                     bool stripSpaces) const;
-
-    bool GetStringTag(std::string& result,
-                      const gdcm::Tag& tag,
-                      bool stripSpaces) const;
-
-    bool GetIntegerTag(int& result,
-                       const gdcm::Tag& tag) const;
-
-    std::string GetWadoUrl(const OrthancPluginHttpRequest* request) const;
-
-    Orthanc::DicomTransferSyntax GetTransferSyntax() const
-    {
-      return GetOrthancTransferSyntax(GetFile().GetHeader().GetDataSetTransferSyntax());
-    }
-    
-    static gdcm::TransferSyntax GetGdcmTransferSyntax(Orthanc::DicomTransferSyntax syntax);
-    
-    static Orthanc::DicomTransferSyntax GetOrthancTransferSyntax(gdcm::TransferSyntax syntax);
-  };
-}
--- a/Plugin/GdcmParsedDicomFile_TransferSyntaxes.impl.h	Tue May 26 11:05:10 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,121 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * 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
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-// This file is autogenerated by "../Resources/GenerateTransferSyntaxes.py"
-
-namespace OrthancPlugins
-{
-  gdcm::TransferSyntax GdcmParsedDicomFile::GetGdcmTransferSyntax(Orthanc::DicomTransferSyntax syntax)
-  {
-    switch (syntax)
-    {
-      case Orthanc::DicomTransferSyntax_LittleEndianImplicit:
-        return gdcm::TransferSyntax::ImplicitVRLittleEndian;
-
-      case Orthanc::DicomTransferSyntax_LittleEndianExplicit:
-        return gdcm::TransferSyntax::ExplicitVRLittleEndian;
-
-      case Orthanc::DicomTransferSyntax_JPEGProcess1:
-        return gdcm::TransferSyntax::JPEGBaselineProcess1;
-
-      case Orthanc::DicomTransferSyntax_JPEGProcess2_4:
-        return gdcm::TransferSyntax::JPEGExtendedProcess2_4;
-
-      case Orthanc::DicomTransferSyntax_JPEGProcess14:
-        return gdcm::TransferSyntax::JPEGLosslessProcess14;
-
-      case Orthanc::DicomTransferSyntax_JPEGProcess14SV1:
-        return gdcm::TransferSyntax::JPEGLosslessProcess14_1;
-
-      case Orthanc::DicomTransferSyntax_JPEGLSLossless:
-        return gdcm::TransferSyntax::JPEGLSLossless;
-
-      case Orthanc::DicomTransferSyntax_JPEGLSLossy:
-        return gdcm::TransferSyntax::JPEGLSNearLossless;
-
-      case Orthanc::DicomTransferSyntax_JPEG2000LosslessOnly:
-        return gdcm::TransferSyntax::JPEG2000Lossless;
-
-      case Orthanc::DicomTransferSyntax_JPEG2000:
-        return gdcm::TransferSyntax::JPEG2000;
-
-      case Orthanc::DicomTransferSyntax_JPEG2000MulticomponentLosslessOnly:
-        return gdcm::TransferSyntax::JPEG2000Part2Lossless;
-
-      case Orthanc::DicomTransferSyntax_JPEG2000Multicomponent:
-        return gdcm::TransferSyntax::JPEG2000Part2;
-
-      case Orthanc::DicomTransferSyntax_RLELossless:
-        return gdcm::TransferSyntax::RLELossless;
-
-      default:
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-    }
-  }
-
-
-  Orthanc::DicomTransferSyntax GdcmParsedDicomFile::GetOrthancTransferSyntax(gdcm::TransferSyntax syntax)
-  {
-    switch (syntax)
-    {
-      case gdcm::TransferSyntax::ImplicitVRLittleEndian:
-        return Orthanc::DicomTransferSyntax_LittleEndianImplicit;
-
-      case gdcm::TransferSyntax::ExplicitVRLittleEndian:
-        return Orthanc::DicomTransferSyntax_LittleEndianExplicit;
-
-      case gdcm::TransferSyntax::JPEGBaselineProcess1:
-        return Orthanc::DicomTransferSyntax_JPEGProcess1;
-
-      case gdcm::TransferSyntax::JPEGExtendedProcess2_4:
-        return Orthanc::DicomTransferSyntax_JPEGProcess2_4;
-
-      case gdcm::TransferSyntax::JPEGLosslessProcess14:
-        return Orthanc::DicomTransferSyntax_JPEGProcess14;
-
-      case gdcm::TransferSyntax::JPEGLosslessProcess14_1:
-        return Orthanc::DicomTransferSyntax_JPEGProcess14SV1;
-
-      case gdcm::TransferSyntax::JPEGLSLossless:
-        return Orthanc::DicomTransferSyntax_JPEGLSLossless;
-
-      case gdcm::TransferSyntax::JPEGLSNearLossless:
-        return Orthanc::DicomTransferSyntax_JPEGLSLossy;
-
-      case gdcm::TransferSyntax::JPEG2000Lossless:
-        return Orthanc::DicomTransferSyntax_JPEG2000LosslessOnly;
-
-      case gdcm::TransferSyntax::JPEG2000:
-        return Orthanc::DicomTransferSyntax_JPEG2000;
-
-      case gdcm::TransferSyntax::JPEG2000Part2Lossless:
-        return Orthanc::DicomTransferSyntax_JPEG2000MulticomponentLosslessOnly;
-
-      case gdcm::TransferSyntax::JPEG2000Part2:
-        return Orthanc::DicomTransferSyntax_JPEG2000Multicomponent;
-
-      case gdcm::TransferSyntax::RLELossless:
-        return Orthanc::DicomTransferSyntax_RLELossless;
-
-      default:
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-    }
-  }
-}
--- a/Plugin/Plugin.cpp	Tue May 26 11:05:10 2020 +0200
+++ b/Plugin/Plugin.cpp	Tue May 26 11:06:13 2020 +0200
@@ -20,7 +20,6 @@
 
 #include "DicomWebClient.h"
 #include "DicomWebServers.h"
-#include "GdcmParsedDicomFile.h"
 #include "QidoRs.h"
 #include "StowRs.h"
 #include "WadoRs.h"
@@ -483,9 +482,6 @@
       // Read the configuration
       OrthancPlugins::Configuration::Initialize();
 
-      // Initialize GDCM
-      OrthancPlugins::GdcmParsedDicomFile::Initialize();
-
       // Configure the DICOMweb callbacks
       if (OrthancPlugins::Configuration::GetBooleanValue("Enable", true))
       {
--- a/Plugin/StowRs.cpp	Tue May 26 11:05:10 2020 +0200
+++ b/Plugin/StowRs.cpp	Tue May 26 11:06:13 2020 +0200
@@ -191,10 +191,8 @@
     
     std::string answer;
     
-    {
-      DicomWebFormatter::Locker locker(OrthancPluginDicomWebBinaryMode_Ignore, "");
-      locker.Apply(answer, context_, result_, xml_);
-    }
+    DicomWebFormatter::Apply(answer, context_, result_, xml_,
+                             OrthancPluginDicomWebBinaryMode_Ignore, "");
       
     OrthancPluginAnswerBuffer(context_, output, answer.c_str(), answer.size(),
                               xml_ ? "application/dicom+xml" : "application/dicom+json");
--- a/Plugin/WadoRs.cpp	Tue May 26 11:05:10 2020 +0200
+++ b/Plugin/WadoRs.cpp	Tue May 26 11:06:13 2020 +0200
@@ -692,9 +692,9 @@
       std::string dicomweb;
       {
         // TODO - Avoid a global mutex => Need to change Orthanc SDK
-        OrthancPlugins::DicomWebFormatter::Locker locker(OrthancPluginDicomWebBinaryMode_Ignore, "");
-        locker.Apply(dicomweb, OrthancPlugins::GetGlobalContext(),
-                     buffer.GetData(), buffer.GetSize(), false /* JSON */);
+        OrthancPlugins::DicomWebFormatter::Apply(
+          dicomweb, OrthancPlugins::GetGlobalContext(), buffer.GetData(), buffer.GetSize(),
+          false /* JSON */, OrthancPluginDicomWebBinaryMode_Ignore, "");
       }
 
       buffer.RestApiPut("/instances/" + orthancId + "/attachments/4444", dicomweb, false);
--- a/Plugin/WadoRsRetrieveFrames.cpp	Tue May 26 11:05:10 2020 +0200
+++ b/Plugin/WadoRsRetrieveFrames.cpp	Tue May 26 11:06:13 2020 +0200
@@ -21,16 +21,11 @@
 
 #include "WadoRs.h"
 
-#include "GdcmParsedDicomFile.h"
-
 #include <Core/Toolbox.h>
 #include <Plugins/Samples/Common/OrthancPluginCppWrapper.h>
 
 #include <memory>
 #include <list>
-#include <gdcmImageReader.h>
-#include <gdcmImageWriter.h>
-#include <gdcmImageChangeTransferSyntax.h>
 #include <boost/algorithm/string/replace.hpp>
 #include <boost/lexical_cast.hpp>
 
@@ -61,8 +56,8 @@
 
 
 
-static Orthanc::DicomTransferSyntax ParseTransferSyntax(const OrthancPluginHttpRequest* request,
-                                                        Orthanc::DicomTransferSyntax sourceTransferSyntax)
+static bool ParseTransferSyntax(Orthanc::DicomTransferSyntax& syntax,
+                                const OrthancPluginHttpRequest* request)
 {
   for (uint32_t i = 0; i < request->headersCount; i++)
   {
@@ -77,7 +72,8 @@
       if (tokens.size() == 0 ||
           tokens[0] == "*/*")
       {
-        return Orthanc::DicomTransferSyntax_LittleEndianExplicit;
+        syntax = Orthanc::DicomTransferSyntax_LittleEndianExplicit;
+        return true;
       }
 
       if (tokens[0] != "multipart/related")
@@ -113,14 +109,21 @@
 
       if (type == "application/octet-stream")
       {
-        if (transferSyntax.empty())
+        if (transferSyntax.empty() ||
+            transferSyntax == "1.2.840.10008.1.2.1")
         {
-          return Orthanc::DicomTransferSyntax(Orthanc::DicomTransferSyntax_LittleEndianExplicit);
+          syntax = Orthanc::DicomTransferSyntax_LittleEndianExplicit;
+          return true;
+        }
+        else if (transferSyntax == "1.2.840.10008.1.2")
+        {
+          syntax = Orthanc::DicomTransferSyntax_LittleEndianImplicit;
+          return true;
         }
         else if (transferSyntax == "*")
         {
           // New in DICOMweb plugin 1.1.0
-          return sourceTransferSyntax;
+          return false;
         }
         else
         {
@@ -139,51 +142,62 @@
         if (type == "image/jpeg" && (transferSyntax.empty() ||  // Default
                                      transferSyntax == "1.2.840.10008.1.2.4.70"))
         {
-          return Orthanc::DicomTransferSyntax_JPEGProcess14SV1;
+          syntax = Orthanc::DicomTransferSyntax_JPEGProcess14SV1;
+          return true;
         }
         else if (type == "image/jpeg" && transferSyntax == "1.2.840.10008.1.2.4.50")
         {
-          return Orthanc::DicomTransferSyntax_JPEGProcess1;
+          syntax = Orthanc::DicomTransferSyntax_JPEGProcess1;
+          return true;
         }
         else if (type == "image/jpeg" && transferSyntax == "1.2.840.10008.1.2.4.51")
         {
-          return Orthanc::DicomTransferSyntax_JPEGProcess2_4;
+          syntax = Orthanc::DicomTransferSyntax_JPEGProcess2_4;
+          return true;
         }
         else if (type == "image/jpeg" && transferSyntax == "1.2.840.10008.1.2.4.57")
         {
-          return Orthanc::DicomTransferSyntax_JPEGProcess14;
+          syntax = Orthanc::DicomTransferSyntax_JPEGProcess14;
+          return true;
         }
         else if (type == "image/x-dicom-rle" && (transferSyntax.empty() ||  // Default
                                                  transferSyntax == "1.2.840.10008.1.2.5"))
         {
-          return Orthanc::DicomTransferSyntax_RLELossless;
+          syntax = Orthanc::DicomTransferSyntax_RLELossless;
+          return true;
         }
         else if (type == "image/x-jls" && (transferSyntax.empty() ||  // Default
                                            transferSyntax == "1.2.840.10008.1.2.4.80"))
         {
-          return Orthanc::DicomTransferSyntax_JPEGLSLossless;
+          syntax = Orthanc::DicomTransferSyntax_JPEGLSLossless;
+          return true;
         }
         else if (type == "image/x-jls" && transferSyntax == "1.2.840.10008.1.2.4.81")
         {
-          return Orthanc::DicomTransferSyntax_JPEGLSLossy;
+          syntax = Orthanc::DicomTransferSyntax_JPEGLSLossy;
+          return true;
         }
         else if (type == "image/jp2" && (transferSyntax.empty() ||  // Default
                                          transferSyntax == "1.2.840.10008.1.2.4.90"))
         {
-          return Orthanc::DicomTransferSyntax_JPEG2000LosslessOnly;
+          syntax = Orthanc::DicomTransferSyntax_JPEG2000LosslessOnly;
+          return true;
         }
         else if (type == "image/jp2" && transferSyntax == "1.2.840.10008.1.2.4.91")
         {
-          return Orthanc::DicomTransferSyntax_JPEG2000;
+          syntax = Orthanc::DicomTransferSyntax_JPEG2000;
+          return true;
         }
         else if (type == "image/jpx" && (transferSyntax.empty() ||  // Default
                                          transferSyntax == "1.2.840.10008.1.2.4.92"))
         {
-          return Orthanc::DicomTransferSyntax_JPEG2000MulticomponentLosslessOnly;
+          syntax = Orthanc::DicomTransferSyntax_JPEG2000MulticomponentLosslessOnly;
+          return true;
         }
         else if (type == "image/jpx" && transferSyntax == "1.2.840.10008.1.2.4.93")
         {
-          return Orthanc::DicomTransferSyntax_JPEG2000Multicomponent;
+          syntax = Orthanc::DicomTransferSyntax_JPEG2000Multicomponent;
+          return true;
         }
 
 
@@ -193,52 +207,63 @@
          **/
         if (type == "image/dicom+jpeg" && transferSyntax == "1.2.840.10008.1.2.4.50")
         {
-          return Orthanc::DicomTransferSyntax_JPEGProcess1;
+          syntax = Orthanc::DicomTransferSyntax_JPEGProcess1;
+          return true;
         }
         else if (type == "image/dicom+jpeg" && transferSyntax == "1.2.840.10008.1.2.4.51")
         {
-          return Orthanc::DicomTransferSyntax_JPEGProcess2_4;
+          syntax = Orthanc::DicomTransferSyntax_JPEGProcess2_4;
+          return true;
         }
         else if (type == "image/dicom+jpeg" && transferSyntax == "1.2.840.10008.1.2.4.57")
         {
-          return Orthanc::DicomTransferSyntax_JPEGProcess14;
+          syntax = Orthanc::DicomTransferSyntax_JPEGProcess14;
+          return true;
         }
         else if (type == "image/dicom+jpeg" && (transferSyntax.empty() ||
                                                 transferSyntax == "1.2.840.10008.1.2.4.70"))
         {
-          return Orthanc::DicomTransferSyntax_JPEGProcess14SV1;
+          syntax = Orthanc::DicomTransferSyntax_JPEGProcess14SV1;
+          return true;
         }
         else if (type == "image/dicom+rle" && (transferSyntax.empty() ||
                                                transferSyntax == "1.2.840.10008.1.2.5"))
         {
-          return Orthanc::DicomTransferSyntax_RLELossless;
+          syntax = Orthanc::DicomTransferSyntax_RLELossless;
+          return true;
         }
         else if (type == "image/dicom+jpeg-ls" && (transferSyntax.empty() ||
                                                    transferSyntax == "1.2.840.10008.1.2.4.80"))
         {
-          return Orthanc::DicomTransferSyntax_JPEGLSLossless;
+          syntax = Orthanc::DicomTransferSyntax_JPEGLSLossless;
+          return true;
         }
         else if (type == "image/dicom+jpeg-ls" && transferSyntax == "1.2.840.10008.1.2.4.81")
         {
-          return Orthanc::DicomTransferSyntax_JPEGLSLossy;
+          syntax = Orthanc::DicomTransferSyntax_JPEGLSLossy;
+          return true;
         }
         else if (type == "image/dicom+jp2" && (transferSyntax.empty() ||
                                                transferSyntax == "1.2.840.10008.1.2.4.90"))
         {
-          return Orthanc::DicomTransferSyntax_JPEG2000LosslessOnly;
+          syntax = Orthanc::DicomTransferSyntax_JPEG2000LosslessOnly;
+          return true;
         }
         else if (type == "image/dicom+jp2" && transferSyntax == "1.2.840.10008.1.2.4.91")
         {
-          return Orthanc::DicomTransferSyntax_JPEG2000;
+          syntax = Orthanc::DicomTransferSyntax_JPEG2000;
+          return true;
         }
         else if (type == "image/dicom+jpx" && (transferSyntax.empty() ||
                                                transferSyntax == "1.2.840.10008.1.2.4.92"))
         {
-          return Orthanc::DicomTransferSyntax_JPEG2000MulticomponentLosslessOnly;
+          syntax = Orthanc::DicomTransferSyntax_JPEG2000MulticomponentLosslessOnly;
+          return true;
         }
         else if (type == "image/dicom+jpx" && transferSyntax == "1.2.840.10008.1.2.4.93")
         {
-          return Orthanc::DicomTransferSyntax_JPEG2000Multicomponent;
+          syntax = Orthanc::DicomTransferSyntax_JPEG2000Multicomponent;
+          return true;
         }
 
         throw Orthanc::OrthancException(
@@ -250,7 +275,8 @@
   }
 
   // By default, DICOMweb expectes Little Endian uncompressed pixel data
-  return Orthanc::DicomTransferSyntax_LittleEndianExplicit;
+  syntax = Orthanc::DicomTransferSyntax_LittleEndianExplicit;
+  return true;
 }
 
 
@@ -286,7 +312,6 @@
 }                           
 
 
-
 static const char* GetMimeType(const Orthanc::DicomTransferSyntax& syntax)
 {
   // http://dicom.nema.org/medical/dicom/current/output/html/part18.html#table_6.1.1.8-3b
@@ -340,268 +365,55 @@
 }
 
 
-
-static void ConvertYbrToRgb(uint8_t rgb[3],
-                            const uint8_t ybr[3])
+static void AnswerFrames(OrthancPluginRestOutput* output,
+                         const OrthancPluginHttpRequest* request,
+                         const OrthancPlugins::DicomInstance& instance,
+                         const std::string& studyInstanceUid,
+                         const std::string& seriesInstanceUid,
+                         const std::string& sopInstanceUid,
+                         const std::list<unsigned int>& frames,
+                         Orthanc::DicomTransferSyntax outputSyntax)
 {
-  // http://dicom.nema.org/medical/dicom/current/output/chtml/part03/sect_C.7.6.3.html#sect_C.7.6.3.1.2
-  // https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion
-    
-  const float Y  = ybr[0];
-  const float Cb = ybr[1];
-  const float Cr = ybr[2];
-
-  const float result[3] = {
-    Y                             + 1.402f    * (Cr - 128.0f),
-    Y - 0.344136f * (Cb - 128.0f) - 0.714136f * (Cr - 128.0f),
-    Y + 1.772f    * (Cb - 128.0f)
-  };
-
-  for (uint8_t i = 0; i < 3 ; i++)
-  {
-    if (result[i] < 0)
-    {
-      rgb[i] = 0;
-    }
-    else if (result[i] > 255)
-    {
-      rgb[i] = 255;
-    }
-    else
-    {
-      rgb[i] = static_cast<uint8_t>(result[i]);
-    }
-  }    
-}
-
-
-static void AnswerSingleFrame(OrthancPluginRestOutput* output,
-                              const OrthancPluginHttpRequest* request,
-                              const OrthancPlugins::GdcmParsedDicomFile& dicom,
-                              const char* frame,
-                              size_t size,
-                              unsigned int frameIndex,
-                              bool convertYbr)
-{
-  /**
-   * Fix the photometric interpretation, typically needed for some
-   * multiframe US images (as the one in issue 164). Also check out
-   * the "Plugins/Samples/GdcmDecoder/GdcmImageDecoder.cpp" file in
-   * the source distribution of Orthanc, and Osimis issue WVB-319
-   * ("Some images are not loading in US_MF").
-   **/
-
-  std::vector<uint8_t> copied;  // Don't move this variable inside the
-                                // "if", as "frame" might point to it
-
-  if (convertYbr &&
-      size > 0)
+  if (OrthancPluginStartMultipartAnswer(
+        OrthancPlugins::GetGlobalContext(), 
+        output, "related", GetMimeType(outputSyntax)) != OrthancPluginErrorCode_Success)
   {
-    copied.resize(size);
-    memcpy(&copied[0], frame, size);
-
-    uint8_t *p = &copied[0];
-    for (size_t i = 0; i < size / 3; i++)
-    {
-      uint8_t ybr[3], rgb[3];
-      ybr[0] = p[0];
-      ybr[1] = p[1];
-      ybr[2] = p[2];
-
-      ConvertYbrToRgb(rgb, ybr);
-      p[0] = rgb[0];
-      p[1] = rgb[1];
-      p[2] = rgb[2];
-
-      p += 3;
-    }
-
-    frame = reinterpret_cast<const char*>(&copied[0]);
-  }  
-
-
-  OrthancPluginErrorCode error;
-
-#if HAS_SEND_MULTIPART_ITEM_2 == 1
-  std::string location = dicom.GetWadoUrl(request) + "frames/" + boost::lexical_cast<std::string>(frameIndex + 1);
-  const char *keys[] = { "Content-Location" };
-  const char *values[] = { location.c_str() };
-  error = OrthancPluginSendMultipartItem2(OrthancPlugins::GetGlobalContext(), output, frame, size, 1, keys, values);
-#else
-  error = OrthancPluginSendMultipartItem(OrthancPlugins::GetGlobalContext(), output, frame, size);
-#endif
-
-  if (error != OrthancPluginErrorCode_Success)
-  {
-    throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);      
-  }
-}
-
-
-static bool AnswerFrames(OrthancPluginRestOutput* output,
-                         const OrthancPluginHttpRequest* request,
-                         const OrthancPlugins::GdcmParsedDicomFile& dicom,
-                         const Orthanc::DicomTransferSyntax& syntax,
-                         std::list<unsigned int>& frames)
-{
-  static const gdcm::Tag DICOM_TAG_BITS_ALLOCATED(0x0028, 0x0100);
-  static const gdcm::Tag DICOM_TAG_COLUMNS(0x0028, 0x0011);
-  static const gdcm::Tag DICOM_TAG_PIXEL_DATA(0x7fe0, 0x0010);
-  static const gdcm::Tag DICOM_TAG_ROWS(0x0028, 0x0010);
-  static const gdcm::Tag DICOM_TAG_SAMPLES_PER_PIXEL(0x0028, 0x0002);
-  static const gdcm::Tag DICOM_TAG_PHOTOMETRIC_INTERPRETATION(0x0028, 0x0004);
-
-  if (!dicom.GetDataSet().FindDataElement(DICOM_TAG_PIXEL_DATA))
-  {
-    throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat);
-  }
-
-  const gdcm::DataElement& pixelData = dicom.GetDataSet().GetDataElement(DICOM_TAG_PIXEL_DATA);
-  const gdcm::SequenceOfFragments* fragments = pixelData.GetSequenceOfFragments();
-
-  if (OrthancPluginStartMultipartAnswer(OrthancPlugins::GetGlobalContext(), 
-                                        output, "related", GetMimeType(syntax)) != OrthancPluginErrorCode_Success)
-  {
-    return false;
+    throw Orthanc::OrthancException(Orthanc::ErrorCode_Plugin,
+                                    "Cannot start a multipart answer");
   }
 
-  int samplesPerPixel;
-
-  if (!dicom.GetIntegerTag(samplesPerPixel, DICOM_TAG_SAMPLES_PER_PIXEL))
-  {
-    throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
-  }
-  
-  bool convertYbr = false;
+  const std::string base = OrthancPlugins::Configuration::GetBaseUrl(request);
     
+  for (std::list<unsigned int>::const_iterator
+         frame = frames.begin(); frame != frames.end(); ++frame)
   {
-    std::string photometric;
-    if (samplesPerPixel == 3 &&
-        dicom.GetStringTag(photometric, DICOM_TAG_PHOTOMETRIC_INTERPRETATION, true) &&
-        photometric == "YBR_FULL" &&
-        // Only applicable to uncompressed transfer syntaxes
-        (syntax == Orthanc::DicomTransferSyntax_LittleEndianImplicit ||
-         syntax == Orthanc::DicomTransferSyntax_LittleEndianExplicit ||
-         syntax == Orthanc::DicomTransferSyntax_BigEndianExplicit))
-    {
-      convertYbr = true;
-    }
-  }  
-  
-  if (fragments == NULL)
-  {
-    // Single-fragment image
+    std::string content;
+    instance.GetRawFrame(content, *frame);
+
+    const char* data = content.empty() ? NULL : content.c_str();
+    size_t size = content.size();
+        
+    OrthancPluginErrorCode error;
 
-    if (pixelData.GetByteValue() == NULL)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError,
-                                      "Image was not properly decoded");
-    }
-
-    int width, height, bits;
+#if HAS_SEND_MULTIPART_ITEM_2 == 1
+    std::string location = (
+      OrthancPlugins::Configuration::GetWadoUrl(base, studyInstanceUid, seriesInstanceUid, sopInstanceUid) +
+      "frames/" + boost::lexical_cast<std::string>(*frame + 1));
+    const char *keys[] = { "Content-Location" };
+    const char *values[] = { location.c_str() };
+    error = OrthancPluginSendMultipartItem2(OrthancPlugins::GetGlobalContext(), output, data, size, 1, keys, values);
+#else
+    error = OrthancPluginSendMultipartItem(OrthancPlugins::GetGlobalContext(), output, data, size);
+#endif
 
-    if (!dicom.GetIntegerTag(height, DICOM_TAG_ROWS) ||
-        !dicom.GetIntegerTag(width, DICOM_TAG_COLUMNS) ||
-        !dicom.GetIntegerTag(bits, DICOM_TAG_BITS_ALLOCATED))
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
-    }
-
-    size_t frameSize = height * width * bits * samplesPerPixel / 8;
-
-    if (frameSize == 0)
+    if (error != OrthancPluginErrorCode_Success)
     {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
-    }
-
-    /**
-     * The number of bytes in "pixelData" might not be divisible by
-     * "frameSize", because "pixelData" might contain one padding byte
-     * to have an even number of bytes.
-     * https://bitbucket.org/sjodogne/orthanc/issues/164/
-     **/
-    
-    if (pixelData.GetByteValue()->GetLength() % frameSize != 0 &&
-        (/* allow one padding byte to be present */
-          pixelData.GetByteValue()->GetLength() % 2 == 0 &&
-          pixelData.GetByteValue()->GetLength() % frameSize != 1))
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);      
-    }
-
-    size_t framesCount = pixelData.GetByteValue()->GetLength() / frameSize;
-
-    if (frames.empty())
-    {
-      // If no frame is provided, return all the frames (this is an extension)
-      for (size_t i = 0; i < framesCount; i++)
-      {
-        frames.push_back(i);
-      }
-    }
-
-    const char* buffer = pixelData.GetByteValue()->GetPointer();
-    assert(sizeof(char) == 1);
-
-    for (std::list<unsigned int>::const_iterator 
-           frame = frames.begin(); frame != frames.end(); ++frame)
-    {
-      if (*frame >= framesCount)
-      {
-        throw Orthanc::OrthancException(
-          Orthanc::ErrorCode_ParameterOutOfRange,
-          "Trying to access frame number " + boost::lexical_cast<std::string>(*frame + 1) + 
-          " of an image with " + boost::lexical_cast<std::string>(framesCount) + " frames");
-      }
-      else
-      {
-        const char* p = buffer + (*frame) * frameSize;
-        AnswerSingleFrame(output, request, dicom, p, frameSize, *frame, convertYbr);
-      }
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);      
     }
   }
-  else
-  {
-    // Multi-fragment image, we assume that each fragment corresponds to one frame
-
-    if (frames.empty())
-    {
-      // If no frame is provided, return all the frames (this is an extension)
-      for (size_t i = 0; i < fragments->GetNumberOfFragments(); i++)
-      {
-        frames.push_back(i);
-      }
-    }
-
-    for (std::list<unsigned int>::const_iterator 
-           frame = frames.begin(); frame != frames.end(); ++frame)
-    {
-      if (*frame >= fragments->GetNumberOfFragments())
-      {
-        // TODO A frame is not a fragment, looks like a bug
-        throw Orthanc::OrthancException(
-          Orthanc::ErrorCode_ParameterOutOfRange,
-          "Trying to access frame number " + 
-          boost::lexical_cast<std::string>(*frame + 1) + 
-          " of an image with " + 
-          boost::lexical_cast<std::string>(fragments->GetNumberOfFragments()) + 
-          " frames");
-      }
-      else
-      {
-        AnswerSingleFrame(output, request, dicom,
-                          fragments->GetFragment(*frame).GetByteValue()->GetPointer(),
-                          fragments->GetFragment(*frame).GetByteValue()->GetLength(),
-                          *frame, convertYbr);
-      }
-    }
-  }
-
-  return true;
 }
 
 
-
 void RetrieveFrames(OrthancPluginRestOutput* output,
                     const char* url,
                     const OrthancPluginHttpRequest* request)
@@ -609,12 +421,10 @@
   std::list<unsigned int> frames;
   ParseFrameList(frames, request);
 
-  Json::Value header;
   std::string orthancId, studyInstanceUid, seriesInstanceUid, sopInstanceUid;
   OrthancPlugins::MemoryBuffer content;
   if (LocateInstance(output, orthancId, studyInstanceUid, seriesInstanceUid, sopInstanceUid, request) &&
-      content.RestApiGet("/instances/" + orthancId + "/file", false) &&
-      OrthancPlugins::RestApiGet(header, "/instances/" + orthancId + "/header?simplify", false))
+      content.RestApiGet("/instances/" + orthancId + "/file", false))
   {
     {
       std::string s = "DICOMweb RetrieveFrames on " + orthancId + ", frames: ";
@@ -627,81 +437,23 @@
       OrthancPlugins::LogInfo(s);
     }
 
-    std::auto_ptr<OrthancPlugins::GdcmParsedDicomFile> source;
-    
-    Orthanc::DicomTransferSyntax sourceSyntax;
+    Orthanc::DicomTransferSyntax targetSyntax;
 
-    if (header.type() == Json::objectValue &&
-        header.isMember("TransferSyntaxUID"))
-    {
-      std::string uid = header["TransferSyntaxUID"].asString();
-      if (!Orthanc::LookupTransferSyntax(sourceSyntax, uid))
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange,
-                                        "Unknown transfer syntax: " + uid);
-      }
-    }
-    else
+    std::unique_ptr<OrthancPlugins::DicomInstance> instance;
+    if (ParseTransferSyntax(targetSyntax, request))
     {
-      source.reset(new OrthancPlugins::GdcmParsedDicomFile(content));
-      sourceSyntax = source->GetTransferSyntax();
-    }
-
-    Orthanc::DicomTransferSyntax targetSyntax = ParseTransferSyntax(request, sourceSyntax);
+      OrthancPlugins::LogInfo("DICOMweb RetrieveFrames: Transcoding instance " + orthancId + 
+                              " to transfer syntax " + Orthanc::GetTransferSyntaxUid(targetSyntax));
 
-    if (sourceSyntax == targetSyntax)
-    {
-      // No need to change the transfer syntax
-
-      if (source.get() == NULL)
-      {
-        source.reset(new OrthancPlugins::GdcmParsedDicomFile(content));
-      }
-
-      AnswerFrames(output, request, *source, targetSyntax, frames);
+      instance.reset(OrthancPlugins::DicomInstance::Transcode(
+                       content.GetData(), content.GetSize(), GetTransferSyntaxUid(targetSyntax)));
     }
     else
     {
-      // Need to convert the transfer syntax (transcoding)
-      OrthancPlugins::LogInfo("DICOMweb RetrieveFrames: Transcoding instance " + orthancId + 
-                              " from transfer syntax " + Orthanc::GetTransferSyntaxUid(sourceSyntax) +
-                              " to " + Orthanc::GetTransferSyntaxUid(targetSyntax));
-
-      gdcm::ImageChangeTransferSyntax change;
-      change.SetTransferSyntax(OrthancPlugins::GdcmParsedDicomFile::GetGdcmTransferSyntax(targetSyntax));
-
-      // TODO Avoid this unnecessary memcpy by defining a stream over the MemoryBuffer
-      std::string dicom(content.GetData(), content.GetData() + content.GetSize());
-      std::stringstream stream(dicom);
-
-      gdcm::ImageReader reader;
-      reader.SetStream(stream);
-      if (!reader.Read())
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat,
-                                        "Cannot decode the image");
-      }
+      instance.reset(new OrthancPlugins::DicomInstance(content.GetData(), content.GetSize()));
+    }
 
-      change.SetInput(reader.GetImage());
-      if (!change.Change())
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError,
-                                        "Cannot change the transfer syntax of the image");
-      }
-
-      gdcm::ImageWriter writer;
-      writer.SetImage(change.GetOutput());
-      writer.SetFile(reader.GetFile());
-      
-      std::stringstream ss;
-      writer.SetStream(ss);
-      if (!writer.Write())
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_NotEnoughMemory);
-      }
-
-      OrthancPlugins::GdcmParsedDicomFile transcoded(ss.str());
-      AnswerFrames(output, request, transcoded, targetSyntax, frames);
-    }
+    AnswerFrames(output, request, *instance, studyInstanceUid, seriesInstanceUid,
+                 sopInstanceUid, frames, targetSyntax);
   }    
 }
--- a/Resources/CMake/GdcmConfiguration.cmake	Tue May 26 11:05:10 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,144 +0,0 @@
-# Orthanc - A Lightweight, RESTful DICOM Store
-# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
-# Department, University Hospital of Liege, Belgium
-# Copyright (C) 2017-2020 Osimis S.A., Belgium
-#
-# This program is free software: you can redistribute it and/or
-# modify it under the terms of the GNU Affero General Public License
-# as published by the Free Software Foundation, either version 3 of
-# the License, or (at your option) any later version.
-#
-# 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
-# Affero General Public License for more details.
-# 
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-
-if (STATIC_BUILD OR NOT USE_SYSTEM_GDCM)
-  if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR
-      ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" OR
-      ${CMAKE_SYSTEM_NAME} STREQUAL "kFreeBSD")
-    # If using gcc, build GDCM with the "-fPIC" argument to allow its
-    # embedding into the shared library containing the Orthanc plugin
-    set(AdditionalFlags "-fPIC")
-  elseif (${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
-    # This definition is necessary to compile
-    # "Source/MediaStorageAndFileFormat/gdcmFileStreamer.cxx"
-    set(AdditionalFlags "-Doff64_t=off_t") 
-  endif()
-  
-  set(Flags
-    "-DCMAKE_C_FLAGS=${CMAKE_C_FLAGS} ${AdditionalFlags}"
-    "-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} ${AdditionalFlags}"
-    -DCMAKE_C_FLAGS_DEBUG=${CMAKE_C_FLAGS_DEBUG}
-    -DCMAKE_CXX_FLAGS_DEBUG=${CMAKE_CXX_FLAGS_DEBUG}
-    -DCMAKE_C_FLAGS_RELEASE=${CMAKE_C_FLAGS_RELEASE}
-    -DCMAKE_CXX_FLAGS_RELEASE=${CMAKE_CXX_FLAGS_RELEASE}
-    -DCMAKE_C_FLAGS_MINSIZEREL=${CMAKE_C_FLAGS_MINSIZEREL}
-    -DCMAKE_CXX_FLAGS_MINSIZEREL=${CMAKE_CXX_FLAGS_MINSIZEREL} 
-    -DCMAKE_C_FLAGS_RELWITHDEBINFO=${CMAKE_C_FLAGS_RELWITHDEBINFO} 
-    -DCMAKE_CXX_FLAGS_RELWITHDEBINFO=${CMAKE_CXX_FLAGS_RELWITHDEBINFO}
-    )
-
-  if (CMAKE_TOOLCHAIN_FILE)
-    # Take absolute path to the toolchain
-    get_filename_component(TMP ${CMAKE_TOOLCHAIN_FILE} REALPATH BASE ${CMAKE_SOURCE_DIR})
-    list(APPEND Flags -DCMAKE_TOOLCHAIN_FILE=${TMP})
-  endif()
-
-  # Don't build manpages (since gdcm 2.8.4)
-  list(APPEND Flags -DGDCM_BUILD_DOCBOOK_MANPAGES=OFF)
-
-  if ("${CMAKE_SYSTEM_VERSION}" STREQUAL "LinuxStandardBase")
-    # Trick to disable the compilation of socket++ by gdcm, which is
-    # incompatible with LSB, but fortunately only required for DICOM
-    # Networking
-    list(APPEND Flags -DGDCM_USE_SYSTEM_SOCKETXX=ON)
-
-    # Detect the number of CPU cores to run "make" with as much
-    # parallelism as possible
-    include(ProcessorCount)
-    ProcessorCount(N)
-    if (NOT N EQUAL 0)
-      set(MAKE_PARALLEL -j${N})
-    endif()
-      
-    # For Linux Standard Base, avoid building incompatible target gdcmMEXD (*)
-    set(BUILD_COMMAND BUILD_COMMAND
-      ${CMAKE_MAKE_PROGRAM} ${MAKE_PARALLEL}
-      gdcmMSFF gdcmcharls gdcmDICT gdcmDSED gdcmIOD gdcmjpeg8
-      gdcmjpeg12 gdcmjpeg16 gdcmopenjp2 gdcmzlib gdcmCommon gdcmexpat gdcmuuid)
-  endif()
-
-  include(ExternalProject)
-  externalproject_add(GDCM
-    URL "http://orthanc.osimis.io/ThirdPartyDownloads/gdcm-3.0.4.tar.gz"
-    URL_MD5 "f12dbded708356d5fa0b5ed37ccdb66e"
-    TIMEOUT 60
-    CMAKE_ARGS -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} ${Flags}
-    ${BUILD_COMMAND}    # Customize "make", only for Linux Standard Base (*)
-    INSTALL_COMMAND ""  # Skip the install step
-    )
-
-  if(MSVC)
-    set(Suffix ".lib")
-    set(Prefix "")
-  else()
-    set(Suffix ".a")
-    list(GET CMAKE_FIND_LIBRARY_PREFIXES 0 Prefix)
-  endif()
-
-  set(GDCM_LIBRARIES
-    # WARNING: The order of the libraries below *is* important!
-    ${Prefix}gdcmMSFF${Suffix}
-    ${Prefix}gdcmcharls${Suffix}
-    ${Prefix}gdcmDICT${Suffix}
-    ${Prefix}gdcmDSED${Suffix}
-    ${Prefix}gdcmIOD${Suffix}
-    ${Prefix}gdcmjpeg8${Suffix}
-    ${Prefix}gdcmjpeg12${Suffix}
-    ${Prefix}gdcmjpeg16${Suffix}
-    ${Prefix}gdcmopenjp2${Suffix}
-    ${Prefix}gdcmzlib${Suffix}
-    ${Prefix}gdcmCommon${Suffix}
-    ${Prefix}gdcmexpat${Suffix}
-
-    #${Prefix}socketxx${Suffix}
-    #${Prefix}gdcmMEXD${Suffix}  # DICOM Networking, unneeded by Orthanc plugins
-    #${Prefix}gdcmgetopt${Suffix}
-    )
-
-  if (${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
-    list(APPEND GDCM_LIBRARIES
-      rpcrt4   # For UUID stuff
-      )
-  else()
-    list(APPEND GDCM_LIBRARIES
-      ${Prefix}gdcmuuid${Suffix}
-      )
-  endif()
-
-  ExternalProject_Get_Property(GDCM binary_dir)
-  include_directories(${binary_dir}/Source/Common)
-  link_directories(${binary_dir}/bin)
-
-  ExternalProject_Get_Property(GDCM source_dir)
-  include_directories(
-    ${source_dir}/Source/Common
-    ${source_dir}/Source/DataDictionary
-    ${source_dir}/Source/MediaStorageAndFileFormat
-    ${source_dir}/Source/DataStructureAndEncodingDefinition
-    )
-
-else()
-  find_package(GDCM REQUIRED)
-  if (GDCM_FOUND)
-    include(${GDCM_USE_FILE})
-    set(GDCM_LIBRARIES gdcmCommon gdcmMSFF)
-  else(GDCM_FOUND)
-    message(FATAL_ERROR "Cannot find GDCM, did you set GDCM_DIR?")
-  endif(GDCM_FOUND)
-endif()
--- a/Resources/GenerateTransferSyntaxes.mustache	Tue May 26 11:05:10 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * 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
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-// This file is autogenerated by "../Resources/GenerateTransferSyntaxes.py"
-
-namespace OrthancPlugins
-{
-  gdcm::TransferSyntax GdcmParsedDicomFile::GetGdcmTransferSyntax(Orthanc::DicomTransferSyntax syntax)
-  {
-    switch (syntax)
-    {
-      {{#Syntaxes}}
-      {{#GDCM}}
-      case Orthanc::DicomTransferSyntax_{{Value}}:
-        return {{GDCM}};
-
-      {{/GDCM}}
-      {{/Syntaxes}}
-      default:
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-    }
-  }
-
-
-  Orthanc::DicomTransferSyntax GdcmParsedDicomFile::GetOrthancTransferSyntax(gdcm::TransferSyntax syntax)
-  {
-    switch (syntax)
-    {
-      {{#Syntaxes}}
-      {{#GDCM}}
-      case {{GDCM}}:
-        return Orthanc::DicomTransferSyntax_{{Value}};
-
-      {{/GDCM}}
-      {{/Syntaxes}}
-      default:
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-    }
-  }
-}
--- a/Resources/GenerateTransferSyntaxes.py	Tue May 26 11:05:10 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-#!/usr/bin/python
-
-# Orthanc - A Lightweight, RESTful DICOM Store
-# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
-# Department, University Hospital of Liege, Belgium
-# Copyright (C) 2017-2020 Osimis S.A., 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/>.
-
-
-import json
-import os
-import pystache
-
-ORTHANC_ROOT = '/home/jodogne/Subversion/orthanc/'
-BASE = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
-
-
-with open(os.path.join(ORTHANC_ROOT, 'Resources', 'DicomTransferSyntaxes.json'), 'r') as f:
-    SYNTAXES = json.loads(f.read())
-
-
-with open(os.path.join(BASE, 'Plugin', 'GdcmParsedDicomFile_TransferSyntaxes.impl.h'), 'w') as b:
-    with open(os.path.join(BASE, 'Resources', 'GenerateTransferSyntaxes.mustache'), 'r') as a:
-        b.write(pystache.render(a.read(), {
-            'Syntaxes' : SYNTAXES
-        }))
--- a/Resources/Orthanc/DownloadOrthancFramework.cmake	Tue May 26 11:05:10 2020 +0200
+++ b/Resources/Orthanc/DownloadOrthancFramework.cmake	Tue May 26 11:06:13 2020 +0200
@@ -70,6 +70,12 @@
       set(ORTHANC_FRAMEWORK_MINOR 999)
       set(ORTHANC_FRAMEWORK_REVISION 999)
 
+    elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "transcoding")  # TODO - REMOVE
+      set(ORTHANC_FRAMEWORK_BRANCH "transcoding")
+      set(ORTHANC_FRAMEWORK_MAJOR 999)
+      set(ORTHANC_FRAMEWORK_MINOR 999)
+      set(ORTHANC_FRAMEWORK_REVISION 999)
+
     else()
       set(ORTHANC_FRAMEWORK_BRANCH "Orthanc-${ORTHANC_FRAMEWORK_VERSION}")