changeset 6020:8d9bc0217a38 attach-custom-data

integration mainline->attach-custom-data
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 24 Feb 2025 09:19:12 +0100
parents 91394d783aef (current diff) fa5aa4209b63 (diff)
children 80acfe62ace3
files NEWS OrthancFramework/Sources/FileStorage/StorageAccessor.cpp OrthancFramework/Sources/FileStorage/StorageAccessor.h OrthancServer/Resources/Configuration.json OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp OrthancServer/Sources/ServerContext.cpp OrthancServer/Sources/ServerContext.h TODO
diffstat 30 files changed, 843 insertions(+), 294 deletions(-) [+]
line wrap: on
line diff
--- a/INSTALL	Fri Feb 21 15:12:47 2025 +0100
+++ b/INSTALL	Mon Feb 24 09:19:12 2025 +0100
@@ -76,7 +76,7 @@
 
 # cd [...]\Orthanc\Build
 # cmake -DSTANDALONE_BUILD=ON -DSTATIC_BUILD=ON -DALLOW_DOWNLOADS=ON \
-  -DUSE_LEGACY_JSONCPP=ON -G "Visual Studio 9 2008" [...]\OrthancServer
+  -DUSE_LEGACY_JSONCPP=ON -DUSE_LEGACY_BOOST=ON -G "Visual Studio 9 2008" [...]\OrthancServer
 
 Then open the "[...]\Orthanc\Build\Orthanc.sln" with Visual Studio.
 
@@ -145,17 +145,20 @@
 Cross-Compilation for Windows under GNU/Linux
 ---------------------------------------------
 
-Some versions of MinGW-W64 might have problems with C++11 (notably
-those shipped in Ubuntu 16.04 LTS, in the "mingw-w64" package). Use
-the following command to disable C++11:
+Some versions of MinGW-W64 may have insufficient support C++11 to
+compile recent versions of Boost or ICU (notably those shipped in
+Ubuntu 22.04 LTS, in the "g++-mingw-w64-i686-win32" package). Use the
+following command to disable C++11 in Boost and ICU:
 
 # cd ~/Orthanc/Build
 # cmake ../OrthancServer \
-        -DCMAKE_BUILD_TYPE=Debug \
-        -DCMAKE_TOOLCHAIN_FILE=~/Orthanc/Resources/MinGW-W64-Toolchain32.cmake \
+        -DCMAKE_BUILD_TYPE=Release \
+        -DCMAKE_TOOLCHAIN_FILE=../OrthancFramework/Resources/Toolchains/MinGW-W64-Toolchain32.cmake \
         -DSTANDALONE_BUILD=ON \
         -DSTATIC_BUILD=ON \
-        -DUSE_LEGACY_JSONCPP=ON
+        -DBOOST_LOCALE_BACKEND=icu \
+        -DUSE_LEGACY_BOOST=ON \
+        -DUSE_LEGACY_LIBICU=ON
 # make
 
 NB: Use the toolchain "MinGW-W64-Toolchain64.cmake" to produce 64bit
@@ -170,10 +173,12 @@
 
 # cd ~/Orthanc/Build
 # cmake ../OrthancServer \
-        -DCMAKE_BUILD_TYPE=Debug \
-        -DCMAKE_TOOLCHAIN_FILE=~/Orthanc/Resources/MinGWToolchain.cmake \
+        -DCMAKE_BUILD_TYPE=Release \
+        -DCMAKE_TOOLCHAIN_FILE=../OrthancFramework/Resources/Toolchains/MinGWToolchain.cmake \
         -DSTANDALONE_BUILD=ON \
         -DSTATIC_BUILD=ON \
         -DDCMTK_STATIC_VERSION=3.6.0 \
-        -DUSE_LEGACY_JSONCPP=ON
+        -DUSE_LEGACY_JSONCPP=ON \
+        -DUSE_LEGACY_BOOST=ON \
+        -DUSE_LEGACY_LIBICU=ON
 # make
--- a/NEWS	Fri Feb 21 15:12:47 2025 +0100
+++ b/NEWS	Mon Feb 24 09:19:12 2025 +0100
@@ -14,7 +14,12 @@
 --------
 
 * API version upgraded to 28
-
+* GET /studies/../archive and sibbling routes now all accept a 'filename' GET argument.
+* POST /studies/../archive and sibbling routes now all accept a 'Filename' query argument.
+* GET /instances/../file and sibbling ../attachments/../data routes now all accept a 'filename' GET argument.
+* All routes accepting a "transcode" url argument or a "Transcode" field in the payload now also
+  accepts a "lossy-quality" url argument or a "LossyQuality" field to define the compression quality factor.
+  If not specified, the "DicomLossyTranscodingQuality" configuration is taken into account.
 
 Plugins
 -------
@@ -22,12 +27,18 @@
 * New database plugin SDK (vX) to handle customData for attachments.
 * New storage plugin SDK (v3) to handle customData for attachments,
 
-
 Maintenance
 -----------
 
-* In the "ExtendedFind" mode, optimized "tools/find" when "StorageAccessMode" is 
-  set to "Never".
+* In the "ExtendedFind" mode:
+  - optimized "tools/find" when "StorageAccessMode" is set to "Never".
+  - "tools/find" is now returning results when e.g, ordering instances against a metadata they don't have
+* Fixed interpretation of returnUnsupportedImage in /preview route. 
+* GET /series/../study now also contain LastUpdate field:
+  https://discourse.orthanc-server.org/t/lastupdate-coherency/5524
+* Recovered compatibility with Windows XP that was broken because of DCMTK 3.6.9
+* Enabled support of the 1.2.840.10008.1.2.1.99 transfer syntax
+  (Deflated Explicit VR Little Endian) in static builds
 
 
 Version 1.12.6 (2025-01-22)
--- a/OrthancFramework/Resources/CMake/DcmtkConfiguration.cmake	Fri Feb 21 15:12:47 2025 +0100
+++ b/OrthancFramework/Resources/CMake/DcmtkConfiguration.cmake	Mon Feb 24 09:19:12 2025 +0100
@@ -286,6 +286,7 @@
     find_path(DCMTK_DICTIONARY_DIR_AUTO dicom.dic
       /usr/share/dcmtk
       /usr/share/dcmtk-3.6.8
+      /usr/share/dcmtk-3.6.9
       /usr/share/libdcmtk1
       /usr/share/libdcmtk2
       /usr/share/libdcmtk3
--- a/OrthancFramework/Resources/CMake/DcmtkConfigurationStatic-3.6.9.cmake	Fri Feb 21 15:12:47 2025 +0100
+++ b/OrthancFramework/Resources/CMake/DcmtkConfigurationStatic-3.6.9.cmake	Mon Feb 24 09:19:12 2025 +0100
@@ -203,6 +203,14 @@
   message(FATAL_ERROR "Invalid value for DCMTK_LOCALE_BACKEND: ${DCMTK_LOCALE_BACKEND}")
 endif()
 
+
+# Enable support of the 1.2.840.10008.1.2.1.99 transfer syntax in
+# static builds of Orthanc (Deflated Explicit VR Little
+# Endian). Defining "WITH_ZLIB" is always OK, as zlib is part of the
+# core dependencies of the Orthanc framework.
+# https://discourse.orthanc-server.org/t/transcoding-to-deflated-transfer-syntax-fails/
+set(WITH_ZLIB ON)
+
 CONFIGURE_FILE(
   ${DCMTK_SOURCES_DIR}/CMake/osconfig.h.in
   ${DCMTK_SOURCES_DIR}/config/include/dcmtk/config/osconfig.h)
--- a/OrthancFramework/Resources/Patches/curl-8.9.0.patch	Fri Feb 21 15:12:47 2025 +0100
+++ b/OrthancFramework/Resources/Patches/curl-8.9.0.patch	Mon Feb 24 09:19:12 2025 +0100
@@ -1,6 +1,6 @@
 diff -urEb curl-8.9.0.orig/CMake/Macros.cmake curl-8.9.0/CMake/Macros.cmake
---- curl-8.9.0.orig/CMake/Macros.cmake	2024-07-26 18:47:52.920588300 +0200
-+++ curl-8.9.0/CMake/Macros.cmake	2024-07-26 18:48:08.345522100 +0200
+--- curl-8.9.0.orig/CMake/Macros.cmake	2025-02-18 16:04:59.818585107 +0100
++++ curl-8.9.0/CMake/Macros.cmake	2025-02-18 16:05:16.867458366 +0100
 @@ -48,7 +48,7 @@
      message(STATUS "Performing Test ${CURL_TEST}")
      try_compile(${CURL_TEST}
@@ -10,3 +10,15 @@
        CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS}
        "${CURL_TEST_ADD_LIBRARIES}"
        OUTPUT_VARIABLE OUTPUT)
+diff -urEb curl-8.9.0.orig/lib/system_win32.c curl-8.9.0/lib/system_win32.c
+--- curl-8.9.0.orig/lib/system_win32.c	2025-02-18 16:04:59.834584988 +0100
++++ curl-8.9.0/lib/system_win32.c	2025-02-18 16:06:26.448941452 +0100
+@@ -273,7 +273,7 @@
+ 
+ bool Curl_win32_impersonating(void)
+ {
+-#ifndef CURL_WINDOWS_APP
++#if !defined(CURL_WINDOWS_APP) && !defined(__MINGW32__)
+   HANDLE token = NULL;
+   if(OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &token)) {
+     CloseHandle(token);
--- a/OrthancFramework/Resources/Patches/dcmtk-3.6.9-visual-studio.patch	Fri Feb 21 15:12:47 2025 +0100
+++ b/OrthancFramework/Resources/Patches/dcmtk-3.6.9-visual-studio.patch	Mon Feb 24 09:19:12 2025 +0100
@@ -1,6 +1,6 @@
 diff -urEb dcmtk-3.6.9.orig/config/math.cc dcmtk-3.6.9/config/math.cc
---- dcmtk-3.6.9.orig/config/math.cc	2025-01-16 18:04:46.121846434 +0100
-+++ dcmtk-3.6.9/config/math.cc	2025-01-17 07:45:46.252026376 +0100
+--- dcmtk-3.6.9.orig/config/math.cc	2025-02-18 18:03:13.501406015 +0100
++++ dcmtk-3.6.9/config/math.cc	2025-02-18 18:05:52.950086576 +0100
 @@ -42,11 +42,17 @@
  #include <windows.h>
  #endif
@@ -54,8 +54,8 @@
  #else
      return ::isinf( d );
 diff -urEb dcmtk-3.6.9.orig/dcmjpeg/libijg12/jccoefct.c dcmtk-3.6.9/dcmjpeg/libijg12/jccoefct.c
---- dcmtk-3.6.9.orig/dcmjpeg/libijg12/jccoefct.c	2025-01-16 18:04:46.158846116 +0100
-+++ dcmtk-3.6.9/dcmjpeg/libijg12/jccoefct.c	2025-01-16 20:11:42.016717178 +0100
+--- dcmtk-3.6.9.orig/dcmjpeg/libijg12/jccoefct.c	2025-02-18 18:03:13.530405562 +0100
++++ dcmtk-3.6.9/dcmjpeg/libijg12/jccoefct.c	2025-02-18 18:05:52.950086576 +0100
 @@ -343,7 +343,7 @@
  METHODDEF(boolean)
  compress_output (j_compress_ptr cinfo, JSAMPIMAGE input_buf)
@@ -66,8 +66,8 @@
    c_coef_ptr coef = (c_coef_ptr) lossyc->coef_private;
    JDIMENSION MCU_col_num;   /* index of current MCU within row */
 diff -urEb dcmtk-3.6.9.orig/dcmjpeg/libijg12/jcdiffct.c dcmtk-3.6.9/dcmjpeg/libijg12/jcdiffct.c
---- dcmtk-3.6.9.orig/dcmjpeg/libijg12/jcdiffct.c	2025-01-16 18:04:46.158846116 +0100
-+++ dcmtk-3.6.9/dcmjpeg/libijg12/jcdiffct.c	2025-01-16 20:11:42.016717178 +0100
+--- dcmtk-3.6.9.orig/dcmjpeg/libijg12/jcdiffct.c	2025-02-18 18:03:13.531405546 +0100
++++ dcmtk-3.6.9/dcmjpeg/libijg12/jcdiffct.c	2025-02-18 18:05:52.951086562 +0100
 @@ -302,7 +302,7 @@
  METHODDEF(boolean)
  compress_output (j_compress_ptr cinfo, JSAMPIMAGE input_buf)
@@ -78,8 +78,8 @@
    c_diff_ptr diff = (c_diff_ptr) losslsc->diff_private;
    /* JDIMENSION MCU_col_num; */ /* index of current MCU within row */
 diff -urEb dcmtk-3.6.9.orig/dcmjpeg/libijg12/jcpred.c dcmtk-3.6.9/dcmjpeg/libijg12/jcpred.c
---- dcmtk-3.6.9.orig/dcmjpeg/libijg12/jcpred.c	2025-01-16 18:04:46.158846116 +0100
-+++ dcmtk-3.6.9/dcmjpeg/libijg12/jcpred.c	2025-01-16 20:11:42.017717169 +0100
+--- dcmtk-3.6.9.orig/dcmjpeg/libijg12/jcpred.c	2025-02-18 18:03:13.530405562 +0100
++++ dcmtk-3.6.9/dcmjpeg/libijg12/jcpred.c	2025-02-18 18:05:52.951086562 +0100
 @@ -213,7 +213,7 @@
           const JSAMPROW input_buf, JSAMPROW prev_row,
           JDIFFROW diff_buf, JDIMENSION width)
@@ -90,8 +90,8 @@
  
    /*
 diff -urEb dcmtk-3.6.9.orig/dcmjpeg/libijg12/jctrans.c dcmtk-3.6.9/dcmjpeg/libijg12/jctrans.c
---- dcmtk-3.6.9.orig/dcmjpeg/libijg12/jctrans.c	2025-01-16 18:04:46.158846116 +0100
-+++ dcmtk-3.6.9/dcmjpeg/libijg12/jctrans.c	2025-01-16 20:11:42.017717169 +0100
+--- dcmtk-3.6.9.orig/dcmjpeg/libijg12/jctrans.c	2025-02-18 18:03:13.530405562 +0100
++++ dcmtk-3.6.9/dcmjpeg/libijg12/jctrans.c	2025-02-18 18:05:52.951086562 +0100
 @@ -267,7 +267,7 @@
  METHODDEF(boolean)
  compress_output (j_compress_ptr cinfo, JSAMPIMAGE input_buf)
@@ -102,8 +102,8 @@
    c_coef_ptr coef = (c_coef_ptr) lossyc->coef_private;
    JDIMENSION MCU_col_num;   /* index of current MCU within row */
 diff -urEb dcmtk-3.6.9.orig/dcmjpeg/libijg12/jdmerge.c dcmtk-3.6.9/dcmjpeg/libijg12/jdmerge.c
---- dcmtk-3.6.9.orig/dcmjpeg/libijg12/jdmerge.c	2025-01-16 18:04:46.158846116 +0100
-+++ dcmtk-3.6.9/dcmjpeg/libijg12/jdmerge.c	2025-01-16 20:11:42.017717169 +0100
+--- dcmtk-3.6.9.orig/dcmjpeg/libijg12/jdmerge.c	2025-02-18 18:03:13.530405562 +0100
++++ dcmtk-3.6.9/dcmjpeg/libijg12/jdmerge.c	2025-02-18 18:05:52.951086562 +0100
 @@ -148,7 +148,7 @@
              JDIMENSION out_rows_avail)
  /* 2:1 vertical sampling case: may need a spare row. */
@@ -125,8 +125,8 @@
  
    /* Just do the upsampling. */
 diff -urEb dcmtk-3.6.9.orig/dcmjpeg/libijg12/jdpostct.c dcmtk-3.6.9/dcmjpeg/libijg12/jdpostct.c
---- dcmtk-3.6.9.orig/dcmjpeg/libijg12/jdpostct.c	2025-01-16 18:04:46.158846116 +0100
-+++ dcmtk-3.6.9/dcmjpeg/libijg12/jdpostct.c	2025-01-16 20:11:42.017717169 +0100
+--- dcmtk-3.6.9.orig/dcmjpeg/libijg12/jdpostct.c	2025-02-18 18:03:13.530405562 +0100
++++ dcmtk-3.6.9/dcmjpeg/libijg12/jdpostct.c	2025-02-18 18:05:52.952086549 +0100
 @@ -161,8 +161,8 @@
                JSAMPARRAY output_buf, JDIMENSION *out_row_ctr,
                JDIMENSION out_rows_avail)
@@ -152,8 +152,8 @@
    my_post_ptr post = (my_post_ptr) cinfo->post;
    JDIMENSION num_rows, max_rows;
 diff -urEb dcmtk-3.6.9.orig/dcmjpeg/libijg12/jdpred.c dcmtk-3.6.9/dcmjpeg/libijg12/jdpred.c
---- dcmtk-3.6.9.orig/dcmjpeg/libijg12/jdpred.c	2025-01-16 18:04:46.158846116 +0100
-+++ dcmtk-3.6.9/dcmjpeg/libijg12/jdpred.c	2025-01-16 20:11:42.017717169 +0100
+--- dcmtk-3.6.9.orig/dcmjpeg/libijg12/jdpred.c	2025-02-18 18:03:13.530405562 +0100
++++ dcmtk-3.6.9/dcmjpeg/libijg12/jdpred.c	2025-02-18 18:05:52.952086549 +0100
 @@ -101,8 +101,8 @@
             const JDIFFROW diff_buf, const JDIFFROW prev_row,
             JDIFFROW undiff_buf, JDIMENSION width)
@@ -241,8 +241,8 @@
  
    UNDIFFERENCE_1D(INITIAL_PREDICTORx);
 diff -urEb dcmtk-3.6.9.orig/dcmjpeg/libijg12/jdsample.c dcmtk-3.6.9/dcmjpeg/libijg12/jdsample.c
---- dcmtk-3.6.9.orig/dcmjpeg/libijg12/jdsample.c	2025-01-16 18:04:46.158846116 +0100
-+++ dcmtk-3.6.9/dcmjpeg/libijg12/jdsample.c	2025-01-16 20:11:42.018717160 +0100
+--- dcmtk-3.6.9.orig/dcmjpeg/libijg12/jdsample.c	2025-02-18 18:03:13.530405562 +0100
++++ dcmtk-3.6.9/dcmjpeg/libijg12/jdsample.c	2025-02-18 18:05:52.952086549 +0100
 @@ -92,7 +92,7 @@
            JSAMPARRAY output_buf, JDIMENSION *out_row_ctr,
            JDIMENSION out_rows_avail)
@@ -295,8 +295,8 @@
    register JSAMPROW inptr, outptr;
    register JSAMPLE invalue;
 diff -urEb dcmtk-3.6.9.orig/dcmjpeg/libijg12/jdscale.c dcmtk-3.6.9/dcmjpeg/libijg12/jdscale.c
