changeset 6629:71400f1c6143

merge
author Alain Mazy <am@orthanc.team>
date Wed, 18 Mar 2026 12:13:09 +0100
parents 640fc89647a6 (current diff) 7f44bfb58e7c (diff)
children 742fb41bff59 dfa66ab964c9
files NEWS
diffstat 8 files changed, 217 insertions(+), 19 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS	Wed Mar 18 12:12:12 2026 +0100
+++ b/NEWS	Wed Mar 18 12:13:09 2026 +0100
@@ -6,7 +6,6 @@
 
 * New experimental configuration "PatientLevelEnabled" (TODO: work in progree)
 
-
 REST API
 --------
 
@@ -23,6 +22,9 @@
 * New option "Utf8" available in the "{...}/archive" and "/tools/create-archive" routes
   to use UTF-8 filenames in the generated ZIP archives. It defaults to the value
   of the new configuration option "ZipUseUtf8".
+* Support for OF and OD value representations in "/instances/{...}/file" with content type
+  "application/dicom+json" (i.e. DICOMweb) and in "/instances/{...}/tags" (contribution by
+  Yusuf Sayıta, Philips).
 * New "Content.Resources" field in "DicomModalityStore", "OrthancPeerStore" and "ResourceModification" 
   jobs that contains JSON objects with "ID" and "Type" of each resource.  The "Content.ParentResources"
   field that only contains the IDs is preserved for backward compatibility.
@@ -53,6 +55,7 @@
   https://orthanc.uclouvain.be/bugs/show_bug.cgi?id=255
 * Save the jobs registry in DB only if it has changed.
   https://discourse.orthanc-server.org/t/frequent-idle-messages-between-postgres-and-orthanc/6406
+* New CMake option: "USE_SYSTEM_MINIZIP" to use the system-wide version of minizip
 * Upgraded dependencies for static builds:
   - boost 1.89.0
   - dcmtk 3.7.0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancFramework/Resources/CMake/MinizipConfiguration.cmake	Wed Mar 18 12:13:09 2026 +0100
@@ -0,0 +1,53 @@
+# Orthanc - A Lightweight, RESTful DICOM Store
+# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+# Department, University Hospital of Liege, Belgium
+# Copyright (C) 2017-2023 Osimis S.A., Belgium
+# Copyright (C) 2024-2026 Orthanc Team SRL, Belgium
+# Copyright (C) 2021-2026 Sebastien Jodogne, ICTEAM UCLouvain, Belgium
+#
+# This program is free software: you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public License
+# as published by the Free Software Foundation, either version 3 of
+# the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+
+
+if (NOT ENABLE_ZLIB)
+  message(FATAL_ERROR "This file cannot be used if zlib is disabled")
+endif()
+
+if (STATIC_BUILD OR
+    ORTHANC_SANDBOXED OR   # For WebAssembly
+    NOT USE_SYSTEM_MINIZIP)
+  add_definitions(-DORTHANC_USE_SYSTEM_MINIZIP=0)
+
+  list(APPEND ORTHANC_CORE_SOURCES_DEPENDENCIES
+    ${CMAKE_CURRENT_LIST_DIR}/../../Resources/ThirdParty/minizip/ioapi.c
+    ${CMAKE_CURRENT_LIST_DIR}/../../Resources/ThirdParty/minizip/unzip.c
+    ${CMAKE_CURRENT_LIST_DIR}/../../Resources/ThirdParty/minizip/zip.c
+    )
+
+else()
+  add_definitions(-DORTHANC_USE_SYSTEM_MINIZIP=1)
+
+  CHECK_INCLUDE_FILE_CXX(minizip/zip.h HAVE_MINIZIP_H)
+  if (NOT HAVE_MINIZIP_H)
+    message(FATAL_ERROR "Please install the libminizip-dev package")
+  endif()
+
+  CHECK_LIBRARY_EXISTS(minizip "zipOpen2_64" "" HAVE_MINIZIP_LIB)
+  if (NOT HAVE_MINIZIP_LIB)
+    message(FATAL_ERROR "Please install the libminizip-dev package")
+  endif()
+
+  link_libraries(minizip)
+
+endif()
--- a/OrthancFramework/Resources/CMake/OrthancFrameworkConfiguration.cmake	Wed Mar 18 12:12:12 2026 +0100
+++ b/OrthancFramework/Resources/CMake/OrthancFrameworkConfiguration.cmake	Wed Mar 18 12:13:09 2026 +0100
@@ -752,13 +752,9 @@
   ${CMAKE_CURRENT_LIST_DIR}/../../Resources/ThirdParty/base64/base64.cpp
   )
 
