[med-svn] [Git][med-team/orthanc-dicomweb][upstream] New upstream version 1.24+dfsg

Sebastien Jodogne (@jodogne-guest) gitlab at salsa.debian.org
Thu Sep 10 13:32:43 BST 2026



Sebastien Jodogne pushed to branch upstream at Debian Med / orthanc-dicomweb


Commits:
0df45877 by jodogne-guest at 2026-09-10T14:20:29+02:00
New upstream version 1.24+dfsg
- - - - -


26 changed files:

- + .github/workflows/mirror.yml
- .hg_archival.txt
- CMakeLists.txt
- NEWS
- Plugin/Configuration.cpp
- Plugin/Configuration.h
- Plugin/DicomWebClient.cpp
- Plugin/DicomWebFormatter.cpp
- Plugin/DicomWebFormatter.h
- Plugin/DicomWebServers.cpp
- Plugin/Plugin.cpp
- Plugin/QidoRs.cpp
- Plugin/StowRs.cpp
- Plugin/WadoRs.cpp
- Plugin/WadoRsRetrieveFrames.cpp
- Plugin/WadoRsRetrieveRendered.cpp
- Plugin/WadoUri.cpp
- Resources/Orthanc/CMake/DownloadOrthancFramework.cmake
- Resources/Orthanc/CMake/DownloadPackage.cmake
- Resources/Orthanc/Plugins/OrthancPluginCppWrapper.cpp
- Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h
- Resources/Orthanc/Plugins/OrthancPluginException.h
- Resources/SyncOrthancFolder.py
- TODO
- WebApplication/app.js
- WebApplication/index.html


Changes:

=====================================
.github/workflows/mirror.yml
=====================================
@@ -0,0 +1,53 @@
+name: Mirror Mercurial to GitHub
+
+env:
+  HG_SOURCE_URL: https://orthanc.uclouvain.be/hg/orthanc-dicomweb/
+  GITHUB_TARGET: jodogne/mirror-orthanc-dicomweb
+  FAST_EXPORT_VERSION: fast-export-260405
+
+on:
+  schedule:
+    - cron: '20 2 * * *'
+  workflow_dispatch: {}
+
+concurrency:
+  group: hg-mirror
+  cancel-in-progress: false
+
+permissions:
+  contents: write  # GITHUB_TOKEN is read-only by default; this grants push access
+
+jobs:
+  mirror:
+    if: ${{ github.repository == 'jodogne/mirror-orthanc-dicomweb' }}
+    runs-on: ubuntu-slim
+    steps:
+      - name: Install Mercurial and fast-export
+        run: |
+          sudo apt-get update
+          DEBIAN_FRONTEND=noninteractive sudo apt-get install -y mercurial git wget
+          cd /tmp/
+          wget https://orthanc.uclouvain.be/downloads/third-party-downloads/${FAST_EXPORT_VERSION}.tar.gz
+          tar xvf ${FAST_EXPORT_VERSION}.tar.gz
+
+      - name: Full clone of Mercurial source
+        run: |
+          hg clone "${HG_SOURCE_URL}" hg-repo
+
+      - name: Full conversion (fresh git repo every run)
+        run: |
+          git init --bare git-repo
+          cd git-repo
+          /tmp/${FAST_EXPORT_VERSION}/hg-fast-export.sh -r ../hg-repo --hg-hash --hgtags
+
+      - name: Push (never force, so it can't rewrite or delete history)
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        run: |
+          cd git-repo
+          git remote add origin "https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_TARGET}"
+          git push origin 'refs/heads/*:refs/heads/*'
+          git push origin 'refs/tags/*:refs/tags/*'
+          if git rev-parse --verify refs/notes/hg >/dev/null 2>&1; then
+            git push origin refs/notes/hg:refs/notes/hg
+          fi


=====================================
.hg_archival.txt
=====================================
@@ -1,6 +1,6 @@
 repo: d5f45924411123cfd02d035fd50b8e37536eadef
-node: 99493d452c72d2a99b748f2394888e09e391cf4e
-branch: OrthancDicomWeb-1.23
+node: 93298adf159db7b1a57e4e1e206b67c0d5a21062
+branch: OrthancDicomWeb-1.24
 latesttag: null
-latesttagdistance: 666
-changessincelatesttag: 719
+latesttagdistance: 694
+changessincelatesttag: 747


=====================================
CMakeLists.txt
=====================================
@@ -23,15 +23,13 @@ cmake_minimum_required(VERSION 2.8...4.0)
 
 project(OrthancDicomWeb)
 
-set(ORTHANC_DICOM_WEB_VERSION "1.23")
+set(ORTHANC_DICOM_WEB_VERSION "1.24")
 
 if (ORTHANC_DICOM_WEB_VERSION STREQUAL "mainline")
   set(ORTHANC_FRAMEWORK_DEFAULT_VERSION "mainline")
   set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "hg")
 else()
-  # Do NOT use framework 1.12.11 here, which is buggy because of:
-  # https://orthanc.uclouvain.be/hg/orthanc/rev/3bd8715e21bc
-  set(ORTHANC_FRAMEWORK_DEFAULT_VERSION "1.12.10")
+  set(ORTHANC_FRAMEWORK_DEFAULT_VERSION "1.13.0")
   set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "web")
 endif()
 
@@ -48,7 +46,7 @@ set(ORTHANC_FRAMEWORK_ROOT "" CACHE STRING "Path to the Orthanc source directory
 
 # Advanced parameters to fine-tune linking against system libraries
 set(USE_SYSTEM_ORTHANC_SDK ON CACHE BOOL "Use the system version of the Orthanc plugin SDK")
-set(ORTHANC_SDK_VERSION "1.12.4" CACHE STRING "Version of the Orthanc plugin SDK to use, if not using the system version (can be \"framework\" or \"1.12.1\" or \"1.12.4\")")
+set(ORTHANC_SDK_VERSION "1.13.0" CACHE STRING "Version of the Orthanc plugin SDK to use, if not using the system version (can be \"framework\" or \"1.12.1\" or \"1.12.4\" or \"1.13.0\")")
 set(ORTHANC_FRAMEWORK_STATIC OFF CACHE BOOL "If linking against the Orthanc framework system library, indicates whether this library was statically linked")
 mark_as_advanced(ORTHANC_FRAMEWORK_STATIC)
 
@@ -109,6 +107,8 @@ if (STATIC_BUILD OR NOT USE_SYSTEM_ORTHANC_SDK)
     include_directories(${CMAKE_SOURCE_DIR}/Resources/Orthanc/Sdk-1.12.1)
   elseif(ORTHANC_SDK_VERSION STREQUAL "1.12.4")
     include_directories(${CMAKE_SOURCE_DIR}/Resources/Orthanc/Sdk-1.12.4)
+  elseif(ORTHANC_SDK_VERSION STREQUAL "1.13.0")
+    include_directories(${CMAKE_SOURCE_DIR}/Resources/Orthanc/Sdk-1.13.0)
   elseif (ORTHANC_SDK_VERSION STREQUAL "framework")
     include_directories(${ORTHANC_FRAMEWORK_ROOT}/../../OrthancServer/Plugins/Include/)
   else()


=====================================
NEWS
=====================================
@@ -1,5 +1,25 @@
-Pending changes in the mainline
-===============================
+Version 1.24 (2026-08-19)
+=========================
+
+=> Minimum Orthanc version: 1.12.5 <=
+=> Recommended SDK version: 1.13.0 <=
+=> Minimum SDK version: 1.12.1 <=
+
+
+* Allow searching DICOM resources by "Study ID" in the DICOMweb client Web user interface.
+* New configuration option "OtherBinaryMode" to control how OB, OD,
+  OF, OL, OV, or OW value representations are encoded in WADO-RS
+  responses. Possible values: "BulkDataURI" (new default),
+  "InlineBinary", "ArrayOfValues". Previous releases used the
+  "ArrayOfValues" mode, which was valid but could be problematic for
+  large datasets. (contribution by Yusuf Sayita, Philips)
+* Clarified error message when trying to access a single frame from a video in WADO-RS.
+* Fixed rendering of multiframe RGB48 images.
+* Fix: If the metadata cache attachment is missing from disk for a series, it is now generated again.
+
+
+Version 1.23 (2026-04-15)
+=========================
 
 * If calling "/rendered" route on a Encapsulated PDF, the plugin will now return the PDF file
   instead of generating a 400 error.


=====================================
Plugin/Configuration.cpp
=====================================
@@ -43,6 +43,7 @@ static std::unique_ptr<OrthancPlugins::OrthancConfiguration> dicomWebConfigurati
 static std::unique_ptr<OrthancPlugins::OrthancConfiguration> globalConfiguration_;
 static bool serversInDatabase_ = false;
 static const int32_t GLOBAL_PROPERTY_SERVERS = 5468;
+static OrthancPluginDicomWebBinaryMode otherBinaryMode_ = OrthancPluginDicomWebBinaryMode_BulkDataUri;
 
 
 namespace OrthancPlugins
@@ -194,7 +195,9 @@ namespace OrthancPlugins
   {
     if (json.type() != Json::objectValue)
     {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+      throw Orthanc::OrthancException(
+        Orthanc::ErrorCode_BadFileFormat,
+        "The field \"" + key + "\" must be a JSON object");
     }
     else if (!json.isMember(key))
     {
@@ -220,7 +223,7 @@ namespace OrthancPlugins
   {
     if (json.type() != Json::objectValue)
     {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+      ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_BadFileFormat);
     }
     else if (!json.isMember(key))
     {
@@ -229,7 +232,7 @@ namespace OrthancPlugins
     else if (json[key].type() != Json::intValue &&
              json[key].type() != Json::uintValue)
     {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);      
+      ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_BadFileFormat);
     }
     else
     {
@@ -245,7 +248,7 @@ namespace OrthancPlugins
   {
     if (json.type() != Json::objectValue)
     {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+      ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_BadFileFormat);
     }
     else if (!json.isMember(key))
     {
@@ -253,7 +256,7 @@ namespace OrthancPlugins
     }
     else if (json[key].type() != Json::booleanValue)
     {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);      
+      ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_BadFileFormat);
     }
     else
     {
@@ -297,6 +300,15 @@ namespace OrthancPlugins
       }
     }
 
+
+    static std::string GetStringValue(const std::string& key,
+                                      const std::string& defaultValue)
+    {
+      assert(dicomWebConfiguration_.get() != NULL);
+      return dicomWebConfiguration_->GetStringValue(key, defaultValue);
+    }
+
+
     void Initialize()
     {
       dicomWebConfiguration_.reset(new OrthancConfiguration);
@@ -335,14 +347,43 @@ namespace OrthancPlugins
       GetExtrapolatedMetadataTags(tags, Orthanc::ResourceType_Series);
 
       LoadMainDicomTags(globalConfiguration_->GetJson());
-    }
 
+      {
+        // New in DICOMweb 1.24
+        static const std::string OTHER_BINARY_MODE = "OtherBinaryMode";
+        static const std::string BULK_DATA_URI = "BulkDataURI";
+        static const std::string INLINE_BINARY = "InlineBinary";
+        static const std::string ARRAY_OF_VALUES = "ArrayOfValues";
 
-    std::string GetStringValue(const std::string& key,
-                               const std::string& defaultValue)
-    {
-      assert(dicomWebConfiguration_.get() != NULL);
-      return dicomWebConfiguration_->GetStringValue(key, defaultValue);
+        std::string value = GetStringValue(OTHER_BINARY_MODE, BULK_DATA_URI);
+
+        if (value == BULK_DATA_URI)
+        {
+          otherBinaryMode_ = OrthancPluginDicomWebBinaryMode_BulkDataUri;
+        }
+        else if (value == INLINE_BINARY)
+        {
+          otherBinaryMode_ = OrthancPluginDicomWebBinaryMode_InlineBinary;
+        }
+        else if (value == ARRAY_OF_VALUES)
+        {
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 13, 0)
+          otherBinaryMode_ = OrthancPluginDicomWebBinaryMode_ArrayOfValues;
+#else
+          LOG(WARNING) << "You need to compile the DICOMweb plugin against Orthanc SDK >= 1.13.0 to use the \""
+                       << ARRAY_OF_VALUES << "\" value in option \"" << OTHER_BINARY_MODE
+                       << "\", fallback to default \"" << BULK_DATA_URI << "\"";
+          otherBinaryMode_ = OrthancPluginDicomWebBinaryMode_BulkDataUri;
+#endif
+        }
+        else
+        {
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange,
+                                          "Bad value for option \"" + OTHER_BINARY_MODE + "\""
+                                          ": Should be either \"" + BULK_DATA_URI + "\" or \"" +
+                                          INLINE_BINARY + "\" or \"" + ARRAY_OF_VALUES + "\"");
+        }
+      }
     }
 
 
@@ -720,7 +761,7 @@ namespace OrthancPlugins
           break;
 
         default:
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+          ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_ParameterOutOfRange);
       }
 
       std::string value = GetStringValue(key, FULL);
@@ -787,7 +828,7 @@ namespace OrthancPlugins
           break;
 
         default:
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+          ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_ParameterOutOfRange);
       }
     }
 
@@ -843,5 +884,11 @@ namespace OrthancPlugins
         }
       }
     }