---- dcmtk-3.6.9.orig/dcmjpeg/libijg12/jdscale.c	2025-01-16 18:04:46.157846124 +0100
-+++ dcmtk-3.6.9/dcmjpeg/libijg12/jdscale.c	2025-01-16 20:11:42.018717160 +0100
+--- dcmtk-3.6.9.orig/dcmjpeg/libijg12/jdscale.c	2025-02-18 18:03:13.530405562 +0100
++++ dcmtk-3.6.9/dcmjpeg/libijg12/jdscale.c	2025-02-18 18:05:52.953086535 +0100
 @@ -67,7 +67,7 @@
  	const JDIFFROW diff_buf, JSAMPROW output_buf,
  	JDIMENSION width)
@@ -307,8 +307,8 @@
  
    for (xindex = 0; xindex < width; xindex++)
 diff -urEb dcmtk-3.6.9.orig/dcmjpeg/libijg12/jerror.c dcmtk-3.6.9/dcmjpeg/libijg12/jerror.c
---- dcmtk-3.6.9.orig/dcmjpeg/libijg12/jerror.c	2025-01-16 18:04:46.158846116 +0100
-+++ dcmtk-3.6.9/dcmjpeg/libijg12/jerror.c	2025-01-17 11:14:11.975382089 +0100
+--- dcmtk-3.6.9.orig/dcmjpeg/libijg12/jerror.c	2025-02-18 18:03:13.530405562 +0100
++++ dcmtk-3.6.9/dcmjpeg/libijg12/jerror.c	2025-02-18 18:05:52.953086535 +0100
 @@ -34,6 +34,10 @@
  #define EXIT_FAILURE  1
  #endif
@@ -321,8 +321,8 @@
  /*
   * Create the message string table.
 diff -urEb dcmtk-3.6.9.orig/dcmjpeg/libijg12/jquant1.c dcmtk-3.6.9/dcmjpeg/libijg12/jquant1.c
---- dcmtk-3.6.9.orig/dcmjpeg/libijg12/jquant1.c	2025-01-16 18:04:46.157846124 +0100
-+++ dcmtk-3.6.9/dcmjpeg/libijg12/jquant1.c	2025-01-16 20:11:42.018717160 +0100
+--- dcmtk-3.6.9.orig/dcmjpeg/libijg12/jquant1.c	2025-02-18 18:03:13.530405562 +0100
++++ dcmtk-3.6.9/dcmjpeg/libijg12/jquant1.c	2025-02-18 18:05:52.953086535 +0100
 @@ -251,8 +251,8 @@
     * (Forcing the upper and lower values to the limits ensures that
     * dithering can't produce a color outside the selected gamut.)
@@ -364,8 +364,8 @@
  }
  
 diff -urEb dcmtk-3.6.9.orig/dcmjpeg/libijg12/jquant2.c dcmtk-3.6.9/dcmjpeg/libijg12/jquant2.c
---- dcmtk-3.6.9.orig/dcmjpeg/libijg12/jquant2.c	2025-01-16 18:04:46.157846124 +0100
-+++ dcmtk-3.6.9/dcmjpeg/libijg12/jquant2.c	2025-01-16 20:11:42.018717160 +0100
+--- dcmtk-3.6.9.orig/dcmjpeg/libijg12/jquant2.c	2025-02-18 18:03:13.530405562 +0100
++++ dcmtk-3.6.9/dcmjpeg/libijg12/jquant2.c	2025-02-18 18:05:52.954086521 +0100
 @@ -224,7 +224,7 @@
  prescan_quantize (j_decompress_ptr cinfo, JSAMPARRAY input_buf,
            JSAMPARRAY output_buf, int num_rows)
@@ -385,8 +385,8 @@
  }
  
 diff -urEb dcmtk-3.6.9.orig/dcmjpeg/libijg16/jccoefct.c dcmtk-3.6.9/dcmjpeg/libijg16/jccoefct.c
---- dcmtk-3.6.9.orig/dcmjpeg/libijg16/jccoefct.c	2025-01-16 18:04:46.159846107 +0100
-+++ dcmtk-3.6.9/dcmjpeg/libijg16/jccoefct.c	2025-01-16 20:11:42.019717150 +0100
+--- dcmtk-3.6.9.orig/dcmjpeg/libijg16/jccoefct.c	2025-02-18 18:03:13.531405546 +0100
++++ dcmtk-3.6.9/dcmjpeg/libijg16/jccoefct.c	2025-02-18 18:05:52.954086521 +0100
 @@ -343,7 +343,7 @@
  METHODDEF(boolean)
  compress_output (j_compress_ptr cinfo, JSAMPIMAGE input_buf)
@@ -397,8 +397,8 @@
    c_coef_ptr coef = (c_coef_ptr) lossyc->coef_private;
    JDIMENSION MCU_col_num;   /* index of current MCU within row */
 diff -urEb dcmtk-3.6.9.orig/dcmjpeg/libijg16/jcdiffct.c dcmtk-3.6.9/dcmjpeg/libijg16/jcdiffct.c
---- dcmtk-3.6.9.orig/dcmjpeg/libijg16/jcdiffct.c	2025-01-16 18:04:46.159846107 +0100
-+++ dcmtk-3.6.9/dcmjpeg/libijg16/jcdiffct.c	2025-01-16 20:11:42.019717150 +0100
+--- dcmtk-3.6.9.orig/dcmjpeg/libijg16/jcdiffct.c	2025-02-18 18:03:13.531405546 +0100
++++ dcmtk-3.6.9/dcmjpeg/libijg16/jcdiffct.c	2025-02-18 18:05:52.954086521 +0100
 @@ -302,7 +302,7 @@
  METHODDEF(boolean)
  compress_output (j_compress_ptr cinfo, JSAMPIMAGE input_buf)
@@ -409,8 +409,8 @@
    c_diff_ptr diff = (c_diff_ptr) losslsc->diff_private;
    /* JDIMENSION MCU_col_num; */ /* index of current MCU within row */
 diff -urEb dcmtk-3.6.9.orig/dcmjpeg/libijg16/jcpred.c dcmtk-3.6.9/dcmjpeg/libijg16/jcpred.c
---- dcmtk-3.6.9.orig/dcmjpeg/libijg16/jcpred.c	2025-01-16 18:04:46.159846107 +0100
-+++ dcmtk-3.6.9/dcmjpeg/libijg16/jcpred.c	2025-01-16 20:11:42.019717150 +0100
+--- dcmtk-3.6.9.orig/dcmjpeg/libijg16/jcpred.c	2025-02-18 18:03:13.531405546 +0100
++++ dcmtk-3.6.9/dcmjpeg/libijg16/jcpred.c	2025-02-18 18:05:52.954086521 +0100
 @@ -213,7 +213,7 @@
           const JSAMPROW input_buf, JSAMPROW prev_row,
           JDIFFROW diff_buf, JDIMENSION width)
@@ -421,8 +421,8 @@
  
    /*
 diff -urEb dcmtk-3.6.9.orig/dcmjpeg/libijg16/jctrans.c dcmtk-3.6.9/dcmjpeg/libijg16/jctrans.c
---- dcmtk-3.6.9.orig/dcmjpeg/libijg16/jctrans.c	2025-01-16 18:04:46.159846107 +0100
-+++ dcmtk-3.6.9/dcmjpeg/libijg16/jctrans.c	2025-01-16 20:11:42.019717150 +0100
+--- dcmtk-3.6.9.orig/dcmjpeg/libijg16/jctrans.c	2025-02-18 18:03:13.531405546 +0100
++++ dcmtk-3.6.9/dcmjpeg/libijg16/jctrans.c	2025-02-18 18:05:52.954086521 +0100
 @@ -267,7 +267,7 @@
  METHODDEF(boolean)
  compress_output (j_compress_ptr cinfo, JSAMPIMAGE input_buf)
@@ -433,8 +433,8 @@
    c_coef_ptr coef = (c_coef_ptr) lossyc->coef_private;
    JDIMENSION MCU_col_num;   /* index of current MCU within row */
 diff -urEb dcmtk-3.6.9.orig/dcmjpeg/libijg16/jdmerge.c dcmtk-3.6.9/dcmjpeg/libijg16/jdmerge.c
---- dcmtk-3.6.9.orig/dcmjpeg/libijg16/jdmerge.c	2025-01-16 18:04:46.159846107 +0100
-+++ dcmtk-3.6.9/dcmjpeg/libijg16/jdmerge.c	2025-01-16 20:11:42.019717150 +0100
+--- dcmtk-3.6.9.orig/dcmjpeg/libijg16/jdmerge.c	2025-02-18 18:03:13.531405546 +0100
++++ dcmtk-3.6.9/dcmjpeg/libijg16/jdmerge.c	2025-02-18 18:05:52.955086508 +0100
 @@ -169,7 +169,7 @@
              JDIMENSION out_rows_avail)
  /* 2:1 vertical sampling case: may need a spare row. */
@@ -456,8 +456,8 @@
  
    /* Just do the upsampling. */
 diff -urEb dcmtk-3.6.9.orig/dcmjpeg/libijg16/jdpostct.c dcmtk-3.6.9/dcmjpeg/libijg16/jdpostct.c
---- dcmtk-3.6.9.orig/dcmjpeg/libijg16/jdpostct.c	2025-01-16 18:04:46.159846107 +0100
-+++ dcmtk-3.6.9/dcmjpeg/libijg16/jdpostct.c	2025-01-16 20:11:42.019717150 +0100
+--- dcmtk-3.6.9.orig/dcmjpeg/libijg16/jdpostct.c	2025-02-18 18:03:13.531405546 +0100
++++ dcmtk-3.6.9/dcmjpeg/libijg16/jdpostct.c	2025-02-18 18:05:52.955086508 +0100
 @@ -161,8 +161,8 @@
                JSAMPARRAY output_buf, JDIMENSION *out_row_ctr,
                JDIMENSION out_rows_avail)
@@ -483,8 +483,8 @@
    JDIMENSION num_rows, max_rows;
  
 diff -urEb dcmtk-3.6.9.orig/dcmjpeg/libijg16/jdpred.c dcmtk-3.6.9/dcmjpeg/libijg16/jdpred.c
---- dcmtk-3.6.9.orig/dcmjpeg/libijg16/jdpred.c	2025-01-16 18:04:46.159846107 +0100
-+++ dcmtk-3.6.9/dcmjpeg/libijg16/jdpred.c	2025-01-16 20:11:42.020717140 +0100
+--- dcmtk-3.6.9.orig/dcmjpeg/libijg16/jdpred.c	2025-02-18 18:03:13.531405546 +0100
++++ dcmtk-3.6.9/dcmjpeg/libijg16/jdpred.c	2025-02-18 18:05:52.955086508 +0100
 @@ -101,8 +101,8 @@
             const JDIFFROW diff_buf, const JDIFFROW prev_row,
             JDIFFROW undiff_buf, JDIMENSION width)
@@ -616,8 +616,8 @@
  
    UNDIFFERENCE_1D(INITIAL_PREDICTORx);
 diff -urEb dcmtk-3.6.9.orig/dcmjpeg/libijg16/jdsample.c dcmtk-3.6.9/dcmjpeg/libijg16/jdsample.c
---- dcmtk-3.6.9.orig/dcmjpeg/libijg16/jdsample.c	2025-01-16 18:04:46.159846107 +0100
-+++ dcmtk-3.6.9/dcmjpeg/libijg16/jdsample.c	2025-01-16 20:11:42.020717140 +0100
+--- dcmtk-3.6.9.orig/dcmjpeg/libijg16/jdsample.c	2025-02-18 18:03:13.531405546 +0100
++++ dcmtk-3.6.9/dcmjpeg/libijg16/jdsample.c	2025-02-18 18:05:52.955086508 +0100
 @@ -92,7 +92,7 @@
            JSAMPARRAY output_buf, JDIMENSION *out_row_ctr,
            JDIMENSION out_rows_avail)
@@ -670,8 +670,8 @@
    register JSAMPROW inptr, outptr;
    register JSAMPLE invalue;
 diff -urEb dcmtk-3.6.9.orig/dcmjpeg/libijg16/jdscale.c dcmtk-3.6.9/dcmjpeg/libijg16/jdscale.c
---- dcmtk-3.6.9.orig/dcmjpeg/libijg16/jdscale.c	2025-01-16 18:04:46.158846116 +0100
-+++ dcmtk-3.6.9/dcmjpeg/libijg16/jdscale.c	2025-01-16 20:11:42.020717140 +0100
+--- dcmtk-3.6.9.orig/dcmjpeg/libijg16/jdscale.c	2025-02-18 18:03:13.531405546 +0100
++++ dcmtk-3.6.9/dcmjpeg/libijg16/jdscale.c	2025-02-18 18:05:52.955086508 +0100
 @@ -67,7 +67,7 @@
  	const JDIFFROW diff_buf, JSAMPROW output_buf,
  	JDIMENSION width)
@@ -682,8 +682,8 @@
  
    for (xindex = 0; xindex < width; xindex++)
 diff -urEb dcmtk-3.6.9.orig/dcmjpeg/libijg16/jerror.c dcmtk-3.6.9/dcmjpeg/libijg16/jerror.c
---- dcmtk-3.6.9.orig/dcmjpeg/libijg16/jerror.c	2025-01-16 18:04:46.159846107 +0100
-+++ dcmtk-3.6.9/dcmjpeg/libijg16/jerror.c	2025-01-17 11:14:19.002328399 +0100
+--- dcmtk-3.6.9.orig/dcmjpeg/libijg16/jerror.c	2025-02-18 18:03:13.531405546 +0100
++++ dcmtk-3.6.9/dcmjpeg/libijg16/jerror.c	2025-02-18 18:05:52.955086508 +0100
 @@ -34,6 +34,10 @@
  #define EXIT_FAILURE  1
  #endif
@@ -696,8 +696,8 @@
  /*
   * Create the message string table.
 diff -urEb dcmtk-3.6.9.orig/dcmjpeg/libijg16/jquant1.c dcmtk-3.6.9/dcmjpeg/libijg16/jquant1.c
---- dcmtk-3.6.9.orig/dcmjpeg/libijg16/jquant1.c	2025-01-16 18:04:46.158846116 +0100
-+++ dcmtk-3.6.9/dcmjpeg/libijg16/jquant1.c	2025-01-16 20:11:42.020717140 +0100
+--- dcmtk-3.6.9.orig/dcmjpeg/libijg16/jquant1.c	2025-02-18 18:03:13.531405546 +0100
++++ dcmtk-3.6.9/dcmjpeg/libijg16/jquant1.c	2025-02-18 18:05:52.956086494 +0100
 @@ -251,8 +251,8 @@
     * (Forcing the upper and lower values to the limits ensures that
     * dithering can't produce a color outside the selected gamut.)
@@ -739,8 +739,8 @@
  }
  
 diff -urEb dcmtk-3.6.9.orig/dcmjpeg/libijg16/jquant2.c dcmtk-3.6.9/dcmjpeg/libijg16/jquant2.c
---- dcmtk-3.6.9.orig/dcmjpeg/libijg16/jquant2.c	2025-01-16 18:04:46.158846116 +0100
-+++ dcmtk-3.6.9/dcmjpeg/libijg16/jquant2.c	2025-01-16 20:11:42.021717131 +0100
+--- dcmtk-3.6.9.orig/dcmjpeg/libijg16/jquant2.c	2025-02-18 18:03:13.531405546 +0100
++++ dcmtk-3.6.9/dcmjpeg/libijg16/jquant2.c	2025-02-18 18:05:52.956086494 +0100
 @@ -224,7 +224,7 @@
  prescan_quantize (j_decompress_ptr cinfo, JSAMPARRAY input_buf,
            JSAMPARRAY output_buf, int num_rows)
@@ -760,8 +760,8 @@
  }
  
 diff -urEb dcmtk-3.6.9.orig/dcmjpeg/libijg8/jccoefct.c dcmtk-3.6.9/dcmjpeg/libijg8/jccoefct.c
---- dcmtk-3.6.9.orig/dcmjpeg/libijg8/jccoefct.c	2025-01-16 18:04:46.161846090 +0100
-+++ dcmtk-3.6.9/dcmjpeg/libijg8/jccoefct.c	2025-01-16 20:11:42.021717131 +0100
+--- dcmtk-3.6.9.orig/dcmjpeg/libijg8/jccoefct.c	2025-02-18 18:03:13.533405515 +0100
++++ dcmtk-3.6.9/dcmjpeg/libijg8/jccoefct.c	2025-02-18 18:05:52.956086494 +0100
 @@ -343,7 +343,7 @@
  METHODDEF(boolean)
  compress_output (j_compress_ptr cinfo, JSAMPIMAGE input_buf)
@@ -772,8 +772,8 @@
    c_coef_ptr coef = (c_coef_ptr) lossyc->coef_private;
    JDIMENSION MCU_col_num;   /* index of current MCU within row */
 diff -urEb dcmtk-3.6.9.orig/dcmjpeg/libijg8/jcdiffct.c dcmtk-3.6.9/dcmjpeg/libijg8/jcdiffct.c
---- dcmtk-3.6.9.orig/dcmjpeg/libijg8/jcdiffct.c	2025-01-16 18:04:46.161846090 +0100
-+++ dcmtk-3.6.9/dcmjpeg/libijg8/jcdiffct.c	2025-01-16 20:11:42.021717131 +0100
+--- dcmtk-3.6.9.orig/dcmjpeg/libijg8/jcdiffct.c	2025-02-18 18:03:13.533405515 +0100
++++ dcmtk-3.6.9/dcmjpeg/libijg8/jcdiffct.c	2025-02-18 18:05:52.956086494 +0100
 @@ -302,7 +302,7 @@
  METHODDEF(boolean)
  compress_output (j_compress_ptr cinfo, JSAMPIMAGE input_buf)
@@ -784,8 +784,8 @@
    c_diff_ptr diff = (c_diff_ptr) losslsc->diff_private;
    /* JDIMENSION MCU_col_num; */ /* index of current MCU within row */
 diff -urEb dcmtk-3.6.9.orig/dcmjpeg/libijg8/jcpred.c dcmtk-3.6.9/dcmjpeg/libijg8/jcpred.c
---- dcmtk-3.6.9.orig/dcmjpeg/libijg8/jcpred.c	2025-01-16 18:04:46.161846090 +0100
-+++ dcmtk-3.6.9/dcmjpeg/libijg8/jcpred.c	2025-01-16 20:11:42.021717131 +0100
+--- dcmtk-3.6.9.orig/dcmjpeg/libijg8/jcpred.c	2025-02-18 18:03:13.533405515 +0100
++++ dcmtk-3.6.9/dcmjpeg/libijg8/jcpred.c	2025-02-18 18:05:52.957086481 +0100
 @@ -213,7 +213,7 @@
           const JSAMPROW input_buf, JSAMPROW prev_row,
           JDIFFROW diff_buf, JDIMENSION width)