-if (ENABLE_ZLIB AND NOT ORTHANC_SANDBOXED)
-  list(APPEND ORTHANC_CORE_SOURCES_DEPENDENCIES
-    # This is the minizip distribution to create/decode ZIP files using zlib
-    ${CMAKE_CURRENT_LIST_DIR}/../../Resources/ThirdParty/minizip/ioapi.c
-    ${CMAKE_CURRENT_LIST_DIR}/../../Resources/ThirdParty/minizip/unzip.c
-    ${CMAKE_CURRENT_LIST_DIR}/../../Resources/ThirdParty/minizip/zip.c
-    )
+if (ENABLE_ZLIB)
+  # This is the minizip distribution to create/decode ZIP files using zlib
+  include(${CMAKE_CURRENT_LIST_DIR}/MinizipConfiguration.cmake)
 endif()
 
 
--- a/OrthancFramework/Resources/CMake/OrthancFrameworkParameters.cmake	Wed Mar 18 12:12:12 2026 +0100
+++ b/OrthancFramework/Resources/CMake/OrthancFrameworkParameters.cmake	Wed Mar 18 12:13:09 2026 +0100
@@ -69,6 +69,7 @@
 set(USE_SYSTEM_LIBP11 OFF CACHE BOOL "Use the system version of libp11 (PKCS#11 wrapper library)")
 set(USE_SYSTEM_LIBPNG ON CACHE BOOL "Use the system version of libpng")
 set(USE_SYSTEM_LUA ON CACHE BOOL "Use the system version of Lua")
+set(USE_SYSTEM_MINIZIP OFF CACHE BOOL "Use the system version minizip (new in Orthanc 1.12.11)")
 set(USE_SYSTEM_MONGOOSE ON CACHE BOOL "Use the system version of Mongoose")
 set(USE_SYSTEM_OPENSSL ON CACHE BOOL "Use the system version of OpenSSL")
 set(USE_SYSTEM_PROTOBUF ON CACHE BOOL "Use the system version of Google Protocol Buffers")
--- a/OrthancFramework/Sources/Compression/ZipReader.cpp	Wed Mar 18 12:12:12 2026 +0100
+++ b/OrthancFramework/Sources/Compression/ZipReader.cpp	Wed Mar 18 12:13:09 2026 +0100
@@ -28,10 +28,20 @@
 #define NOMINMAX
 #endif
 
+#if !defined(ORTHANC_USE_SYSTEM_MINIZIP)
+#  error The macro ORTHANC_USE_SYSTEM_MINIZIP must be defined
+#endif
+
 #include "ZipReader.h"
 
+#if ORTHANC_USE_SYSTEM_MINIZIP == 1
+#  include <minizip/unzip.h>
+#else
+#  include "../../Resources/ThirdParty/minizip/unzip.h"
+#endif
+
+
 #include "../OrthancException.h"
-#include "../../Resources/ThirdParty/minizip/unzip.h"
 
 #if ORTHANC_SANDBOXED != 1
 #  include "../SystemToolbox.h"
--- a/OrthancFramework/Sources/Compression/ZipWriter.cpp	Wed Mar 18 12:12:12 2026 +0100
+++ b/OrthancFramework/Sources/Compression/ZipWriter.cpp	Wed Mar 18 12:13:09 2026 +0100
@@ -28,13 +28,23 @@
 #define NOMINMAX
 #endif
 
+#if !defined(ORTHANC_USE_SYSTEM_MINIZIP)
+#  error The macro ORTHANC_USE_SYSTEM_MINIZIP must be defined
+#endif
+
 #include "ZipWriter.h"
 
