# HG changeset patch # User Alain Mazy # Date 1773832389 -3600 # Node ID 71400f1c614328f37371a3d3cf31fcc4dd597bab # Parent 640fc89647a67965913871328db1f5114aa2e23f# Parent 7f44bfb58e7ccfd4aaf33037d766dc03bf631c1d merge diff -r 640fc89647a6 -r 71400f1c6143 NEWS --- 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 diff -r 640fc89647a6 -r 71400f1c6143 OrthancFramework/Resources/CMake/MinizipConfiguration.cmake --- /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 +# . + + +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() diff -r 640fc89647a6 -r 71400f1c6143 OrthancFramework/Resources/CMake/OrthancFrameworkConfiguration.cmake --- 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() diff -r 640fc89647a6 -r 71400f1c6143 OrthancFramework/Resources/CMake/OrthancFrameworkParameters.cmake --- 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") diff -r 640fc89647a6 -r 71400f1c6143 OrthancFramework/Sources/Compression/ZipReader.cpp --- 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 +#else +# include "../../Resources/ThirdParty/minizip/unzip.h" +#endif + + #include "../OrthancException.h" -#include "../../Resources/ThirdParty/minizip/unzip.h" #if ORTHANC_SANDBOXED != 1 # include "../SystemToolbox.h" diff -r 640fc89647a6 -r 71400f1c6143 OrthancFramework/Sources/Compression/ZipWriter.cpp --- 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 #include #include -#include "../../Resources/ThirdParty/minizip/zip.h" +#if ORTHANC_USE_SYSTEM_MINIZIP == 1 +# include +#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) diff -r 640fc89647a6 -r 71400f1c6143 OrthancFramework/Sources/Compression/ZipWriter.h --- 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 { diff -r 640fc89647a6 -r 71400f1c6143 OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp --- 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(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(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(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(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(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(element); @@ -3060,9 +3115,6 @@ } case EVR_FD: // float double-precision -#if DCMTK_VERSION_NUMBER >= 361 - case EVR_OD: -#endif { DcmFloatingPointDouble& content = dynamic_cast(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(element); + + std::vector values; + + Float32* floatArray = NULL; + if (content.getFloat32Array(floatArray).good() && floatArray != NULL) + { + const unsigned long numFloats = static_cast(element.getLength() / sizeof(Float32)); + values.reserve(numFloats); + + for (unsigned long i = 0; i < numFloats; i++) + { + values.push_back(static_cast(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(element); + + std::vector values; + + Float64* doubleArray = NULL; + if (content.getFloat64Array(doubleArray).good() && doubleArray != NULL) + { + const unsigned long numDoubles = static_cast(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.