+
+
+    OrthancPluginDicomWebBinaryMode GetOtherBinaryMode()
+    {
+      return otherBinaryMode_;
+    }
   }
 }


=====================================
Plugin/Configuration.h
=====================================
@@ -148,5 +148,7 @@ namespace OrthancPlugins
     bool IsReadOnly();
 
     bool IsPerformanceLogsEnabled();
+
+    OrthancPluginDicomWebBinaryMode GetOtherBinaryMode();
   }
 }


=====================================
Plugin/DicomWebClient.cpp
=====================================
@@ -118,7 +118,7 @@ protected:
 
     if (factory_ != NULL)
     {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+      ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
     else
     {
@@ -281,7 +281,7 @@ public:
   {
     if (factory_ == NULL)
     {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+      ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
 
     FunctionResult result;
@@ -307,7 +307,7 @@ public:
         return OrthancPluginJobStepStatus_Failure;
 
       default:
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+        ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_InternalError);
     }
   }
 
@@ -526,7 +526,7 @@ static void ParseStowRequest(std::list<std::string>& instances /* out */,
   {
     if (resources[i].type() != Json::stringValue)
     {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+      ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_BadFileFormat);
     }
 
     std::string resource = resources[i].asString();
@@ -553,7 +553,7 @@ static void ParseStowRequest(std::list<std::string>& instances /* out */,
     {
       if (tmpInstances.type() != Json::arrayValue)
       {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+        ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_InternalError);
       }
 
       AddResourceForJobContent(resourcesForJobContent, Orthanc::StringToResourceType(tmpResource["Type"].asString().c_str()), resource);
@@ -1068,7 +1068,7 @@ private:
     }
     else
     {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);      
+      ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_InternalError);
     }
 
     if (debug_)
@@ -1116,7 +1116,7 @@ public:
     }
     else if (state_ != State_Headers)
     {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+      ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_InternalError);
     }
 
     if (boost::iequals(key, "Content-Type"))
@@ -1299,7 +1299,7 @@ private:
       const Resource* resource = resources_[position_++];
       if (resource == NULL)
       {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+        ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_InternalError);
       }
 
       const std::map<std::string, std::string>& headers = resource->GetAdditionalHeaders();


=====================================
Plugin/DicomWebFormatter.cpp
=====================================
@@ -22,6 +22,7 @@
 
 
 #include "DicomWebFormatter.h"
+#include "Configuration.h"
 
 #include "../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h"
 
@@ -54,11 +55,36 @@ namespace OrthancPlugins
   {
     const DicomWebFormatter& that = *reinterpret_cast<const DicomWebFormatter*>(payload);
 
-    switch (that.mode_)
+    /**
+     * For "Other" binary VRs (OF, OD, OL) that now come through
+     * VisitDoubles/VisitIntegers, apply the configured otherBinaryMode_.
+     **/
+    OrthancPluginDicomWebBinaryMode effectiveMode = that.mode_;
+
+    if (vr == OrthancPluginValueRepresentation_OB ||
+        vr == OrthancPluginValueRepresentation_OF ||
+        vr == OrthancPluginValueRepresentation_OW)
+    {
+      effectiveMode = that.otherBinaryMode_;
+    }
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 13, 0)
+    else if (vr == OrthancPluginValueRepresentation_OD ||
+             vr == OrthancPluginValueRepresentation_OL ||
+             vr == OrthancPluginValueRepresentation_OV)
+    {
+      effectiveMode = that.otherBinaryMode_;
+    }
+#endif
+
+    switch (effectiveMode)
     {
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 13, 0)
+      case OrthancPluginDicomWebBinaryMode_ArrayOfValues:
+#endif
+
       case OrthancPluginDicomWebBinaryMode_Ignore:
       case OrthancPluginDicomWebBinaryMode_InlineBinary:
-        setter(node, that.mode_, NULL);
+        setter(node, effectiveMode, NULL);
         break;
 
       case OrthancPluginDicomWebBinaryMode_BulkDataUri:
@@ -73,13 +99,25 @@ namespace OrthancPlugins
     
         uri += "/" + FormatTag(tagGroup, tagElement);
     
-        setter(node, that.mode_, uri.c_str());
+        setter(node, effectiveMode, uri.c_str());
         break;
       }
+
+    default:
+        ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_ParameterOutOfRange);
     }
   }
 
 
+  DicomWebFormatter::DicomWebFormatter(OrthancPluginDicomWebBinaryMode mode,
+                                       const std::string& bulkRoot) :
+    mode_(mode),
+    otherBinaryMode_(Configuration::GetOtherBinaryMode()),
+    bulkRoot_(bulkRoot)
+  {
+  }
+
+
   void DicomWebFormatter::Apply(std::string& target,
                                 OrthancPluginContext* context,
                                 const void* data,
@@ -168,7 +206,7 @@ namespace OrthancPlugins
     if (context_ == NULL ||
         (isXml_ && output_ == NULL))  // allow no output when working with Json output.
     {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
+      ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_NullPointer);
     }
 
     if (isXml_)


=====================================
Plugin/DicomWebFormatter.h
=====================================
@@ -39,6 +39,7 @@ namespace OrthancPlugins
   {
   private:
     OrthancPluginDicomWebBinaryMode  mode_;
+    OrthancPluginDicomWebBinaryMode  otherBinaryMode_;
     std::string                      bulkRoot_;
 
     static void Callback(OrthancPluginDicomWebNode *node,
@@ -53,11 +54,7 @@ namespace OrthancPlugins
                          void* payload);
 
     DicomWebFormatter(OrthancPluginDicomWebBinaryMode mode,
-                      const std::string& bulkRoot) :
-      mode_(mode),
-      bulkRoot_(bulkRoot)
-    {
-    }
+                      const std::string& bulkRoot);
     
   public:
     static void Apply(std::string& target,


=====================================
Plugin/DicomWebServers.cpp
=====================================
@@ -321,7 +321,7 @@ namespace OrthancPlugins
 
     if (json.type() != Json::objectValue)
     {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+      ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_InternalError);
     }
 
     Json::Value::Members members = json.getMemberNames();
@@ -331,7 +331,7 @@ namespace OrthancPlugins
 
       if (json[key].type() != Json::stringValue)
       {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+        ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_InternalError);
       }
       else
       {


=====================================
Plugin/Plugin.cpp
=====================================
@@ -508,7 +508,8 @@ static OrthancPluginErrorCode OnChangeCallback(OrthancPluginChangeType changeTyp
           }
         }
 
-      }; break;
+        break;
+      }
 
       case OrthancPluginChangeType_StableSeries:
         if (!OrthancPlugins::Configuration::IsReadOnly())
@@ -736,7 +737,7 @@ extern "C"
               root[0] != '/' ||
               root[root.size() - 1] != '/')
           {
-            throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+            ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_InternalError);
           }
 
           std::map<std::string, std::string> dictionary;


=====================================
Plugin/QidoRs.cpp
=====================================
@@ -113,7 +113,7 @@ namespace
           break;
 
         default:
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+          ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_InternalError);
       }
     }
 
@@ -275,7 +275,7 @@ namespace
           break;
 
         default:
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+          ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_InternalError);
       }
 
       bool caseSensitive;
@@ -413,7 +413,7 @@ static void ApplyMatcher(OrthancPluginRestOutput* output,
   if (!OrthancPlugins::RestApiPost(resources, "/tools/find", find, httpHeaders, true) ||
       resources.type() != Json::arrayValue)
   {
-    throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+    ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_InternalError);
   }
 
   std::string wadoBasePublicUrl = OrthancPlugins::Configuration::GetBasePublicUrl(request);


=====================================
Plugin/StowRs.cpp
=====================================
@@ -300,7 +300,7 @@ namespace OrthancPlugins
   
     if (request->method != OrthancPluginHttpMethod_Post)
     {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+      ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_InternalError);
     }
 
     std::map<std::string, std::string> headers;


=====================================
Plugin/WadoRs.cpp
=====================================
@@ -61,6 +61,17 @@ static bool isSystemReadOnly_ = false;
 
 static boost::mutex preloaderThreadsCounterMutex;
 static uint32_t preloaderThreadsCounter = 0;
+
+static std::string GetWadoLoaderThreadName()
+{
+  boost::mutex::scoped_lock lock(preloaderThreadsCounterMutex);
+  std::string threadName = std::string("WADO-LOAD-") + boost::lexical_cast<std::string>(preloaderThreadsCounter++);
+  preloaderThreadsCounter %= 1000000;
+
+  return threadName;
+}
+
+
 static WeightedAverageMetrics<float> wadorsAverageBandwidth(300);
 
 static boost::mutex wadoRsTotalBytesTransferredMutex;
@@ -107,7 +118,7 @@ static std::string GetResourceUri(Orthanc::ResourceType level,
       return "/instances/" + publicId;
       
     default:
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+      ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_InternalError);
   }
 }
 
@@ -530,11 +541,9 @@ public:
 
   static void PreloaderWorkerThread(ThreadedInstanceLoader* that)
   {
-    {
-      boost::mutex::scoped_lock lock(preloaderThreadsCounterMutex);
-      Orthanc::Logging::SetCurrentThreadName(std::string("WADO-LOAD-") + boost::lexical_cast<std::string>(preloaderThreadsCounter++));
-      preloaderThreadsCounter %= 1000000;
-    }
+#if ORTHANC_FRAMEWORK_VERSION_IS_ABOVE(1, 13, 0)
+    Orthanc::Logging::ScopedCurrentThreadNameSetter setter(GetWadoLoaderThreadName());
+#endif
 
     LOG(INFO) << "Loader thread has started";
 
@@ -781,7 +790,7 @@ static void AnswerListOfDicomInstances(OrthancPluginRestOutput* output,
           context, output, reinterpret_cast<const char*>(dicom->GetBuffer()),
           dicom->GetSize()) != 0)
       {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+        ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_InternalError);
       }
       perfTotalSizeInBytes += dicom->GetSize();
 
@@ -962,7 +971,7 @@ namespace
       if (value.type() != Json::objectValue ||
           !value.isMember(MAIN_DICOM_TAGS))
       {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+        ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_InternalError);
       }
 
       dicom.FromDicomAsJson(value[MAIN_DICOM_TAGS], false /* append */, true /* parseSequences */);
@@ -975,7 +984,7 @@ namespace
         }
         else
         {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+          ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_InternalError);
         }
       }
         
@@ -988,7 +997,7 @@ namespace
         }
         else
         {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+          ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_InternalError);
         }
       }
 
@@ -1022,7 +1031,7 @@ namespace
               if (!value.isMember(INSTANCES) ||
                   value[INSTANCES].type() != Json::arrayValue)
               {
-                throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+                ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_InternalError);
               }
               else
               {
@@ -1030,7 +1039,7 @@ namespace
                 {
                   if (value[INSTANCES][i].type() != Json::stringValue)
                   {
-                    throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+                    ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_InternalError);
                   }
                   else
                   {
@@ -1048,7 +1057,7 @@ namespace
               {
                 if (tmp.type() != Json::arrayValue)
                 {
-                  throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+                  ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_InternalError);
                 }
 
                 for (Json::Value::ArrayIndex i = 0; i < tmp.size(); i++)
@@ -1057,7 +1066,7 @@ namespace
                       !tmp[i].isMember("ID") ||
                       tmp[i]["ID"].type() != Json::stringValue)
                   {
-                    throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+                    ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_InternalError);
                   }
                   else
                   {
@@ -1070,7 +1079,7 @@ namespace
             }
 
             default:
-              throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+              ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_NotImplemented);
           }
 
           
@@ -1228,7 +1237,7 @@ static void WriteInstanceMetadata(OrthancPlugins::DicomWebFormatter::HttpWriter&
     }
 
     default:
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+      ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_InternalError);
   }
     
   
@@ -1337,7 +1346,7 @@ bool LocateResource(OrthancPluginRestOutput* output,
     if (!OrthancPlugins::RestApiPost(resources, "/tools/find", payload, httpHeaders, true) ||
         resources.type() != Json::arrayValue)
     {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+      ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_InternalError);
     }
 
     if (resources.size() == 0)
@@ -1706,18 +1715,21 @@ public:
 class InstanceWorkerData : public boost::noncopyable
 {
 private:
-  Orthanc::SharedMessageQueue* instancesQueue_;
-  std::string wadoBase_;
+  Orthanc::SharedMessageQueue*  instancesQueue_;
+  std::string                   wadoBase_;
+  unsigned int                  threadId_;
 
 public:
   InstanceWorkerData(Orthanc::SharedMessageQueue* instancesQueue,
-                     const std::string& wadoBase) :
+                     const std::string& wadoBase,
+                     unsigned int threadId) :
     instancesQueue_(instancesQueue),
-    wadoBase_(wadoBase)
+    wadoBase_(wadoBase),
+    threadId_(threadId)
   {
     if (instancesQueue == NULL)
     {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
+      ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_NullPointer);
     }
   }
 
@@ -1730,12 +1742,18 @@ public:
   {
     return wadoBase_;
   }