+
 #include <limits>
 #include <boost/filesystem.hpp>
 #include <boost/date_time/posix_time/posix_time.hpp>
 
-#include "../../Resources/ThirdParty/minizip/zip.h"
+#if ORTHANC_USE_SYSTEM_MINIZIP == 1
+#  include <minizip/zip.h>
+#else
+#  include "../../Resources/ThirdParty/minizip/zip.h"
+#endif
+
 #include "../Logging.h"
 #include "../OrthancException.h"
 #include "../SystemToolbox.h"
@@ -586,6 +596,21 @@
     }
   }
 
+
+  void ZipWriter::SetAllowUtf8(bool allowUtf8)
+  {
+    allowUtf8_ = allowUtf8;
+
+#if ORTHANC_USE_SYSTEM_MINIZIP == 1
+    if (allowUtf8_)
+    {
+      LOG(WARNING) << "UTF-8 paths requested in ZIP file, but Orthanc is linked against "
+                   << "the system-wide minizip library, which may not support this feature";
+    }
+#endif
+  }
+
+
   void ZipWriter::SetCompressionLevel(uint8_t level)
   {
     if (level >= 10)
--- a/OrthancFramework/Sources/Compression/ZipWriter.h	Wed Mar 18 12:12:12 2026 +0100
+++ b/OrthancFramework/Sources/Compression/ZipWriter.h	Wed Mar 18 12:13:09 2026 +0100
@@ -154,13 +154,13 @@
      * The behavior of Orthanc <= 1.12.10 corresponds to
      * "SetAllowUtf8(false)".
      *
+     * Note that if linking against the system-wide version of minizip,
+     * there is no way to check that its version is above 1.3.2.
+     *
      * https://zlib.net/ChangeLog.txt
      * https://discourse.orthanc-server.org/t/seriesdescription-characters-and-removed-during-oe2-zip-export/6397
      **/
-    void SetAllowUtf8(bool allowUtf8)
-    {
-      allowUtf8_ = allowUtf8;
-    }
+    void SetAllowUtf8(bool allowUtf8);
 
     bool IsAllowUtf8() const
     {
--- a/OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp	Wed Mar 18 12:12:12 2026 +0100
+++ b/OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp	Wed Mar 18 12:13:09 2026 +0100
@@ -803,7 +803,6 @@
          **/
 
         case EVR_OB:  // other byte
-        case EVR_OF:  // other float
         case EVR_OW:  // other word
         case EVR_UN:  // unknown value representation
         case EVR_ox:  // OB or OW depending on context
@@ -876,6 +875,63 @@
           return ApplyDcmtkToCTypeConverter<DcmtkToFloat64Converter>(element);
         }
 
+        case EVR_OF:  // other float - binary array of 32-bit floats (new in Orthanc 1.12.11)
+        {
+          /**
+           * OF stores a binary array of 32-bit IEEE floats. Unlike FL where getVM()
+           * returns the count of values, for OF getVM() returns 1 (the entire binary
+           * blob is considered one "value"). We must use getFloat32Array() to access
+           * the raw float buffer, then build a string of all values.
+           * The resulting string is formatted as in ApplyDcmtkToCTypeConverter().
+           **/
+          DcmFloatingPointSingle& content = dynamic_cast<DcmFloatingPointSingle&>(element);
+          Float32* floatArray = NULL;
+          if (content.getFloat32Array(floatArray).good() && floatArray != NULL)
+          {
+            const unsigned long numFloats = element.getLength() / sizeof(Float32);
+            std::string result;
+            for (unsigned long i = 0; i < numFloats; i++)
+            {
+              if (i > 0)
+              {
+                result += "\\";
+              }
+              result += boost::lexical_cast<std::string>(floatArray[i]);
+            }
+            return new DicomValue(result, false);
+          }
+          return new DicomValue;
+        }
+
+#if DCMTK_VERSION_NUMBER >= 361
+        case EVR_OD:  // other double - binary array of 64-bit floats (new in Orthanc 1.12.11)
+        {
+          /**
+           * OD stores a binary array of 64-bit IEEE doubles. Similar to OF,
+           * getVM() returns 1 for OD. We must use getFloat64Array() to access
+           * the raw double buffer.
+           * The resulting string is formatted as in ApplyDcmtkToCTypeConverter().
+           **/
+          DcmFloatingPointDouble& content = dynamic_cast<DcmFloatingPointDouble&>(element);
+          Float64* doubleArray = NULL;
+          if (content.getFloat64Array(doubleArray).good() && doubleArray != NULL)
+          {
+            const unsigned long numDoubles = element.getLength() / sizeof(Float64);
+            std::string result;
+            for (unsigned long i = 0; i < numDoubles; i++)
+            {
+              if (i > 0)
+              {
+                result += "\\";
+              }
+              result += boost::lexical_cast<std::string>(doubleArray[i]);
+            }
+            return new DicomValue(result, false);
+          }
+          return new DicomValue;
+        }
+#endif
+
 
         /**
          * Attribute tag.
@@ -3039,7 +3095,6 @@
         }
 
         case EVR_FL:  // float single-precision
-        case EVR_OF:
         {
           DcmFloatingPointSingle& content = dynamic_cast<DcmFloatingPointSingle&>(element);
 
@@ -3060,9 +3115,6 @@
         }
 
         case EVR_FD:  // float double-precision
-#if DCMTK_VERSION_NUMBER >= 361
-        case EVR_OD:
-#endif
         {
           DcmFloatingPointDouble& content = dynamic_cast<DcmFloatingPointDouble&>(element);
 
@@ -3082,6 +3134,64 @@
           break;
         }
 
+        case EVR_OF:  // other float - binary array of 32-bit floats (new in Orthanc 1.12.11)
+        {
+          /**
+           * OF stores a binary array of 32-bit IEEE floats. Unlike FL where getVM()
+           * returns the count of values, for OF getVM() returns 1 (the entire binary
+           * blob is considered one "value"). We must use getFloat32Array() to access
+           * the raw float buffer, then iterate over all values.
+           **/
+          DcmFloatingPointSingle& content = dynamic_cast<DcmFloatingPointSingle&>(element);
+
+          std::vector<double> values;
+
+          Float32* floatArray = NULL;
+          if (content.getFloat32Array(floatArray).good() && floatArray != NULL)
+          {
+            const unsigned long numFloats = static_cast<unsigned long>(element.getLength() / sizeof(Float32));
+            values.reserve(numFloats);
+
+            for (unsigned long i = 0; i < numFloats; i++)
+            {
+              values.push_back(static_cast<double>(floatArray[i]));
+            }
+          }
+
+          action = visitor.VisitDoubles(parentTags, parentIndexes, tag, vr, values);
+          break;
+        }
+
+#if DCMTK_VERSION_NUMBER >= 361
+        case EVR_OD:  // other double - binary array of 64-bit floats (new in Orthanc 1.12.11)
+        {
+          /**
+           * OD stores a binary array of 64-bit IEEE doubles. Unlike FD where getVM()
+           * returns the count of values, for OD getVM() returns 1 (the entire binary
+           * blob is considered one "value"). We must use getFloat64Array() to access
+           * the raw double buffer, then iterate over all values.
+           **/
+          DcmFloatingPointDouble& content = dynamic_cast<DcmFloatingPointDouble&>(element);
+
+          std::vector<double> values;
+
+          Float64* doubleArray = NULL;
+          if (content.getFloat64Array(doubleArray).good() && doubleArray != NULL)
+          {
+            const unsigned long numDoubles = static_cast<unsigned long>(element.getLength() / sizeof(Float64));
+            values.reserve(numDoubles);
+
+            for (unsigned long i = 0; i < numDoubles; i++)
+            {
+              values.push_back(doubleArray[i]);
+            }
+          }
+
+          action = visitor.VisitDoubles(parentTags, parentIndexes, tag, vr, values);
+          break;
+        }
+#endif
+
 
         /**
          * Attribute tag.