@@ -796,8 +796,8 @@
  
    /*
 diff -urEb dcmtk-3.6.9.orig/dcmjpeg/libijg8/jctrans.c dcmtk-3.6.9/dcmjpeg/libijg8/jctrans.c
---- dcmtk-3.6.9.orig/dcmjpeg/libijg8/jctrans.c	2025-01-16 18:04:46.161846090 +0100
-+++ dcmtk-3.6.9/dcmjpeg/libijg8/jctrans.c	2025-01-16 20:11:42.021717131 +0100
+--- dcmtk-3.6.9.orig/dcmjpeg/libijg8/jctrans.c	2025-02-18 18:03:13.533405515 +0100
++++ dcmtk-3.6.9/dcmjpeg/libijg8/jctrans.c	2025-02-18 18:05:52.957086481 +0100
 @@ -267,7 +267,7 @@
  METHODDEF(boolean)
  compress_output (j_compress_ptr cinfo, JSAMPIMAGE input_buf)
@@ -808,8 +808,8 @@
    c_coef_ptr coef = (c_coef_ptr) lossyc->coef_private;
    JDIMENSION MCU_col_num;   /* index of current MCU within row */
 diff -urEb dcmtk-3.6.9.orig/dcmjpeg/libijg8/jdmerge.c dcmtk-3.6.9/dcmjpeg/libijg8/jdmerge.c
---- dcmtk-3.6.9.orig/dcmjpeg/libijg8/jdmerge.c	2025-01-16 18:04:46.161846090 +0100
-+++ dcmtk-3.6.9/dcmjpeg/libijg8/jdmerge.c	2025-01-16 20:11:42.021717131 +0100
+--- dcmtk-3.6.9.orig/dcmjpeg/libijg8/jdmerge.c	2025-02-18 18:03:13.533405515 +0100
++++ dcmtk-3.6.9/dcmjpeg/libijg8/jdmerge.c	2025-02-18 18:05:52.957086481 +0100
 @@ -148,7 +148,7 @@
              JDIMENSION out_rows_avail)
  /* 2:1 vertical sampling case: may need a spare row. */
@@ -831,8 +831,8 @@
  
    /* Just do the upsampling. */
 diff -urEb dcmtk-3.6.9.orig/dcmjpeg/libijg8/jdpostct.c dcmtk-3.6.9/dcmjpeg/libijg8/jdpostct.c
---- dcmtk-3.6.9.orig/dcmjpeg/libijg8/jdpostct.c	2025-01-16 18:04:46.160846099 +0100
-+++ dcmtk-3.6.9/dcmjpeg/libijg8/jdpostct.c	2025-01-16 20:11:42.022717122 +0100
+--- dcmtk-3.6.9.orig/dcmjpeg/libijg8/jdpostct.c	2025-02-18 18:03:13.533405515 +0100
++++ dcmtk-3.6.9/dcmjpeg/libijg8/jdpostct.c	2025-02-18 18:05:52.957086481 +0100
 @@ -161,8 +161,8 @@
                JSAMPARRAY output_buf, JDIMENSION *out_row_ctr,
                JDIMENSION out_rows_avail)
@@ -858,8 +858,8 @@
    JDIMENSION num_rows, max_rows;
  
 diff -urEb dcmtk-3.6.9.orig/dcmjpeg/libijg8/jdpred.c dcmtk-3.6.9/dcmjpeg/libijg8/jdpred.c
---- dcmtk-3.6.9.orig/dcmjpeg/libijg8/jdpred.c	2025-01-16 18:04:46.161846090 +0100
-+++ dcmtk-3.6.9/dcmjpeg/libijg8/jdpred.c	2025-01-16 20:11:42.022717122 +0100
+--- dcmtk-3.6.9.orig/dcmjpeg/libijg8/jdpred.c	2025-02-18 18:03:13.533405515 +0100
++++ dcmtk-3.6.9/dcmjpeg/libijg8/jdpred.c	2025-02-18 18:05:52.957086481 +0100
 @@ -101,8 +101,8 @@
             const JDIFFROW diff_buf, const JDIFFROW prev_row,
             JDIFFROW undiff_buf, JDIMENSION width)
@@ -947,8 +947,8 @@
  
    UNDIFFERENCE_1D(INITIAL_PREDICTORx);
 diff -urEb dcmtk-3.6.9.orig/dcmjpeg/libijg8/jdsample.c dcmtk-3.6.9/dcmjpeg/libijg8/jdsample.c
---- dcmtk-3.6.9.orig/dcmjpeg/libijg8/jdsample.c	2025-01-16 18:04:46.161846090 +0100
-+++ dcmtk-3.6.9/dcmjpeg/libijg8/jdsample.c	2025-01-16 20:11:42.022717122 +0100
+--- dcmtk-3.6.9.orig/dcmjpeg/libijg8/jdsample.c	2025-02-18 18:03:13.533405515 +0100
++++ dcmtk-3.6.9/dcmjpeg/libijg8/jdsample.c	2025-02-18 18:05:52.957086481 +0100
 @@ -92,7 +92,7 @@
            JSAMPARRAY output_buf, JDIMENSION *out_row_ctr,
            JDIMENSION out_rows_avail)
@@ -1001,8 +1001,8 @@
    register JSAMPROW inptr, outptr;
    register JSAMPLE invalue;
 diff -urEb dcmtk-3.6.9.orig/dcmjpeg/libijg8/jdscale.c dcmtk-3.6.9/dcmjpeg/libijg8/jdscale.c
---- dcmtk-3.6.9.orig/dcmjpeg/libijg8/jdscale.c	2025-01-16 18:04:46.160846099 +0100
-+++ dcmtk-3.6.9/dcmjpeg/libijg8/jdscale.c	2025-01-16 20:11:42.022717122 +0100
+--- dcmtk-3.6.9.orig/dcmjpeg/libijg8/jdscale.c	2025-02-18 18:03:13.532405530 +0100
++++ dcmtk-3.6.9/dcmjpeg/libijg8/jdscale.c	2025-02-18 18:05:52.958086467 +0100
 @@ -67,7 +67,7 @@
  	const JDIFFROW diff_buf, JSAMPROW output_buf,
  	JDIMENSION width)
@@ -1013,8 +1013,8 @@
  
    for (xindex = 0; xindex < width; xindex++)
 diff -urEb dcmtk-3.6.9.orig/dcmjpeg/libijg8/jerror.c dcmtk-3.6.9/dcmjpeg/libijg8/jerror.c
---- dcmtk-3.6.9.orig/dcmjpeg/libijg8/jerror.c	2025-01-16 18:04:46.160846099 +0100
-+++ dcmtk-3.6.9/dcmjpeg/libijg8/jerror.c	2025-01-17 11:14:02.543454148 +0100
+--- dcmtk-3.6.9.orig/dcmjpeg/libijg8/jerror.c	2025-02-18 18:03:13.533405515 +0100
++++ dcmtk-3.6.9/dcmjpeg/libijg8/jerror.c	2025-02-18 18:05:52.958086467 +0100
 @@ -34,6 +34,10 @@
  #define EXIT_FAILURE  1
  #endif