+
+  unsigned int GetThreadId() const
+  {
+    return threadId_;
+  }
 };
 
 void InstanceWorkerThread(InstanceWorkerData* data)
 {
-  static uint16_t threadCounter = 0;
-  Orthanc::Logging::SetCurrentThreadName(std::string("DW-META-") + boost::lexical_cast<std::string>(threadCounter++));
+#if ORTHANC_FRAMEWORK_VERSION_IS_ABOVE(1, 13, 0)
+  Orthanc::Logging::ScopedCurrentThreadNameSetter setter(std::string("DW-META-") + boost::lexical_cast<std::string>(data->GetThreadId()));
+#endif
 
   while (true)
   {
@@ -1744,7 +1762,7 @@ void InstanceWorkerThread(InstanceWorkerData* data)
       std::unique_ptr<InstanceToLoad> instanceToLoad(data->Dequeue());
       if (instanceToLoad.get() == NULL)
       {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+        ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_InternalError);
       }
 
       if (instanceToLoad->GetOrthancId() == EXIT_WORKER_MESSAGE)
@@ -1825,7 +1843,7 @@ void RetrieveSeriesMetadataInternal(std::set<std::string>& instancesIds,
 
     for (unsigned int t = 0; t < workersCount; t++)
     {
-      InstanceWorkerData* threadData = new InstanceWorkerData(&instancesQueue, wadoBase);
+      InstanceWorkerData* threadData = new InstanceWorkerData(&instancesQueue, wadoBase, t);
       instancesWorkersData.push_back(boost::shared_ptr<InstanceWorkerData>(threadData));
       instancesWorkers.push_back(boost::shared_ptr<boost::thread>(new boost::thread(InstanceWorkerThread, threadData)));
     }
@@ -2019,7 +2037,17 @@ void RetrieveSeriesMetadataInternalWithCache(OrthancPlugins::DicomWebFormatter::
 
     std::string attachmentUrl = "/series/" + seriesOrthancId + "/attachments/" + SERIES_METADATA_ATTACHMENT_ID;
 
-    if (OrthancPlugins::RestApiGetString(cacheContent, attachmentUrl + "/data", false))
+    bool attachmentFound = false;
+    try
+    {
+      attachmentFound = OrthancPlugins::RestApiGetString(cacheContent, attachmentUrl + "/data", false);
+    }
+    catch (...)
+    {
+      // ignore the error and regenerate
+    }
+
+    if (attachmentFound)
     {
       if (boost::starts_with(cacheContent, "2;"))  // version 2, cacheContent is "2;sorted-instances-list-md5;compressedSeriesMetadata"
       {
@@ -2194,7 +2222,7 @@ void RetrieveBulkData(OrthancPluginRestOutput* output,
         if (frames.type() != Json::arrayValue ||
             OrthancPluginStartMultipartAnswer(context, output, "related", "application/octet-stream") != 0)
         {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_Plugin);
+          ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_Plugin);
         }
 
         for (Json::Value::ArrayIndex i = 0; i < frames.size(); i++)
@@ -2205,7 +2233,7 @@ void RetrieveBulkData(OrthancPluginRestOutput* output,
               !OrthancPlugins::RestApiGetString(frame, orthanc + "/" + frames[i].asString(), false) ||
               OrthancPluginSendMultipartItem(context, output, frame.c_str(), frame.size()) != 0)
           {
-            throw Orthanc::OrthancException(Orthanc::ErrorCode_Plugin);
+            ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_Plugin);
           }
         }
       }
@@ -2247,7 +2275,7 @@ void RetrieveBulkData(OrthancPluginRestOutput* output,
         if (OrthancPluginStartMultipartAnswer(context, output, "related", "application/octet-stream") != 0 ||
             OrthancPluginSendMultipartItem(context, output, result.c_str(), result.size()) != 0)
         {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_Plugin);
+          ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_Plugin);
         }
       }
       else


=====================================
Plugin/WadoRsRetrieveFrames.cpp
=====================================
@@ -378,6 +378,36 @@ static const char* GetMimeType(const Orthanc::DicomTransferSyntax& syntax)
     case Orthanc::DicomTransferSyntax_JPEG2000Multicomponent:
       return "image/jpx; transfer-syntax=1.2.840.10008.1.2.4.93";
 
+    case Orthanc::DicomTransferSyntax_JPEGXLLossless:
+      return "image/jxl; transfer-syntax=1.2.840.10008.1.2.4.110";
+
+    case Orthanc::DicomTransferSyntax_JPEGXLJPEGRecompression:
+      return "image/jxl; transfer-syntax=1.2.840.10008.1.2.4.111";
+
+    case Orthanc::DicomTransferSyntax_JPEGXL:
+      return "image/jxl; transfer-syntax=1.2.840.10008.1.2.4.112";
+      
+#if ORTHANC_FRAMEWORK_VERSION_IS_ABOVE(1, 12, 11)
+    case Orthanc::DicomTransferSyntax_HighThroughputJPEG2000LosslessOnly:
+      return "image/jphc; transfer-syntax=1.2.840.10008.1.2.4.201";
+
+    case Orthanc::DicomTransferSyntax_HighThroughputJPEG2000withRPCLOptionsLosslessOnly:
+      return "image/jphc; transfer-syntax=1.2.840.10008.1.2.4.202";
+
+    case Orthanc::DicomTransferSyntax_HighThroughputJPEG2000:
+      return "image/jphc; transfer-syntax=1.2.840.10008.1.2.4.203";
+#endif
+
+    case Orthanc::DicomTransferSyntax_MPEG2MainProfileAtMainLevel:    // note: this should not happen when retrieving a single frame
+    case Orthanc::DicomTransferSyntax_MPEG2MainProfileAtHighLevel:
+    case Orthanc::DicomTransferSyntax_MPEG4HighProfileLevel4_1:
+    case Orthanc::DicomTransferSyntax_MPEG4BDcompatibleHighProfileLevel4_1:
+    case Orthanc::DicomTransferSyntax_MPEG4HighProfileLevel4_2_For2DVideo:
+    case Orthanc::DicomTransferSyntax_MPEG4HighProfileLevel4_2_For3DVideo:
+    case Orthanc::DicomTransferSyntax_MPEG4StereoHighProfileLevel4_2:
+    case Orthanc::DicomTransferSyntax_HEVCMainProfileLevel5_1:
+    case Orthanc::DicomTransferSyntax_HEVCMain10ProfileLevel5_1:
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, std::string("WADO RS Retrieve frame: unhandled Transfer syntax ") + Orthanc::GetTransferSyntaxUid(syntax) + ", Orthanc does not allow accessing single frame from videos");
     default:
       throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, std::string("WADO RS Retrieve frame: unhandled Transfer syntax ") + Orthanc::GetTransferSyntaxUid(syntax));
   }
@@ -564,7 +594,7 @@ static void RetrieveFrames(OrthancPluginRestOutput* output,
 
     if (instance.get() == NULL)
     {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
+      ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_NullPointer);
     }
 
     if (allFrames)


=====================================
Plugin/WadoRsRetrieveRendered.cpp
=====================================
@@ -688,7 +688,7 @@ static void ApplyWindowing(Orthanc::ImageAccessor& target,
         }
 
         default:
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+          ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_NotImplemented);
       }
 
       Orthanc::ImageTraits<Orthanc::PixelFormat_Grayscale8>::SetFloatPixel(target, b, x, y);
@@ -751,14 +751,14 @@ static void ApplyRendering(Orthanc::ImageAccessor& target,
           break;
 
         default:
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+          ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_NotImplemented);
       }
           
       break;
     }
 
     default:
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+      ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_NotImplemented);
   }
 
   if (parameters.IsFlipX())
@@ -999,7 +999,8 @@ static void AnswerFrameRendered(OrthancPluginRestOutput* output,
 
     Orthanc::PixelFormat targetFormat;
     OrthancPluginPixelFormat sdkFormat;
-    if (dicom.GetPixelFormat() == OrthancPluginPixelFormat_RGB24)
+    if (dicom.GetPixelFormat() == OrthancPluginPixelFormat_RGB24 ||
+      dicom.GetPixelFormat() == OrthancPluginPixelFormat_RGB48)
     {
       targetFormat = Orthanc::PixelFormat_RGB24;
       sdkFormat = OrthancPluginPixelFormat_RGB24;
@@ -1047,7 +1048,7 @@ static void AnswerFrameRendered(OrthancPluginRestOutput* output,
         break;
 
       default:
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+        ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_NotImplemented);
     }
   }
 }


=====================================
Plugin/WadoUri.cpp
=====================================
@@ -106,7 +106,7 @@ static bool LocateInstanceWadoUri(std::string& instance,
   if (!OrthancPlugins::RestApiPost(resources, "/tools/find", payload, httpHeaders, true) ||
       resources.type() != Json::arrayValue)
   {
-    throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+    ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_InternalError);
   }
 
   if (resources.size() == 0)
@@ -171,7 +171,7 @@ static void AnswerPreview(OrthancPluginRestOutput* output,
   else
   {
     LOG(ERROR) << "WADO-URI: Unable to generate a preview image for " << uri;
-    throw Orthanc::OrthancException(Orthanc::ErrorCode_Plugin);
+    ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(Orthanc::ErrorCode_Plugin);
   }
 }
 


=====================================
Resources/Orthanc/CMake/DownloadOrthancFramework.cmake
=====================================
@@ -179,6 +179,8 @@ if (ORTHANC_FRAMEWORK_SOURCE STREQUAL "hg" OR
         set(ORTHANC_FRAMEWORK_MD5 "d5e1ba442104c89a24013cb859a9d6bf")
       elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.12.11")
         set(ORTHANC_FRAMEWORK_MD5 "389b273b64b513ba8fc3233f34201cc1")
+      elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.13.0")
+        set(ORTHANC_FRAMEWORK_MD5 "cc95d3e509612b541e10e91c831fbfe3")
 
       # Below this point are development snapshots that were used to
       # release some plugin, before an official release of the Orthanc
@@ -238,6 +240,11 @@ if (ORTHANC_FRAMEWORK_SOURCE STREQUAL "hg" OR
         # for HttpClient that returns the answer body in case of HTTP error
         set(ORTHANC_FRAMEWORK_PRE_RELEASE ON)
         set(ORTHANC_FRAMEWORK_MD5 "665f8aa70d7c5091bc20da37cf664910")
+      elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "004b351797fe")
+        # PixelsMasker 0.1.2 (framework post-1.12.11)
+        # for ScopedThreadNameSetter
+        set(ORTHANC_FRAMEWORK_PRE_RELEASE ON)
+        set(ORTHANC_FRAMEWORK_MD5 "f078ca997217b831ab3f6741f08a8c07")
       endif()
     endif()
   endif()


=====================================
Resources/Orthanc/CMake/DownloadPackage.cmake
=====================================
@@ -92,7 +92,7 @@ macro(DownloadFile MD5 Url)
 
   set(TMP_PATH "${CMAKE_SOURCE_DIR}/ThirdPartyDownloads/${TMP_FILENAME}")
   if (NOT EXISTS "${TMP_PATH}")
-    message("Downloading ${Url}")
+    message("Downloading ${Url} since the file was not found in ${TMP_PATH}")
 
     # This fixes issue 6: "I think cmake shouldn't download the
     # packages which are not in the system, it should stop and let


=====================================
Resources/Orthanc/Plugins/OrthancPluginCppWrapper.cpp
=====================================
@@ -28,7 +28,7 @@
 #include <boost/move/unique_ptr.hpp>
 #include <boost/thread.hpp>
 #include <boost/algorithm/string/join.hpp>
-
+#include <limits>
 
 #include <json/reader.h>
 #include <json/version.h>
@@ -61,6 +61,13 @@ static const OrthancPluginErrorCode OrthancPluginErrorCode_NullPointer = Orthanc
 #endif
 
 
+#ifdef _MSC_VER
+#  define ORTHANC_SCANF sscanf_s
+#else
+#  define ORTHANC_SCANF sscanf
+#endif
+
+
 namespace OrthancPlugins
 {
   static OrthancPluginContext* globalContext_ = NULL;
@@ -117,7 +124,7 @@ namespace OrthancPlugins
   }
 
 
-#if HAS_ORTHANC_PLUGIN_LOG_MESSAGE == 1
+#if HAS_ORTHANC_PLUGINS_LOG_MESSAGE == 1
   void LogMessage(OrthancPluginLogLevel level,
                   const char* file,
                   uint32_t line,
@@ -125,7 +132,7 @@ namespace OrthancPlugins
   {
     if (HasGlobalContext())
     {
-#if HAS_ORTHANC_PLUGIN_LOG_MESSAGE == 1
+#if HAS_ORTHANC_PLUGINS_LOG_MESSAGE == 1
       const char* pluginName = (pluginName_.empty() ? NULL : pluginName_.c_str());
       OrthancPluginLogMessage(GetGlobalContext(), message.c_str(), pluginName, file, line, OrthancPluginLogCategory_Generic, level);
 #else
@@ -184,7 +191,7 @@ namespace OrthancPlugins
       // Prevent using garbage information
       buffer_.data = NULL;
       buffer_.size = 0;
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code);
+      ORTHANC_PLUGINS_THROW_ERROR_CODE(code);
     }
   }
 
@@ -209,7 +216,7 @@ namespace OrthancPlugins
     }
     else
     {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code);
+      ORTHANC_PLUGINS_THROW_ERROR_CODE(code);
     }
   }
 
@@ -227,7 +234,7 @@ namespace OrthancPlugins
     {
       Clear();
     }
-    catch (ORTHANC_PLUGINS_EXCEPTION_CLASS&)
+    catch (ORTHANC_PLUGINS_EXCEPTION_CLASS&) // NOLINT(bugprone-empty-catch)
     {
       // Don't throw exceptions in destructors
     }