@@ -1027,8 +1027,8 @@
  /*
   * Create the message string table.
 diff -urEb dcmtk-3.6.9.orig/dcmjpeg/libijg8/jquant1.c dcmtk-3.6.9/dcmjpeg/libijg8/jquant1.c
---- dcmtk-3.6.9.orig/dcmjpeg/libijg8/jquant1.c	2025-01-16 18:04:46.160846099 +0100
-+++ dcmtk-3.6.9/dcmjpeg/libijg8/jquant1.c	2025-01-16 20:11:42.022717122 +0100
+--- dcmtk-3.6.9.orig/dcmjpeg/libijg8/jquant1.c	2025-02-18 18:03:13.532405530 +0100
++++ dcmtk-3.6.9/dcmjpeg/libijg8/jquant1.c	2025-02-18 18:05:52.958086467 +0100
 @@ -251,8 +251,8 @@
     * (Forcing the upper and lower values to the limits ensures that
     * dithering can't produce a color outside the selected gamut.)
@@ -1070,8 +1070,8 @@
  }
  
 diff -urEb dcmtk-3.6.9.orig/dcmjpeg/libijg8/jquant2.c dcmtk-3.6.9/dcmjpeg/libijg8/jquant2.c
---- dcmtk-3.6.9.orig/dcmjpeg/libijg8/jquant2.c	2025-01-16 18:04:46.160846099 +0100
-+++ dcmtk-3.6.9/dcmjpeg/libijg8/jquant2.c	2025-01-16 20:11:42.023717112 +0100
+--- dcmtk-3.6.9.orig/dcmjpeg/libijg8/jquant2.c	2025-02-18 18:03:13.532405530 +0100
++++ dcmtk-3.6.9/dcmjpeg/libijg8/jquant2.c	2025-02-18 18:05:52.958086467 +0100
 @@ -224,7 +224,7 @@
  prescan_quantize (j_decompress_ptr cinfo, JSAMPARRAY input_buf,
            JSAMPARRAY output_buf, int num_rows)
@@ -1091,8 +1091,8 @@
  }
  
 diff -urEb dcmtk-3.6.9.orig/oficonv/libsrc/citrus_bcs.h dcmtk-3.6.9/oficonv/libsrc/citrus_bcs.h
---- dcmtk-3.6.9.orig/oficonv/libsrc/citrus_bcs.h	2025-01-16 18:04:46.146846219 +0100
-+++ dcmtk-3.6.9/oficonv/libsrc/citrus_bcs.h	2025-01-17 11:14:44.746131630 +0100
+--- dcmtk-3.6.9.orig/oficonv/libsrc/citrus_bcs.h	2025-02-18 18:03:13.519405733 +0100
++++ dcmtk-3.6.9/oficonv/libsrc/citrus_bcs.h	2025-02-18 18:05:52.958086467 +0100
 @@ -39,7 +39,21 @@
  #include <errno.h>
  #include <stdint.h>
@@ -1116,8 +1116,8 @@
  #define CITRUS_DECONST(type, var)    ((type)(uintptr_t)(const void *)(var))
  
 diff -urEb dcmtk-3.6.9.orig/oficonv/libsrc/citrus_big5.c dcmtk-3.6.9/oficonv/libsrc/citrus_big5.c
---- dcmtk-3.6.9.orig/oficonv/libsrc/citrus_big5.c	2025-01-16 18:04:46.137846297 +0100
-+++ dcmtk-3.6.9/oficonv/libsrc/citrus_big5.c	2025-01-17 09:59:15.249381150 +0100
+--- dcmtk-3.6.9.orig/oficonv/libsrc/citrus_big5.c	2025-02-18 18:03:13.511405858 +0100
++++ dcmtk-3.6.9/oficonv/libsrc/citrus_big5.c	2025-02-18 18:05:52.959086454 +0100
 @@ -218,13 +218,6 @@
      return (0);
  }
@@ -1152,8 +1152,8 @@
      TAILQ_INIT(&ei->excludes);
  
 diff -urEb dcmtk-3.6.9.orig/oficonv/libsrc/citrus_db_hash.h dcmtk-3.6.9/oficonv/libsrc/citrus_db_hash.h
---- dcmtk-3.6.9.orig/oficonv/libsrc/citrus_db_hash.h	2025-01-16 18:04:46.135846314 +0100
-+++ dcmtk-3.6.9/oficonv/libsrc/citrus_db_hash.h	2025-01-17 09:36:14.692449741 +0100
+--- dcmtk-3.6.9.orig/oficonv/libsrc/citrus_db_hash.h	2025-02-18 18:03:13.510405874 +0100
++++ dcmtk-3.6.9/oficonv/libsrc/citrus_db_hash.h	2025-02-18 18:05:52.959086454 +0100
 @@ -29,7 +29,8 @@
  
  #include "dcmtk/config/osconfig.h"
@@ -1165,8 +1165,8 @@
  struct _citrus_region;
  
 diff -urEb dcmtk-3.6.9.orig/oficonv/libsrc/citrus_esdb.c dcmtk-3.6.9/oficonv/libsrc/citrus_esdb.c
---- dcmtk-3.6.9.orig/oficonv/libsrc/citrus_esdb.c	2025-01-16 18:04:46.137846297 +0100
-+++ dcmtk-3.6.9/oficonv/libsrc/citrus_esdb.c	2025-01-17 07:57:57.711032732 +0100
+--- dcmtk-3.6.9.orig/oficonv/libsrc/citrus_esdb.c	2025-02-18 18:03:13.511405858 +0100
++++ dcmtk-3.6.9/oficonv/libsrc/citrus_esdb.c	2025-02-18 18:05:52.959086454 +0100
 @@ -34,7 +34,15 @@
  
  #include <errno.h>
@@ -1184,8 +1184,8 @@
  #include <stdlib.h>
  #include <string.h>
 diff -urEb dcmtk-3.6.9.orig/oficonv/libsrc/citrus_esdb.h dcmtk-3.6.9/oficonv/libsrc/citrus_esdb.h
---- dcmtk-3.6.9.orig/oficonv/libsrc/citrus_esdb.h	2025-01-16 18:04:46.137846297 +0100
-+++ dcmtk-3.6.9/oficonv/libsrc/citrus_esdb.h	2025-01-17 07:58:02.525992089 +0100
+--- dcmtk-3.6.9.orig/oficonv/libsrc/citrus_esdb.h	2025-02-18 18:03:13.512405843 +0100
++++ dcmtk-3.6.9/oficonv/libsrc/citrus_esdb.h	2025-02-18 18:05:52.959086454 +0100
 @@ -29,7 +29,14 @@
  
  #include "dcmtk/config/osconfig.h"
@@ -1202,8 +1202,8 @@
  struct _citrus_esdb_charset {
      _citrus_csid_t           ec_csid;
 diff -urEb dcmtk-3.6.9.orig/oficonv/libsrc/citrus_gbk2k.c dcmtk-3.6.9/oficonv/libsrc/citrus_gbk2k.c
---- dcmtk-3.6.9.orig/oficonv/libsrc/citrus_gbk2k.c	2025-01-16 18:04:46.146846219 +0100
-+++ dcmtk-3.6.9/oficonv/libsrc/citrus_gbk2k.c	2025-01-17 07:58:08.702939916 +0100
+--- dcmtk-3.6.9.orig/oficonv/libsrc/citrus_gbk2k.c	2025-02-18 18:03:13.519405733 +0100
++++ dcmtk-3.6.9/oficonv/libsrc/citrus_gbk2k.c	2025-02-18 18:05:52.959086454 +0100
 @@ -34,7 +34,15 @@
  
  #include <errno.h>
@@ -1221,8 +1221,8 @@
  #include <stdio.h>
  #include <stdlib.h>
 diff -urEb dcmtk-3.6.9.orig/oficonv/libsrc/citrus_hz.c dcmtk-3.6.9/oficonv/libsrc/citrus_hz.c
---- dcmtk-3.6.9.orig/oficonv/libsrc/citrus_hz.c	2025-01-16 18:04:46.144846236 +0100
-+++ dcmtk-3.6.9/oficonv/libsrc/citrus_hz.c	2025-01-17 10:04:16.511792074 +0100
+--- dcmtk-3.6.9.orig/oficonv/libsrc/citrus_hz.c	2025-02-18 18:03:13.518405749 +0100
++++ dcmtk-3.6.9/oficonv/libsrc/citrus_hz.c	2025-02-18 18:05:52.959086454 +0100
 @@ -571,13 +571,6 @@
      return (0);
  }
@@ -1284,48 +1284,9 @@
      memset(ei, 0, sizeof(*ei));
      TAILQ_INIT(E0SET(ei));
      TAILQ_INIT(E1SET(ei));
-diff -urEb dcmtk-3.6.9.orig/oficonv/libsrc/citrus_iconv.c dcmtk-3.6.9/oficonv/libsrc/citrus_iconv.c
---- dcmtk-3.6.9.orig/oficonv/libsrc/citrus_iconv.c	2025-01-16 18:04:46.146846219 +0100
-+++ dcmtk-3.6.9/oficonv/libsrc/citrus_iconv.c	2025-01-17 09:23:11.596327937 +0100
-@@ -49,7 +49,15 @@
- #endif
- 
- #include <limits.h>
-+
-+#if (_MSC_VER >= 1900)
- #include <stdbool.h>
-+#else
-+#define bool int
-+#define false 0
-+#define true 1
-+#endif
-+
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
-@@ -299,14 +307,17 @@
- _citrus_iconv_open(struct _citrus_iconv * * rcv,
-     const char * src, const char * dst)
- {
--struct _citrus_iconv *cv = NULL;
-+#ifdef HAVE_WINDOWS_H
-+    char current_codepage[20];
-+#endif
-+
-+    struct _citrus_iconv *cv = NULL;
-     struct _citrus_iconv_shared *ci = NULL;
-     char realdst[OFICONV_PATH_MAX], realsrc[OFICONV_PATH_MAX];
-     int ret;
- 
-     init_cache();
- #ifdef HAVE_WINDOWS_H
--    char current_codepage[20];
-     snprintf(current_codepage, sizeof(current_codepage), "%lu", (unsigned long) GetConsoleOutputCP());
- #endif
- 
 diff -urEb dcmtk-3.6.9.orig/oficonv/libsrc/citrus_iconv_local.h dcmtk-3.6.9/oficonv/libsrc/citrus_iconv_local.h
---- dcmtk-3.6.9.orig/oficonv/libsrc/citrus_iconv_local.h	2025-01-16 18:04:46.146846219 +0100
-+++ dcmtk-3.6.9/oficonv/libsrc/citrus_iconv_local.h	2025-01-17 07:58:20.317841727 +0100
+--- dcmtk-3.6.9.orig/oficonv/libsrc/citrus_iconv_local.h	2025-02-18 18:03:13.520405718 +0100
++++ dcmtk-3.6.9/oficonv/libsrc/citrus_iconv_local.h	2025-02-18 18:05:52.960086440 +0100
 @@ -29,7 +29,15 @@
  
  #include "dcmtk/config/osconfig.h"
@@ -1343,8 +1304,8 @@
  
  #ifdef HAVE_SYS_QUEUE_H
 diff -urEb dcmtk-3.6.9.orig/oficonv/libsrc/citrus_iconv_none.c dcmtk-3.6.9/oficonv/libsrc/citrus_iconv_none.c
---- dcmtk-3.6.9.orig/oficonv/libsrc/citrus_iconv_none.c	2025-01-16 18:04:46.141846262 +0100
-+++ dcmtk-3.6.9/oficonv/libsrc/citrus_iconv_none.c	2025-01-17 07:58:25.509797800 +0100
+--- dcmtk-3.6.9.orig/oficonv/libsrc/citrus_iconv_none.c	2025-02-18 18:03:13.516405780 +0100
++++ dcmtk-3.6.9/oficonv/libsrc/citrus_iconv_none.c	2025-02-18 18:05:52.960086440 +0100
 @@ -35,7 +35,15 @@
  
  
@@ -1362,8 +1323,8 @@
  #include <string.h>
  
 diff -urEb dcmtk-3.6.9.orig/oficonv/libsrc/citrus_iconv_std.c dcmtk-3.6.9/oficonv/libsrc/citrus_iconv_std.c
---- dcmtk-3.6.9.orig/oficonv/libsrc/citrus_iconv_std.c	2025-01-16 18:04:46.141846262 +0100
-+++ dcmtk-3.6.9/oficonv/libsrc/citrus_iconv_std.c	2025-01-17 07:58:30.205758052 +0100
+--- dcmtk-3.6.9.orig/oficonv/libsrc/citrus_iconv_std.c	2025-02-18 18:03:13.516405780 +0100
++++ dcmtk-3.6.9/oficonv/libsrc/citrus_iconv_std.c	2025-02-18 18:05:52.960086440 +0100
 @@ -36,7 +36,15 @@
  
  #include <errno.h>
@@ -1381,8 +1342,8 @@
  #include <stdlib.h>
  #include <string.h>
 diff -urEb dcmtk-3.6.9.orig/oficonv/libsrc/citrus_iso2022.c dcmtk-3.6.9/oficonv/libsrc/citrus_iso2022.c
---- dcmtk-3.6.9.orig/oficonv/libsrc/citrus_iso2022.c	2025-01-16 18:04:46.142846254 +0100
-+++ dcmtk-3.6.9/oficonv/libsrc/citrus_iso2022.c	2025-01-17 07:58:35.485713336 +0100
+--- dcmtk-3.6.9.orig/oficonv/libsrc/citrus_iso2022.c	2025-02-18 18:03:13.516405780 +0100
++++ dcmtk-3.6.9/oficonv/libsrc/citrus_iso2022.c	2025-02-18 18:05:52.960086440 +0100
 @@ -35,7 +35,15 @@
  
  #include <errno.h>
@@ -1400,8 +1361,8 @@
  #include <stdio.h>
  #include <stdlib.h>
 diff -urEb dcmtk-3.6.9.orig/oficonv/libsrc/citrus_johab.c dcmtk-3.6.9/oficonv/libsrc/citrus_johab.c
---- dcmtk-3.6.9.orig/oficonv/libsrc/citrus_johab.c	2025-01-16 18:04:46.144846236 +0100
-+++ dcmtk-3.6.9/oficonv/libsrc/citrus_johab.c	2025-01-17 07:58:40.390671776 +0100
+--- dcmtk-3.6.9.orig/oficonv/libsrc/citrus_johab.c	2025-02-18 18:03:13.518405749 +0100
++++ dcmtk-3.6.9/oficonv/libsrc/citrus_johab.c	2025-02-18 18:05:52.961086427 +0100
 @@ -34,7 +34,15 @@
  
  #include <errno.h>
@@ -1419,8 +1380,8 @@
  #include <stdint.h>
  #include <stdio.h>
 diff -urEb dcmtk-3.6.9.orig/oficonv/libsrc/citrus_module.c dcmtk-3.6.9/oficonv/libsrc/citrus_module.c
---- dcmtk-3.6.9.orig/oficonv/libsrc/citrus_module.c	2025-01-16 18:04:46.146846219 +0100
-+++ dcmtk-3.6.9/oficonv/libsrc/citrus_module.c	2025-01-17 07:58:45.172631238 +0100
+--- dcmtk-3.6.9.orig/oficonv/libsrc/citrus_module.c	2025-02-18 18:03:13.520405718 +0100
++++ dcmtk-3.6.9/oficonv/libsrc/citrus_module.c	2025-02-18 18:05:52.961086427 +0100
 @@ -98,7 +98,15 @@
  #endif
  #include <errno.h>
@@ -1438,8 +1399,8 @@
  #include <stdio.h>
  #include <stdlib.h>
 diff -urEb dcmtk-3.6.9.orig/oficonv/libsrc/citrus_mskanji.c dcmtk-3.6.9/oficonv/libsrc/citrus_mskanji.c
---- dcmtk-3.6.9.orig/oficonv/libsrc/citrus_mskanji.c	2025-01-16 18:04:46.146846219 +0100
-+++ dcmtk-3.6.9/oficonv/libsrc/citrus_mskanji.c	2025-01-17 07:58:51.749575452 +0100
+--- dcmtk-3.6.9.orig/oficonv/libsrc/citrus_mskanji.c	2025-02-18 18:03:13.519405733 +0100
++++ dcmtk-3.6.9/oficonv/libsrc/citrus_mskanji.c	2025-02-18 18:05:52.961086427 +0100
 @@ -67,7 +67,15 @@
  
  #include <errno.h>
@@ -1457,8 +1418,8 @@
  #include <stdio.h>
  #include <stdlib.h>
 diff -urEb dcmtk-3.6.9.orig/oficonv/libsrc/citrus_prop.c dcmtk-3.6.9/oficonv/libsrc/citrus_prop.c
---- dcmtk-3.6.9.orig/oficonv/libsrc/citrus_prop.c	2025-01-16 18:04:46.140846271 +0100
-+++ dcmtk-3.6.9/oficonv/libsrc/citrus_prop.c	2025-01-17 07:58:55.534543331 +0100
+--- dcmtk-3.6.9.orig/oficonv/libsrc/citrus_prop.c	2025-02-18 18:03:13.514405812 +0100
++++ dcmtk-3.6.9/oficonv/libsrc/citrus_prop.c	2025-02-18 18:05:52.961086427 +0100
 @@ -30,7 +30,15 @@
  
  #include <errno.h>
@@ -1476,8 +1437,8 @@
  #include <stdlib.h>
  #include <string.h>
 diff -urEb dcmtk-3.6.9.orig/oficonv/libsrc/citrus_region.h dcmtk-3.6.9/oficonv/libsrc/citrus_region.h
---- dcmtk-3.6.9.orig/oficonv/libsrc/citrus_region.h	2025-01-16 18:04:46.142846254 +0100
-+++ dcmtk-3.6.9/oficonv/libsrc/citrus_region.h	2025-01-17 07:59:02.917480641 +0100
+--- dcmtk-3.6.9.orig/oficonv/libsrc/citrus_region.h	2025-02-18 18:03:13.516405780 +0100
++++ dcmtk-3.6.9/oficonv/libsrc/citrus_region.h	2025-02-18 18:05:52.961086427 +0100
 @@ -31,7 +31,14 @@
  #include "dcmtk/config/osconfig.h"
  #include <stdint.h>
@@ -1494,8 +1455,8 @@
  #ifdef HAVE_SYS_TYPES_H
  #include <sys/types.h>
 diff -urEb dcmtk-3.6.9.orig/oficonv/libsrc/citrus_utf8.c dcmtk-3.6.9/oficonv/libsrc/citrus_utf8.c
---- dcmtk-3.6.9.orig/oficonv/libsrc/citrus_utf8.c	2025-01-16 18:04:46.146846219 +0100
-+++ dcmtk-3.6.9/oficonv/libsrc/citrus_utf8.c	2025-01-17 07:57:46.519127132 +0100
+--- dcmtk-3.6.9.orig/oficonv/libsrc/citrus_utf8.c	2025-02-18 18:03:13.519405733 +0100
++++ dcmtk-3.6.9/oficonv/libsrc/citrus_utf8.c	2025-02-18 18:05:52.961086427 +0100
 @@ -66,7 +66,15 @@
  
  #include <errno.h>
@@ -1513,8 +1474,8 @@
  #include <stdio.h>
  #include <stdlib.h>
 diff -urEb dcmtk-3.6.9.orig/oficonv/libsrc/oficonv_iconv.c dcmtk-3.6.9/oficonv/libsrc/oficonv_iconv.c
---- dcmtk-3.6.9.orig/oficonv/libsrc/oficonv_iconv.c	2025-01-16 18:04:46.137846297 +0100
-+++ dcmtk-3.6.9/oficonv/libsrc/oficonv_iconv.c	2025-01-17 09:16:42.622548106 +0100
+--- dcmtk-3.6.9.orig/oficonv/libsrc/oficonv_iconv.c	2025-02-18 18:03:13.512405843 +0100
++++ dcmtk-3.6.9/oficonv/libsrc/oficonv_iconv.c	2025-02-18 18:05:52.962086413 +0100
 @@ -40,7 +40,15 @@
  
  #include <errno.h>
@@ -1532,8 +1493,8 @@
  #include <string.h>
  #include <stdio.h>
 diff -urEb dcmtk-3.6.9.orig/oficonv/libsrc/windows_mmap.h dcmtk-3.6.9/oficonv/libsrc/windows_mmap.h
---- dcmtk-3.6.9.orig/oficonv/libsrc/windows_mmap.h	2025-01-16 18:04:46.137846297 +0100
-+++ dcmtk-3.6.9/oficonv/libsrc/windows_mmap.h	2025-01-17 09:21:53.565976060 +0100
+--- dcmtk-3.6.9.orig/oficonv/libsrc/windows_mmap.h	2025-02-18 18:03:13.511405858 +0100
++++ dcmtk-3.6.9/oficonv/libsrc/windows_mmap.h	2025-02-18 18:05:52.962086413 +0100
 @@ -74,6 +74,12 @@
  
  static void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset)
@@ -1599,8 +1560,8 @@
  	{
  		if (mc->addr == addr)
 diff -urEb dcmtk-3.6.9.orig/ofstd/include/dcmtk/ofstd/oftypes.h dcmtk-3.6.9/ofstd/include/dcmtk/ofstd/oftypes.h
---- dcmtk-3.6.9.orig/ofstd/include/dcmtk/ofstd/oftypes.h	2025-01-16 18:04:46.148846202 +0100
-+++ dcmtk-3.6.9/ofstd/include/dcmtk/ofstd/oftypes.h	2025-01-17 07:49:26.122182761 +0100
+--- dcmtk-3.6.9.orig/ofstd/include/dcmtk/ofstd/oftypes.h	2025-02-18 18:03:13.523405671 +0100
++++ dcmtk-3.6.9/ofstd/include/dcmtk/ofstd/oftypes.h	2025-02-18 18:05:52.962086413 +0100
 @@ -79,10 +79,9 @@
  
  #include <cstddef>
--- a/OrthancFramework/Resources/Patches/dcmtk-3.6.9.patch	Fri Feb 21 15:12:47 2025 +0100
+++ b/OrthancFramework/Resources/Patches/dcmtk-3.6.9.patch	Mon Feb 24 09:19:12 2025 +0100
@@ -1,6 +1,6 @@
 diff -urEb dcmtk-3.6.9.orig/CMake/GenerateDCMTKConfigure.cmake dcmtk-3.6.9/CMake/GenerateDCMTKConfigure.cmake
---- dcmtk-3.6.9.orig/CMake/GenerateDCMTKConfigure.cmake	2025-01-21 15:53:12.632715225 +0100
-+++ dcmtk-3.6.9/CMake/GenerateDCMTKConfigure.cmake	2025-01-21 15:53:27.614627545 +0100
+--- dcmtk-3.6.9.orig/CMake/GenerateDCMTKConfigure.cmake	2025-02-18 18:03:13.505405952 +0100
++++ dcmtk-3.6.9/CMake/GenerateDCMTKConfigure.cmake	2025-02-18 18:06:53.925278621 +0100
 @@ -227,12 +227,15 @@
  
  # Check the sizes of various types
@@ -18,8 +18,8 @@
  # Check for include files, libraries, and functions
  include("${DCMTK_CMAKE_INCLUDE}CMake/dcmtkTryCompile.cmake")
 diff -urEb dcmtk-3.6.9.orig/dcmdata/include/dcmtk/dcmdata/dcdict.h dcmtk-3.6.9/dcmdata/include/dcmtk/dcmdata/dcdict.h
---- dcmtk-3.6.9.orig/dcmdata/include/dcmtk/dcmdata/dcdict.h	2025-01-21 15:53:12.622715283 +0100
-+++ dcmtk-3.6.9/dcmdata/include/dcmtk/dcmdata/dcdict.h	2025-01-21 15:53:27.614627545 +0100
+--- dcmtk-3.6.9.orig/dcmdata/include/dcmtk/dcmdata/dcdict.h	2025-02-18 18:03:13.497406077 +0100
++++ dcmtk-3.6.9/dcmdata/include/dcmtk/dcmdata/dcdict.h	2025-02-18 18:06:53.925278621 +0100
 @@ -163,6 +163,12 @@
      /// returns an iterator to the end of the repeating groups data dictionary
      DcmDictEntryListIterator repeatingEnd() { return repDict.end(); }
@@ -34,8 +34,8 @@
  
      /** private undefined assignment operator
 diff -urEb dcmtk-3.6.9.orig/dcmdata/libsrc/dcdict.cc dcmtk-3.6.9/dcmdata/libsrc/dcdict.cc
---- dcmtk-3.6.9.orig/dcmdata/libsrc/dcdict.cc	2025-01-21 15:53:12.625715265 +0100
-+++ dcmtk-3.6.9/dcmdata/libsrc/dcdict.cc	2025-01-21 15:53:27.615627539 +0100
+--- dcmtk-3.6.9.orig/dcmdata/libsrc/dcdict.cc	2025-02-18 18:03:13.499406046 +0100
++++ dcmtk-3.6.9/dcmdata/libsrc/dcdict.cc	2025-02-18 18:06:53.926278608 +0100
 @@ -904,3 +904,5 @@
    wrlock().clear();
    wrunlock();
@@ -43,8 +43,8 @@
 +
 +#include "dcdict_orthanc.cc"
 diff -urEb dcmtk-3.6.9.orig/dcmdata/libsrc/dcpxitem.cc dcmtk-3.6.9/dcmdata/libsrc/dcpxitem.cc
---- dcmtk-3.6.9.orig/dcmdata/libsrc/dcpxitem.cc	2025-01-21 15:53:12.623715277 +0100
-+++ dcmtk-3.6.9/dcmdata/libsrc/dcpxitem.cc	2025-01-21 15:53:27.615627539 +0100
+--- dcmtk-3.6.9.orig/dcmdata/libsrc/dcpxitem.cc	2025-02-18 18:03:13.497406077 +0100
++++ dcmtk-3.6.9/dcmdata/libsrc/dcpxitem.cc	2025-02-18 18:06:53.926278608 +0100
 @@ -31,6 +31,8 @@
  #include "dcmtk/dcmdata/dcostrma.h"    /* for class DcmOutputStream */
  #include "dcmtk/dcmdata/dcwcache.h"    /* for class DcmWriteCache */
@@ -55,8 +55,8 @@
  // ********************************
  
 diff -urEb dcmtk-3.6.9.orig/dcmnet/libsrc/scu.cc dcmtk-3.6.9/dcmnet/libsrc/scu.cc
---- dcmtk-3.6.9.orig/dcmnet/libsrc/scu.cc	2025-01-21 15:53:12.655715091 +0100
-+++ dcmtk-3.6.9/dcmnet/libsrc/scu.cc	2025-01-21 15:53:27.616627533 +0100
+--- dcmtk-3.6.9.orig/dcmnet/libsrc/scu.cc	2025-02-18 18:03:13.525405640 +0100
++++ dcmtk-3.6.9/dcmnet/libsrc/scu.cc	2025-02-18 18:06:53.927278595 +0100
 @@ -19,6 +19,11 @@
   *
   */
@@ -70,8 +70,8 @@
  
  #include "dcmtk/dcmdata/dcostrmf.h" /* for class DcmOutputFileStream */
 diff -urEb dcmtk-3.6.9.orig/oficonv/include/dcmtk/oficonv/iconv.h dcmtk-3.6.9/oficonv/include/dcmtk/oficonv/iconv.h
---- dcmtk-3.6.9.orig/oficonv/include/dcmtk/oficonv/iconv.h	2025-01-21 15:53:12.637715196 +0100
-+++ dcmtk-3.6.9/oficonv/include/dcmtk/oficonv/iconv.h	2025-01-21 15:53:27.617627527 +0100
+--- dcmtk-3.6.9.orig/oficonv/include/dcmtk/oficonv/iconv.h	2025-02-18 18:03:13.510405874 +0100
++++ dcmtk-3.6.9/oficonv/include/dcmtk/oficonv/iconv.h	2025-02-18 18:06:53.927278595 +0100
 @@ -55,7 +55,12 @@
  #endif
  
@@ -85,21 +85,138 @@
  
  #ifndef OFICONV_CITRUS_WC_T_DEFINED
  #define OFICONV_CITRUS_WC_T_DEFINED
+diff -urEb dcmtk-3.6.9.orig/oficonv/libsrc/citrus_csmapper.c dcmtk-3.6.9/oficonv/libsrc/citrus_csmapper.c
+--- dcmtk-3.6.9.orig/oficonv/libsrc/citrus_csmapper.c	2025-02-18 18:03:13.510405874 +0100
++++ dcmtk-3.6.9/oficonv/libsrc/citrus_csmapper.c	2025-02-18 18:06:53.927278595 +0100
+@@ -63,7 +63,8 @@
+ 
+ #ifdef WITH_THREADS
+ #ifdef HAVE_WINDOWS_H
+-static SRWLOCK ma_lock = SRWLOCK_INIT;
++static int ma_lock_initialized = 0;
++static CRITICAL_SECTION ma_lock;
+ #elif defined(HAVE_PTHREAD_H)
+ static pthread_rwlock_t ma_lock = PTHREAD_RWLOCK_INITIALIZER;
+ #endif
+@@ -382,6 +383,14 @@
+     char mapper_path[OFICONV_PATH_MAX];
+     unsigned long norm;
+     int ret;
++
++#if defined(WITH_THREADS) && defined(HAVE_WINDOWS_H)
++    if (ma_lock_initialized == 0) { /* Very minor risk of race condition here */
++      InitializeCriticalSection(&ma_lock);
++      ma_lock_initialized = 1;
++    }
++#endif
++
+     norm = 0;
+ 
+     getCSMapperPath(mapper_path, sizeof(mapper_path), NULL);
+diff -urEb dcmtk-3.6.9.orig/oficonv/libsrc/citrus_iconv.c dcmtk-3.6.9/oficonv/libsrc/citrus_iconv.c
+--- dcmtk-3.6.9.orig/oficonv/libsrc/citrus_iconv.c	2025-02-18 18:03:13.520405718 +0100
++++ dcmtk-3.6.9/oficonv/libsrc/citrus_iconv.c	2025-02-18 18:10:35.928614598 +0100
+@@ -49,7 +49,15 @@
+ #endif
+ 
+ #include <limits.h>
++
++#if (_MSC_VER >= 1900)
+ #include <stdbool.h>
++#else
++#define bool int
++#define false 0
++#define true 1
++#endif
++
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+@@ -80,7 +88,8 @@
+ 
+ #ifdef WITH_THREADS
+ #ifdef HAVE_WINDOWS_H
+-static SRWLOCK ci_lock = SRWLOCK_INIT;
++static int ci_lock_initialized = 0;
++static CRITICAL_SECTION ci_lock;
+ #elif defined(HAVE_PTHREAD_H)
+ static pthread_rwlock_t ci_lock = PTHREAD_RWLOCK_INITIALIZER;
+ #endif
+@@ -299,14 +308,24 @@
+ _citrus_iconv_open(struct _citrus_iconv * * rcv,
+     const char * src, const char * dst)
+ {
+-struct _citrus_iconv *cv = NULL;
++#ifdef HAVE_WINDOWS_H
++    char current_codepage[20];
++#endif
++
++    struct _citrus_iconv *cv = NULL;
+     struct _citrus_iconv_shared *ci = NULL;
+     char realdst[OFICONV_PATH_MAX], realsrc[OFICONV_PATH_MAX];
+     int ret;
+ 
++#if defined(WITH_THREADS) && defined(HAVE_WINDOWS_H)
++    if (ci_lock_initialized == 0) { /* Very minor risk of race condition here */
++      InitializeCriticalSection(&ci_lock);
++      ci_lock_initialized = 1;
++    }
++#endif
++
+     init_cache();
+ #ifdef HAVE_WINDOWS_H
+-    char current_codepage[20];
+     snprintf(current_codepage, sizeof(current_codepage), "%lu", (unsigned long) GetConsoleOutputCP());
+ #endif
+ 
 diff -urEb dcmtk-3.6.9.orig/oficonv/libsrc/citrus_lock.h dcmtk-3.6.9/oficonv/libsrc/citrus_lock.h
---- dcmtk-3.6.9.orig/oficonv/libsrc/citrus_lock.h	2025-01-21 15:53:12.646715143 +0100
-+++ dcmtk-3.6.9/oficonv/libsrc/citrus_lock.h	2025-01-21 16:43:36.463693959 +0100
-@@ -31,7 +31,7 @@
+--- dcmtk-3.6.9.orig/oficonv/libsrc/citrus_lock.h	2025-02-18 18:03:13.518405749 +0100
++++ dcmtk-3.6.9/oficonv/libsrc/citrus_lock.h	2025-02-18 18:06:53.927278595 +0100
+@@ -31,11 +31,11 @@
  
  #ifdef WITH_THREADS
  
 -#ifdef HAVE_WINDOWS_H
-+#if defined(HAVE_WINDOWS_H) && !defined(HAVE_PTHREAD_H)  /* Favor pthread if available, for MinGW */
++#if defined(HAVE_WINDOWS_H)
  
  #include <windows.h>
- #define WLOCK(lock)  AcquireSRWLockExclusive(lock);
+-#define WLOCK(lock)  AcquireSRWLockExclusive(lock);
+-#define UNLOCK(lock) ReleaseSRWLockExclusive(lock);
++#define WLOCK(lock)  EnterCriticalSection(lock);
++#define UNLOCK(lock) LeaveCriticalSection(lock);
+ 
+ #else /* HAVE_WINDOWS_H */
+ 
+diff -urEb dcmtk-3.6.9.orig/oficonv/libsrc/citrus_mapper.c dcmtk-3.6.9/oficonv/libsrc/citrus_mapper.c
+--- dcmtk-3.6.9.orig/oficonv/libsrc/citrus_mapper.c	2025-02-18 18:03:13.516405780 +0100
++++ dcmtk-3.6.9/oficonv/libsrc/citrus_mapper.c	2025-02-18 18:06:53.928278582 +0100
+@@ -64,7 +64,8 @@
+ 
+ #ifdef WITH_THREADS
+ #ifdef HAVE_WINDOWS_H
+-static SRWLOCK cm_lock = SRWLOCK_INIT;
++static int cm_lock_initialized = 0;
++static CRITICAL_SECTION cm_lock;
+ #elif defined(HAVE_PTHREAD_H)
+ static pthread_rwlock_t cm_lock = PTHREAD_RWLOCK_INITIALIZER;
+ #endif
+@@ -355,6 +356,13 @@
+     const char *module, *variable;
+     int hashval, ret;
+ 
++#if defined(WITH_THREADS) && defined(HAVE_WINDOWS_H)
++    if (cm_lock_initialized == 0) { /* Very minor risk of race condition here */
++      InitializeCriticalSection(&cm_lock);
++      cm_lock_initialized = 1;
++    }
++#endif
++
+     variable = NULL;
+ 
+     WLOCK(&cm_lock);
 diff -urEb dcmtk-3.6.9.orig/oflog/include/dcmtk/oflog/thread/syncpub.h dcmtk-3.6.9/oflog/include/dcmtk/oflog/thread/syncpub.h
---- dcmtk-3.6.9.orig/oflog/include/dcmtk/oflog/thread/syncpub.h	2025-01-21 15:53:12.605715381 +0100
-+++ dcmtk-3.6.9/oflog/include/dcmtk/oflog/thread/syncpub.h	2025-01-21 15:53:27.617627527 +0100
+--- dcmtk-3.6.9.orig/oflog/include/dcmtk/oflog/thread/syncpub.h	2025-02-18 18:03:13.473406452 +0100
++++ dcmtk-3.6.9/oflog/include/dcmtk/oflog/thread/syncpub.h	2025-02-18 18:06:53.928278582 +0100
 @@ -63,7 +63,7 @@
  
  DCMTK_LOG4CPLUS_INLINE_EXPORT
@@ -137,8 +254,8 @@
  
  
 diff -urEb dcmtk-3.6.9.orig/oflog/libsrc/oflog.cc dcmtk-3.6.9/oflog/libsrc/oflog.cc
---- dcmtk-3.6.9.orig/oflog/libsrc/oflog.cc	2025-01-21 15:53:12.606715376 +0100
-+++ dcmtk-3.6.9/oflog/libsrc/oflog.cc	2025-01-21 15:53:27.617627527 +0100
+--- dcmtk-3.6.9.orig/oflog/libsrc/oflog.cc	2025-02-18 18:03:13.475406421 +0100
++++ dcmtk-3.6.9/oflog/libsrc/oflog.cc	2025-02-18 18:06:53.928278582 +0100
 @@ -19,6 +19,11 @@
   *
   */
@@ -152,8 +269,8 @@
  #include "dcmtk/oflog/oflog.h"
  
 diff -urEb dcmtk-3.6.9.orig/ofstd/include/dcmtk/ofstd/offile.h dcmtk-3.6.9/ofstd/include/dcmtk/ofstd/offile.h
---- dcmtk-3.6.9.orig/ofstd/include/dcmtk/ofstd/offile.h	2025-01-21 15:53:12.651715114 +0100
-+++ dcmtk-3.6.9/ofstd/include/dcmtk/ofstd/offile.h	2025-01-21 15:53:27.618627521 +0100
+--- dcmtk-3.6.9.orig/ofstd/include/dcmtk/ofstd/offile.h	2025-02-18 18:03:13.523405671 +0100
++++ dcmtk-3.6.9/ofstd/include/dcmtk/ofstd/offile.h	2025-02-18 18:06:53.929278570 +0100
 @@ -569,7 +569,7 @@
     */
    void setlinebuf()
@@ -164,8 +281,8 @@
  #else
      :: setlinebuf(file_);
 diff -urEb dcmtk-3.6.9.orig/ofstd/libsrc/ofstub.cc dcmtk-3.6.9/ofstd/libsrc/ofstub.cc
---- dcmtk-3.6.9.orig/ofstd/libsrc/ofstub.cc	2025-01-21 15:53:12.652715108 +0100
-+++ dcmtk-3.6.9/ofstd/libsrc/ofstub.cc	2025-01-21 15:53:27.618627521 +0100
+--- dcmtk-3.6.9.orig/ofstd/libsrc/ofstub.cc	2025-02-18 18:03:13.523405671 +0100
++++ dcmtk-3.6.9/ofstd/libsrc/ofstub.cc	2025-02-18 18:06:53.929278570 +0100
 @@ -35,6 +35,10 @@
  #include <windows.h>
  #endif /* HAVE_WINDOWS_H */
--- a/OrthancFramework/Sources/DicomParsing/DcmtkTranscoder.cpp	Fri Feb 21 15:12:47 2025 +0100
+++ b/OrthancFramework/Sources/DicomParsing/DcmtkTranscoder.cpp	Mon Feb 24 09:19:12 2025 +0100
@@ -51,7 +51,7 @@
 namespace Orthanc
 {
   DcmtkTranscoder::DcmtkTranscoder(unsigned int maxConcurrentExecutions) :
-    lossyQuality_(90),
+  defaultLossyQuality_(90),
     maxConcurrentExecutionsSemaphore_(maxConcurrentExecutions)
   {
   }
@@ -64,26 +64,26 @@
   }
 
   
-  void DcmtkTranscoder::SetLossyQuality(unsigned int quality)
+  void DcmtkTranscoder::SetDefaultLossyQuality(unsigned int quality)
   {
     if (quality == 0 ||
         quality > 100)
     {
       throw OrthancException(
         ErrorCode_ParameterOutOfRange,
-        "The quality for lossy transcoding must be an integer between 1 and 100, received: " +
+        "The default quality for lossy transcoding must be an integer between 1 and 100, received: " +
         boost::lexical_cast<std::string>(quality));
     }
     else
     {
-      LOG(INFO) << "Quality for lossy transcoding using DCMTK is set to: " << quality;
-      lossyQuality_ = quality;
+      LOG(INFO) << "Default quality for lossy transcoding using DCMTK is set to: " << quality;
+      defaultLossyQuality_ = quality;
     }
   }
 
-  unsigned int DcmtkTranscoder::GetLossyQuality() const
+  unsigned int DcmtkTranscoder::GetDefaultLossyQuality() const
   {
-    return lossyQuality_;
+    return defaultLossyQuality_;
   }
 
   bool TryTranscode(std::vector<std::string>& failureReasons, /* out */
@@ -109,7 +109,8 @@
                                          std::string& failureReason /* out */,
                                          DcmFileFormat& dicom, /* in/out */
                                          const std::set<DicomTransferSyntax>& allowedSyntaxes,
-                                         bool allowNewSopInstanceUid) 
+                                         bool allowNewSopInstanceUid,
+                                         unsigned int lossyQuality) 
   {
     std::vector<std::string> failureReasons;
 
@@ -169,7 +170,7 @@
       else
       {
         // Check out "dcmjpeg/apps/dcmcjpeg.cc"
-        DJ_RPLossy parameters(lossyQuality_);
+        DJ_RPLossy parameters(lossyQuality);
           
         if (FromDcmtkBridge::Transcode(dicom, DicomTransferSyntax_JPEGProcess1, &parameters))
         {
@@ -195,7 +196,7 @@
       else
       {
         // Check out "dcmjpeg/apps/dcmcjpeg.cc"
-        DJ_RPLossy parameters(lossyQuality_);
+        DJ_RPLossy parameters(lossyQuality);
         if (FromDcmtkBridge::Transcode(dicom, DicomTransferSyntax_JPEGProcess2_4, &parameters))
         {
           selectedSyntax = DicomTransferSyntax_JPEGProcess2_4;
@@ -312,11 +313,20 @@
     return false;
   }
 
+  bool DcmtkTranscoder::Transcode(DicomImage& target,
+                                  DicomImage& source /* in, "GetParsed()" possibly modified */,
+                                  const std::set<DicomTransferSyntax>& allowedSyntaxes,
+                                  bool allowNewSopInstanceUid)
+  {
+    return Transcode(target, source, allowedSyntaxes, allowNewSopInstanceUid, defaultLossyQuality_);
+  }
+
 
   bool DcmtkTranscoder::Transcode(DicomImage& target,
                                   DicomImage& source /* in, "GetParsed()" possibly modified */,
                                   const std::set<DicomTransferSyntax>& allowedSyntaxes,
-                                  bool allowNewSopInstanceUid)
+                                  bool allowNewSopInstanceUid,
+                                  unsigned int lossyQuality)
   {
     Semaphore::Locker lock(maxConcurrentExecutionsSemaphore_); // limit the number of concurrent executions
 
@@ -363,7 +373,7 @@
       return true;
     }
     else if (InplaceTranscode(targetSyntax, failureReason, source.GetParsed(),
-                              allowedSyntaxes, allowNewSopInstanceUid))
+                              allowedSyntaxes, allowNewSopInstanceUid, lossyQuality))
     {   
       // Sanity check
       DicomTransferSyntax targetSyntax2;
--- a/OrthancFramework/Sources/DicomParsing/DcmtkTranscoder.h	Fri Feb 21 15:12:47 2025 +0100
+++ b/OrthancFramework/Sources/DicomParsing/DcmtkTranscoder.h	Mon Feb 24 09:19:12 2025 +0100
@@ -41,21 +41,22 @@
   class ORTHANC_PUBLIC DcmtkTranscoder : public IDicomTranscoder
   {
   private:
-    unsigned int  lossyQuality_;
+    unsigned int  defaultLossyQuality_;
     Semaphore maxConcurrentExecutionsSemaphore_;
 
     bool InplaceTranscode(DicomTransferSyntax& selectedSyntax /* out */,
                           std::string& failureReason /* out */,
                           DcmFileFormat& dicom,
                           const std::set<DicomTransferSyntax>& allowedSyntaxes,
-                          bool allowNewSopInstanceUid);
+                          bool allowNewSopInstanceUid,
+                          unsigned int lossyQuality);
     
   public:
     explicit DcmtkTranscoder(unsigned int maxConcurrentExecutions);
 
-    void SetLossyQuality(unsigned int quality);
+    void SetDefaultLossyQuality(unsigned int quality);
 
-    unsigned int GetLossyQuality() const;
+    unsigned int GetDefaultLossyQuality() const;
     
     static bool IsSupported(DicomTransferSyntax syntax);
 
@@ -63,5 +64,11 @@
                            DicomImage& source /* in, "GetParsed()" possibly modified */,
                            const std::set<DicomTransferSyntax>& allowedSyntaxes,
                            bool allowNewSopInstanceUid) ORTHANC_OVERRIDE;
+
+    virtual bool Transcode(DicomImage& target,
+                           DicomImage& source /* in, "GetParsed()" possibly modified */,
+                           const std::set<DicomTransferSyntax>& allowedSyntaxes,
+                           bool allowNewSopInstanceUid,
+                           unsigned int lossyQuality) ORTHANC_OVERRIDE;
   };
 }
--- a/OrthancFramework/Sources/DicomParsing/IDicomTranscoder.h	Fri Feb 21 15:12:47 2025 +0100
+++ b/OrthancFramework/Sources/DicomParsing/IDicomTranscoder.h	Mon Feb 24 09:19:12 2025 +0100
@@ -116,6 +116,12 @@
                            const std::set<DicomTransferSyntax>& allowedSyntaxes,
                            bool allowNewSopInstanceUid) = 0;
 
+    virtual bool Transcode(DicomImage& target,
+                           DicomImage& source /* in, "GetParsed()" possibly modified */,
+                           const std::set<DicomTransferSyntax>& allowedSyntaxes,
+                           bool allowNewSopInstanceUid,
+                           unsigned int lossyQuality) = 0;
+
     static std::string GetSopInstanceUid(DcmFileFormat& dicom);
   };
 }
--- a/OrthancFramework/Sources/DicomParsing/MemoryBufferTranscoder.cpp	Fri Feb 21 15:12:47 2025 +0100
+++ b/OrthancFramework/Sources/DicomParsing/MemoryBufferTranscoder.cpp	Mon Feb 24 09:19:12 2025 +0100
@@ -56,6 +56,14 @@
 #endif
   }
     
+  bool MemoryBufferTranscoder::Transcode(DicomImage& target,
+                                         DicomImage& source,
+                                         const std::set<DicomTransferSyntax>& allowedSyntaxes,
+                                         bool allowNewSopInstanceUid,
+                                         unsigned int lossyQualityNotUsed)
+  {
+    return Transcode(target, source, allowedSyntaxes, allowNewSopInstanceUid);
+  }
 
   bool MemoryBufferTranscoder::Transcode(DicomImage& target,
                                          DicomImage& source,
--- a/OrthancFramework/Sources/DicomParsing/MemoryBufferTranscoder.h	Fri Feb 21 15:12:47 2025 +0100
+++ b/OrthancFramework/Sources/DicomParsing/MemoryBufferTranscoder.h	Mon Feb 24 09:19:12 2025 +0100
@@ -43,5 +43,11 @@
                            DicomImage& source,
                            const std::set<DicomTransferSyntax>& allowedSyntaxes,
                            bool allowNewSopInstanceUid) ORTHANC_OVERRIDE;
+
+    virtual bool Transcode(DicomImage& target /* out */,
+                           DicomImage& source,
+                           const std::set<DicomTransferSyntax>& allowedSyntaxes,
+                           bool allowNewSopInstanceUid,
+                           unsigned int lossyQualityNotUsed) ORTHANC_OVERRIDE;
   };
 }
--- a/OrthancFramework/Sources/FileStorage/StorageAccessor.cpp	Fri Feb 21 15:12:47 2025 +0100
+++ b/OrthancFramework/Sources/FileStorage/StorageAccessor.cpp	Mon Feb 24 09:19:12 2025 +0100
@@ -817,9 +817,10 @@
 #if ORTHANC_ENABLE_CIVETWEB == 1 || ORTHANC_ENABLE_MONGOOSE == 1
   void StorageAccessor::AnswerFile(HttpOutput& output,
                                    const FileInfo& info,
-                                   MimeType mime)
+                                   MimeType mime,
+                                   const std::string& contentFilename)
   {
-    AnswerFile(output, info, EnumerationToString(mime));
+    AnswerFile(output, info, EnumerationToString(mime), contentFilename);
   }
 #endif
 
@@ -827,10 +828,12 @@
 #if ORTHANC_ENABLE_CIVETWEB == 1 || ORTHANC_ENABLE_MONGOOSE == 1
   void StorageAccessor::AnswerFile(HttpOutput& output,
                                    const FileInfo& info,
-                                   const std::string& mime)
+                                   const std::string& mime,
+                                   const std::string& contentFilename)
   {
     BufferHttpSender sender;
     SetupSender(sender, info, mime);
+    sender.SetContentFilename(contentFilename);
   
     HttpStreamTranscoder transcoder(sender, CompressionType_None); // since 1.11.2, the storage accessor only returns uncompressed buffers
     output.Answer(transcoder);
@@ -841,9 +844,10 @@
 #if ORTHANC_ENABLE_CIVETWEB == 1 || ORTHANC_ENABLE_MONGOOSE == 1
   void StorageAccessor::AnswerFile(RestApiOutput& output,
                                    const FileInfo& info,
-                                   MimeType mime)
+                                   MimeType mime,
+                                   const std::string& contentFilename)
   {
-    AnswerFile(output, info, EnumerationToString(mime));
+    AnswerFile(output, info, EnumerationToString(mime), contentFilename);
   }
 #endif
 
@@ -851,11 +855,13 @@
 #if ORTHANC_ENABLE_CIVETWEB == 1 || ORTHANC_ENABLE_MONGOOSE == 1
   void StorageAccessor::AnswerFile(RestApiOutput& output,
                                    const FileInfo& info,
-                                   const std::string& mime)
+                                   const std::string& mime,
+                                   const std::string& contentFilename)
   {
     BufferHttpSender sender;
     SetupSender(sender, info, mime);
-  
+    sender.SetContentFilename(contentFilename);
+
     HttpStreamTranscoder transcoder(sender, CompressionType_None); // since 1.11.2, the storage accessor only returns uncompressed buffers
     output.AnswerStream(transcoder);
   }