@@ -264,7 +271,7 @@ namespace OrthancPlugins
     }
     else
     {
-      if (size > 0)
+      if (buffer != NULL && size > 0)
       {
         memcpy(buffer_.data, buffer, size);
       }
@@ -365,7 +372,7 @@ namespace OrthancPlugins
   }
 
 
-#if (HAS_ORTHANC_PLUGIN_PEERS == 1) || (HAS_ORTHANC_PLUGIN_HTTP_CLIENT == 1) || (HAS_ORTHANC_PLUGIN_GENERIC_CALL_REST_API == 1)
+#if (HAS_ORTHANC_PLUGINS_PEERS == 1) || (HAS_ORTHANC_PLUGINS_HTTP_CLIENT == 1) || (HAS_ORTHANC_PLUGINS_GENERIC_CALL_REST_API == 1)
   static void DecodeHttpHeaders(HttpHeaders& target,
                                 const MemoryBuffer& source)
   {
@@ -465,7 +472,7 @@ namespace OrthancPlugins
     }
   }
 
-#if HAS_ORTHANC_PLUGIN_GENERIC_CALL_REST_API == 1
+#if HAS_ORTHANC_PLUGINS_GENERIC_CALL_REST_API == 1
 
   bool MemoryBuffer::RestApiPost(const std::string& uri,
                                  const void* body,
@@ -675,7 +682,7 @@ namespace OrthancPlugins
     {
       Clear();
     }
-    catch (ORTHANC_PLUGINS_EXCEPTION_CLASS&)
+    catch (ORTHANC_PLUGINS_EXCEPTION_CLASS&) // NOLINT(bugprone-empty-catch)
     {
       // Don't throw exceptions in destructors
     }
@@ -839,7 +846,7 @@ namespace OrthancPlugins
     }
     else
     {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(error);
+      ORTHANC_PLUGINS_THROW_ERROR_CODE(error);
     }
   }
 
@@ -977,7 +984,15 @@ namespace OrthancPlugins
         return true;
 
       case Json::uintValue:
-        target = configuration_[key].asUInt();
+        if (configuration_[key].asUInt() > static_cast<unsigned int>(std::numeric_limits<int>::max()))
+        {
+          ORTHANC_PLUGINS_LOG_ERROR("The configuration option \"" + GetPath(key) +
+                                    "\" is too large to fit in an integer");
+
+          ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);          
+        }
+
+        target = static_cast<int>(configuration_[key].asUInt());
         return true;
 
       default:
@@ -1338,7 +1353,7 @@ namespace OrthancPlugins
     {
       Clear();
     }
-    catch (ORTHANC_PLUGINS_EXCEPTION_CLASS&)
+    catch (ORTHANC_PLUGINS_EXCEPTION_CLASS&) // NOLINT(bugprone-empty-catch)
     {
       // Don't throw exceptions in destructors
     }
@@ -1473,7 +1488,7 @@ namespace OrthancPlugins
   }
 
 
-#if HAS_ORTHANC_PLUGIN_FIND_MATCHER == 1
+#if HAS_ORTHANC_PLUGINS_FIND_MATCHER == 1
   FindMatcher::FindMatcher(const OrthancPluginWorklistQuery* worklist) :
     matcher_(NULL),
     worklist_(worklist)
@@ -1542,18 +1557,18 @@ namespace OrthancPlugins
     }
   }
 
-#endif /* HAS_ORTHANC_PLUGIN_FIND_MATCHER == 1 */
+#endif /* HAS_ORTHANC_PLUGINS_FIND_MATCHER == 1 */
 
   static void CheckAnswerSizeIsLessThan4GB(const std::string& answer)
   {
     if (answer.size() > static_cast<size_t>(std::numeric_limits<uint32_t>::max()))
     {
-  #if HAS_ORTHANC_EXCEPTION == 1
+#if HAS_ORTHANC_EXCEPTION == 1
       throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange, "Cannot send HTTP response larger than 4GB");
-  #else
+#else
       ORTHANC_PLUGINS_LOG_ERROR("Cannot send HTTP response larger than 4GB");
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);          
-  #endif
+      ORTHANC_PLUGINS_THROW_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
+#endif
     }
   }
 
@@ -1726,7 +1741,7 @@ namespace OrthancPlugins
     }
   }
 
-#if HAS_ORTHANC_PLUGIN_GENERIC_CALL_REST_API == 1
+#if HAS_ORTHANC_PLUGINS_GENERIC_CALL_REST_API == 1
   bool RestApiPost(Json::Value& result,
                    const std::string& uri,
                    const Json::Value& body,
@@ -1821,7 +1836,7 @@ namespace OrthancPlugins
     }
     else
     {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(error);
+      ORTHANC_PLUGINS_THROW_ERROR_CODE(error);
     }
   }
 
@@ -1850,12 +1865,6 @@ namespace OrthancPlugins
       return true;
     }
 