--- a/OrthancFramework/Sources/FileStorage/StorageAccessor.h	Fri Feb 21 15:12:47 2025 +0100
+++ b/OrthancFramework/Sources/FileStorage/StorageAccessor.h	Mon Feb 24 09:19:12 2025 +0100
@@ -176,19 +176,23 @@
 #if ORTHANC_ENABLE_CIVETWEB == 1 || ORTHANC_ENABLE_MONGOOSE == 1
     void AnswerFile(HttpOutput& output,
                     const FileInfo& info,
-                    MimeType mime);
+                    MimeType mime,
+                    const std::string& contentFilename);
 
     void AnswerFile(HttpOutput& output,
                     const FileInfo& info,
-                    const std::string& mime);
+                    const std::string& mime,
+                    const std::string& contentFilename);
 
     void AnswerFile(RestApiOutput& output,
                     const FileInfo& info,
-                    MimeType mime);
+                    MimeType mime,
+                    const std::string& contentFilename);
 
     void AnswerFile(RestApiOutput& output,
                     const FileInfo& info,
-                    const std::string& mime);
+                    const std::string& mime,
+                    const std::string& contentFilename);
 #endif
 
     bool HandlesCustomData();
--- a/OrthancFramework/Sources/RestApi/RestApiGetCall.cpp	Fri Feb 21 15:12:47 2025 +0100
+++ b/OrthancFramework/Sources/RestApi/RestApiGetCall.cpp	Mon Feb 24 09:19:12 2025 +0100
@@ -69,4 +69,31 @@
                              name + "\", found: " + found->second);
     }
   }
+
+  uint32_t RestApiGetCall::GetUnsignedInteger32Argument(const std::string& name,
+                                                        uint32_t defaultValue) const
+  {
+    HttpToolbox::Arguments::const_iterator found = getArguments_.find(name);
+
+    uint32_t value;
+    
+    if (found == getArguments_.end())
+    {
+      return defaultValue;
+    }
+    else if (found->second.empty())
+    {
+      return true;
+    }
+    else if (SerializationToolbox::ParseUnsignedInteger32(value, found->second))
+    {
+      return value;
+    }
+    else
+    {
+      throw OrthancException(ErrorCode_ParameterOutOfRange, "Expected a Unsigned Int for GET argument \"" +
+                             name + "\", found: " + found->second);
+    }
+  }
+
 }
--- a/OrthancFramework/Sources/RestApi/RestApiGetCall.h	Fri Feb 21 15:12:47 2025 +0100
+++ b/OrthancFramework/Sources/RestApi/RestApiGetCall.h	Mon Feb 24 09:19:12 2025 +0100
@@ -65,7 +65,10 @@
 
     bool GetBooleanArgument(const std::string& name,
                             bool defaultValue) const;
-    
+
+    uint32_t GetUnsignedInteger32Argument(const std::string& name,      
+                                          uint32_t defaultValue) const;
+  
     virtual bool ParseJsonRequest(Json::Value& result) const ORTHANC_OVERRIDE;
   };
 }
--- a/OrthancServer/Resources/Configuration.json	Fri Feb 21 15:12:47 2025 +0100
+++ b/OrthancServer/Resources/Configuration.json	Mon Feb 24 09:19:12 2025 +0100
@@ -920,8 +920,10 @@
   // that have a compressed transfer syntax (new in Orthanc 1.8.2).
   "IngestTranscodingOfCompressed" : true,
   
-  // The compression level that is used when transcoding to one of the
-  // lossy/JPEG transfer syntaxes (integer between 1 and 100).
+  // The default compression level that is used when transcoding to one
+  // of the lossy/JPEG transfer syntaxes (integer between 1 and 100).
+  // This value is currently only used by the default built-in DCMTK
+  // transcoder and is not provided to transcoding plugins.
   "DicomLossyTranscodingQuality" : 90,
 
   // Whether "fsync()" is called after each write to the storage area
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancServer/Resources/RunCppCheck-2.17.0.sh	Mon Feb 24 09:19:12 2025 +0100
@@ -0,0 +1,110 @@
+#!/bin/bash
+
+set -ex
+
+CPPCHECK=cppcheck
+
+if [ $# -ge 1 ]; then
+    CPPCHECK=$1
+fi
+
+cat <<EOF > /tmp/cppcheck-suppressions.txt
+nullPointer:../../OrthancFramework/UnitTestsSources/RestApiTests.cpp:321
+stlFindInsert:../../OrthancFramework/Sources/DicomFormat/DicomMap.cpp:1525
+stlFindInsert:../../OrthancFramework/Sources/RestApi/RestApiCallDocumentation.cpp:166
+stlFindInsert:../../OrthancFramework/Sources/RestApi/RestApiCallDocumentation.cpp:74
+stlFindInsert:../../OrthancServer/Sources/Database/MainDicomTagsRegistry.cpp:65
+stlFindInsert:../../OrthancServer/Sources/OrthancWebDav.cpp:328
+stlFindInsert:../../OrthancServer/Sources/ServerJobs/MergeStudyJob.cpp:41
+stlFindInsert:../../OrthancServer/Sources/ServerJobs/SplitStudyJob.cpp:191
+stlFindInsert:../../OrthancServer/Sources/ServerJobs/ResourceModificationJob.cpp:361
+syntaxError:../../OrthancFramework/Sources/SQLite/FunctionContext.h:53
+syntaxError:../../OrthancFramework/UnitTestsSources/DicomMapTests.cpp:74
+syntaxError:../../OrthancFramework/UnitTestsSources/ZipTests.cpp:133
+syntaxError:../../OrthancServer/UnitTestsSources/UnitTestsMain.cpp:322
+uninitMemberVar:../../OrthancServer/Sources/ServerJobs/StorageCommitmentScpJob.cpp:417
+unreadVariable:../../OrthancServer/Sources/OrthancRestApi/OrthancRestModalities.cpp:1173
+useInitializationList:../../OrthancFramework/Sources/Images/PngReader.cpp:91
+useInitializationList:../../OrthancFramework/Sources/Images/PngWriter.cpp:99
+useInitializationList:../../OrthancServer/Sources/ServerJobs/DicomModalityStoreJob.cpp:275
+assertWithSideEffect:../../OrthancServer/Plugins/Engine/OrthancPluginDatabase.cpp:277
+assertWithSideEffect:../../OrthancServer/Plugins/Engine/OrthancPluginDatabase.cpp:1026
+assertWithSideEffect:../../OrthancServer/Sources/Database/Compatibility/DatabaseLookup.cpp:292
+assertWithSideEffect:../../OrthancServer/Sources/Database/Compatibility/DatabaseLookup.cpp:391
+assertWithSideEffect:../../OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp:3058
+assertWithSideEffect:../../OrthancServer/Sources/ServerJobs/ResourceModificationJob.cpp:286
+assertWithSideEffect:../../OrthancFramework/Sources/DicomNetworking/Internals/CommandDispatcher.cpp:454
+EOF
+
+${CPPCHECK} --enable=all --std=gnu++11 --library=boost \
+            --suppressions-list=/tmp/cppcheck-suppressions.txt \
+            -I/usr/include/ \
+            -I/usr/include/jsoncpp/ \
+            -I/usr/include/linux/ \
+            -I/usr/include/c++/11/ \
+            -I/usr/include/c++/11/tr1/ \
+            -I/usr/include/x86_64-linux-gnu/c++/11/ \
+            -DBOOST_HAS_DATE_TIME=1 \
+            -DBOOST_HAS_FILESYSTEM_V3=1 \
+            -DBOOST_HAS_REGEX=1 \
+            -DCIVETWEB_HAS_DISABLE_KEEP_ALIVE=1 \
+            -DCIVETWEB_HAS_WEBDAV_WRITING=1 \
+            -DDCMTK_VERSION_NUMBER=369 \
+            -DJCONFIG_INCLUDED \
+            -DHAVE_MALLOPT=1 \
+            -DMONGOOSE_USE_CALLBACKS=1 \
+            -DJSONCPP_VERSION_MAJOR=1 \
+            -DJSONCPP_VERSION_MINOR=0 \
+            -DORTHANC_BUILDING_FRAMEWORK_LIBRARY=0 \
+            -DORTHANC_BUILD_UNIT_TESTS=1 \
+            -DORTHANC_ENABLE_BASE64=1 \
+            -DORTHANC_ENABLE_CIVETWEB=1 \
+            -DORTHANC_ENABLE_CURL=1 \
+            -DORTHANC_ENABLE_DCMTK=1 \
+            -DORTHANC_ENABLE_DCMTK_JPEG=1 \
+            -DORTHANC_ENABLE_DCMTK_JPEG_LOSSLESS=1 \
+            -DORTHANC_ENABLE_DCMTK_NETWORKING=1 \
+            -DORTHANC_ENABLE_DCMTK_TRANSCODING=1 \
+            -DORTHANC_ENABLE_JPEG=1 \
+            -DORTHANC_ENABLE_LOCALE=1 \
+            -DORTHANC_ENABLE_LOGGING=1 \
+            -DORTHANC_ENABLE_LOGGING_STDIO=1 \
+            -DORTHANC_ENABLE_LUA=1 \
+            -DORTHANC_ENABLE_MD5=1 \
+            -DORTHANC_ENABLE_MONGOOSE=0 \
+            -DORTHANC_ENABLE_PKCS11=1 \
+            -DORTHANC_ENABLE_PLUGINS=1 \
+            -DORTHANC_ENABLE_PNG=1 \
+            -DORTHANC_ENABLE_PUGIXML=1 \
+            -DORTHANC_ENABLE_SQLITE=1 \
+            -DORTHANC_ENABLE_SSL=1 \
+            -DORTHANC_ENABLE_ZLIB=1 \
+            -DORTHANC_SANDBOXED=0 \
+            -DORTHANC_SQLITE_VERSION=3027001 \
+            -DORTHANC_UNIT_TESTS_LINK_FRAMEWORK=0 \
+            -DPUGIXML_VERSION=150 \
+            -DUNIT_TESTS_WITH_HTTP_CONNEXIONS=1 \
+            -D__BYTE_ORDER=__LITTLE_ENDIAN \
+            -D__GNUC__=11 \
+            -D__GNUC_MINOR__=4 \
+            -D__GNUC_PATCHLEVEL_=0 \
+            -D__STDC__ \
+            -D__cplusplus=201103 \
+            -D__linux__ \
+            -UNDEBUG \
+            -DHAS_ORTHANC_EXCEPTION=1 \
+            \
+            ../../OrthancFramework/Sources \
+            ../../OrthancFramework/UnitTestsSources \
+            ../../OrthancServer/Plugins/Engine \
+            ../../OrthancServer/Plugins/Include \
+            ../../OrthancServer/Sources \
+            ../../OrthancServer/UnitTestsSources \
+            ../../OrthancServer/Plugins/Samples/Common \
+            ../../OrthancServer/Plugins/Samples/ConnectivityChecks \
+            ../../OrthancServer/Plugins/Samples/DelayedDeletion \
+            ../../OrthancServer/Plugins/Samples/Housekeeper \
+            ../../OrthancServer/Plugins/Samples/ModalityWorklists \
+            ../../OrthancServer/Plugins/Samples/MultitenantDicom \
+            \
+            2>&1
--- a/OrthancServer/Sources/OrthancConfiguration.cpp	Fri Feb 21 15:12:47 2025 +0100
+++ b/OrthancServer/Sources/OrthancConfiguration.cpp	Mon Feb 24 09:19:12 2025 +0100
@@ -46,6 +46,7 @@
 static const char* const DATABASE_SERVER_IDENTIFIER = "DatabaseServerIdentifier";
 static const char* const WARNINGS = "Warnings";
 static const char* const JOBS_ENGINE_THREADS_COUNT = "JobsEngineThreadsCount";
+static const char* const DICOM_LOSSY_TRANSCODING_QUALITY = "DicomLossyTranscodingQuality";
 
 namespace Orthanc
 {
@@ -700,6 +701,11 @@
     }
   }
 
+  unsigned int OrthancConfiguration::GetDicomLossyTranscodingQuality() const
+  {
+    return GetUnsignedIntegerParameter(DICOM_LOSSY_TRANSCODING_QUALITY, 90);
+  }
+
 
   bool OrthancConfiguration::SetupRegisteredUsers(HttpServer& httpServer) const
   {
--- a/OrthancServer/Sources/OrthancConfiguration.h	Fri Feb 21 15:12:47 2025 +0100
+++ b/OrthancServer/Sources/OrthancConfiguration.h	Mon Feb 24 09:19:12 2025 +0100
@@ -197,6 +197,8 @@
 
     void GetListOfOrthancPeers(std::set<std::string>& target) const;
 
+    unsigned int GetDicomLossyTranscodingQuality() const;
+    
     // Returns "true" iff. at least one user is registered
     bool SetupRegisteredUsers(HttpServer& httpServer) const;
 
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestAnonymizeModify.cpp	Fri Feb 21 15:12:47 2025 +0100
+++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestAnonymizeModify.cpp	Mon Feb 24 09:19:12 2025 +0100
@@ -58,6 +58,8 @@
 static const char* const SERIES = "Series";
 static const char* const TAGS = "Tags";
 static const char* const TRANSCODE = "Transcode";
+static const char* const LOSSY_QUALITY = "LossyQuality";
+
 
 
 namespace Orthanc
@@ -88,6 +90,10 @@
       .SetRequestField(TRANSCODE, RestApiCallDocumentation::Type_String,
                        "Transcode the DICOM instances to the provided DICOM transfer syntax: "
                        "https://orthanc.uclouvain.be/book/faq/transcoding.html", false)
+      .SetRequestField(LOSSY_QUALITY, RestApiCallDocumentation::Type_Number,
+                        "If transcoding to a lossy transfer syntax, this entry defines the quality "
+                        "as an integer between 1 and 100.  If not provided, the value is defined "
+                        "by the \"DicomLossyTranscodingQuality\" configuration. (new in v1.12.7)", false)
       .SetRequestField(FORCE, RestApiCallDocumentation::Type_Boolean,
                        "Allow the modification of tags related to DICOM identifiers, at the risk of "
                        "breaking the DICOM model of the real world", false)
@@ -116,6 +122,10 @@
       .SetRequestField(TRANSCODE, RestApiCallDocumentation::Type_String,
                        "Transcode the DICOM instances to the provided DICOM transfer syntax: "
                        "https://orthanc.uclouvain.be/book/faq/transcoding.html", false)
+      .SetRequestField(LOSSY_QUALITY, RestApiCallDocumentation::Type_Number,
+                        "If transcoding to a lossy transfer syntax, this entry defines the quality "
+                        "as an integer between 1 and 100.  If not provided, the value is defined "
+                        "by the \"DicomLossyTranscodingQuality\" configuration. (new in v1.12.7)", false)
       .SetRequestField(FORCE, RestApiCallDocumentation::Type_Boolean,
                        "Allow the modification of tags related to DICOM identifiers, at the risk of "
                        "breaking the DICOM model of the real world", false)
@@ -196,7 +206,8 @@
   static void AnonymizeOrModifyInstance(DicomModification& modification,
                                         RestApiPostCall& call,
                                         bool transcode,
-                                        DicomTransferSyntax targetSyntax)
+                                        DicomTransferSyntax targetSyntax,
+                                        unsigned int lossyQuality)
   {
     ServerContext& context = OrthancRestApi::GetContext(call);
     std::string id = call.GetUriComponent("id", "");
@@ -220,7 +231,7 @@
       std::set<DicomTransferSyntax> s;
       s.insert(targetSyntax);
       
-      if (context.Transcode(transcoded, source, s, true))
+      if (context.Transcode(transcoded, source, s, true, lossyQuality))
       {      
         call.GetOutput().AnswerBuffer(transcoded.GetBufferData(),
                                       transcoded.GetBufferSize(), MimeType_Dicom);
@@ -259,6 +270,20 @@
     }
   }
 
+  static unsigned int GetLossyQuality(const Json::Value& request)
+  {
+    unsigned int lossyQuality;
+    {
+      OrthancConfiguration::ReaderLock lock;
+      lossyQuality = lock.GetConfiguration().GetDicomLossyTranscodingQuality();
+    }
+
+    if (request.isMember(LOSSY_QUALITY)) 
+    {
+      lossyQuality = SerializationToolbox::ReadUnsignedInteger(request, LOSSY_QUALITY);
+    }
+    return lossyQuality;
+}
 
   static void ModifyInstance(RestApiPostCall& call)
   {
@@ -286,11 +311,11 @@
     if (request.isMember(TRANSCODE))
     {
       std::string s = SerializationToolbox::ReadString(request, TRANSCODE);
-      
+
       DicomTransferSyntax syntax;
       if (LookupTransferSyntax(syntax, s))
       {
-        AnonymizeOrModifyInstance(modification, call, true, syntax);
+        AnonymizeOrModifyInstance(modification, call, true, syntax, GetLossyQuality(request));
       }
       else
       {
@@ -300,7 +325,7 @@
     else
     {
       AnonymizeOrModifyInstance(modification, call, false /* no transcoding */,
-                                DicomTransferSyntax_LittleEndianImplicit /* unused */);
+                                DicomTransferSyntax_LittleEndianImplicit /* unused */, 0 /* unused */);
     }
   }
 
@@ -326,8 +351,25 @@
     Json::Value request;
     ParseAnonymizationRequest(request, modification, call);
 
-    AnonymizeOrModifyInstance(modification, call, false /* no transcoding */,
-                              DicomTransferSyntax_LittleEndianImplicit /* unused */);
+    if (request.isMember(TRANSCODE))
+    {
+      std::string s = SerializationToolbox::ReadString(request, TRANSCODE);
+
+      DicomTransferSyntax syntax;
+      if (LookupTransferSyntax(syntax, s))
+      {
+        AnonymizeOrModifyInstance(modification, call, true, syntax, GetLossyQuality(request));
+      }
+      else
+      {
+        throw OrthancException(ErrorCode_ParameterOutOfRange, "Unknown transfer syntax: " + s);
+      }
+    }
+    else
+    {
+      AnonymizeOrModifyInstance(modification, call, false /* no transcoding */,
+                                DicomTransferSyntax_LittleEndianImplicit /* unused */, 0 /* unused */);
+    }
   }
 
 
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestArchive.cpp	Fri Feb 21 15:12:47 2025 +0100
+++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestArchive.cpp	Mon Feb 24 09:19:12 2025 +0100
@@ -42,6 +42,13 @@
   static const char* const KEY_RESOURCES = "Resources";
   static const char* const KEY_EXTENDED = "Extended";
   static const char* const KEY_TRANSCODE = "Transcode";
+  static const char* const KEY_LOSSY_QUALITY = "LossyQuality";
+  static const char* const KEY_FILENAME = "Filename";
+  
+  static const char* const GET_TRANSCODE = "transcode";
+  static const char* const GET_LOSSY_QUALITY = "lossy-quality";
+  static const char* const GET_FILENAME = "filename";
+  static const char* const GET_RESOURCES = "resources";
 
   static const char* const CONFIG_LOADER_THREADS = "ZipLoaderThreads";
 
@@ -113,10 +120,13 @@
                                bool& extended,               /* out */
                                bool& transcode,              /* out */
                                DicomTransferSyntax& syntax,  /* out */
+                               unsigned int& lossyQuality,   /* out */
                                int& priority,                /* out */
                                unsigned int& loaderThreads,  /* out */
+                               std::string& filename,        /* out */
                                const Json::Value& body,      /* in */
-                               const bool defaultExtended    /* in */)
+                               const bool defaultExtended    /* in */,
+                               const std::string& defaultFilename /* in */)
   {
     synchronous = OrthancRestApi::IsSynchronousJobRequest
       (true /* synchronous by default */, body);
@@ -138,12 +148,32 @@
     {
       transcode = true;
       syntax = Orthanc::GetTransferSyntax(SerializationToolbox::ReadString(body, KEY_TRANSCODE));
+      
+      {
+        OrthancConfiguration::ReaderLock lock;
+        lossyQuality = lock.GetConfiguration().GetDicomLossyTranscodingQuality();
+      }
+
+      if (body.isMember(KEY_LOSSY_QUALITY)) 
+      {
+        lossyQuality = SerializationToolbox::ReadUnsignedInteger(body, KEY_LOSSY_QUALITY);
+      }
     }
     else
     {
       transcode = false;
     }
 
+    if (body.type() == Json::objectValue &&
+      body.isMember(KEY_FILENAME) && body[KEY_FILENAME].isString())
+    {
+      filename = body[KEY_FILENAME].asString();
+    }
+    else
+    {
+      filename = defaultFilename;
+    }
+
     {
       OrthancConfiguration::ReaderLock lock;
       loaderThreads = lock.GetConfiguration().GetUnsignedIntegerParameter(CONFIG_LOADER_THREADS, 0);  // New in Orthanc 1.10.0
@@ -487,6 +517,7 @@
     }
     else
     {
+      job->SetFilename(filename);
       OrthancRestApi::SubmitGenericJob(output, context, job.release(), false, priority);
     }
   }
@@ -508,6 +539,13 @@
       .SetRequestField(KEY_TRANSCODE, RestApiCallDocumentation::Type_String,
                        "If present, the DICOM files in the archive will be transcoded to the provided "
                        "transfer syntax: https://orthanc.uclouvain.be/book/faq/transcoding.html", false)
+      .SetRequestField(KEY_LOSSY_QUALITY, RestApiCallDocumentation::Type_Number,
+                        "If transcoding to a lossy transfer syntax, this entry defines the quality "
+                        "as an integer between 1 and 100.  If not provided, the value is defined "
+                        "by the \"DicomLossyTranscodingQuality\" configuration. (new in v1.12.7)", false)
+      .SetRequestField(KEY_FILENAME, RestApiCallDocumentation::Type_String,
+                        "Filename to set in the \"Content-Disposition\" HTTP header "
+                        "(including file extension)", false)
       .SetRequestField("Priority", RestApiCallDocumentation::Type_Number,
                        "In asynchronous mode, the priority of the job. The higher the value, the higher the priority.", false)
       .AddAnswerType(MimeType_Zip, "In synchronous mode, the ZIP file containing the archive")
@@ -539,8 +577,11 @@
         .SetSummary("Create " + m)
         .SetDescription("Create a " + m + " containing the DICOM resources (patients, studies, series, or instances) "
                         "whose Orthanc identifiers are provided in the body")
-        .SetRequestField("Resources", RestApiCallDocumentation::Type_JsonListOfStrings,
-                         "The list of Orthanc identifiers of interest.", false);
+        .SetRequestField(KEY_RESOURCES, RestApiCallDocumentation::Type_JsonListOfStrings,
+                         "The list of Orthanc identifiers of interest.", false)
+        .SetRequestField(KEY_FILENAME, RestApiCallDocumentation::Type_String,
+                         "Filename to set in the \"Content-Disposition\" HTTP header "
+                         "(including file extension)", false);
       return;
     }
 
@@ -553,8 +594,11 @@
       DicomTransferSyntax transferSyntax;
       int priority;
       unsigned int loaderThreads;
-      GetJobParameters(synchronous, extended, transcode, transferSyntax,
-                       priority, loaderThreads, body, DEFAULT_IS_EXTENDED);
+      std::string filename;
+      unsigned int lossyQuality;
+
+      GetJobParameters(synchronous, extended, transcode, transferSyntax, lossyQuality,
+                       priority, loaderThreads, filename, body, DEFAULT_IS_EXTENDED, "Archive.zip");
       
       std::unique_ptr<ArchiveJob> job(new ArchiveJob(context, IS_MEDIA, extended, ResourceType_Patient));
       AddResourcesOfInterest(*job, body);
@@ -562,11 +606,12 @@
       if (transcode)
       {
         job->SetTranscode(transferSyntax);
+        job->SetLossyQuality(lossyQuality);
       }
       
       job->SetLoaderThreads(loaderThreads);
 
-      SubmitJob(call.GetOutput(), context, job, priority, synchronous, "Archive.zip");
+      SubmitJob(call.GetOutput(), context, job, priority, synchronous, filename);
     }
     else
     {
@@ -574,15 +619,23 @@
                              "Expected a list of resources to archive in the body");
     }
   }