-#ifdef _MSC_VER
-#define ORTHANC_SCANF sscanf_s
-#else
-#define ORTHANC_SCANF sscanf
-#endif
-
     // Parse the version
     int aa, bb, cc = 0;
     if ((ORTHANC_SCANF(version, "%4d.%4d.%4d", &aa, &bb, &cc) != 3 &&
@@ -1943,7 +1952,7 @@ namespace OrthancPlugins
 #endif
 
 
-#if HAS_ORTHANC_PLUGIN_PEERS == 1
+#if HAS_ORTHANC_PLUGINS_PEERS == 1
   size_t OrthancPeers::GetPeerIndex(const std::string& name) const
   {
     size_t index;
@@ -1967,7 +1976,7 @@ namespace OrthancPlugins
 
     if (peers_ == NULL)
     {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_Plugin);
+      ORTHANC_PLUGINS_THROW_ERROR_CODE(OrthancPluginErrorCode_Plugin);
     }
 
     uint32_t count = OrthancPluginGetPeersCount(GetGlobalContext(), peers_);
@@ -1978,7 +1987,7 @@ namespace OrthancPlugins
       if (name == NULL)
       {
         OrthancPluginFreePeers(GetGlobalContext(), peers_);
-        ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_Plugin);
+        ORTHANC_PLUGINS_THROW_ERROR_CODE(OrthancPluginErrorCode_Plugin);
       }
 
       index_[name] = i;
@@ -2016,14 +2025,14 @@ namespace OrthancPlugins
   {
     if (index >= index_.size())
     {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
+      ORTHANC_PLUGINS_THROW_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
     }
     else
     {
       const char* s = OrthancPluginGetPeerName(GetGlobalContext(), peers_, static_cast<uint32_t>(index));
       if (s == NULL)
       {
-        ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_Plugin);
+        ORTHANC_PLUGINS_THROW_ERROR_CODE(OrthancPluginErrorCode_Plugin);
       }
       else
       {
@@ -2037,14 +2046,14 @@ namespace OrthancPlugins
   {
     if (index >= index_.size())
     {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
+      ORTHANC_PLUGINS_THROW_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
     }
     else
     {
       const char* s = OrthancPluginGetPeerUrl(GetGlobalContext(), peers_, static_cast<uint32_t>(index));
       if (s == NULL)
       {
-        ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_Plugin);
+        ORTHANC_PLUGINS_THROW_ERROR_CODE(OrthancPluginErrorCode_Plugin);
       }
       else
       {
@@ -2066,7 +2075,7 @@ namespace OrthancPlugins
   {
     if (index >= index_.size())
     {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
+      ORTHANC_PLUGINS_THROW_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
     }
     else
     {
@@ -2099,7 +2108,7 @@ namespace OrthancPlugins
   {
     if (index >= index_.size())
     {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
+      ORTHANC_PLUGINS_THROW_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
     }
 
     OrthancPlugins::MemoryBuffer answer;
@@ -2297,7 +2306,7 @@ namespace OrthancPlugins
   {
     if (index >= index_.size())
     {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
+      ORTHANC_PLUGINS_THROW_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
     }
 
     if (body.size() > 0xffffffffu)
@@ -2337,7 +2346,7 @@ namespace OrthancPlugins
   {
     if (index >= index_.size())
     {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
+      ORTHANC_PLUGINS_THROW_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
     }
 
     if (body.size() > 0xffffffffu)
@@ -2383,7 +2392,7 @@ namespace OrthancPlugins
   {
     if (index >= index_.size())
     {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
+      ORTHANC_PLUGINS_THROW_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
     }
 
     OrthancPlugins::MemoryBuffer answer;
@@ -2424,7 +2433,7 @@ namespace OrthancPlugins
    ** JOBS
    ******************************************************************/
 
-#if HAS_ORTHANC_PLUGIN_JOB == 1
+#if HAS_ORTHANC_PLUGINS_JOB == 1
   void OrthancJob::CallbackFinalize(void* job)
   {
     if (job != NULL)
@@ -2622,7 +2631,7 @@ namespace OrthancPlugins
 
     if (content.type() != Json::objectValue)
     {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_BadFileFormat);
+      ORTHANC_PLUGINS_THROW_ERROR_CODE(OrthancPluginErrorCode_BadFileFormat);
     }
     else
     {
@@ -2642,7 +2651,7 @@ namespace OrthancPlugins
   {
     if (serialized.type() != Json::objectValue)
     {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_BadFileFormat);
+      ORTHANC_PLUGINS_THROW_ERROR_CODE(OrthancPluginErrorCode_BadFileFormat);
     }
     else
     {
@@ -2657,7 +2666,7 @@ namespace OrthancPlugins
     if (progress < 0 ||
         progress > 1)
     {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
+      ORTHANC_PLUGINS_THROW_ERROR_CODE(OrthancPluginErrorCode_ParameterOutOfRange);
     }
 
     progress_ = progress;
@@ -2677,7 +2686,7 @@ namespace OrthancPlugins
   {
     if (job == NULL)
     {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_NullPointer);
+      ORTHANC_PLUGINS_THROW_ERROR_CODE(OrthancPluginErrorCode_NullPointer);
     }
 
     OrthancPluginJob* orthanc =
@@ -2692,7 +2701,7 @@ namespace OrthancPlugins
 
     if (orthanc == NULL)
     {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_Plugin);
+      ORTHANC_PLUGINS_THROW_ERROR_CODE(OrthancPluginErrorCode_Plugin);
     }
     else
     {
@@ -2706,7 +2715,7 @@ namespace OrthancPlugins
   {
     if (job == NULL)
     {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_NullPointer);
+      ORTHANC_PLUGINS_THROW_ERROR_CODE(OrthancPluginErrorCode_NullPointer);
     }
 
     OrthancPluginJob* orthanc = Create(job);
@@ -2717,7 +2726,7 @@ namespace OrthancPlugins
     {
       ORTHANC_PLUGINS_LOG_ERROR("Plugin cannot submit job");
       OrthancPluginFreeJob(GetGlobalContext(), orthanc);
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_Plugin);
+      ORTHANC_PLUGINS_THROW_ERROR_CODE(OrthancPluginErrorCode_Plugin);
     }
     else
     {
@@ -2745,7 +2754,7 @@ namespace OrthancPlugins
           !status.isMember("State") ||
           status["State"].type() != Json::stringValue)
       {
-        ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_InexistentItem);        
+        ORTHANC_PLUGINS_THROW_ERROR_CODE(OrthancPluginErrorCode_InexistentItem);
       }
 
       const std::string state = status["State"].asString();
@@ -2774,30 +2783,30 @@ namespace OrthancPlugins
         if (!status.isMember("ErrorCode") ||
             status["ErrorCode"].type() != Json::intValue)
         {
-          ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_InternalError);
+          ORTHANC_PLUGINS_THROW_ERROR_CODE(OrthancPluginErrorCode_InternalError);
         }
         else
         {
           if (!status.isMember("ErrorDescription") ||
               status["ErrorDescription"].type() != Json::stringValue)
           {
-            ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(status["ErrorCode"].asInt());
+            ORTHANC_PLUGINS_THROW_ERROR_CODE(status["ErrorCode"].asInt());
           }
           else
           {
-  #if HAS_ORTHANC_EXCEPTION == 1
+#if HAS_ORTHANC_EXCEPTION == 1
             throw Orthanc::OrthancException(static_cast<Orthanc::ErrorCode>(status["ErrorCode"].asInt()),
                                             status["ErrorDescription"].asString());
-  #else
+#else
             ORTHANC_PLUGINS_LOG_ERROR("Exception while executing the job: " + status["ErrorDescription"].asString());
-            ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(status["ErrorCode"].asInt());          
-  #endif
+            ORTHANC_PLUGINS_THROW_ERROR_CODE(status["ErrorCode"].asInt());
+#endif
           }
         }
       }
       else
       {
-        ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_InternalError);
+        ORTHANC_PLUGINS_THROW_ERROR_CODE(OrthancPluginErrorCode_InternalError);
       }
     }
   }
@@ -2914,7 +2923,7 @@ namespace OrthancPlugins
    ** METRICS
    ******************************************************************/
 
-#if HAS_ORTHANC_PLUGIN_METRICS == 1
+#if HAS_ORTHANC_PLUGINS_METRICS == 1
   MetricsTimer::MetricsTimer(const char* name) :
     name_(name)
   {
@@ -2937,7 +2946,7 @@ namespace OrthancPlugins
    ** HTTP CLIENT
    ******************************************************************/
 
-#if HAS_ORTHANC_PLUGIN_HTTP_CLIENT == 1
+#if HAS_ORTHANC_PLUGINS_HTTP_CLIENT == 1
   class HttpClient::RequestBodyWrapper : public boost::noncopyable
   {
   private:
@@ -3001,7 +3010,7 @@ namespace OrthancPlugins
   };
 
 
-#if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT == 1
+#if HAS_ORTHANC_PLUGINS_CHUNKED_HTTP_CLIENT == 1
   static OrthancPluginErrorCode AnswerAddHeaderCallback(void* answer,
                                                         const char* key,
                                                         const char* value)
@@ -3025,7 +3034,7 @@ namespace OrthancPlugins
 #endif
 
 
-#if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT == 1
+#if HAS_ORTHANC_PLUGINS_CHUNKED_HTTP_CLIENT == 1
   static OrthancPluginErrorCode AnswerAddChunkCallback(void* answer,
                                                        const void* data,
                                                        uint32_t size)
@@ -3289,7 +3298,7 @@ namespace OrthancPlugins
     };
 
 
-#if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT == 1
+#if HAS_ORTHANC_PLUGINS_CHUNKED_HTTP_CLIENT == 1
     class MemoryAnswer : public HttpClient::IAnswer
     {
     private:
@@ -3323,7 +3332,7 @@ namespace OrthancPlugins
   }
 
 
-#if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT == 1
+#if HAS_ORTHANC_PLUGINS_CHUNKED_HTTP_CLIENT == 1
   void HttpClient::ExecuteWithStream(uint16_t& httpStatus,
                                      IAnswer& answer,
                                      IRequestBody& body) const
@@ -3379,7 +3388,7 @@ namespace OrthancPlugins
 
     if (error != OrthancPluginErrorCode_Success)
     {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(error);
+      ORTHANC_PLUGINS_THROW_ERROR_CODE(error);
     }
   }
 #endif    
@@ -3421,7 +3430,7 @@ namespace OrthancPlugins
 
     if (error != OrthancPluginErrorCode_Success)
     {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(error);
+      ORTHANC_PLUGINS_THROW_ERROR_CODE(error);
     }
 
     DecodeHttpHeaders(answerHeaders, answerHeadersBuffer);
@@ -3431,7 +3440,7 @@ namespace OrthancPlugins
 
   void HttpClient::Execute(IAnswer& answer)
   {
-#if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT == 1
+#if HAS_ORTHANC_PLUGINS_CHUNKED_HTTP_CLIENT == 1
     if (allowChunkedTransfers_)
     {
       if (chunkedBody_ != NULL)
@@ -3472,7 +3481,7 @@ namespace OrthancPlugins
   void HttpClient::Execute(HttpHeaders& answerHeaders /* out */,
                            std::string& answerBody /* out */)
   {
-#if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT == 1
+#if HAS_ORTHANC_PLUGINS_CHUNKED_HTTP_CLIENT == 1
     if (allowChunkedTransfers_)
     {
       MemoryAnswer answer;
@@ -3530,7 +3539,7 @@ namespace OrthancPlugins
     Execute(answerHeaders, body);
   }
 
-#endif  /* HAS_ORTHANC_PLUGIN_HTTP_CLIENT == 1 */
+#endif  /* HAS_ORTHANC_PLUGINS_HTTP_CLIENT == 1 */
 
 
 
@@ -3555,7 +3564,7 @@ namespace OrthancPlugins
     }
 
 
-#if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_SERVER == 1
+#if HAS_ORTHANC_PLUGINS_CHUNKED_HTTP_SERVER == 1
 
     OrthancPluginErrorCode ChunkedRequestReaderAddChunk(
       OrthancPluginServerChunkedRequestReader* reader,
@@ -3750,7 +3759,7 @@ namespace OrthancPlugins
       }
       catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
       {
-#if HAS_ORTHANC_EXCEPTION == 1 && HAS_ORTHANC_PLUGIN_EXCEPTION_DETAILS == 1
+#if HAS_ORTHANC_EXCEPTION == 1 && HAS_ORTHANC_PLUGINS_EXCEPTION_DETAILS == 1
         if (HasGlobalContext() &&
             e.HasDetails())
         {
@@ -3777,7 +3786,7 @@ namespace OrthancPlugins
   }
 
 
-#if HAS_ORTHANC_PLUGIN_STORAGE_COMMITMENT_SCP == 1
+#if HAS_ORTHANC_PLUGINS_STORAGE_COMMITMENT_SCP == 1
   OrthancPluginErrorCode IStorageCommitmentScpHandler::Lookup(
     OrthancPluginStorageCommitmentFailureReason* target,
     void* rawHandler,
@@ -3805,7 +3814,7 @@ namespace OrthancPlugins
 #endif
 
 
-#if HAS_ORTHANC_PLUGIN_STORAGE_COMMITMENT_SCP == 1
+#if HAS_ORTHANC_PLUGINS_STORAGE_COMMITMENT_SCP == 1
   void IStorageCommitmentScpHandler::Destructor(void* rawHandler)
   {
     assert(rawHandler != NULL);
@@ -3929,7 +3938,7 @@ namespace OrthancPlugins
     }
     else
     {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code);
+      ORTHANC_PLUGINS_THROW_ERROR_CODE(code);
     }
   }
 #endif
@@ -3966,7 +3975,7 @@ namespace OrthancPlugins
     }
     else
     {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code);
+      ORTHANC_PLUGINS_THROW_ERROR_CODE(code);
     }
   }
 #endif
@@ -4015,7 +4024,7 @@ namespace OrthancPlugins
 #endif
 
 
-#if HAS_ORTHANC_PLUGIN_WEBDAV == 1
+#if HAS_ORTHANC_PLUGINS_WEBDAV == 1
   static std::vector<std::string> WebDavConvertPath(uint32_t pathSize,
                                                     const char* const*  pathItems)
   {
@@ -4031,7 +4040,7 @@ namespace OrthancPlugins
 #endif
   
     
-#if HAS_ORTHANC_PLUGIN_WEBDAV == 1
+#if HAS_ORTHANC_PLUGINS_WEBDAV == 1
   static OrthancPluginErrorCode WebDavIsExistingFolder(uint8_t*            isExisting,
                                                        uint32_t            pathSize,
                                                        const char* const*  pathItems,
@@ -4056,7 +4065,7 @@ namespace OrthancPlugins
 #endif
 
   
-#if HAS_ORTHANC_PLUGIN_WEBDAV == 1
+#if HAS_ORTHANC_PLUGINS_WEBDAV == 1
   static OrthancPluginErrorCode WebDavListFolder(uint8_t*                        isExisting,
                                                  OrthancPluginWebDavCollection*  collection,
                                                  OrthancPluginWebDavAddFile      addFile,
@@ -4120,7 +4129,7 @@ namespace OrthancPlugins
 #endif    
 
 
-#if HAS_ORTHANC_PLUGIN_WEBDAV == 1
+#if HAS_ORTHANC_PLUGINS_WEBDAV == 1
   static OrthancPluginErrorCode WebDavRetrieveFile(OrthancPluginWebDavCollection*   collection,
                                                    OrthancPluginWebDavRetrieveFile  retrieveFile,
                                                    uint32_t                         pathSize,
@@ -4156,7 +4165,7 @@ namespace OrthancPlugins
 #endif
 
 
-#if HAS_ORTHANC_PLUGIN_WEBDAV == 1
+#if HAS_ORTHANC_PLUGINS_WEBDAV == 1
   static OrthancPluginErrorCode WebDavStoreFileCallback(uint8_t*            isReadOnly, /* out */
                                                         uint32_t            pathSize,
                                                         const char* const*  pathItems,
@@ -4189,7 +4198,7 @@ namespace OrthancPlugins
 #endif
 
   
-#if HAS_ORTHANC_PLUGIN_WEBDAV == 1
+#if HAS_ORTHANC_PLUGINS_WEBDAV == 1
   static OrthancPluginErrorCode WebDavCreateFolderCallback(uint8_t*            isReadOnly, /* out */
                                                            uint32_t            pathSize,
                                                            const char* const*  pathItems,
@@ -4214,7 +4223,7 @@ namespace OrthancPlugins
 #endif
   
   
-#if HAS_ORTHANC_PLUGIN_WEBDAV == 1
+#if HAS_ORTHANC_PLUGINS_WEBDAV == 1
   static OrthancPluginErrorCode WebDavDeleteItemCallback(uint8_t*            isReadOnly, /* out */
                                                          uint32_t            pathSize,
                                                          const char* const*  pathItems,
@@ -4239,7 +4248,7 @@ namespace OrthancPlugins
 #endif
 
   
-#if HAS_ORTHANC_PLUGIN_WEBDAV == 1
+#if HAS_ORTHANC_PLUGINS_WEBDAV == 1
   void IWebDavCollection::Register(const std::string& uri,
                                    IWebDavCollection& collection)
   {
@@ -4249,7 +4258,7 @@ namespace OrthancPlugins
 
     if (code != OrthancPluginErrorCode_Success)
     {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code);
+      ORTHANC_PLUGINS_THROW_ERROR_CODE(code);
     }
   }
 #endif
@@ -4339,7 +4348,7 @@ namespace OrthancPlugins
   }
 
 
-#if HAS_ORTHANC_PLUGIN_GENERIC_CALL_REST_API == 1
+#if HAS_ORTHANC_PLUGINS_GENERIC_CALL_REST_API == 1
   RestApiClient::RestApiClient() :
     method_(OrthancPluginHttpMethod_Get),
     path_("/"),
@@ -4373,7 +4382,7 @@ namespace OrthancPlugins
 #endif
 
 
-#if HAS_ORTHANC_PLUGIN_GENERIC_CALL_REST_API == 1
+#if HAS_ORTHANC_PLUGINS_GENERIC_CALL_REST_API == 1
   void RestApiClient::AddRequestHeader(const std::string& key,
                                        const std::string& value)
   {
@@ -4389,7 +4398,7 @@ namespace OrthancPlugins
 #endif
 
 
-#if HAS_ORTHANC_PLUGIN_GENERIC_CALL_REST_API == 1
+#if HAS_ORTHANC_PLUGINS_GENERIC_CALL_REST_API == 1
   void RestApiClient::SetRequestHeader(const std::string& key,
                                        const std::string& value)
   {
@@ -4398,7 +4407,7 @@ namespace OrthancPlugins
 #endif
 
 
-#if HAS_ORTHANC_PLUGIN_GENERIC_CALL_REST_API == 1
+#if HAS_ORTHANC_PLUGINS_GENERIC_CALL_REST_API == 1
   bool RestApiClient::Execute()
   {
     if (requestBody_.size() > 0xffffffffu)
@@ -4440,7 +4449,7 @@ namespace OrthancPlugins
       }
       else
       {
-        ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code);
+        ORTHANC_PLUGINS_THROW_ERROR_CODE(code);
       }
     }
   }
@@ -4481,7 +4490,7 @@ namespace OrthancPlugins
 #endif
 
 
-#if HAS_ORTHANC_PLUGIN_GENERIC_CALL_REST_API == 1
+#if HAS_ORTHANC_PLUGINS_GENERIC_CALL_REST_API == 1
   uint16_t RestApiClient::GetHttpStatus() const
   {
     if (httpStatus_ == 0)
@@ -4496,7 +4505,7 @@ namespace OrthancPlugins
 #endif
 
 
-#if HAS_ORTHANC_PLUGIN_GENERIC_CALL_REST_API == 1
+#if HAS_ORTHANC_PLUGINS_GENERIC_CALL_REST_API == 1
   bool RestApiClient::LookupAnswerHeader(std::string& value,
                                          const std::string& key) const
   {
@@ -4521,7 +4530,7 @@ namespace OrthancPlugins
 #endif
 
 
-#if HAS_ORTHANC_PLUGIN_GENERIC_CALL_REST_API == 1
+#if HAS_ORTHANC_PLUGINS_GENERIC_CALL_REST_API == 1
   const std::string& RestApiClient::GetAnswerBody() const
   {
     if (httpStatus_ == 0)
@@ -4536,7 +4545,7 @@ namespace OrthancPlugins
 #endif
 
 
-#if HAS_ORTHANC_PLUGIN_KEY_VALUE_STORES == 1
+#if HAS_ORTHANC_PLUGINS_KEY_VALUE_STORES == 1
   KeyValueStore::Iterator::Iterator(OrthancPluginKeysValuesIterator  *iterator) :
     iterator_(iterator)
   {
@@ -4548,7 +4557,7 @@ namespace OrthancPlugins
 #endif
 
 
-#if HAS_ORTHANC_PLUGIN_KEY_VALUE_STORES == 1
+#if HAS_ORTHANC_PLUGINS_KEY_VALUE_STORES == 1
   KeyValueStore::Iterator::~Iterator()
   {
     OrthancPluginFreeKeysValuesIterator(OrthancPlugins::GetGlobalContext(), iterator_);
@@ -4556,7 +4565,7 @@ namespace OrthancPlugins
 #endif
 
 
-#if HAS_ORTHANC_PLUGIN_KEY_VALUE_STORES == 1
+#if HAS_ORTHANC_PLUGINS_KEY_VALUE_STORES == 1
   bool KeyValueStore::Iterator::Next()
   {
     uint8_t done;
@@ -4564,7 +4573,7 @@ namespace OrthancPlugins
 
     if (code != OrthancPluginErrorCode_Success)
     {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code);
+      ORTHANC_PLUGINS_THROW_ERROR_CODE(code);
     }
     else
     {
@@ -4574,7 +4583,7 @@ namespace OrthancPlugins
 #endif
 
 
-#if HAS_ORTHANC_PLUGIN_KEY_VALUE_STORES == 1
+#if HAS_ORTHANC_PLUGINS_KEY_VALUE_STORES == 1
   std::string KeyValueStore::Iterator::GetKey() const
   {
     const char* s = OrthancPluginKeysValuesIteratorGetKey(OrthancPlugins::GetGlobalContext(), iterator_);
@@ -4590,7 +4599,7 @@ namespace OrthancPlugins
 #endif
 
 
-#if HAS_ORTHANC_PLUGIN_KEY_VALUE_STORES == 1
+#if HAS_ORTHANC_PLUGINS_KEY_VALUE_STORES == 1
   void KeyValueStore::Iterator::GetValue(std::string& value) const
   {
     OrthancPlugins::MemoryBuffer valueBuffer;
@@ -4598,7 +4607,7 @@ namespace OrthancPlugins
 
     if (code != OrthancPluginErrorCode_Success)
     {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code);
+      ORTHANC_PLUGINS_THROW_ERROR_CODE(code);
     }
     else
     {
@@ -4608,7 +4617,7 @@ namespace OrthancPlugins
 #endif
 
 
-#if HAS_ORTHANC_PLUGIN_KEY_VALUE_STORES == 1
+#if HAS_ORTHANC_PLUGINS_KEY_VALUE_STORES == 1
   void KeyValueStore::Store(const std::string& key,
                             const void* value,
                             size_t valueSize)
@@ -4622,13 +4631,13 @@ namespace OrthancPlugins
                                                              key.c_str(), value, static_cast<uint32_t>(valueSize));
     if (code != OrthancPluginErrorCode_Success)
     {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code);
+      ORTHANC_PLUGINS_THROW_ERROR_CODE(code);
     }
   }
 #endif
 
 
-#if HAS_ORTHANC_PLUGIN_KEY_VALUE_STORES == 1
+#if HAS_ORTHANC_PLUGINS_KEY_VALUE_STORES == 1
   bool KeyValueStore::GetValue(std::string& value,
                                const std::string& key)
   {
@@ -4639,7 +4648,7 @@ namespace OrthancPlugins
 
     if (code != OrthancPluginErrorCode_Success)
     {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code);
+      ORTHANC_PLUGINS_THROW_ERROR_CODE(code);
     }
     else if (found)
     {
@@ -4654,7 +4663,7 @@ namespace OrthancPlugins
 #endif
 
 
-#if HAS_ORTHANC_PLUGIN_KEY_VALUE_STORES == 1
+#if HAS_ORTHANC_PLUGINS_KEY_VALUE_STORES == 1
   void KeyValueStore::DeleteKey(const std::string& key)
   {
     OrthancPluginErrorCode code = OrthancPluginDeleteKeyValue(OrthancPlugins::GetGlobalContext(),
@@ -4662,13 +4671,13 @@ namespace OrthancPlugins
 
     if (code != OrthancPluginErrorCode_Success)
     {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code);
+      ORTHANC_PLUGINS_THROW_ERROR_CODE(code);
     }
   }
 #endif
 
 
-#if HAS_ORTHANC_PLUGIN_KEY_VALUE_STORES == 1
+#if HAS_ORTHANC_PLUGINS_KEY_VALUE_STORES == 1
   KeyValueStore::Iterator* KeyValueStore::CreateIterator()
   {
     return new Iterator(OrthancPluginCreateKeysValuesIterator(OrthancPlugins::GetGlobalContext(), storeId_.c_str()));
@@ -4676,7 +4685,7 @@ namespace OrthancPlugins
 #endif
 
 
-#if HAS_ORTHANC_PLUGIN_QUEUES == 1
+#if HAS_ORTHANC_PLUGINS_QUEUES == 1
   void Queue::Enqueue(const void* value,
                       size_t valueSize)
   {
@@ -4690,13 +4699,13 @@ namespace OrthancPlugins
 
     if (code != OrthancPluginErrorCode_Success)
     {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code);
+      ORTHANC_PLUGINS_THROW_ERROR_CODE(code);
     }
   }
 #endif
 
 
-#if HAS_ORTHANC_PLUGIN_QUEUES == 1
+#if HAS_ORTHANC_PLUGINS_QUEUES == 1
   bool Queue::DequeueInternal(std::string& value,
                               OrthancPluginQueueOrigin origin)
   {
@@ -4711,7 +4720,7 @@ namespace OrthancPlugins
 
     if (code != OrthancPluginErrorCode_Success)
     {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code);
+      ORTHANC_PLUGINS_THROW_ERROR_CODE(code);
     }
     else if (found)
     {
@@ -4726,7 +4735,7 @@ namespace OrthancPlugins
 #endif
 
 
-#if HAS_ORTHANC_PLUGIN_QUEUES == 1
+#if HAS_ORTHANC_PLUGINS_QUEUES == 1
   uint64_t Queue::GetSize()
   {
     uint64_t size = 0;
@@ -4734,7 +4743,7 @@ namespace OrthancPlugins
 
     if (code != OrthancPluginErrorCode_Success)
     {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code);
+      ORTHANC_PLUGINS_THROW_ERROR_CODE(code);
     }
     else
     {
@@ -4744,7 +4753,7 @@ namespace OrthancPlugins
 #endif
 
 
-#if HAS_ORTHANC_PLUGIN_RESERVE_QUEUE_VALUE == 1
+#if HAS_ORTHANC_PLUGINS_RESERVE_QUEUE_VALUE == 1
   bool Queue::ReserveInternal(std::string& value, uint64_t& valueId, OrthancPluginQueueOrigin origin, uint32_t releaseTimeout)
   {
     uint8_t found = false;
@@ -4755,7 +4764,7 @@ namespace OrthancPlugins
 
     if (code != OrthancPluginErrorCode_Success)
     {
-      ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code);
+      ORTHANC_PLUGINS_THROW_ERROR_CODE(code);
     }
     else if (found)
     {
@@ -4770,7 +4779,7 @@ namespace OrthancPlugins
 #endif
 
 
-#if HAS_ORTHANC_PLUGIN_RESERVE_QUEUE_VALUE == 1
+#if HAS_ORTHANC_PLUGINS_RESERVE_QUEUE_VALUE == 1
   bool Queue::ReserveBack(std::string& value, uint64_t& valueId, uint32_t releaseTimeout)
   {
     return ReserveInternal(value, valueId, OrthancPluginQueueOrigin_Back, releaseTimeout);
@@ -4778,7 +4787,7 @@ namespace OrthancPlugins
 #endif
 
 
-#if HAS_ORTHANC_PLUGIN_RESERVE_QUEUE_VALUE == 1
+#if HAS_ORTHANC_PLUGINS_RESERVE_QUEUE_VALUE == 1
   bool Queue::ReserveFront(std::string& value, uint64_t& valueId, uint32_t releaseTimeout)
   {
     return ReserveInternal(value, valueId, OrthancPluginQueueOrigin_Front, releaseTimeout);
@@ -4786,7 +4795,7 @@ namespace OrthancPlugins
 #endif
 
 
-#if HAS_ORTHANC_PLUGIN_RESERVE_QUEUE_VALUE == 1
+#if HAS_ORTHANC_PLUGINS_RESERVE_QUEUE_VALUE == 1
   void Queue::Acknowledge(uint64_t valueId)
   {
     OrthancPluginAcknowledgeQueueValue(OrthancPlugins::GetGlobalContext(), queueId_.c_str(), valueId);


=====================================
Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h
=====================================
@@ -66,98 +66,98 @@
 
 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 2, 0)
 // The "OrthancPluginFindMatcher()" primitive was introduced in Orthanc 1.2.0
-#  define HAS_ORTHANC_PLUGIN_FIND_MATCHER  1
+#  define HAS_ORTHANC_PLUGINS_FIND_MATCHER  1
 #else
-#  define HAS_ORTHANC_PLUGIN_FIND_MATCHER  0
+#  define HAS_ORTHANC_PLUGINS_FIND_MATCHER  0
 #endif
 
 
 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 4, 2)
-#  define HAS_ORTHANC_PLUGIN_PEERS  1
-#  define HAS_ORTHANC_PLUGIN_JOB    1
+#  define HAS_ORTHANC_PLUGINS_PEERS  1
+#  define HAS_ORTHANC_PLUGINS_JOB    1
 #else
-#  define HAS_ORTHANC_PLUGIN_PEERS  0
-#  define HAS_ORTHANC_PLUGIN_JOB    0
+#  define HAS_ORTHANC_PLUGINS_PEERS  0
+#  define HAS_ORTHANC_PLUGINS_JOB    0
 #endif
 
 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 0)
-#  define HAS_ORTHANC_PLUGIN_EXCEPTION_DETAILS  1
+#  define HAS_ORTHANC_PLUGINS_EXCEPTION_DETAILS  1
 #else
-#  define HAS_ORTHANC_PLUGIN_EXCEPTION_DETAILS  0
+#  define HAS_ORTHANC_PLUGINS_EXCEPTION_DETAILS  0
 #endif
 
 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 4)
-#  define HAS_ORTHANC_PLUGIN_METRICS  1
+#  define HAS_ORTHANC_PLUGINS_METRICS  1
 #else
-#  define HAS_ORTHANC_PLUGIN_METRICS  0
+#  define HAS_ORTHANC_PLUGINS_METRICS  0
 #endif
 
 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 1, 0)
-#  define HAS_ORTHANC_PLUGIN_HTTP_CLIENT  1
+#  define HAS_ORTHANC_PLUGINS_HTTP_CLIENT  1
 #else
-#  define HAS_ORTHANC_PLUGIN_HTTP_CLIENT  0
+#  define HAS_ORTHANC_PLUGINS_HTTP_CLIENT  0
 #endif
 
 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 7)
-#  define HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT  1
+#  define HAS_ORTHANC_PLUGINS_CHUNKED_HTTP_CLIENT  1
 #else
-#  define HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT  0
+#  define HAS_ORTHANC_PLUGINS_CHUNKED_HTTP_CLIENT  0
 #endif
 
 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 7)
-#  define HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_SERVER  1
+#  define HAS_ORTHANC_PLUGINS_CHUNKED_HTTP_SERVER  1
 #else
-#  define HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_SERVER  0
+#  define HAS_ORTHANC_PLUGINS_CHUNKED_HTTP_SERVER  0
 #endif
 
 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 6, 0)
-#  define HAS_ORTHANC_PLUGIN_STORAGE_COMMITMENT_SCP  1
+#  define HAS_ORTHANC_PLUGINS_STORAGE_COMMITMENT_SCP  1
 #else
-#  define HAS_ORTHANC_PLUGIN_STORAGE_COMMITMENT_SCP  0
+#  define HAS_ORTHANC_PLUGINS_STORAGE_COMMITMENT_SCP  0
 #endif
 
 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 9, 2)
-#  define HAS_ORTHANC_PLUGIN_GENERIC_CALL_REST_API  1
+#  define HAS_ORTHANC_PLUGINS_GENERIC_CALL_REST_API  1
 #else
-#  define HAS_ORTHANC_PLUGIN_GENERIC_CALL_REST_API  0
+#  define HAS_ORTHANC_PLUGINS_GENERIC_CALL_REST_API  0
 #endif
 
 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 10, 1)
-#  define HAS_ORTHANC_PLUGIN_WEBDAV  1
+#  define HAS_ORTHANC_PLUGINS_WEBDAV  1
 #else
-#  define HAS_ORTHANC_PLUGIN_WEBDAV  0
+#  define HAS_ORTHANC_PLUGINS_WEBDAV  0
 #endif
 
 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 12, 4)
-#  define HAS_ORTHANC_PLUGIN_LOG_MESSAGE  1
+#  define HAS_ORTHANC_PLUGINS_LOG_MESSAGE  1
 #else
-#  define HAS_ORTHANC_PLUGIN_LOG_MESSAGE  0
+#  define HAS_ORTHANC_PLUGINS_LOG_MESSAGE  0
 #endif
 
 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 12, 8)
-#  define HAS_ORTHANC_PLUGIN_KEY_VALUE_STORES  1
-#  define HAS_ORTHANC_PLUGIN_QUEUES            1
+#  define HAS_ORTHANC_PLUGINS_KEY_VALUE_STORES  1
+#  define HAS_ORTHANC_PLUGINS_QUEUES            1
 #else
-#  define HAS_ORTHANC_PLUGIN_KEY_VALUE_STORES  0
-#  define HAS_ORTHANC_PLUGIN_QUEUES            0
+#  define HAS_ORTHANC_PLUGINS_KEY_VALUE_STORES  0
+#  define HAS_ORTHANC_PLUGINS_QUEUES            0
 #endif
 
 #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 12, 10)
-#  define HAS_ORTHANC_PLUGIN_RESERVE_QUEUE_VALUE   1
+#  define HAS_ORTHANC_PLUGINS_RESERVE_QUEUE_VALUE   1
 #else
-#  define HAS_ORTHANC_PLUGIN_RESERVE_QUEUE_VALUE   0
+#  define HAS_ORTHANC_PLUGINS_RESERVE_QUEUE_VALUE   0
 #endif
 
 
 // Macro to tag a function as having been deprecated
 #if (__cplusplus >= 201402L)  // C++14
-#  define ORTHANC_PLUGIN_CPP_WRAPPER_DEPRECATED(f) [[deprecated]] f
+#  define ORTHANC_PLUGINS_CPP_WRAPPER_DEPRECATED(f) [[deprecated]] f
 #elif defined(__GNUC__) || defined(__clang__)
-#  define ORTHANC_PLUGIN_CPP_WRAPPER_DEPRECATED(f) f __attribute__((deprecated))
+#  define ORTHANC_PLUGINS_CPP_WRAPPER_DEPRECATED(f) f __attribute__((deprecated))
 #elif defined(_MSC_VER)
-#  define ORTHANC_PLUGIN_CPP_WRAPPER_DEPRECATED(f) __declspec(deprecated) f
+#  define ORTHANC_PLUGINS_CPP_WRAPPER_DEPRECATED(f) __declspec(deprecated) f
 #else
-#  define ORTHANC_PLUGIN_CPP_WRAPPER_DEPRECATED
+#  define ORTHANC_PLUGINS_CPP_WRAPPER_DEPRECATED
 #endif
 
 
@@ -171,7 +171,7 @@
 #endif
 
 
-#if HAS_ORTHANC_PLUGIN_LOG_MESSAGE == 1
+#if HAS_ORTHANC_PLUGINS_LOG_MESSAGE == 1
 #  define ORTHANC_PLUGINS_LOG_ERROR(msg)   ::OrthancPlugins::LogMessage(OrthancPluginLogLevel_Error, __ORTHANC_FILE__, __LINE__, msg)
 #  define ORTHANC_PLUGINS_LOG_WARNING(msg) ::OrthancPlugins::LogMessage(OrthancPluginLogLevel_Warning, __ORTHANC_FILE__, __LINE__, msg)
 #  define ORTHANC_PLUGINS_LOG_INFO(msg)    ::OrthancPlugins::LogMessage(OrthancPluginLogLevel_Info, __ORTHANC_FILE__, __LINE__, msg)
@@ -296,7 +296,7 @@ namespace OrthancPlugins
                      const Json::Value& body,
                      bool applyPlugins);
 
-#if HAS_ORTHANC_PLUGIN_GENERIC_CALL_REST_API == 1
+#if HAS_ORTHANC_PLUGINS_GENERIC_CALL_REST_API == 1
     bool RestApiPost(const std::string& uri,
                      const Json::Value& body,
                      const HttpHeaders& httpHeaders,
@@ -395,7 +395,7 @@ namespace OrthancPlugins
     void ToJson(Json::Value& target) const;
   
     void ToJsonWithoutComments(Json::Value& target) const;
-};
+  };
 
 
   class OrthancConfiguration : public boost::noncopyable
@@ -534,7 +534,7 @@ namespace OrthancPlugins
   };
 
 
-#if HAS_ORTHANC_PLUGIN_FIND_MATCHER == 1
+#if HAS_ORTHANC_PLUGINS_FIND_MATCHER == 1
   class FindMatcher : public boost::noncopyable
   {
   private:
@@ -621,7 +621,7 @@ namespace OrthancPlugins
                    size_t bodySize,
                    bool applyPlugins);
 
-#if HAS_ORTHANC_PLUGIN_GENERIC_CALL_REST_API == 1
+#if HAS_ORTHANC_PLUGINS_GENERIC_CALL_REST_API == 1
   bool RestApiPost(Json::Value& result,
                    const std::string& uri,
                    const Json::Value& body,
@@ -700,30 +700,30 @@ namespace OrthancPlugins
   const char* AutodetectMimeType(const std::string& path);
 #endif
 
-#if HAS_ORTHANC_PLUGIN_LOG_MESSAGE == 1
+#if HAS_ORTHANC_PLUGINS_LOG_MESSAGE == 1
   void LogMessage(OrthancPluginLogLevel level,
                   const char* file,
                   uint32_t line,
                   const std::string& message);
 #endif
 
-#if HAS_ORTHANC_PLUGIN_LOG_MESSAGE == 1
+#if HAS_ORTHANC_PLUGINS_LOG_MESSAGE == 1
   // Use macro ORTHANC_PLUGINS_LOG_ERROR() instead
-  ORTHANC_PLUGIN_CPP_WRAPPER_DEPRECATED(void LogError(const std::string& message));
+  ORTHANC_PLUGINS_CPP_WRAPPER_DEPRECATED(void LogError(const std::string& message));
 #else
   void LogError(const std::string& message);
 #endif
 
-#if HAS_ORTHANC_PLUGIN_LOG_MESSAGE == 1
+#if HAS_ORTHANC_PLUGINS_LOG_MESSAGE == 1
   // Use macro ORTHANC_PLUGINS_LOG_WARNING() instead
-  ORTHANC_PLUGIN_CPP_WRAPPER_DEPRECATED(void LogWarning(const std::string& message));
+  ORTHANC_PLUGINS_CPP_WRAPPER_DEPRECATED(void LogWarning(const std::string& message));
 #else
   void LogWarning(const std::string& message);
 #endif
 
-#if HAS_ORTHANC_PLUGIN_LOG_MESSAGE == 1
+#if HAS_ORTHANC_PLUGINS_LOG_MESSAGE == 1
   // Use macro ORTHANC_PLUGINS_LOG_INFO() instead
-  ORTHANC_PLUGIN_CPP_WRAPPER_DEPRECATED(void LogInfo(const std::string& message));
+  ORTHANC_PLUGINS_CPP_WRAPPER_DEPRECATED(void LogInfo(const std::string& message));
 #else
   void LogInfo(const std::string& message);
 #endif
@@ -755,7 +755,7 @@ namespace OrthancPlugins
       }
       catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
       {
-#if HAS_ORTHANC_EXCEPTION == 1 && HAS_ORTHANC_PLUGIN_EXCEPTION_DETAILS == 1
+#if HAS_ORTHANC_EXCEPTION == 1 && HAS_ORTHANC_PLUGINS_EXCEPTION_DETAILS == 1
         if (HasGlobalContext() &&
             e.HasDetails())
         {
@@ -798,7 +798,7 @@ namespace OrthancPlugins
   }
 
 
-#if HAS_ORTHANC_PLUGIN_PEERS == 1
+#if HAS_ORTHANC_PLUGINS_PEERS == 1
   class OrthancPeers : public boost::noncopyable
   {
   private:
@@ -943,7 +943,7 @@ namespace OrthancPlugins
 
 
 
-#if HAS_ORTHANC_PLUGIN_JOB == 1
+#if HAS_ORTHANC_PLUGINS_JOB == 1
   class OrthancJob : public boost::noncopyable
   {
   private:
@@ -1022,7 +1022,7 @@ namespace OrthancPlugins
 #endif
 
 
-#if HAS_ORTHANC_PLUGIN_METRICS == 1
+#if HAS_ORTHANC_PLUGINS_METRICS == 1
   inline void SetMetricsValue(const char* name,
                               float value)
   {
@@ -1042,7 +1042,7 @@ namespace OrthancPlugins
 #endif
 
 
-#if HAS_ORTHANC_PLUGIN_METRICS == 1
+#if HAS_ORTHANC_PLUGINS_METRICS == 1
   class MetricsTimer : public boost::noncopyable
   {
   private:
@@ -1057,7 +1057,7 @@ namespace OrthancPlugins
 #endif
 
 
-#if HAS_ORTHANC_PLUGIN_HTTP_CLIENT == 1
+#if HAS_ORTHANC_PLUGINS_HTTP_CLIENT == 1
   class HttpClient : public boost::noncopyable
   {
   public:
@@ -1105,7 +1105,7 @@ namespace OrthancPlugins
     IRequestBody*            chunkedBody_;
     bool                     allowChunkedTransfers_;
 
-#if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_CLIENT == 1
+#if HAS_ORTHANC_PLUGINS_CHUNKED_HTTP_CLIENT == 1
     void ExecuteWithStream(uint16_t& httpStatus,  // out
                            IAnswer& answer,       // out
                            IRequestBody& body) const;
@@ -1235,11 +1235,11 @@ namespace OrthancPlugins
                                                    const OrthancPluginHttpRequest* request);
 
 
-#if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_SERVER == 1
+#if HAS_ORTHANC_PLUGINS_CHUNKED_HTTP_SERVER == 1
     template <ChunkedRestCallback Callback>
     static OrthancPluginErrorCode ChunkedProtect(OrthancPluginServerChunkedRequestReader** reader,
-                                                const char* url,
-                                                const OrthancPluginHttpRequest* request)
+                                                 const char* url,
+                                                 const OrthancPluginHttpRequest* request)
     {
       try
       {
@@ -1327,7 +1327,7 @@ namespace OrthancPlugins
   public:
     static void Apply(const std::string& uri)
     {
-#if HAS_ORTHANC_PLUGIN_CHUNKED_HTTP_SERVER == 1
+#if HAS_ORTHANC_PLUGINS_CHUNKED_HTTP_SERVER == 1
       OrthancPluginRegisterChunkedRestCallback(
         GetGlobalContext(), uri.c_str(),
         GetHandler == Internals::NullRestCallback         ? NULL : Internals::Protect<GetHandler>,
@@ -1347,7 +1347,7 @@ namespace OrthancPlugins
 
   
 
-#if HAS_ORTHANC_PLUGIN_STORAGE_COMMITMENT_SCP == 1
+#if HAS_ORTHANC_PLUGINS_STORAGE_COMMITMENT_SCP == 1
   class IStorageCommitmentScpHandler : public boost::noncopyable
   {
   public:
@@ -1460,15 +1460,15 @@ namespace OrthancPlugins
   };
 
 // helper method to convert Http headers from the plugin SDK to a std::map
-void GetHttpHeaders(HttpHeaders& result, const OrthancPluginHttpRequest* request);
+  void GetHttpHeaders(HttpHeaders& result, const OrthancPluginHttpRequest* request);
 
 // helper method to re-serialize the get arguments from the SDK into a string
-void SerializeGetArguments(std::string& output, const OrthancPluginHttpRequest* request);
+  void SerializeGetArguments(std::string& output, const OrthancPluginHttpRequest* request);
 
 // helper method to convert Get arguments from the plugin SDK to a std::map
-void GetGetArguments(GetArguments& result, const OrthancPluginHttpRequest* request);
+  void GetGetArguments(GetArguments& result, const OrthancPluginHttpRequest* request);
 
-#if HAS_ORTHANC_PLUGIN_WEBDAV == 1
+#if HAS_ORTHANC_PLUGINS_WEBDAV == 1
   class IWebDavCollection : public boost::noncopyable
   {
   public:
@@ -1579,7 +1579,7 @@ void GetGetArguments(GetArguments& result, const OrthancPluginHttpRequest* reque
                              const std::string& javascript);
 
 
-#if HAS_ORTHANC_PLUGIN_GENERIC_CALL_REST_API == 1
+#if HAS_ORTHANC_PLUGINS_GENERIC_CALL_REST_API == 1
   class RestApiClient : public boost::noncopyable
   {
   private:
@@ -1679,7 +1679,7 @@ void GetGetArguments(GetArguments& result, const OrthancPluginHttpRequest* reque
 #endif
 
 
-#if HAS_ORTHANC_PLUGIN_KEY_VALUE_STORES == 1
+#if HAS_ORTHANC_PLUGINS_KEY_VALUE_STORES == 1
   class KeyValueStore : public boost::noncopyable
   {
   public:
@@ -1734,7 +1734,7 @@ void GetGetArguments(GetArguments& result, const OrthancPluginHttpRequest* reque
 #endif
 
 
-#if HAS_ORTHANC_PLUGIN_QUEUES == 1
+#if HAS_ORTHANC_PLUGINS_QUEUES == 1
   class Queue : public boost::noncopyable
   {
   private:
@@ -1742,7 +1742,7 @@ void GetGetArguments(GetArguments& result, const OrthancPluginHttpRequest* reque
 
     bool DequeueInternal(std::string& value, OrthancPluginQueueOrigin origin);
 
-#if HAS_ORTHANC_PLUGIN_RESERVE_QUEUE_VALUE == 1
+#if HAS_ORTHANC_PLUGINS_RESERVE_QUEUE_VALUE == 1
     bool ReserveInternal(std::string& value, uint64_t& valueId, OrthancPluginQueueOrigin origin, uint32_t releaseTimeout);
 #endif
 
@@ -1765,7 +1765,7 @@ void GetGetArguments(GetArguments& result, const OrthancPluginHttpRequest* reque
       Enqueue(value.empty() ? NULL : value.c_str(), value.size());
     }
 
-#if HAS_ORTHANC_PLUGIN_RESERVE_QUEUE_VALUE == 1
+#if HAS_ORTHANC_PLUGINS_RESERVE_QUEUE_VALUE == 1
     // Use ReserveBack() instead
     ORTHANC_PLUGIN_DEPRECATED
 #endif
@@ -1774,7 +1774,7 @@ void GetGetArguments(GetArguments& result, const OrthancPluginHttpRequest* reque
       return DequeueInternal(value, OrthancPluginQueueOrigin_Back);
     }
 
-#if HAS_ORTHANC_PLUGIN_RESERVE_QUEUE_VALUE == 1
+#if HAS_ORTHANC_PLUGINS_RESERVE_QUEUE_VALUE == 1
     // Use ReserveFront() instead
     ORTHANC_PLUGIN_DEPRECATED
 #endif
@@ -1785,15 +1785,15 @@ void GetGetArguments(GetArguments& result, const OrthancPluginHttpRequest* reque
 
     uint64_t GetSize();
 
-#if HAS_ORTHANC_PLUGIN_RESERVE_QUEUE_VALUE == 1
+#if HAS_ORTHANC_PLUGINS_RESERVE_QUEUE_VALUE == 1
     bool ReserveBack(std::string& value, uint64_t& valueId, uint32_t releaseTimeout);
 #endif
 
-#if HAS_ORTHANC_PLUGIN_RESERVE_QUEUE_VALUE == 1
+#if HAS_ORTHANC_PLUGINS_RESERVE_QUEUE_VALUE == 1
     bool ReserveFront(std::string& value, uint64_t& valueId, uint32_t releaseTimeout);
 #endif
 
-#if HAS_ORTHANC_PLUGIN_RESERVE_QUEUE_VALUE == 1
+#if HAS_ORTHANC_PLUGINS_RESERVE_QUEUE_VALUE == 1
     void Acknowledge(uint64_t valueId);
 #endif
   };


=====================================
Resources/Orthanc/Plugins/OrthancPluginException.h
=====================================
@@ -41,13 +41,45 @@
 #endif
 
 
-#define ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code)                   \
+#if HAS_ORTHANC_EXCEPTION == 1 && defined(__ORTHANC_FILE__)   // the OrthancException class accepts a "details" argument -> add the file and line number
+
+#  include <boost/lexical_cast.hpp>
+
+#  define ORTHANC_PLUGINS_EXCEPTION_STRINGIFY_LINE_HELPER(line) #line
+#  define ORTHANC_PLUGINS_EXCEPTION_STRINGIFY_LINE(line) ORTHANC_PLUGINS_EXCEPTION_STRINGIFY_LINE_HELPER(line)
+#  define ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO_HELPER(errorCode, errorCodeStr) \
+  throw ::Orthanc::OrthancException(                                    \
+    errorCode, "Plugin error code " + errorCodeStr + " triggered from " __ORTHANC_FILE__ ":" \
+    ORTHANC_PLUGINS_EXCEPTION_STRINGIFY_LINE(__LINE__))
+
+#  define ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(errorCode)      \
+  throw ::Orthanc::OrthancException(                                    \
+    errorCode, #errorCode " triggered from " __ORTHANC_FILE__ ":"       \
+    ORTHANC_PLUGINS_EXCEPTION_STRINGIFY_LINE(__LINE__))
+
+#  define ORTHANC_PLUGINS_THROW_ERROR_CODE(code)                        \
+  ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO_HELPER(                 \
+    static_cast<ORTHANC_PLUGINS_ERROR_ENUMERATION>(code),               \
+    boost::lexical_cast<std::string>(code))
+
+#  define ORTHANC_PLUGINS_THROW_EXCEPTION(code)                         \
+  ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO_HELPER(                 \
+    ORTHANC_PLUGINS_GET_ERROR_CODE(code),                               \
+    boost::lexical_cast<std::string>(ORTHANC_PLUGINS_GET_ERROR_CODE(code)))
+
+#else // the PluginException does not accept a "details" argument
+
+#  define ORTHANC_PLUGINS_THROW_ERROR_CODE(code)                        \
   throw ORTHANC_PLUGINS_EXCEPTION_CLASS(static_cast<ORTHANC_PLUGINS_ERROR_ENUMERATION>(code));
 
+#  define ORTHANC_PLUGINS_THROW_WITH_FILE_AND_LINE_INFO(errorCode)      \
+  ORTHANC_PLUGINS_THROW_ERROR_CODE(errorCode)
 
-#define ORTHANC_PLUGINS_THROW_EXCEPTION(code)                           \
+#  define ORTHANC_PLUGINS_THROW_EXCEPTION(code)                         \
   throw ORTHANC_PLUGINS_EXCEPTION_CLASS(ORTHANC_PLUGINS_GET_ERROR_CODE(code));
-                                                  
+
+#endif                                                  
+
 
 #define ORTHANC_PLUGINS_CHECK_ERROR(code)                           \
   if (code != ORTHANC_PLUGINS_GET_ERROR_CODE(Success))              \


=====================================
Resources/SyncOrthancFolder.py
=====================================
@@ -11,7 +11,7 @@ import stat
 import urllib.request
 
 TARGET = os.path.join(os.path.dirname(__file__), 'Orthanc')
-PLUGIN_SDK_VERSION = [ '1.12.4' ]
+PLUGIN_SDK_VERSION = [ '1.12.4', '1.13.0' ]
 REPOSITORY = 'https://orthanc.uclouvain.be/hg/orthanc/raw-file'
 
 FILES = [


=====================================
TODO
=====================================
@@ -1,7 +1,14 @@
+* Limit memory usage in ThreadedInstancesLoader (re-use the config from the SequentialReaderWindowCapacity ?)
+  Check if we can re-use the SequentialReaders from Orthanc.  Best solution: Orthanc exposes an SDK function to
+  load and provide sequential instances so we don't have to re-implement it in this plugin and the RAM remains
+  under control.
+
 * Force usage of StudyInstanceUID & SeriesInstanceUID in WADO-URI for single instances:
   https://discourse.orthanc-server.org/t/dicomweb-wado-uri-does-not-work-if-duplicated-instances/5863
 
-
+* Find a solution for slow and memory greedy computation of metadata for files containing large float arrays.
+  https://discourse.orthanc-server.org/t/orthanc-1-12-11-performance-issues-with-deformable-registrations/6448
+  File available at https://public-files.orthanc.team/test-files/429_MB_REG_OF.dcm
 
 * https://orthanc.uclouvain.be/book/plugins/dicomweb.html#retrieving-dicom-resources-from-a-wado-rs-server
   Retrieve shall return the list of orthanc IDs -> it is not !
@@ -22,6 +29,26 @@
   https://discourse.orthanc-server.org/t/possible-memory-leak-with-multiframe-dicom-orthanc-ohif/3988/12
 
 
+* When OHIF downloads individual frames of a multiframe instance, we perform the same calls to tools/lookup to find the orthanc id of the study
+  from its StudyInstanceUID -> implements some caching to avoid this call.  Maybe implement the same hierarchy caching as the one we have in the auth-plugin:
+
+I0429 06:00:33.212574          HTTP-22 ElapsedTimer.cpp:102] (http) GET /dicom-web/studies/1.2.156.112536.1.2142.152204228000250042.15483240020.2/series/1.2.156.112536.1.2142.152204228000250042.15483240020.3/instances/1.2.156.112536.1.2142.152204228000250042.15483243750.11/frames/147
+I0429 06:00:33.212712          HTTP-22 authorization:/Plugin.cpp:563] Testing whether user has the required permissions 'all|view' based on the HTTP header 'Authorization' required to match '^/dicom-web/studies/([.0-9]+)/series/([.0-9]+)/instances/([.0-9]+)(|/|/frames/.*|/rendered|/thumbnail|/metadata|/bulk/.*)(|/)$'
+I0429 06:00:33.212785          HTTP-22 authorization:/Plugin.cpp:483] Testing whether user has the required permissions 'all|view' based on the HTTP header 'Authorization' required to match '^/dicom-web/studies/([.0-9]+)/series/([.0-9]+)/instances/([.0-9]+)(|/|/frames/.*|/rendered|/thumbnail|/metadata|/bulk/.*)(|/)$' -> not granted
+I0429 06:00:33.212853          HTTP-22 authorization:/Plugin.cpp:370] Testing whether access to study "232a1d86-c4472d8b-000318a1-760becb0-2a8a93bd" is allowed with a resource token
+I0429 06:00:33.212870          HTTP-22 authorization:/Plugin.cpp:400] Testing whether access to study "232a1d86-c4472d8b-000318a1-760becb0-2a8a93bd" is allowed with a resource token -> granted
+I0429 06:00:33.212962          HTTP-22 OrthancPlugins.cpp:2849] (plugins) Delegating HTTP request to plugin for URI: /dicom-web/studies/1.2.156.112536.1.2142.152204228000250042.15483240020.2/series/1.2.156.112536.1.2142.152204228000250042.15483240020.3/instances/1.2.156.112536.1.2142.152204228000250042.15483243750.11/frames/147
+I0429 06:00:33.213025          HTTP-22 OrthancPlugins.cpp:4463] (plugins) Plugin making REST POST call to URI /tools/find (after plugins)
+I0429 06:00:33.213056          HTTP-22 OrthancPlugins.cpp:2849] (plugins) Delegating HTTP request to plugin for URI: /tools/find
+I0429 06:00:33.213102   ****   HTTP-22 OrthancPlugins.cpp:3644] (plugins) Plugin making REST POST call on URI /tools/lookup (built-in API)
+I0429 06:00:33.215805          HTTP-22 authorization:/Plugin.cpp:370] Testing whether access to study "232a1d86-c4472d8b-000318a1-760becb0-2a8a93bd" is allowed with a resource token
+I0429 06:00:33.215833          HTTP-22 authorization:/Plugin.cpp:400] Testing whether access to study "232a1d86-c4472d8b-000318a1-760becb0-2a8a93bd" is allowed with a resource token -> granted
+I0429 06:00:33.215849          HTTP-22 OrthancPlugins.cpp:3644] (plugins) Plugin making REST POST call on URI /tools/find (built-in API)
+I0429 06:00:33.221304   ****   HTTP-22 ResourceFinder.cpp:1106] Number of candidate resources after fast DB filtering on main DICOM tags: 1
+I0429 06:00:33.221585          HTTP-22 dicom-web:/WadoRsRetrieveFrames.cpp:500] DICOMweb RetrieveFrames on d3133a0e-b17babe4-b67118e0-5ec383cc-b31f561b, frames: 147
+I0429 06:00:33.221611          HTTP-22 OrthancPlugins.cpp:3587] (plugins) Plugin making REST GET call on URI /instances/d3133a0e-b17babe4-b67118e0-5ec383cc-b31f561b/frames/146/raw (built-in API)
+I0429 06:00:33.222777          HTTP-22 ElapsedTimer.cpp:107] (http) GET /dicom-web/studies/1.2.156.112536.1.2142.152204228000250042.15483240020.2/series/1.2.156.112536.1.2142.152204228000250042.15483240020.3/instances/1.2.156.112536.1.2142.152204228000250042.15483243750.11/frames/147 (elapsed: 10222 us)
+
 
 * Implement capabilities: https://www.dicomstandard.org/using/dicomweb/capabilities/
   from https://groups.google.com/d/msgid/orthanc-users/c60227f2-c6da-4fd9-9b03-3ce9bf7d1af5n%40googlegroups.com?utm_medium=email&utm_source=footer


=====================================
WebApplication/app.js
=====================================
@@ -61,6 +61,10 @@ var app = new Vue({
       {
         key: DICOM_TAG_STUDY_DATE,
         label: 'Study date'
+      },
+      {
+        key: DICOM_TAG_STUDY_ID,
+        label: 'Study ID'
       }
     ],
     studyToDelete: null,
@@ -158,6 +162,10 @@ var app = new Vue({
         args[DICOM_TAG_STUDY_DATE] = app.lookup.studyDate;
       }
 
+      if ('studyID' in app.lookup) {
+        args[DICOM_TAG_STUDY_ID] = app.lookup.studyID;
+      }
+
       if ('accessionNumber' in app.lookup) {
         args[DICOM_TAG_ACCESSION_NUMBER] = app.lookup.accessionNumber;
       }


=====================================
WebApplication/index.html
=====================================
@@ -121,6 +121,12 @@
               <input type="text" class="form-control" v-model="lookup.studyDate"></input>
             </div>
           </div>
+          <div class="mb-3 row">
+            <label class="col-form-label col-sm-3 cols-lg-3">Study ID:</label>
+            <div class="col-sm-9 cols-lg-9">
+              <input type="text" class="form-control" v-model="lookup.studyID"></input>
+            </div>
+          </div>
           <p class="pull-right">
             <button type="button" class="btn btn-lg btn-success" @click="OnLookup">Do lookup</button>
             <button type="button" class="btn btn-lg btn-outline-danger" @click="OnReset">Reset</button>



View it on GitLab: https://salsa.debian.org/med-team/orthanc-dicomweb/-/commit/0df45877b26f3ba17d3c5fcb99bb63d252bdfadd

-- 
View it on GitLab: https://salsa.debian.org/med-team/orthanc-dicomweb/-/commit/0df45877b26f3ba17d3c5fcb99bb63d252bdfadd
You're receiving this email because of your account on salsa.debian.org. Manage all notifications: https://salsa.debian.org/-/profile/notifications | Help: https://salsa.debian.org/help


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/debian-med-commit/attachments/20260910/942354d7/attachment-0001.htm>


More information about the debian-med-commit mailing list