-  
+
+  static unsigned int GetLossyQuality(const RestApiGetCall& call)
+  {
+    unsigned int lossyQuality;
+
+    OrthancConfiguration::ReaderLock lock;
+    lossyQuality = lock.GetConfiguration().GetDicomLossyTranscodingQuality();
+    lossyQuality = call.GetUnsignedInteger32Argument(GET_LOSSY_QUALITY, lossyQuality);
+    
+    return lossyQuality;
+  }
+
 
   template <bool IS_MEDIA,
             bool DEFAULT_IS_EXTENDED  /* only makes sense for media (i.e. not ZIP archives) */ >
   static void CreateBatchGet(RestApiGetCall& call)
   {
-    static const char* const TRANSCODE = "transcode";
-    static const char* const RESOURCES = "resources";
-
     if (call.IsDocumentation())
     {
       std::string m = (IS_MEDIA ? "DICOMDIR media" : "ZIP archive");
@@ -591,10 +644,17 @@
         .SetSummary("Create " + m)
         .SetDescription("Create a " + m + " containing the DICOM resources (patients, studies, series, or instances) "
                         "whose Orthanc identifiers are provided in the 'resources' argument")
-        .SetHttpGetArgument(TRANSCODE, RestApiCallDocumentation::Type_String,
+        .SetHttpGetArgument(GET_FILENAME, RestApiCallDocumentation::Type_String,
+                          "Filename to set in the \"Content-Disposition\" HTTP header "
+                          "(including file extension)", false)
+        .SetHttpGetArgument(GET_TRANSCODE, RestApiCallDocumentation::Type_String,
                             "If present, the DICOM files will be transcoded to the provided "
                             "transfer syntax: https://orthanc.uclouvain.be/book/faq/transcoding.html", false)
-        .SetHttpGetArgument(RESOURCES, RestApiCallDocumentation::Type_String,
+        .SetHttpGetArgument(GET_LOSSY_QUALITY, RestApiCallDocumentation::Type_Number,
+                            "If transcoding to a lossy transfer syntax, this entry defines the quality "
+                            "as an integer between 1 and 100.  If not provided, the value is defined "
+                            "by the \"DicomLossyTranscodingQuality\" configuration. (new in v1.12.7)", false)
+        .SetHttpGetArgument(GET_RESOURCES, RestApiCallDocumentation::Type_String,
                             "A comma separated list of Orthanc resource identifiers to include in the " + m + ".", true);
       return;
     }
@@ -602,27 +662,32 @@
     ServerContext& context = OrthancRestApi::GetContext(call);
     bool transcode = false;
     DicomTransferSyntax transferSyntax = DicomTransferSyntax_LittleEndianImplicit;  // Initialize variable to avoid warnings
+    unsigned int lossyQuality;
 
-    if (call.HasArgument(TRANSCODE))
+    if (call.HasArgument(GET_TRANSCODE))
     {
       transcode = true;
-      transferSyntax = GetTransferSyntax(call.GetArgument(TRANSCODE, ""));
+      transferSyntax = GetTransferSyntax(call.GetArgument(GET_TRANSCODE, ""));
+      lossyQuality = GetLossyQuality(call);
     }
     
-    if (!call.HasArgument(RESOURCES))
+    if (!call.HasArgument(GET_RESOURCES))
     {
-      throw OrthancException(Orthanc::ErrorCode_BadRequest, std::string("Missing ") + RESOURCES + " argument");
+      throw OrthancException(Orthanc::ErrorCode_BadRequest, std::string("Missing ") + GET_RESOURCES + " argument");
     }
 
     std::unique_ptr<ArchiveJob> job(new ArchiveJob(context, IS_MEDIA, DEFAULT_IS_EXTENDED, ResourceType_Patient));
-    AddResourcesOfInterestFromString(*job, call.GetArgument(RESOURCES, ""));
+    AddResourcesOfInterestFromString(*job, call.GetArgument(GET_RESOURCES, ""));
 
     if (transcode)
     {
       job->SetTranscode(transferSyntax);
+      job->SetLossyQuality(lossyQuality);
     }
 
-    SubmitJob(call.GetOutput(), context, job, 0, true, "Archive.zip");
+    const std::string filename = call.GetArgument(GET_FILENAME, "Archive.zip");  // New in Orthanc 1.12.7
+
+    SubmitJob(call.GetOutput(), context, job, 0, true, filename);
   }
 
 
@@ -630,8 +695,6 @@
             bool IS_MEDIA>
   static void CreateSingleGet(RestApiGetCall& call)
   {
-    static const char* const TRANSCODE = "transcode";
-    static const char* const FILENAME = "filename";
 
     if (call.IsDocumentation())
     {
@@ -646,12 +709,16 @@
                         "which might *not* be desirable to archive large amount of data, as it might "
                         "lead to network timeouts. Prefer the asynchronous version using `POST` method.")
         .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest")
-        .SetHttpGetArgument(FILENAME, RestApiCallDocumentation::Type_String,
+        .SetHttpGetArgument(GET_FILENAME, RestApiCallDocumentation::Type_String,
                             "Filename to set in the \"Content-Disposition\" HTTP header "
                             "(including file extension)", false)
-        .SetHttpGetArgument(TRANSCODE, RestApiCallDocumentation::Type_String,
+        .SetHttpGetArgument(GET_TRANSCODE, RestApiCallDocumentation::Type_String,
                             "If present, the DICOM files in the archive will be transcoded to the provided "
                             "transfer syntax: https://orthanc.uclouvain.be/book/faq/transcoding.html", false)
+        .SetHttpGetArgument(GET_LOSSY_QUALITY, RestApiCallDocumentation::Type_Number,
+                            "If transcoding to a lossy transfer syntax, this entry defines the quality "
+                            "as an integer between 1 and 100.  If not provided, the value is defined "
+                            "by the \"DicomLossyTranscodingQuality\" configuration. (new in v1.12.7)", false)
         .AddAnswerType(MimeType_Zip, "ZIP file containing the archive");
       if (IS_MEDIA)
       {
@@ -665,7 +732,7 @@
     ServerContext& context = OrthancRestApi::GetContext(call);
 
     const std::string id = call.GetUriComponent("id", "");
-    const std::string filename = call.GetArgument(FILENAME, id + ".zip");  // New in Orthanc 1.11.0
+    const std::string filename = call.GetArgument(GET_FILENAME, id + ".zip");  // New in Orthanc 1.11.0
 
     bool extended;
     if (IS_MEDIA)
@@ -680,9 +747,10 @@
     std::unique_ptr<ArchiveJob> job(new ArchiveJob(context, IS_MEDIA, extended, (LEVEL == ResourceType_Patient ? ResourceType_Patient : ResourceType_Study))); // use patient info from study except when exporting a patient
     job->AddResource(id, true, LEVEL);
 
-    if (call.HasArgument(TRANSCODE))
+    if (call.HasArgument(GET_TRANSCODE))
     {
-      job->SetTranscode(GetTransferSyntax(call.GetArgument(TRANSCODE, "")));
+      job->SetTranscode(GetTransferSyntax(call.GetArgument(GET_TRANSCODE, "")));
+      job->SetLossyQuality(GetLossyQuality(call));
     }
 
     {
@@ -726,8 +794,10 @@
       DicomTransferSyntax transferSyntax;
       int priority;
       unsigned int loaderThreads;
-      GetJobParameters(synchronous, extended, transcode, transferSyntax,
-                       priority, loaderThreads, body, false /* by default, not extented */);
+      std::string filename;
+      unsigned int lossyQuality;
+      GetJobParameters(synchronous, extended, transcode, transferSyntax, lossyQuality,
+                       priority, loaderThreads, filename, body, false /* by default, not extented */, id + ".zip");
       
       std::unique_ptr<ArchiveJob> job(new ArchiveJob(context, IS_MEDIA, extended, LEVEL));
       job->AddResource(id, true, LEVEL);
@@ -735,11 +805,12 @@
       if (transcode)
       {
         job->SetTranscode(transferSyntax);
+        job->SetLossyQuality(lossyQuality);
       }
 
       job->SetLoaderThreads(loaderThreads);
 
-      SubmitJob(call.GetOutput(), context, job, priority, synchronous, id + ".zip");
+      SubmitJob(call.GetOutput(), context, job, priority, synchronous, filename);
     }
     else
     {
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp	Fri Feb 21 15:12:47 2025 +0100
+++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp	Mon Feb 24 09:19:12 2025 +0100
@@ -156,7 +156,6 @@
 
     ResourceFinder finder(level, responseContent, context.GetFindStorageAccessMode(), context.GetIndex().HasFindSupport());
     finder.SetOrthancId(level, identifier);
-    finder.SetRetrieveMetadata(retrieveMetadata);
 
     return finder.ExecuteOneResource(target, context, format, retrieveMetadata);
   }
@@ -338,7 +337,9 @@
  
   static void GetInstanceFile(RestApiGetCall& call)
   {
-    static const char* const TRANSCODE = "transcode";
+    static const char* const GET_TRANSCODE = "transcode";
+    static const char* const GET_LOSSY_QUALITY = "lossy-quality";
+    static const char* const GET_FILENAME = "filename";
 
     if (call.IsDocumentation())
     {
@@ -348,9 +349,16 @@
         .SetDescription("Download one DICOM instance")
         .SetUriArgument("id", "Orthanc identifier of the DICOM instance of interest")
         .SetHttpHeader("Accept", "This HTTP header can be set to retrieve the DICOM instance in DICOMweb format")
-        .SetHttpGetArgument(TRANSCODE, RestApiCallDocumentation::Type_String,
+        .SetHttpGetArgument(GET_TRANSCODE, RestApiCallDocumentation::Type_String,
                             "If present, the DICOM file will be transcoded to the provided "
                             "transfer syntax: https://orthanc.uclouvain.be/book/faq/transcoding.html", false)
+        .SetHttpGetArgument(GET_LOSSY_QUALITY, RestApiCallDocumentation::Type_Number,
+                            "If transcoding to a lossy transfer syntax, this entry defines the quality "
+                            "as an integer between 1 and 100.  If not provided, the value is defined "
+                            "by the \"DicomLossyTranscodingQuality\" configuration. (new in v1.12.7)", false)
+        .SetHttpGetArgument(GET_FILENAME, RestApiCallDocumentation::Type_String,
+                              "Filename to set in the \"Content-Disposition\" HTTP header "
+                              "(including file extension)", false)
         .AddAnswerType(MimeType_Dicom, "The DICOM instance")
         .AddAnswerType(MimeType_DicomWebJson, "The DICOM instance, in DICOMweb JSON format")
         .AddAnswerType(MimeType_DicomWebXml, "The DICOM instance, in DICOMweb XML format");
@@ -399,15 +407,40 @@
       }
     }
 
-    if (call.HasArgument(TRANSCODE))
+    const std::string filename = call.GetArgument(GET_FILENAME, publicId + ".dcm");  // New in Orthanc 1.12.7
+
+    if (call.HasArgument(GET_TRANSCODE))
     {
+      unsigned int lossyQuality;
+      unsigned int defaultLossyQuality;
+      {
+        OrthancConfiguration::ReaderLock lock;
+        defaultLossyQuality = lock.GetConfiguration().GetDicomLossyTranscodingQuality();
+      }
+      lossyQuality = call.GetUnsignedInteger32Argument(GET_LOSSY_QUALITY, defaultLossyQuality);
+
       std::string source;
       std::string attachmentId;
       std::string transcoded;
       context.ReadDicom(source, attachmentId, publicId);
 
-      if (context.TranscodeWithCache(transcoded, source, publicId, attachmentId, GetTransferSyntax(call.GetArgument(TRANSCODE, ""))))
+      if (lossyQuality != defaultLossyQuality) // we can't use the cache if the lossy quality is not the default one
       {
+        IDicomTranscoder::DicomImage targetImage;
+        IDicomTranscoder::DicomImage sourceImage;
+        sourceImage.SetExternalBuffer(source);
+        std::set<DicomTransferSyntax> allowedSyntaxes;
+        allowedSyntaxes.insert(GetTransferSyntax(call.GetArgument(GET_TRANSCODE, "")));
+
+        if (context.Transcode(targetImage, sourceImage, allowedSyntaxes, true, lossyQuality))
+        {
+          call.GetOutput().SetContentFilename(filename.c_str());
+          call.GetOutput().AnswerBuffer(targetImage.GetBufferData(), targetImage.GetBufferSize(), MimeType_Dicom);
+        }
+      }
+      else if (context.TranscodeWithCache(transcoded, source, publicId, attachmentId, GetTransferSyntax(call.GetArgument(GET_TRANSCODE, ""))))
+      {
+        call.GetOutput().SetContentFilename(filename.c_str());
         call.GetOutput().AnswerBuffer(transcoded, MimeType_Dicom);
       }
     }
@@ -422,7 +455,7 @@
       }
       else
       {
-        context.AnswerAttachment(call.GetOutput(), info);
+        context.AnswerAttachment(call.GetOutput(), info, filename);
       }
     }
   }
@@ -850,7 +883,7 @@
             .SetTag("Instances")
             .SetUriArgument("id", "Orthanc identifier of the DICOM instance of interest")
             .SetHttpGetArgument("quality", RestApiCallDocumentation::Type_Number, "Quality for JPEG images (between 1 and 100, defaults to 90)", false)
-            .SetHttpGetArgument("returnUnsupportedImage", RestApiCallDocumentation::Type_Boolean, "Returns an unsupported.png placeholder image if unable to provide the image instead of returning a 415 HTTP error (defaults to false)", false)
+            .SetHttpGetArgument("returnUnsupportedImage", RestApiCallDocumentation::Type_Boolean, "Returns an unsupported.png placeholder image if unable to provide the image instead of returning a 415 HTTP error (value is true if option is present)", false)
             .SetHttpHeader("Accept", "Format of the resulting image. Can be `image/png` (default), `image/jpeg` or `image/x-portable-arbitrarymap`")
             .AddAnswerType(MimeType_Png, "PNG image")
             .AddAnswerType(MimeType_Jpeg, "JPEG image")
@@ -913,7 +946,8 @@
           }
           else
           {
-            if (call.HasArgument("returnUnsupportedImage"))
+            // if present and not explicitely set to false
+            if (call.HasArgument("returnUnsupportedImage") && call.GetBooleanArgument("returnUnsupportedImage", true))
             {
               std::string root = "";
               for (size_t i = 1; i < call.GetFullUri().size(); i++)
@@ -2297,6 +2331,8 @@
   {
     const ResourceType level = GetResourceTypeFromUri(call);
 
+    static const char* const GET_FILENAME = "filename";
+
     if (call.IsDocumentation())
     {
       std::string r = GetResourceTypeText(level, false /* plural */, false /* upper case */);
@@ -2307,11 +2343,15 @@
                         std::string(uncompress ? "" : ". The attachment will not be decompressed if `StorageCompression` is `true`."))
         .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest")
         .SetUriArgument("name", "The name of the attachment, or its index (cf. `UserContentType` configuration option)")
+        .SetHttpGetArgument(GET_FILENAME, RestApiCallDocumentation::Type_String,
+          "Filename to set in the \"Content-Disposition\" HTTP header "
+          "(including file extension)", false)
         .AddAnswerType(MimeType_Binary, "The attachment")
         .SetAnswerHeader("ETag", "Revision of the attachment, to be used in further `PUT` or `DELETE` operations")
         .SetHttpHeader("If-None-Match", "Optional revision of the attachment, to check if its content has changed")
         .SetHttpHeader("Content-Range", "Optional content range to access part of the attachment (new in Orthanc 1.12.5)");
-      return;
+    
+        return;
     }
 
     ServerContext& context = OrthancRestApi::GetContext(call);
@@ -2342,6 +2382,8 @@
         return;
       }
 
+      const std::string filename = call.GetArgument(GET_FILENAME, info.GetUuid());  // New in Orthanc 1.12.7
+
       if (hasRangeHeader)
       {
         std::string fragment;
@@ -2355,7 +2397,7 @@
       else if (uncompress ||
                info.GetCompressionType() == CompressionType_None)
       {
-        context.AnswerAttachment(call.GetOutput(), info);
+        context.AnswerAttachment(call.GetOutput(), info, filename);
       }
       else
       {
@@ -3614,6 +3656,14 @@
 
     Json::Value answer;
     finder.Execute(answer, OrthancRestApi::GetContext(call), format, false /* no "Metadata" field */);
+    
+    // Given the data model, if there are no children, it means there is no parent.
+    // https://discourse.orthanc-server.org/t/patients-id-instances-quirk/5498
+    if (answer.size() == 0) 
+    {
+      throw OrthancException(ErrorCode_UnknownResource);
+    }
+
     call.GetOutput().AnswerJson(answer);
   }
 
--- a/OrthancServer/Sources/ResourceFinder.cpp	Fri Feb 21 15:12:47 2025 +0100
+++ b/OrthancServer/Sources/ResourceFinder.cpp	Mon Feb 24 09:19:12 2025 +0100
@@ -1148,6 +1148,15 @@
           Toolbox::RemoveSets(remainingRequestedTags, metaTagsToRemove);
         }
 
+        // if SOPClassUID has been requested, we might still find it at metadata level (useful e.g. for StoneViewer)
+        std::string sopClassUidFromMetadata;
+        if (resource.GetLevel() == ResourceType_Instance &&
+            remainingRequestedTags.find(DICOM_TAG_SOP_CLASS_UID) != remainingRequestedTags.end() && 
+            resource.LookupMetadata(sopClassUidFromMetadata, ResourceType_Instance, MetadataType_Instance_SopClassUid))
+        {
+          outRequestedTags.SetValue(DICOM_TAG_SOP_CLASS_UID, sopClassUidFromMetadata, false);
+          remainingRequestedTags.erase(DICOM_TAG_SOP_CLASS_UID);
+        }
 
         if (!remainingRequestedTags.empty() && 
             !DicomMap::HasOnlyComputedTags(remainingRequestedTags)) // if the only remaining tags are computed tags, it is worthless to read them from disk
--- a/OrthancServer/Sources/Search/ISqlLookupFormatter.cpp	Fri Feb 21 15:12:47 2025 +0100
+++ b/OrthancServer/Sources/Search/ISqlLookupFormatter.cpp	Mon Feb 24 09:19:12 2025 +0100
@@ -373,7 +373,7 @@
     std::string arg = "order" + boost::lexical_cast<std::string>(index);
 
 
-    target = " INNER JOIN Metadata " + arg + " ON " + arg + ".id = " + FormatLevel(requestLevel) +
+    target = " LEFT JOIN Metadata " + arg + " ON " + arg + ".id = " + FormatLevel(requestLevel) +
              ".internalId AND " + arg + ".type = " +
              boost::lexical_cast<std::string>(metadata);
   }
--- a/OrthancServer/Sources/ServerContext.cpp	Fri Feb 21 15:12:47 2025 +0100
+++ b/OrthancServer/Sources/ServerContext.cpp	Mon Feb 24 09:19:12 2025 +0100
@@ -393,8 +393,6 @@
   {
     try
     {
-      unsigned int lossyQuality;
-
       {
         OrthancConfiguration::ReaderLock lock;
 
@@ -425,7 +423,6 @@
         // New options in Orthanc 1.7.0
         transcodeDicomProtocol_ = lock.GetConfiguration().GetBooleanParameter("TranscodeDicomProtocol", true);
         builtinDecoderTranscoderOrder_ = StringToBuiltinDecoderTranscoderOrder(lock.GetConfiguration().GetStringParameter("BuiltinDecoderTranscoderOrder", "After"));
-        lossyQuality = lock.GetConfiguration().GetUnsignedIntegerParameter("DicomLossyTranscodingQuality", 90);
 
         std::string s;
         if (lock.GetConfiguration().LookupStringParameter(s, "IngestTranscoding"))
@@ -502,6 +499,8 @@
         SetAcceptedSopClasses(acceptedSopClasses, rejectedSopClasses);
 
         defaultDicomRetrieveMethod_ = StringToRetrieveMethod(lock.GetConfiguration().GetStringParameter("DicomDefaultRetrieveMethod", "C-MOVE"));
+
+        dynamic_cast<DcmtkTranscoder&>(*dcmtkTranscoder_).SetDefaultLossyQuality(lock.GetConfiguration().GetDicomLossyTranscodingQuality());
       }
 
       jobsEngine_.SetThreadSleep(unitTesting ? 20 : 200);
@@ -516,8 +515,6 @@
 #else
       LOG(INFO) << "Your platform does not support malloc_trim(), not starting the memory trimming thread";
 #endif
-      
-      dynamic_cast<DcmtkTranscoder&>(*dcmtkTranscoder_).SetLossyQuality(lossyQuality);
     }
     catch (OrthancException&)
     {
@@ -992,10 +989,11 @@
 
   
   void ServerContext::AnswerAttachment(RestApiOutput& output,
-                                       const FileInfo& attachment)
+                                       const FileInfo& attachment,
+                                       const std::string& filename)
   {
     StorageAccessor accessor(area_, storageCache_, GetMetricsRegistry());
-    accessor.AnswerFile(output, attachment, GetFileContentMime(attachment.GetContentType()));
+    accessor.AnswerFile(output, attachment, GetFileContentMime(attachment.GetContentType()), filename);
   }
 
 
@@ -1988,15 +1986,31 @@
     return true;
   }
 
-
   bool ServerContext::Transcode(DicomImage& target,
                                 DicomImage& source /* in, "GetParsed()" possibly modified */,
                                 const std::set<DicomTransferSyntax>& allowedSyntaxes,
                                 bool allowNewSopInstanceUid)
   {
+    unsigned int lossyQuality;
+
+    {
+      OrthancConfiguration::ReaderLock lock;
+      lossyQuality = lock.GetConfiguration().GetDicomLossyTranscodingQuality();
+    }
+
+    return Transcode(target, source, allowedSyntaxes, allowNewSopInstanceUid, lossyQuality);
+  }
+
+
+  bool ServerContext::Transcode(DicomImage& target,
+                                DicomImage& source /* in, "GetParsed()" possibly modified */,
+                                const std::set<DicomTransferSyntax>& allowedSyntaxes,
+                                bool allowNewSopInstanceUid,
+                                unsigned int lossyQuality)
+  {
     if (builtinDecoderTranscoderOrder_ == BuiltinDecoderTranscoderOrder_Before)
     {
-      if (dcmtkTranscoder_->Transcode(target, source, allowedSyntaxes, allowNewSopInstanceUid))
+      if (dcmtkTranscoder_->Transcode(target, source, allowedSyntaxes, allowNewSopInstanceUid, lossyQuality))
       {
         return true;
       }
@@ -2006,7 +2020,7 @@
     if (HasPlugins() &&
         GetPlugins().HasCustomTranscoder())
     {
-      if (GetPlugins().Transcode(target, source, allowedSyntaxes, allowNewSopInstanceUid))
+      if (GetPlugins().Transcode(target, source, allowedSyntaxes, allowNewSopInstanceUid))  // TODO: pass lossyQuality to plugins -> needs a new plugin interface
       {
         return true;
       }
@@ -2020,7 +2034,7 @@
 
     if (builtinDecoderTranscoderOrder_ == BuiltinDecoderTranscoderOrder_After)
     {
-      return dcmtkTranscoder_->Transcode(target, source, allowedSyntaxes, allowNewSopInstanceUid);
+      return dcmtkTranscoder_->Transcode(target, source, allowedSyntaxes, allowNewSopInstanceUid, lossyQuality);
     }
     else
     {
--- a/OrthancServer/Sources/ServerContext.h	Fri Feb 21 15:12:47 2025 +0100
+++ b/OrthancServer/Sources/ServerContext.h	Mon Feb 24 09:19:12 2025 +0100
@@ -363,7 +363,8 @@
                                   bool isReconstruct = false);
 
     void AnswerAttachment(RestApiOutput& output,
-                          const FileInfo& fileInfo);
+                          const FileInfo& fileInfo,
+                          const std::string& filename);
 
     void ChangeAttachmentCompression(ResourceType level,
                                      const std::string& resourceId,
@@ -578,6 +579,12 @@
                            DicomImage& source /* in, "GetParsed()" possibly modified */,
                            const std::set<DicomTransferSyntax>& allowedSyntaxes,
                            bool allowNewSopInstanceUid) ORTHANC_OVERRIDE;
+    
+    virtual bool Transcode(DicomImage& target,
+                           DicomImage& source /* in, "GetParsed()" possibly modified */,
+                           const std::set<DicomTransferSyntax>& allowedSyntaxes,
+                           bool allowNewSopInstanceUid,
+                           unsigned int lossyQuality) ORTHANC_OVERRIDE;
 
     virtual bool TranscodeWithCache(std::string& target,
                                     const std::string& source,
--- a/OrthancServer/Sources/ServerJobs/ArchiveJob.cpp	Fri Feb 21 15:12:47 2025 +0100
+++ b/OrthancServer/Sources/ServerJobs/ArchiveJob.cpp	Mon Feb 24 09:19:12 2025 +0100
@@ -88,11 +88,13 @@
     ServerContext&                        context_;
     bool                                  transcode_;
     DicomTransferSyntax                   transferSyntax_;
+    unsigned int                          lossyQuality_;
   public:
-    explicit InstanceLoader(ServerContext& context, bool transcode, DicomTransferSyntax transferSyntax)
+    explicit InstanceLoader(ServerContext& context, bool transcode, DicomTransferSyntax transferSyntax, unsigned int lossyQuality)
     : context_(context),
       transcode_(transcode),
-      transferSyntax_(transferSyntax)
+      transferSyntax_(transferSyntax),
+      lossyQuality_(lossyQuality)
     {
     }
 
@@ -114,7 +116,7 @@
         IDicomTranscoder::DicomImage source, transcoded;
         source.SetExternalBuffer(sourceBuffer);
 
-        if (context_.Transcode(transcoded, source, syntaxes, true /* allow new SOP instance UID */))
+        if (context_.Transcode(transcoded, source, syntaxes, true /* allow new SOP instance UID */, lossyQuality_))
         {
           transcodedBuffer.assign(reinterpret_cast<const char*>(transcoded.GetBufferData()), transcoded.GetBufferSize());
           return true;
@@ -139,8 +141,8 @@
   class ArchiveJob::SynchronousInstanceLoader : public ArchiveJob::InstanceLoader
   {
   public:
-    explicit SynchronousInstanceLoader(ServerContext& context, bool transcode, DicomTransferSyntax transferSyntax)
-    : InstanceLoader(context, transcode, transferSyntax)
+    explicit SynchronousInstanceLoader(ServerContext& context, bool transcode, DicomTransferSyntax transferSyntax, unsigned int lossyQuality)
+    : InstanceLoader(context, transcode, transferSyntax, lossyQuality)
     {
     }
 
@@ -192,8 +194,8 @@
 
 
   public:
-    ThreadedInstanceLoader(ServerContext& context, size_t threadCount, bool transcode, DicomTransferSyntax transferSyntax)
-    : InstanceLoader(context, transcode, transferSyntax),
+    ThreadedInstanceLoader(ServerContext& context, size_t threadCount, bool transcode, DicomTransferSyntax transferSyntax, unsigned int lossyQuality)
+    : InstanceLoader(context, transcode, transferSyntax, lossyQuality),
       availableInstancesSemaphore_(0),
       bufferedInstancesSemaphore_(3*threadCount)
     {
@@ -1243,12 +1245,14 @@
     archive_(new ArchiveIndex(GetArchiveResourceType(jobLevel))),  // get patient Info from this level
     isMedia_(isMedia),
     enableExtendedSopClass_(enableExtendedSopClass),
+    filename_("archive.zip"),
     currentStep_(0),
     instancesCount_(0),
     uncompressedSize_(0),
     archiveSize_(0),
     transcode_(false),
     transferSyntax_(DicomTransferSyntax_LittleEndianImplicit),
+    lossyQuality_(100),
     loaderThreads_(0)
   {
   }
@@ -1296,7 +1300,18 @@
     }
   }
 
-  
+  void ArchiveJob::SetFilename(const std::string& filename)
+  {
+    if (writer_.get() != NULL)   // Already started
+    {
+      throw OrthancException(ErrorCode_BadSequenceOfCalls);
+    }
+    else
+    {
+      filename_ = filename;
+    }
+  }
+
   void ArchiveJob::AddResource(const std::string& publicId,
                                bool mustExist,
                                ResourceType expectedType)
@@ -1337,7 +1352,20 @@
     }
   }
 
-  
+
+  void ArchiveJob::SetLossyQuality(unsigned int lossyQuality)
+  {
+    if (writer_.get() != NULL)   // Already started
+    {
+      throw OrthancException(ErrorCode_BadSequenceOfCalls);
+    }
+    else
+    {
+      lossyQuality_ = lossyQuality;
+    }
+  }
+
+
   void ArchiveJob::SetLoaderThreads(unsigned int loaderThreads)
   {
     if (writer_.get() != NULL)   // Already started
@@ -1363,11 +1391,11 @@
     if (loaderThreads_ == 0)
     {
       // default behaviour before loaderThreads was introducted in 1.10.0
-      instanceLoader_.reset(new SynchronousInstanceLoader(context_, transcode_, transferSyntax_));
+      instanceLoader_.reset(new SynchronousInstanceLoader(context_, transcode_, transferSyntax_, lossyQuality_));
     }
     else
     {
-      instanceLoader_.reset(new ThreadedInstanceLoader(context_, loaderThreads_, transcode_, transferSyntax_));
+      instanceLoader_.reset(new ThreadedInstanceLoader(context_, loaderThreads_, transcode_, transferSyntax_, lossyQuality_));
     }
 
     if (writer_.get() != NULL)
@@ -1582,7 +1610,7 @@
         const DynamicTemporaryFile& f = dynamic_cast<DynamicTemporaryFile&>(accessor.GetItem());
         f.GetFile().Read(output);
         mime = MimeType_Zip;
-        filename = "archive.zip";
+        filename = filename_;
         return true;
       }
       else
--- a/OrthancServer/Sources/ServerJobs/ArchiveJob.h	Fri Feb 21 15:12:47 2025 +0100
+++ b/OrthancServer/Sources/ServerJobs/ArchiveJob.h	Mon Feb 24 09:19:12 2025 +0100
@@ -57,6 +57,7 @@
     bool                                  isMedia_;
     bool                                  enableExtendedSopClass_;
     std::string                           description_;
+    std::string                           filename_;
 
     boost::shared_ptr<ZipWriterIterator>  writer_;
     size_t                                currentStep_;
@@ -68,6 +69,7 @@
     // New in Orthanc 1.7.0
     bool                 transcode_;
     DicomTransferSyntax  transferSyntax_;
+    unsigned int         lossyQuality_;
 
     // New in Orthanc 1.10.0
     unsigned int         loaderThreads_;
@@ -91,12 +93,21 @@
       return description_;
     }
 
+    void SetFilename(const std::string& filename);
+
+    const std::string& GetFilename() const
+    {
+      return filename_;
+    }
+
     void AddResource(const std::string& publicId,
                      bool mustExist,
                      ResourceType expectedType);
 
     void SetTranscode(DicomTransferSyntax transferSyntax);
 
+    void SetLossyQuality(unsigned int lossyQuality);
+
     void SetLoaderThreads(unsigned int loaderThreads);
 
     virtual void Reset() ORTHANC_OVERRIDE;
--- a/TODO	Fri Feb 21 15:12:47 2025 +0100
+++ b/TODO	Mon Feb 24 09:19:12 2025 +0100
@@ -49,6 +49,8 @@
   - Also consider the use case of an Orthanc cluster that is being scaled-down just after one Orthanc instance
     has received a few instances -> we can not only check for missing stable events at startup since no Orthanc will start.  
     We would need to maintain the list of "unstable" resources in DB instead of memory only.
+  - Also check the PG plugin and its new table InvalidChildCounts, with a timestamp there, we can detect for
+    how long a study has not been modified !
 * In prometheus metrics, implement Histograms or Exponential Histograms to measure durations.  Right now, we only provide
   "average" durations that are not very relevant
   (https://opentelemetry.io/docs/specs/otel/metrics/data-model/#histogram)
@@ -197,6 +199,7 @@
 * Support other Transfer Syntaxes in the Worklist plugin:
   https://discourse.orthanc-server.org/t/could-you-please-create-an-option-to-set-the-transfer-syntax-in-the-worklist-plugin-currently-little-endian-explicit-is-fixed/4871
 
+
 ---------
 Long-term
 ---------
@@ -250,6 +253,8 @@
   Alternative option 4: Catch out-of-memory exceptions at quite high level in HTTPHandlers and DICOM receivers
     and implement retries.  After a few retries, fail for good and return "out-of-resources".
     Would be interesting to log these errors and count them in the prometheus metrics.
+  Note: I'm (maybe naively) thinking that you only need the beginning of the file to get the DICOM tags to be stored 
+  in the database, so maybe the rest of the file could be "streamed" directly to disk and not kept in memory?
 * To investigate: usage of mapped_file (not only in the indexer plugin): 
   https://discourse.orthanc-server.org/t/patch-for-orthanc-indexer-plugin-crashing-on-big-non-dicom-files/3849/7